diff --git a/.ci/bwcVersions b/.ci/bwcVersions index ddc36af48d674..6488fda153e91 100644 --- a/.ci/bwcVersions +++ b/.ci/bwcVersions @@ -38,4 +38,40 @@ BWC_VERSION: - "1.2.5" - "1.3.0" - "1.3.1" - - "1.4.0" + - "1.3.2" + - "1.3.3" + - "1.3.4" + - "1.3.5" + - "1.3.6" + - "1.3.7" + - "1.3.8" + - "1.3.9" + - "1.3.10" + - "1.3.11" + - "1.3.12" + - "1.3.13" + - "1.3.14" + - "2.0.0" + - "2.0.1" + - "2.0.2" + - "2.1.0" + - "2.1.1" + - "2.2.0" + - "2.2.1" + - "2.2.2" + - "2.3.0" + - "2.3.1" + - "2.4.0" + - "2.4.1" + - "2.4.2" + - "2.5.0" + - "2.5.1" + - "2.6.0" + - "2.6.1" + - "2.7.0" + - "2.7.1" + - "2.8.0" + - "2.8.1" + - "2.9.0" + - "2.9.1" + - "2.10.0" diff --git a/.ci/dockerOnLinuxExclusions b/.ci/dockerOnLinuxExclusions index 8061248a87df4..dd518c7043e2f 100644 --- a/.ci/dockerOnLinuxExclusions +++ b/.ci/dockerOnLinuxExclusions @@ -12,4 +12,4 @@ ol-7.7 sles-12.3 # older version used in Vagrant image sles-12.5 sles-15.1 -sles-15.2 \ No newline at end of file +sles-15.2 diff --git a/.ci/documentation/issue.md b/.ci/documentation/issue.md new file mode 100644 index 0000000000000..c34905605b2f6 --- /dev/null +++ b/.ci/documentation/issue.md @@ -0,0 +1,11 @@ +**Is your feature request related to a problem?** +A new feature has been added. + +**What solution would you like?** +Document the usage of the new feature. + +**What alternatives have you considered?** +N/A + +**Do you have any additional context?** +See please diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5dfa9099866d1..4fa118e8486f1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1 @@ -# This should match the owning team set up in https://github.com/orgs/opensearch-project/teams -* @opensearch-project/opensearch-core - +* @reta @anasalkouz @andrross @Bukhtawar @CEHENKLE @dblock @gbbafna @setiah @kartg @kotwanikunal @mch2 @nknize @owaiskazi19 @peternied @Rishikesh1159 @ryanbogan @saratvemulapalli @shwetathareja @dreamer-89 @tlfeng @VachaShah @dbwiddis @sachinpkale @sohami @msfroh diff --git a/.github/ISSUE_TEMPLATE/failed_check.md b/.github/ISSUE_TEMPLATE/failed_check.md new file mode 100644 index 0000000000000..71508c9f5bd43 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/failed_check.md @@ -0,0 +1,8 @@ +--- +title: '[AUTOCUT] Gradle Check Failure on push to {{ env.branch_name }}' +labels: '>test-failure, bug, autocut' +--- + +Gradle check has failed on push of your commit {{ env.pr_from_sha }}. +Please examine the workflow log {{ env.workflow_url }}. +Is the failure [a flaky test](https://github.com/opensearch-project/OpenSearch/blob/main/DEVELOPER_GUIDE.md#flaky-tests) unrelated to your change? diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ca972d1b242e3..1f4d309e44a4c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -599,6 +599,11 @@ updates: package-ecosystem: gradle schedule: interval: weekly + - directory: /plugins/crypto-kms/ + open-pull-requests-limit: 1 + package-ecosystem: gradle + schedule: + interval: weekly - directory: /qa/ open-pull-requests-limit: 1 package-ecosystem: gradle diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d7981b5113972..3db0555e7da76 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,15 +1,17 @@ ### Description [Describe what this change achieves] - + ### Issues Resolved [List any issues this PR will resolve] - + ### Check List - [ ] New functionality includes testing. - [ ] All tests pass - [ ] New functionality has been documented. - [ ] New functionality has javadoc added -- [ ] Commits are signed per the DCO using --signoff +- [ ] Commits are signed per the DCO using --signoff +- [ ] Commit changes are listed out in CHANGELOG.md file (See: [Changelog](../blob/main/CONTRIBUTING.md#changelog)) +- [ ] GitHub issue/PR created in [OpenSearch documentation repo](https://github.com/opensearch-project/documentation-website) for the required public documentation changes (#[Issue/PR number]) By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml new file mode 100644 index 0000000000000..252cbda1392f8 --- /dev/null +++ b/.github/workflows/auto-release.yml @@ -0,0 +1,29 @@ +name: Releases + +on: + push: + tags: + - '*' + +jobs: + + build: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: GitHub App token + id: github_app_token + uses: tibdex/github-app-token@v2.1.0 + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + installation_id: 22958780 + - name: Get tag + id: tag + uses: dawidd6/action-get-tag@v1 + - uses: actions/checkout@v4 + - uses: ncipollo/release-action@v1 + with: + github_token: ${{ steps.github_app_token.outputs.token }} + bodyFile: release-notes/opensearch.release-notes-${{steps.tag.outputs.tag}}.md diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index e47d8d88c0243..e190867fbaab5 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -7,22 +7,33 @@ on: jobs: backport: + name: Backport runs-on: ubuntu-latest + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport') + ) + ) permissions: contents: write pull-requests: write - name: Backport steps: - name: GitHub App token id: github_app_token - uses: tibdex/github-app-token@v1.5.0 + uses: tibdex/github-app-token@v2.1.0 with: app_id: ${{ secrets.APP_ID }} private_key: ${{ secrets.APP_PRIVATE_KEY }} installation_id: 22958780 - name: Backport - uses: VachaShah/backport@v1.1.4 + uses: VachaShah/backport@v2.1.0 with: github_token: ${{ steps.github_app_token.outputs.token }} - branch_name: backport/backport-${{ github.event.number }} + head_template: backport/backport-<%= number %>-to-<%= base %> diff --git a/.github/workflows/changelog_verifier.yml b/.github/workflows/changelog_verifier.yml new file mode 100644 index 0000000000000..9456fbf8b4ca0 --- /dev/null +++ b/.github/workflows/changelog_verifier.yml @@ -0,0 +1,19 @@ +name: "Changelog Verifier" +on: + pull_request: + types: [opened, edited, review_requested, synchronize, reopened, ready_for_review, labeled, unlabeled] + +jobs: + # Enforces the update of a changelog file on every pull request + verify-changelog: + if: github.repository == 'opensearch-project/OpenSearch' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.event.pull_request.head.sha }} + + - uses: dangoslen/changelog-enforcer@v3 + with: + skipLabels: "autocut, skip-changelog" diff --git a/.github/workflows/check-compatibility.yml b/.github/workflows/check-compatibility.yml new file mode 100644 index 0000000000000..d93f7e73b91e7 --- /dev/null +++ b/.github/workflows/check-compatibility.yml @@ -0,0 +1,69 @@ +--- +name: Check Compatibility + +on: + pull_request_target + +jobs: + check-compatibility: + if: github.repository == 'opensearch-project/OpenSearch' + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Increase swapfile + run: | + sudo swapoff -a + sudo fallocate -l 10G /swapfile + sudo chmod 600 /swapfile + sudo mkswap /swapfile + sudo swapon /swapfile + sudo swapon --show + + - name: Run compatibility task + run: ./gradlew checkCompatibility -i | tee $HOME/gradlew-check.out + + - name: Get results + run: | + echo '## Compatibility status:' > "${{ github.workspace }}/results.txt" + echo "Checks if related components are compatible with change $(git rev-parse --short HEAD)" >> "${{ github.workspace }}/results.txt" + echo "### Incompatible components" >> "${{ github.workspace }}/results.txt" && grep -e 'Incompatible component' $HOME/gradlew-check.out | sed -e 's/Incompatible component: \[\(.*\)\]/- \1/' >> "${{ github.workspace }}/results.txt" + echo "### Skipped components" >> "${{ github.workspace }}/results.txt" && grep -e 'Skipped component' $HOME/gradlew-check.out | sed -e 's/Skipped component: \[\(.*\)\]/- \1/' >> "${{ github.workspace }}/results.txt" + echo "### Compatible components" >> "${{ github.workspace }}/results.txt" && grep -e 'Compatible component' $HOME/gradlew-check.out | sed -e 's/Compatible component: \[\(.*\)\]/- \1/' >> "${{ github.workspace }}/results.txt" + + - name: Upload results + uses: actions/upload-artifact@v3 + with: + name: results.txt + path: ${{ github.workspace }}/results.txt + + add-comment: + needs: [check-compatibility] + permissions: + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Download results + uses: actions/download-artifact@v3 + with: + name: results.txt + + - name: Find Comment + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.number }} + comment-author: 'github-actions[bot]' + body-includes: 'Compatibility status:' + + - name: Add comment on the PR + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.number }} + body-path: results.txt + edit-mode: replace diff --git a/.github/workflows/copy-linked-issue-labels.yml b/.github/workflows/copy-linked-issue-labels.yml new file mode 100644 index 0000000000000..33b5e92dc10da --- /dev/null +++ b/.github/workflows/copy-linked-issue-labels.yml @@ -0,0 +1,21 @@ +name: Copy labels from linked issues +on: + pull_request_target: + types: [opened, edited, review_requested, synchronize, reopened, ready_for_review] + +jobs: + copy-issue-labels: + if: github.repository == 'opensearch-project/OpenSearch' + runs-on: ubuntu-latest + permissions: + issues: read + contents: read + pull-requests: write + steps: + - name: copy-issue-labels + uses: michalvankodev/copy-issue-labels@v1.3.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + labels-to-exclude: | + untriaged + triaged diff --git a/.github/workflows/create-documentation-issue.yml b/.github/workflows/create-documentation-issue.yml new file mode 100644 index 0000000000000..df63847f8afca --- /dev/null +++ b/.github/workflows/create-documentation-issue.yml @@ -0,0 +1,41 @@ +name: Create Documentation Issue +on: + pull_request: + types: + - labeled +env: + PR_NUMBER: ${{ github.event.number }} + +jobs: + create-issue: + if: ${{ github.event.label.name == 'needs-documentation' }} + runs-on: ubuntu-latest + name: Create Documentation Issue + steps: + - name: GitHub App token + id: github_app_token + uses: tibdex/github-app-token@v2.1.0 + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + installation_id: 22958780 + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Edit the issue template + run: | + echo "https://github.com/opensearch-project/OpenSearch/pull/${{ env.PR_NUMBER }}." >> ./ci/documentation/issue.md + + - name: Create Issue From File + id: create-issue + uses: peter-evans/create-issue-from-file@v4 + with: + title: Add documentation related to new feature + content-filepath: ./ci/documentation/issue.md + labels: documentation + repository: opensearch-project/documentation-website + token: ${{ steps.github_app_token.outputs.token }} + + - name: Print Issue + run: echo Created related documentation issue ${{ steps.create-issue.outputs.issue-number }} diff --git a/.github/workflows/delete_backport_branch.yml b/.github/workflows/delete_backport_branch.yml index d654df6b40257..387a124b8cb6a 100644 --- a/.github/workflows/delete_backport_branch.yml +++ b/.github/workflows/delete_backport_branch.yml @@ -12,4 +12,4 @@ jobs: - name: Delete merged branch uses: SvanBoxel/delete-merged-branch@main env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dependabot_pr.yml b/.github/workflows/dependabot_pr.yml index 2ac904bf4ccf7..add38e306e60b 100644 --- a/.github/workflows/dependabot_pr.yml +++ b/.github/workflows/dependabot_pr.yml @@ -11,14 +11,14 @@ jobs: steps: - name: GitHub App token id: github_app_token - uses: tibdex/github-app-token@v1.5.0 + uses: tibdex/github-app-token@v2.1.0 with: app_id: ${{ secrets.APP_ID }} private_key: ${{ secrets.APP_PRIVATE_KEY }} installation_id: 22958780 - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: token: ${{ steps.github_app_token.outputs.token }} @@ -47,3 +47,17 @@ jobs: commit_user_name: dependabot[bot] commit_user_email: support@github.com commit_options: '--signoff' + + - name: Update the changelog + uses: dangoslen/dependabot-changelog-helper@v1 + with: + version: 'Unreleased' + + - name: Commit the changes + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: "Update changelog" + branch: ${{ github.head_ref }} + commit_user_name: dependabot[bot] + commit_user_email: support@github.com + commit_options: '--signoff' diff --git a/.github/workflows/gradle-check.yml b/.github/workflows/gradle-check.yml new file mode 100644 index 0000000000000..2d4e86a7a3522 --- /dev/null +++ b/.github/workflows/gradle-check.yml @@ -0,0 +1,137 @@ +name: Gradle Check (Jenkins) +on: + push: + branches-ignore: + - 'backport/**' + - 'create-pull-request/**' + - 'dependabot/**' + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + gradle-check: + if: github.repository == 'opensearch-project/OpenSearch' + permissions: + contents: read # to fetch code (actions/checkout) + pull-requests: write # to create or update comment (peter-evans/create-or-update-comment) + issues: write # To create an issue if check fails on push. + + runs-on: ubuntu-latest + timeout-minutes: 130 + steps: + - name: Checkout OpenSearch repo + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Setup environment variables (PR) + if: github.event_name == 'pull_request_target' + run: | + echo "pr_from_sha=$(jq --raw-output .pull_request.head.sha $GITHUB_EVENT_PATH)" >> $GITHUB_ENV + echo "pr_from_clone_url=$(jq --raw-output .pull_request.head.repo.clone_url $GITHUB_EVENT_PATH)" >> $GITHUB_ENV + echo "pr_to_clone_url=$(jq --raw-output .pull_request.base.repo.clone_url $GITHUB_EVENT_PATH)" >> $GITHUB_ENV + echo "pr_title=$(jq --raw-output .pull_request.title $GITHUB_EVENT_PATH)" >> $GITHUB_ENV + echo "pr_number=$(jq --raw-output .pull_request.number $GITHUB_EVENT_PATH)" >> $GITHUB_ENV + + - name: Setup environment variables (Push) + if: github.event_name == 'push' + run: | + repo_url="https://github.com/opensearch-project/OpenSearch" + ref_id=$(git rev-parse HEAD) + branch_name=$(git rev-parse --abbrev-ref HEAD) + echo "branch_name=$branch_name" >> $GITHUB_ENV + echo "pr_from_sha=$ref_id" >> $GITHUB_ENV + echo "pr_from_clone_url=$repo_url" >> $GITHUB_ENV + echo "pr_to_clone_url=$repo_url" >> $GITHUB_ENV + echo "pr_title=Push trigger $branch_name $ref_id $repo_url" >> $GITHUB_ENV + echo "pr_number=Null" >> $GITHUB_ENV + + - name: Checkout opensearch-build repo + uses: actions/checkout@v4 + with: + repository: opensearch-project/opensearch-build + ref: main + path: opensearch-build + + - name: Trigger jenkins workflow to run gradle check + run: | + set -e + set -o pipefail + bash opensearch-build/scripts/gradle/gradle-check.sh ${{ secrets.JENKINS_GRADLE_CHECK_GENERIC_WEBHOOK_TOKEN }} | tee -a gradle-check.log + + - name: Setup Result Status + if: always() + run: | + WORKFLOW_URL=`cat gradle-check.log | grep 'WORKFLOW_URL' | awk '{print $2}'` + RESULT=`cat gradle-check.log | grep 'Result:' | awk '{print $2}'` + echo "workflow_url=$WORKFLOW_URL" >> $GITHUB_ENV + echo "result=$RESULT" >> $GITHUB_ENV + + - name: Upload Coverage Report + if: success() + uses: codecov/codecov-action@v3 + with: + files: ./codeCoverage.xml + + - name: Create Comment Success + if: ${{ github.event_name == 'pull_request_target' && success() && env.result == 'SUCCESS' }} + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ env.pr_number }} + body: | + ### Gradle Check (Jenkins) Run Completed with: + * **RESULT:** ${{ env.result }} :white_check_mark: + * **URL:** ${{ env.workflow_url }} + * **CommitID:** ${{ env.pr_from_sha }} + + - name: Extract Test Failure + if: ${{ github.event_name == 'pull_request_target' && env.result != 'SUCCESS' }} + run: | + TEST_FAILURES=`curl -s "${{ env.workflow_url }}/testReport/api/json?tree=suites\[cases\[status,className,name\]\]" | jq -r '.. | objects | select(.status=="FAILED",.status=="REGRESSION") | (.className + "." + .name)' | uniq -c | sort -n -r | head -n 10` + if [[ "$TEST_FAILURES" != "" ]] + then + echo "test_failures<> $GITHUB_ENV + echo "" >> $GITHUB_ENV + echo "* **TEST FAILURES:**" >> $GITHUB_ENV + echo '```' >> $GITHUB_ENV + echo "$TEST_FAILURES" >> $GITHUB_ENV + echo '```' >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + fi + + - name: Create Comment Flaky + if: ${{ github.event_name == 'pull_request_target' && success() && env.result != 'SUCCESS' }} + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ env.pr_number }} + body: | + ### Gradle Check (Jenkins) Run Completed with: + * **RESULT:** ${{ env.result }} :grey_exclamation: ${{ env.test_failures }} + * **URL:** ${{ env.workflow_url }} + * **CommitID:** ${{ env.pr_from_sha }} + Please review all [flaky tests](https://github.com/opensearch-project/OpenSearch/blob/main/DEVELOPER_GUIDE.md#flaky-tests) that succeeded after retry and create an issue if one does not already exist to track the flaky failure. + + - name: Create Comment Failure + if: ${{ github.event_name == 'pull_request_target' && failure() }} + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ env.pr_number }} + body: | + ### Gradle Check (Jenkins) Run Completed with: + * **RESULT:** ${{ env.result }} :x: ${{ env.test_failures }} + * **URL:** ${{ env.workflow_url }} + * **CommitID:** ${{ env.pr_from_sha }} + Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. + Is the failure [a flaky test](https://github.com/opensearch-project/OpenSearch/blob/main/DEVELOPER_GUIDE.md#flaky-tests) unrelated to your change? + + - name: Create Issue On Push Failure + if: ${{ github.event_name == 'push' && failure() }} + uses: dblock/create-a-github-issue@v3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + assignees: ${{ github.event.head_commit.author.username }}, ${{ github.triggering_actor }} + filename: .github/ISSUE_TEMPLATE/failed_check.md diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index 21fb7ab9086ee..ca026f530b4af 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -2,18 +2,20 @@ name: Link Checker on: schedule: - cron: '0 0 * * *' +permissions: + contents: read # to fetch code (actions/checkout) jobs: linkchecker: if: github.repository == 'opensearch-project/OpenSearch' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: lychee Link Checker id: lychee - uses: lycheeverse/lychee-action@v1.2.0 + uses: lycheeverse/lychee-action@v1.8.0 with: args: --accept=200,403,429 --exclude-mail **/*.html **/*.md **/*.txt **/*.json --exclude-file .lychee.excludes fail: true env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/lucene-snapshots.yml b/.github/workflows/lucene-snapshots.yml new file mode 100644 index 0000000000000..76981276fe085 --- /dev/null +++ b/.github/workflows/lucene-snapshots.yml @@ -0,0 +1,60 @@ +# This workflow will check out, build, and publish snapshots of lucene. + +name: OpenSearch Lucene snapshots + +on: + workflow_dispatch: + # Inputs the workflow accepts. + inputs: + ref: + description: + required: false + default: 'main' + +jobs: + publish-snapshots: + if: github.repository == 'opensearch-project/OpenSearch' + runs-on: ubuntu-latest + # These permissions are needed to interact with GitHub's OIDC Token endpoint. + permissions: + id-token: write + contents: read + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' + + - name: Checkout Lucene + uses: actions/checkout@v4 + with: + repository: 'apache/lucene' + path: lucene + ref: ${{ github.event.inputs.ref }} + + - name: Set hash + working-directory: ./lucene + run: | + echo "REVISION=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + id: version + + - name: Initialize gradle settings + working-directory: ./lucene + run: ./gradlew localSettings + + - name: Publish Lucene to local maven repo. + working-directory: ./lucene + run: ./gradlew publishJarsPublicationToMavenLocal -Pversion.suffix=snapshot-${{ steps.version.outputs.REVISION }} + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ secrets.LUCENE_SNAPSHOTS_ROLE }} + aws-region: us-west-2 + + - name: Copy files to S3 with the aws CLI. + run: | + aws s3 cp ~/.m2/repository/org/apache/lucene/ s3://${{ secrets.LUCENE_SNAPSHOTS_BUCKET }}/snapshots/lucene/org/apache/lucene/ --recursive --no-progress diff --git a/.github/workflows/precommit.yml b/.github/workflows/precommit.yml index 9860be4159b83..f4622859916c7 100644 --- a/.github/workflows/precommit.yml +++ b/.github/workflows/precommit.yml @@ -1,16 +1,30 @@ -name: Gradle Precommit +name: Gradle Precommit and Assemble on: [pull_request] jobs: precommit: - runs-on: ubuntu-latest + if: github.repository == 'opensearch-project/OpenSearch' + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: java-version: 11 - distribution: adopt - - name: Run Gradle + distribution: temurin + cache: gradle + - name: Run Gradle (precommit) run: | - ./gradlew precommit --parallel + ./gradlew javadoc precommit --parallel + - name: Setup docker (missing on MacOS) + if: runner.os == 'macos' + run: | + brew install docker + colima start + sudo ln -sf $HOME/.colima/default/docker.sock /var/run/docker.sock + - name: Run Gradle (assemble) + run: | + ./gradlew assemble --parallel diff --git a/.github/workflows/publish-maven-snapshots.yml b/.github/workflows/publish-maven-snapshots.yml new file mode 100644 index 0000000000000..8c08df269a999 --- /dev/null +++ b/.github/workflows/publish-maven-snapshots.yml @@ -0,0 +1,40 @@ +name: Publish snapshots to maven + +on: + workflow_dispatch: + push: + branches: + - main + - '[0-9]+.[0-9]+' + - '[0-9]+.x' + +jobs: + build-and-publish-snapshots: + if: github.repository == 'opensearch-project/OpenSearch' + runs-on: ubuntu-latest + + permissions: + id-token: write + contents: write + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: adopt + java-version: 17 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + role-to-assume: ${{ secrets.PUBLISH_SNAPSHOTS_ROLE }} + aws-region: us-east-1 + + - name: Publish snapshots to maven + run: | + export SONATYPE_USERNAME=$(aws secretsmanager get-secret-value --secret-id maven-snapshots-username --query SecretString --output text) + export SONATYPE_PASSWORD=$(aws secretsmanager get-secret-value --secret-id maven-snapshots-password --query SecretString --output text) + echo "::add-mask::$SONATYPE_USERNAME" + echo "::add-mask::$SONATYPE_PASSWORD" + ./gradlew publishNebulaPublicationToSnapshotsRepository diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index b42e7c4f2f317..a20c671c137b2 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -5,19 +5,21 @@ on: tags: - '*.*.*' -jobs: +permissions: {} +jobs: build: + if: github.repository == 'opensearch-project/OpenSearch' runs-on: ubuntu-latest steps: - name: GitHub App token id: github_app_token - uses: tibdex/github-app-token@v1.5.0 + uses: tibdex/github-app-token@v2.1.0 with: app_id: ${{ secrets.APP_ID }} private_key: ${{ secrets.APP_PRIVATE_KEY }} installation_id: 22958780 - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Fetch Tag and Version Information run: | TAG=$(echo "${GITHUB_REF#refs/*/}") @@ -29,7 +31,11 @@ jobs: CURRENT_VERSION_ARRAY[2]=$((CURRENT_VERSION_ARRAY[2]+1)) NEXT_VERSION=$(IFS=. ; echo "${CURRENT_VERSION_ARRAY[*]:0:3}") NEXT_VERSION_UNDERSCORE=$(IFS=_ ; echo "V_${CURRENT_VERSION_ARRAY[*]:0:3}") - NEXT_VERSION_ID=$(IFS=0 ; echo "${CURRENT_VERSION_ARRAY[*]:0:3}99") + if [[ ${#CURRENT_VERSION_ARRAY[2]} -gt 1 ]]; then + NEXT_VERSION_ID="${CURRENT_VERSION_ARRAY[0]:0:3}0${CURRENT_VERSION_ARRAY[1]:0:3}${CURRENT_VERSION_ARRAY[2]:0:3}99" + else + NEXT_VERSION_ID=$(IFS=0 ; echo "${CURRENT_VERSION_ARRAY[*]:0:3}99") + fi echo "TAG=$TAG" >> $GITHUB_ENV echo "BASE=$BASE" >> $GITHUB_ENV echo "BASE_X=$BASE_X" >> $GITHUB_ENV @@ -38,7 +44,7 @@ jobs: echo "NEXT_VERSION=$NEXT_VERSION" >> $GITHUB_ENV echo "NEXT_VERSION_UNDERSCORE=$NEXT_VERSION_UNDERSCORE" >> $GITHUB_ENV echo "NEXT_VERSION_ID=$NEXT_VERSION_ID" >> $GITHUB_ENV - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: ref: ${{ env.BASE }} token: ${{ steps.github_app_token.outputs.token }} @@ -49,22 +55,25 @@ jobs: echo " - \"$CURRENT_VERSION\"" >> .ci/bwcVersions sed -i "s/opensearch = $CURRENT_VERSION/opensearch = $NEXT_VERSION/g" buildSrc/version.properties echo Adding $NEXT_VERSION_UNDERSCORE after $CURRENT_VERSION_UNDERSCORE - sed -i "s/public static final Version $CURRENT_VERSION_UNDERSCORE = new Version(\([[:digit:]]\+\)\(.*\));/\0\n public static final Version $NEXT_VERSION_UNDERSCORE = new Version($NEXT_VERSION_ID\2);/g" server/src/main/java/org/opensearch/Version.java - sed -i "s/CURRENT = $CURRENT_VERSION_UNDERSCORE;/CURRENT = $NEXT_VERSION_UNDERSCORE;/g" server/src/main/java/org/opensearch/Version.java + sed -i "s/public static final Version $CURRENT_VERSION_UNDERSCORE = new Version(\([[:digit:]]\+\)\(.*\));/\0\n public static final Version $NEXT_VERSION_UNDERSCORE = new Version($NEXT_VERSION_ID\2);/g" libs/core/src/main/java/org/opensearch/Version.java + sed -i "s/CURRENT = $CURRENT_VERSION_UNDERSCORE;/CURRENT = $NEXT_VERSION_UNDERSCORE;/g" libs/core/src/main/java/org/opensearch/Version.java - name: Create Pull Request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v5 with: token: ${{ steps.github_app_token.outputs.token }} base: ${{ env.BASE }} branch: 'create-pull-request/patch-${{ env.BASE }}' - commit-message: Incremented version to ${{ env.NEXT_VERSION }} + commit-message: Increment version to ${{ env.NEXT_VERSION }} + signoff: true delete-branch: true - title: '[AUTO] Incremented version to ${{ env.NEXT_VERSION }}.' + labels: | + autocut + title: '[AUTO] Increment version to ${{ env.NEXT_VERSION }}.' body: | I've noticed that a new tag ${{ env.TAG }} was pushed, and incremented the version from ${{ env.CURRENT_VERSION }} to ${{ env.NEXT_VERSION }}. - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: ref: ${{ env.BASE_X }} token: ${{ steps.github_app_token.outputs.token }} @@ -74,21 +83,24 @@ jobs: echo Adding bwc version $NEXT_VERSION after $CURRENT_VERSION sed -i "s/- \"$CURRENT_VERSION\"/\0\n - \"$NEXT_VERSION\"/g" .ci/bwcVersions echo Adding $NEXT_VERSION_UNDERSCORE after $CURRENT_VERSION_UNDERSCORE - sed -i "s/public static final Version $CURRENT_VERSION_UNDERSCORE = new Version(\([[:digit:]]\+\)\(.*\));/\0\n public static final Version $NEXT_VERSION_UNDERSCORE = new Version($NEXT_VERSION_ID\2);/g" server/src/main/java/org/opensearch/Version.java + sed -i "s/public static final Version $CURRENT_VERSION_UNDERSCORE = new Version(\([[:digit:]]\+\)\(.*\));/\0\n public static final Version $NEXT_VERSION_UNDERSCORE = new Version($NEXT_VERSION_ID\2);/g" libs/core/src/main/java/org/opensearch/Version.java - name: Create Pull Request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v5 with: token: ${{ steps.github_app_token.outputs.token }} base: ${{ env.BASE_X }} branch: 'create-pull-request/patch-${{ env.BASE_X }}' - commit-message: Added bwc version ${{ env.NEXT_VERSION }} + commit-message: Add bwc version ${{ env.NEXT_VERSION }} + signoff: true delete-branch: true - title: '[AUTO] [${{ env.BASE_X }}] Added bwc version ${{ env.NEXT_VERSION }}.' + labels: | + autocut + title: '[AUTO] [${{ env.BASE_X }}] Add bwc version ${{ env.NEXT_VERSION }}.' body: | I've noticed that a new tag ${{ env.TAG }} was pushed, and added a bwc version ${{ env.NEXT_VERSION }}. - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: ref: main token: ${{ steps.github_app_token.outputs.token }} @@ -98,16 +110,19 @@ jobs: echo Adding bwc version $NEXT_VERSION after $CURRENT_VERSION sed -i "s/- \"$CURRENT_VERSION\"/\0\n - \"$NEXT_VERSION\"/g" .ci/bwcVersions echo Adding $NEXT_VERSION_UNDERSCORE after $CURRENT_VERSION_UNDERSCORE - sed -i "s/public static final Version $CURRENT_VERSION_UNDERSCORE = new Version(\([[:digit:]]\+\)\(.*\));/\0\n public static final Version $NEXT_VERSION_UNDERSCORE = new Version($NEXT_VERSION_ID\2);/g" server/src/main/java/org/opensearch/Version.java + sed -i "s/public static final Version $CURRENT_VERSION_UNDERSCORE = new Version(\([[:digit:]]\+\)\(.*\));/\0\n public static final Version $NEXT_VERSION_UNDERSCORE = new Version($NEXT_VERSION_ID\2);/g" libs/core/src/main/java/org/opensearch/Version.java - name: Create Pull Request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v5 with: token: ${{ steps.github_app_token.outputs.token }} base: main branch: 'create-pull-request/patch-main' - commit-message: Added bwc version ${{ env.NEXT_VERSION }} + commit-message: Add bwc version ${{ env.NEXT_VERSION }} + signoff: true delete-branch: true - title: '[AUTO] [main] Added bwc version ${{ env.NEXT_VERSION }}.' + labels: | + autocut + title: '[AUTO] [main] Add bwc version ${{ env.NEXT_VERSION }}.' body: | I've noticed that a new tag ${{ env.TAG }} was pushed, and added a bwc version ${{ env.NEXT_VERSION }}. diff --git a/.github/workflows/wrapper.yml b/.github/workflows/wrapper.yml index d577699b66dc0..6dd48ca15eaa9 100644 --- a/.github/workflows/wrapper.yml +++ b/.github/workflows/wrapper.yml @@ -1,10 +1,11 @@ name: Validate Gradle Wrapper -on: [push, pull_request] +on: [pull_request] jobs: validate: name: Validate + if: github.repository == 'opensearch-project/OpenSearch' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: gradle/wrapper-validation-action@v1 \ No newline at end of file + - uses: actions/checkout@v4 + - uses: gradle/wrapper-validation-action@v1 diff --git a/.gitignore b/.gitignore index e2cb6d8d37a82..82914fb4fc1e7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,13 @@ out/ !.idea/runConfigurations/Debug_OpenSearch.xml !.idea/vcs.xml -# These files are generated in the main tree by IntelliJ +# These files are generated in the main tree by annotation processors benchmarks/src/main/generated/* +benchmarks/bin/* +benchmarks/build-eclipse-default/* +server/bin/* +server/build-eclipse-default/* +test/framework/build-eclipse-default/* # eclipse files .project @@ -56,3 +61,6 @@ testfixtures_shared/ # These are generated from .ci/jobs.t .ci/jobs/ + +# build files generated +doc-tools/missing-doclet/bin/ \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 5cf789707c58c..f090532aecfc2 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,8 +2,11 @@ - \ No newline at end of file + diff --git a/.whitesource b/.whitesource deleted file mode 100644 index 81bf84f97dd66..0000000000000 --- a/.whitesource +++ /dev/null @@ -1,15 +0,0 @@ -{ - "scanSettings": { - "configMode": "LOCAL", - "configExternalURL": "", - "projectToken": "", - "baseBranches": ["1.x"] - }, - "checkRunSettings": { - "vulnerableCheckRunConclusionLevel": "failure", - "displayMode": "diff" - }, - "issueSettings": { - "minSeverityLevel": "LOW" - } -} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000..89b3bdce85ed8 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,70 @@ +# CHANGELOG +All notable changes to this project are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). See the [CONTRIBUTING guide](./CONTRIBUTING.md#Changelog) for instructions on how to add changelog entries. + +## [Unreleased 2.x] +### Added +- Add coordinator level stats for search latency ([#8386](https://github.com/opensearch-project/OpenSearch/issues/8386)) +- Add metrics for thread_pool task wait time ([#9681](https://github.com/opensearch-project/OpenSearch/pull/9681)) +- Async blob read support for S3 plugin ([#9694](https://github.com/opensearch-project/OpenSearch/pull/9694)) +- [Telemetry-Otel] Added support for OtlpGrpcSpanExporter exporter ([#9666](https://github.com/opensearch-project/OpenSearch/pull/9666)) +- Async blob read support for encrypted containers ([#10131](https://github.com/opensearch-project/OpenSearch/pull/10131)) +- Implement Visitor Design pattern in QueryBuilder to enable the capability to traverse through the complex QueryBuilder tree. ([#10110](https://github.com/opensearch-project/OpenSearch/pull/10110)) +- Add capability to restrict async durability mode for remote indexes ([#10189](https://github.com/opensearch-project/OpenSearch/pull/10189)) +- Add Doc Status Counter for Indexing Engine ([#4562](https://github.com/opensearch-project/OpenSearch/issues/4562)) +- Add unreferenced file cleanup count to merge stats ([#10204](https://github.com/opensearch-project/OpenSearch/pull/10204)) +- Configurable merge policy for index with an option to choose from LogByteSize and Tiered merge policy ([#9992](https://github.com/opensearch-project/OpenSearch/pull/9992)) +- [Remote Store] Add support to restrict creation & deletion if system repository and mutation of immutable settings of system repository ([#9839](https://github.com/opensearch-project/OpenSearch/pull/9839)) +### Dependencies +- Bump JNA version from 5.5 to 5.13 ([#9963](https://github.com/opensearch-project/OpenSearch/pull/9963)) +- Bump `peter-evans/create-or-update-comment` from 2 to 3 ([#9575](https://github.com/opensearch-project/OpenSearch/pull/9575)) +- Bump `actions/checkout` from 2 to 4 ([#9968](https://github.com/opensearch-project/OpenSearch/pull/9968)) +- Bump OpenTelemetry from 1.26.0 to 1.30.1 ([#9950](https://github.com/opensearch-project/OpenSearch/pull/9950)) +- Bump `org.apache.commons:commons-compress` from 1.23.0 to 1.24.0 ([#9973, #9972](https://github.com/opensearch-project/OpenSearch/pull/9973, https://github.com/opensearch-project/OpenSearch/pull/9972)) +- Bump `com.google.cloud:google-cloud-core-http` from 2.21.1 to 2.23.0 ([#9971](https://github.com/opensearch-project/OpenSearch/pull/9971)) +- Bump `mockito` from 5.4.0 to 5.5.0 ([#10022](https://github.com/opensearch-project/OpenSearch/pull/10022)) +- Bump `bytebuddy` from 1.14.3 to 1.14.7 ([#10022](https://github.com/opensearch-project/OpenSearch/pull/10022)) +- Bump `com.zaxxer:SparseBitSet` from 1.2 to 1.3 ([#10098](https://github.com/opensearch-project/OpenSearch/pull/10098)) +- Bump `tibdex/github-app-token` from 1.5.0 to 2.1.0 ([#10125](https://github.com/opensearch-project/OpenSearch/pull/10125)) +- Bump `org.wiremock:wiremock-standalone` from 2.35.0 to 3.1.0 ([#9752](https://github.com/opensearch-project/OpenSearch/pull/9752)) +- Bump `org.eclipse.jgit` from 6.5.0 to 6.7.0 ([#10147](https://github.com/opensearch-project/OpenSearch/pull/10147)) +- Bump `codecov/codecov-action` from 2 to 3 ([#10209](https://github.com/opensearch-project/OpenSearch/pull/10209)) +- Bump `com.google.http-client:google-http-client-jackson2` from 1.43.2 to 1.43.3 ([#10126](https://github.com/opensearch-project/OpenSearch/pull/10126)) +- Bump `org.xerial.snappy:snappy-java` from 1.1.10.3 to 1.1.10.5 ([#10206](https://github.com/opensearch-project/OpenSearch/pull/10206), [#10299](https://github.com/opensearch-project/OpenSearch/pull/10299)) +- Bump `org.bouncycastle:bcpkix-jdk15to18` from 1.75 to 1.76 ([10219](https://github.com/opensearch-project/OpenSearch/pull/10219))` +- Bump `org.bouncycastle:bcprov-jdk15to18` from 1.75 to 1.76 ([10219](https://github.com/opensearch-project/OpenSearch/pull/10219))` +- Bump `org.bouncycastle:bcmail-jdk15to18` from 1.75 to 1.76 ([10219](https://github.com/opensearch-project/OpenSearch/pull/10219))` +- Bump asm from 9.5 to 9.6 ([#10302](https://github.com/opensearch-project/OpenSearch/pull/10302)) +- Bump netty from 4.1.97.Final to 4.1.99.Final ([#10303](https://github.com/opensearch-project/OpenSearch/pull/10303)) +- Bump `peter-evans/create-pull-request` from 3 to 5 ([#10301](https://github.com/opensearch-project/OpenSearch/pull/10301)) + +### Changed +- Add instrumentation in rest and network layer. ([#9415](https://github.com/opensearch-project/OpenSearch/pull/9415)) +- Allow parameterization of tests with OpenSearchIntegTestCase.SuiteScopeTestCase annotation ([#9916](https://github.com/opensearch-project/OpenSearch/pull/9916)) +- Force merge with `only_expunge_deletes` honors max segment size ([#10036](https://github.com/opensearch-project/OpenSearch/pull/10036)) +- Add instrumentation in transport service. ([#10042](https://github.com/opensearch-project/OpenSearch/pull/10042)) +- [Tracing Framework] Add support for SpanKind. ([#10122](https://github.com/opensearch-project/OpenSearch/pull/10122)) +- Pass parent filter to inner query in nested query ([#10246](https://github.com/opensearch-project/OpenSearch/pull/10246)) +- Disable concurrent segment search when terminate_after is used ([#10200](https://github.com/opensearch-project/OpenSearch/pull/10200)) +- Add instrumentation in Inbound Handler. ([#100143](https://github.com/opensearch-project/OpenSearch/pull/10143)) +- Enable remote segment upload backpressure by default ([#10356](https://github.com/opensearch-project/OpenSearch/pull/10356)) +- [Remote Store] Add support to reload repository metadata inplace ([#9569](https://github.com/opensearch-project/OpenSearch/pull/9569)) +- [Metrics Framework] Add Metrics framework. ([#10241](https://github.com/opensearch-project/OpenSearch/pull/10241)) + +### Deprecated + +### Removed +- Remove spurious SGID bit on directories ([#9447](https://github.com/opensearch-project/OpenSearch/pull/9447)) + +### Fixed +- Fix ignore_missing parameter has no effect when using template snippet in rename ingest processor ([#9725](https://github.com/opensearch-project/OpenSearch/pull/9725)) +- Fix broken backward compatibility from 2.7 for IndexSorted field indices ([#10045](https://github.com/opensearch-project/OpenSearch/pull/10045)) +- Fix concurrent search NPE when track_total_hits, terminate_after and size=0 are used ([#10082](https://github.com/opensearch-project/OpenSearch/pull/10082)) +- Fix remove ingest processor handing ignore_missing parameter not correctly ([10089](https://github.com/opensearch-project/OpenSearch/pull/10089)) +- Fix registration and initialization of multiple extensions ([10256](https://github.com/opensearch-project/OpenSearch/pull/10256)) +- Fix circular dependency in Settings initialization ([10194](https://github.com/opensearch-project/OpenSearch/pull/10194)) + +### Security + +[Unreleased 2.x]: https://github.com/opensearch-project/OpenSearch/compare/2.11...2.x diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9a68cde006a6f..d379d78829318 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,7 @@ - [Documentation Changes](#documentation-changes) - [Contributing Code](#contributing-code) - [Developer Certificate of Origin](#developer-certificate-of-origin) + - [Changelog](#changelog) - [Review Process](#review-process) # Contributing to OpenSearch @@ -59,6 +60,16 @@ If you would like to contribute to the documentation, please do so in the [docum As with other types of contributions, the first step is to [**open an issue on GitHub**](https://github.com/opensearch-project/OpenSearch/issues/new/choose). Opening an issue before you make changes makes sure that someone else isn't already working on that particular problem. It also lets us all work together to find the right approach before you spend a bunch of time on a PR. So again, when in doubt, open an issue. +Additionally, here are a few guidelines to help you decide whether a particular feature should be included in OpenSearch. + +**Is your feature important to most users of OpenSearch?** + +If you believe that a feature is going to fulfill a need for most users of OpenSearch, then it belongs in OpenSearch. However, we don't want every feature built into the core server. If the feature requires additional permissions or brings in extra dependencies it should instead be included as a module in core. + +**Is your feature a common dependency across multiple plugins?** + +Does this feature contain functionality that cuts across multiple plugins? If so, this most likely belongs in OpenSearch as a core module or plugin. + Once you've opened an issue, check out our [Developer Guide](./DEVELOPER_GUIDE.md) for instructions on how to get started. ## Developer Certificate of Origin @@ -106,6 +117,42 @@ Signed-off-by: Jane Smith ``` You may type this line on your own when writing your commit messages. However, if your user.name and user.email are set in your git configs, you can use `-s` or `--signoff` to add the `Signed-off-by` line to the end of the commit message. +## Changelog + +OpenSearch maintains version specific changelog by enforcing a change to the ongoing [CHANGELOG](CHANGELOG.md) file adhering to the [Keep A Changelog](https://keepachangelog.com/en/1.0.0/) format. The purpose of the changelog is for the contributors and maintainers to incrementally build the release notes throughout the development process to avoid a painful and error-prone process of attempting to compile the release notes at release time. On each release the "unreleased" entries of the changelog are moved to the appropriate release notes document in the `./release-notes` folder. Also, incrementally building the changelog provides a concise, human-readable list of significant features that have been added to the unreleased version under development. + +### Which changes require a CHANGELOG entry? +Changelogs are intended for operators/administrators, developers integrating with libraries and APIs, and end-users interacting with OpenSearch Dashboards and/or the REST API (collectively referred to as "user"). In short, any change that a user of OpenSearch might want to be aware of should be included in the changelog. The changelog is _not_ intended to replace the git commit log that developers of OpenSearch itself rely upon. The following are some examples of changes that should be in the changelog: + +- A newly added feature +- A fix for a user-facing bug +- Dependency updates +- Fixes for security issues + +The following are some examples where a changelog entry is not necessary: + +- Adding, modifying, or fixing tests +- An incremental PR for a larger feature (such features should include _one_ changelog entry for the feature) +- Documentation changes or code refactoring +- Build-related changes + +Any PR that does not include a changelog entry will result in a failure of the validation workflow in GitHub. If the contributor and maintainers agree that no changelog entry is required, then the `skip-changelog` label can be applied to the PR which will result in the workflow passing. + +### How to add my changes to [CHANGELOG](CHANGELOG.md)? + +Adding in the change is two step process: +1. Add your changes to the corresponding section within the CHANGELOG file with dummy pull request information, publish the PR +2. Update the entry for your change in [`CHANGELOG.md`](CHANGELOG.md) and make sure that you reference the pull request there. + +### Where should I put my CHANGELOG entry? +Please review the [branching strategy](https://github.com/opensearch-project/.github/blob/main/RELEASING.md#opensearch-branching) document. The changelog on the `main` branch will contain sections for the _next major_ and _next minor_ releases. Your entry should go into the section it is intended to be released in. In practice, most changes to `main` will be backported to the next minor release so most entries will likely be in that section. + +The following examples assume the _next major_ release on main is 3.0, then _next minor_ release is 2.5, and the _current_ release is 2.4. + +- **Add a new feature to release in next minor:** Add a changelog entry to `[Unreleased 2.x]` on main, then backport to 2.x (including the changelog entry). +- **Introduce a breaking API change to release in next major:** Add a changelog entry to `[Unreleased 3.0]` on main, do not backport. +- **Upgrade a dependency to fix a CVE:** Add a changelog entry to `[Unreleased 2.x]` on main, then backport to 2.x (including the changelog entry), then backport to 2.4 and ensure the changelog entry is added to `[Unreleased 2.4.1]`. + ## Review Process We deeply appreciate everyone who takes the time to make a contribution. We will review all contributions as quickly as possible. As a reminder, [opening an issue](https://github.com/opensearch-project/OpenSearch/issues/new/choose) discussing your change before you make it is the best way to smooth the PR process. This will prevent a rejection because someone else is already working on the problem, or because the solution is incompatible with the architectural direction. diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 9b1bc933eb1e3..778fe2d4bc5f5 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -4,14 +4,17 @@ - [Install Prerequisites](#install-prerequisites) - [JDK 11](#jdk-11) - [JDK 14](#jdk-14) - - [Runtime JDK](#runtime-jdk) + - [JDK 17](#jdk-17) + - [Custom Runtime JDK](#custom-runtime-jdk) - [Windows](#windows) - [Docker](#docker) - [Build](#build) + - [Generated Code](#generated-code) - [Run Tests](#run-tests) - [Run OpenSearch](#run-opensearch) - [Use an Editor](#use-an-editor) - [IntelliJ IDEA](#intellij-idea) + - [Remote development using JetBrains Gateway](#remote-development-using-jetbrains-gateway) - [Visual Studio Code](#visual-studio-code) - [Eclipse](#eclipse) - [Project Layout](#project-layout) @@ -24,6 +27,7 @@ - [`server`](#server) - [`test`](#test) - [Java Language Formatting Guidelines](#java-language-formatting-guidelines) + - [Adding Dependencies](#adding-dependencies) - [Editor / IDE Support](#editor--ide-support) - [Formatting Failures](#formatting-failures) - [Gradle Build](#gradle-build) @@ -35,19 +39,28 @@ - [testImplementation](#testimplementation) - [Gradle Plugins](#gradle-plugins) - [Distribution Download Plugin](#distribution-download-plugin) - - [Misc](#misc) - - [git-secrets](#git-secrets) - - [Installation](#installation) - - [Configuration](#configuration) + - [Creating fat-JAR of a Module](#creating-fat-jar-of-a-module) - [Components](#components) - - [Build libraries & interfaces](#build-libraries--interfaces) + - [Build Libraries & Interfaces](#build-libraries--interfaces) - [Clients & Libraries](#clients--libraries) - [Plugins](#plugins-1) - - [Indexing & search](#indexing--search) + - [Indexing & Search](#indexing--search) - [Aggregations](#aggregations) - [Distributed Framework](#distributed-framework) - - [Submitting Changes](#submitting-changes) - - [Backports](#backports) + - [Misc](#misc) + - [Git Secrets](#git-secrets) + - [Installation](#installation) + - [Configuration](#configuration) + - [Submitting Changes](#submitting-changes) + - [Backwards Compatibility](#backwards-compatibility) + - [Data](#data) + - [Developer API](#developer-api) + - [User API](#user-api) + - [Experimental Development](#experimental-development) + - [Backports](#backports) + - [LineLint](#linelint) + - [Lucene Snapshots](#lucene-snapshots) + - [Flaky Tests](#flaky-tests) # Developer Guide @@ -122,6 +135,17 @@ To build a distribution to run on your local platform, run: All distributions built will be under `distributions/archives`. +#### Generated Code + +OpenSearch uses code generators like [Protobuf](https://protobuf.dev/). +OpenSearch build system already takes a dependency of generating code from protobuf, incase you run into compilation errors, run: + +``` +./gradlew generateProto +``` + +Generated code in OpenSearch is used to establish cross version compatibility communication for API contracts within OpenSearch. + ### Run Tests OpenSearch uses a Gradle wrapper for its build. Run `gradlew` on Unix systems, or `gradlew.bat` on Windows in the root of the repository. @@ -240,7 +264,10 @@ This repository is split into many top level directories. The most important one ### `distribution` -Builds our tar and zip archives and our rpm and deb packages. +Builds our tar and zip archives and our rpm and deb packages. There are several flavors of the distributions, with the classifier included in the name of the final deliverable (archive or package): + - default (no classifier), the distribution with bundled JDK + - `-no-jdk-` - the distribution without bundled JDK/JRE, assumes the JDK/JRE is going to be pre-installed on the target systems + - `-jre-` - the distribution bundled with JRE (smaller footprint), supported as experimental feature for some platforms ### `libs` @@ -317,6 +344,18 @@ Please follow these formatting guidelines: * Note that JavaDoc and block comments i.e. `/* ... */` are not formatted, but line comments i.e `// ...` are. * There is an implicit rule that negative boolean expressions should use the form `foo == false` instead of `!foo` for better readability of the code. While this isn't strictly enforced, if might get called out in PR reviews as something to change. +## Adding Dependencies + +When adding a new dependency or removing an existing dependency via any `build.gradle` (that are not in the test scope), update the dependency LICENSE and library SHAs. + +For example, after adding `api "org.slf4j:slf4j-api:${versions.slf4j}"` to [plugins/discovery-ec2/build.gradle](plugins/discovery-ec2/build.gradle), copy the library `LICENSE.txt` and `NOTICE.txt` to `plugins/discovery-ec2/licenses/slf4j-api-LICENSE.txt` and `plugins/discovery-ec2/licenses/slf4j-api-NOTICE.txt`, then run the following to generate `plugins/discovery-ec2/licenses/slf4j-api-1.7.36.jar.sha1`. + +``` +./gradlew :plugins:discovery-ec2:updateSHAs +``` + +Ensure that `./gradlew :plugins:discovery-ec2:check` passes before submitting changes. + ### Editor / IDE Support IntelliJ IDEs can [import](https://blog.jetbrains.com/idea/2014/01/intellij-idea-13-importing-code-formatter-settings-from-eclipse/) the [settings file](buildSrc/formatterConfig.xml), and / or use the [Eclipse Code Formatter](https://plugins.jetbrains.com/plugin/6546-eclipse-code-formatter) @@ -372,38 +411,49 @@ The Distribution Download plugin downloads the latest version of OpenSearch by d ./gradlew integTest -PcustomDistributionUrl="https://ci.opensearch.org/ci/dbc/bundle-build/1.2.0/1127/linux/x64/dist/opensearch-1.2.0-linux-x64.tar.gz" ``` +### Creating fat-JAR of a Module -## Misc +A fat-JAR (or an uber-JAR) is the JAR, which contains classes from all the libraries, on which your project depends and, of course, the classes of current project. -### git-secrets +There might be cases where a developer would like to add some custom logic to the code of a module (or multiple modules) and generate a fat-JAR that can be directly used by the dependency management tool. For example, in [#3665](https://github.com/opensearch-project/OpenSearch/pull/3665) a developer wanted to provide a tentative patch as a fat-JAR to a consumer for changes made in the high level REST client. -Security is our top priority. Avoid checking in credentials. +Use [Gradle Shadow plugin](https://imperceptiblethoughts.com/shadow/). +Add the following to the `build.gradle` file of the module for which you want to create the fat-JAR, e.g. `client/rest-high-level/build.gradle`: -#### Installation -Install [awslabs/git-secrets](https://github.com/awslabs/git-secrets) by running the following commands. ``` -git clone https://github.com/awslabs/git-secrets.git -cd git-secrets -make install +apply plugin: 'com.github.johnrengelman.shadow' ``` -#### Configuration -You can configure git secrets per repository, you need to change the directory to the root of the repository and run the following command. +Run the `shadowJar` command using: ``` -git secrets --install -✓ Installed commit-msg hook to .git/hooks/commit-msg -✓ Installed pre-commit hook to .git/hooks/pre-commit -✓ Installed prepare-commit-msg hook to .git/hooks/prepare-commit-msg +./gradlew :client:rest-high-level:shadowJar ``` -Then, you need to apply patterns for git-secrets, you can install the AWS standard patterns by running the following command. + +This will generate a fat-JAR in the `build/distributions` folder of the module, e.g. .`/client/rest-high-level/build/distributions/opensearch-rest-high-level-client-1.4.0-SNAPSHOT.jar`. + +You can further customize your fat-JAR by customising the plugin, More information about shadow plugin can be found [here](https://imperceptiblethoughts.com/shadow/). + +To use the generated JAR, install the JAR locally, e.g. ``` -git secrets --register-aws +mvn install:install-file -Dfile=src/main/resources/opensearch-rest-high-level-client-1.4.0-SNAPSHOT.jar -DgroupId=org.opensearch.client -DartifactId=opensearch-rest-high-level-client -Dversion=1.4.0-SNAPSHOT -Dpackaging=jar -DgeneratePom=true +``` + +Refer the installed JAR as any other maven artifact, e.g. + +``` + + org.opensearch.client + opensearch-rest-high-level-client + 1.4.0-SNAPSHOT + ``` ## Components + As you work in the OpenSearch repo you may notice issues getting labeled with component labels. It's a housekeeping task to help group together similar pieces of work. You can pretty much ignore it, but if you're curious, here's what the different labels mean: -### Build libraries & interfaces +### Build Libraries & Interfaces + Tasks to make sure the build tasks are useful and packaging and distribution are easy. Includes: @@ -415,8 +465,8 @@ Includes: - Compatibility - Javadoc enforcement - ### Clients & Libraries + APIs and communication mechanisms for external connections to OpenSearch. This includes the “library” directory in OpenSearch (a set of common functions). Includes: @@ -426,6 +476,7 @@ Includes: - CLI ### Plugins + Anything touching the plugin infrastructure within core OpenSearch. Includes: @@ -435,7 +486,8 @@ Includes: - Plugin interfaces -### Indexing & search +### Indexing & Search + The critical path of indexing and search, including: Measure index and search, performance, Improving the performance of indexing and search, ensure synchronization OpenSearch APIs with upstream Lucene change (e.g. new field types, changing doc values and codex). Includes: @@ -446,6 +498,7 @@ Includes: - DocValues ### Aggregations + Making sure OpenSearch can be used as a compute engine. Includes: @@ -454,21 +507,134 @@ Includes: - Framework ### Distributed Framework + Work to make sure that OpenSearch can scale in a distributed manner. Includes: -- Nodes (Master, Data, Compute, Ingest, Discovery, etc.) +- Nodes (Cluster Manager, Data, Compute, Ingest, Discovery, etc.) - Replication & Merge Policies (Document, Segment level) - Snapshot/Restore (repositories; S3, Azure, GCP, NFS) - Translog (e.g., OpenSearch, Kafka, Kinesis) - Shard Strategies - Circuit Breakers -## Submitting Changes +## Misc + +### Git Secrets + +Security is our top priority. Avoid checking in credentials. + +#### Installation + +Install [awslabs/git-secrets](https://github.com/awslabs/git-secrets) by running the following commands. +``` +git clone https://github.com/awslabs/git-secrets.git +cd git-secrets +make install +``` + +#### Configuration + +You can configure git secrets per repository, you need to change the directory to the root of the repository and run the following command. +``` +git secrets --install +✓ Installed commit-msg hook to .git/hooks/commit-msg +✓ Installed pre-commit hook to .git/hooks/pre-commit +✓ Installed prepare-commit-msg hook to .git/hooks/prepare-commit-msg +``` +Then, you need to apply patterns for git-secrets, you can install the AWS standard patterns by running the following command. +``` +git secrets --register-aws +``` + +### Submitting Changes See [CONTRIBUTING](CONTRIBUTING.md). -## Backports +### Backwards Compatibility + +OpenSearch strives for a smooth and easy upgrade experience that is resilient to data loss and corruption while minimizing downtime and ensuring integration with +external systems does not unexpectedly break. + +To provide these guarantees each version must be designed and developed with [forward compatibility](https://en.wikipedia.org/wiki/Forward_compatibility) in mind. +OpenSearch addresses backward and forward compatibility at three different levels: 1. Data, 2. Developer API, 3. User API. These levels and the developer mechanisms +to ensure backwards compatibility are provided below. + +#### Data + +The data level consists of index and application data file formats. OpenSearch guarantees file formats and indexes are compatible only back to the first release of +the previous major version. If on disk formats or encodings need to be changed (including index data, cluster state, or any other persisted data) developers must +use Version checks accordingly (e.g., `Version.onOrAfter`, `Version.before`) to guarantee backwards compatibility. + +#### Developer API + +The Developer API consists of interfaces and foundation software implementations that enable external users to develop new OpenSearch features. This includes obvious +components such as the Plugin and Extension frameworks and less obvious components such as REST Action Handlers. When developing a new feature of OpenSearch it is +important to explicitly mark which implementation components may, or may not, be extended by external implementations. For example, all new API classes with +`@PublicApi` annotation (or documented as `@opensearch.api`) signal that the new component may be extended by an external implementation and therefore provide +backwards compatibility guarantees. Similarly, any class explicitly marked with the `@InternalApi` (or documented as `@opensearch.internal`) annotation, or not +explicitly marked by an annotation should not be extended by external implementation components as it does not guarantee backwards compatibility and may change at +any time. The `@DeprecatedApi` annotation could also be added to any classes annotated with `@PublicApi` (or documented as `@opensearch.api`) or their methods that +are either changed (with replacement) or planned to be removed across major versions. + +The APIs which are designated to be public but have not been stabilized yet should be marked with `@ExperimentalApi` (or documented as `@opensearch.experimental`) +annotation. The presence of this annotation signals that API may change at any time (major, minor or even patch releases). In general, the classes annotated with +`@PublicApi` may expose other classes or methods annotated with `@ExperimentalApi`, in such cases the backward compatibility guarantees would not apply to latter +(see please [Experimental Development](#experimental-development) for more details). + +#### User API + +The User API consists of integration specifications (e.g., [Query Domain Specific Language](https://opensearch.org/docs/latest/opensearch/query-dsl/index/), +[field mappings](https://opensearch.org/docs/latest/opensearch/mappings/)) and endpoints (e.g., [`_search`](https://opensearch.org/docs/latest/api-reference/search/), +[`_cat`](https://opensearch.org/docs/latest/api-reference/cat/index/)) users rely on to integrate and use OpenSearch. Backwards compatibility is critical to the +User API, therefore OpenSearch commits to using [semantic versioning](https://opensearch.org/blog/what-is-semver/) for all User facing APIs. To support this +developers must leverage `Version` checks for any user facing endpoints or API specifications that change across minor versions. Developers must also inform +users of any changes by adding the `>breaking` label on Pull Requests, adding an entry to the [CHANGELOG](https://github.com/opensearch-project/OpenSearch/blob/main/CHANGELOG.md) +and a log message to the OpenSearch deprecation log files using the `DeprecationLogger`. + +#### Experimental Development + +Rapidly developing new features often benefit from several release cycles before committing to an official and long term supported (LTS) API. To enable this cycle OpenSearch +uses an Experimental Development process leveraging [Feature Flags](https://featureflags.io/feature-flags/). This allows a feature to be developed using the same process as +a LTS feature but with additional guard rails and communication mechanisms to signal to the users and development community the feature is not yet stable, may change in a future +release, or be removed altogether. Any Developer or User APIs implemented along with the experimental feature should be marked with `@ExperimentalApi` (or documented as +`@opensearch.experimental`) annotation to signal the implementation is not subject to LTS and does not follow backwards compatibility guidelines. + +### Backports The Github workflow in [`backport.yml`](.github/workflows/backport.yml) creates backport PRs automatically when the original PR with an appropriate label `backport ` is merged to main with the backport workflow run successfully on the PR. For example, if a PR on main needs to be backported to `1.x` branch, add a label `backport 1.x` to the PR and make sure the backport workflow runs on the PR along with other checks. Once this PR is merged to main, the workflow will create a backport PR to the `1.x` branch. + +### LineLint + +A linter in [`code-hygiene.yml`](.github/workflows/code-hygiene.yml) that validates simple newline and whitespace rules in all sorts of files. It can: +- Recursively check a directory tree for files that do not end in a newline +- Automatically fix these files by adding a newline or trimming extra newlines. + +Rules are defined in `.linelint.yml`. + +Executing the binary will automatically search the local directory tree for linting errors. + + linelint . + +Pass a list of files or directories to limit your search. + + linelint README.md LICENSE + +### Lucene Snapshots + +The Github workflow in [lucene-snapshots.yml](.github/workflows/lucene-snapshots.yml) is a Github worfklow executable by maintainers to build a top-down snapshot build of lucene. +These snapshots are available to test compatibility with upcoming changes to Lucene by updating the version at [version.properties](buildsrc/version.properties) with the `version-snapshot-sha` version. Example: `lucene = 10.0.0-snapshot-2e941fc`. + +### Flaky Tests + +OpenSearch has a very large test suite with long running, often failing (flaky), integration tests. Such individual tests are labelled as [Flaky Random Test Failure](https://github.com/opensearch-project/OpenSearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22flaky-test%22). Your help is wanted fixing these! + +If you encounter a build/test failure in CI that is unrelated to the change in your pull request, it may be a known flaky test, or a new test failure. + +1. Follow failed CI links, and locate the failing test(s). +2. Copy-paste the failure into a comment of your PR. +3. Search through [issues](https://github.com/opensearch-project/OpenSearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22flaky-test%22) using the name of the failed test for whether this is a known flaky test. +5. If an existing issue is found, paste a link to the known issue in a comment to your PR. +6. If no existing issue is found, open one. +7. Retry CI via the GitHub UX or by pushing an update to your PR. diff --git a/MAINTAINERS.md b/MAINTAINERS.md index db6cd6c0f3309..42a8a439445ca 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,29 +1,42 @@ -## Maintainers +## Overview -| Maintainer | GitHub ID | Affiliation | -| --------------- | --------- | ----------- | -| Abbas Hussain | [abbashus](https://github.com/abbashus) | Amazon | -| Charlotte Henkle | [CEHENKLE](https://github.com/CEHENKLE) | Amazon | -| Himanshu Setia | [setiah](https://github.com/setiah) | Amazon | -| Nick Knize | [nknize](https://github.com/nknize) | Amazon | -| Rabi Panda | [adnapibar](https://github.com/adnapibar) | Amazon | -| Sarat Vemulapalli | [saratvemulapalli](https://github.com/saratvemulapalli) | Amazon | -| Tianli Feng | [tlfeng](https://github.com/tlfeng) | Amazon | -| Gopala Krishna Ambareesh | [krishna-ggk](https://github.com/krishna-ggk) |Amazon | -| Vengadanathan Srinivasan | [vengadanathan-s](https://github.com/vengadanathan-s) | Amazon | -| Shweta Thareja |[shwetathareja](https://github.com/shwetathareja) | Amazon | -| Itiyama Sadana | [itiyamas](https://github.com/itiyamas) | Amazon | -| Daniel "dB." Doubrovkine | [dblock](https://github.com/dblock) | Amazon | -| Andrew Ross | [andross](https://github.com/andrross)| Amazon | -| Vacha Shah | [VachaShah](https://github.com/VachaShah) | Amazon | -| Anas Alkouz | [anasalkouz](https://github.com/anasalkouz) | Amazon | -| Megha Sai Kavikondala | [meghasaik](https://github.com/meghasaik) | Amazon | -| Rishikesh Pasham | [Rishikesh1159](https://github.com/Rishikesh1159) | Amazon| -| Xue Zhou | [xuezhou25](https://github.com/xuezhou25) | Amazon | -| Kartik Ganesh | [kartg](https://github.com/kartg) | Amazon | -| Marc Handalian | [mch2](https://github.com/mch2) | Amazon | -| Ryan Bogan | [ryanbogan](https://github.com/ryanbogan) | Amazon | -| Owais Kazi | [owaiskazi19](https://github.com/owaiskazi19) | Amazon | +This document contains a list of maintainers in this repo. See [opensearch-project/.github/RESPONSIBILITIES.md](https://github.com/opensearch-project/.github/blob/main/RESPONSIBILITIES.md#maintainer-responsibilities) that explains what the role of maintainer means, what maintainers do in this and other repos, and how they should be doing it. If you're interested in contributing, and becoming a maintainer, see [CONTRIBUTING](CONTRIBUTING.md). +## Current Maintainers -[This document](https://github.com/opensearch-project/.github/blob/main/MAINTAINERS.md) explains what maintainers do in this repo, and how they should be doing it. If you're interested in contributing, see [CONTRIBUTING](CONTRIBUTING.md). +| Maintainer | GitHub ID | Affiliation | +|--------------------------| ------------------------------------------------------- | ----------- | +| Abbas Hussain | [abbashus](https://github.com/abbashus) | Meta | +| Anas Alkouz | [anasalkouz](https://github.com/anasalkouz) | Amazon | +| Andrew Ross | [andrross](https://github.com/andrross) | Amazon | +| Andriy Redko | [reta](https://github.com/reta) | Aiven | +| Bukhtawar Khan | [Bukhtawar](https://github.com/Bukhtawar) | Amazon | +| Charlotte Henkle | [CEHENKLE](https://github.com/CEHENKLE) | Amazon | +| Dan Widdis | [dbwiddis](https://github.com/dbwiddis) | Amazon | +| Daniel "dB." Doubrovkine | [dblock](https://github.com/dblock) | Amazon | +| Gaurav Bafna | [gbbafna](https://github.com/gbbafna) | Amazon | +| Himanshu Setia | [setiah](https://github.com/setiah) | Amazon | +| Kartik Ganesh | [kartg](https://github.com/kartg) | Amazon | +| Kunal Kotwani | [kotwanikunal](https://github.com/kotwanikunal) | Amazon | +| Marc Handalian | [mch2](https://github.com/mch2) | Amazon | +| Michael Froh | [msfroh](https://github.com/msfroh) | Amazon | +| Nick Knize | [nknize](https://github.com/nknize) | Amazon | +| Owais Kazi | [owaiskazi19](https://github.com/owaiskazi19) | Amazon | +| Peter Nied | [peternied](https://github.com/peternied) | Amazon | +| Rabi Panda | [adnapibar](https://github.com/adnapibar) | Independent | +| Rishikesh Pasham | [Rishikesh1159](https://github.com/Rishikesh1159) | Amazon | +| Ryan Bogan | [ryanbogan](https://github.com/ryanbogan) | Amazon | +| Sachin Kale | [sachinpkale](https://github.com/sachinpkale) | Amazon | +| Sarat Vemulapalli | [saratvemulapalli](https://github.com/saratvemulapalli) | Amazon | +| Shweta Thareja | [shwetathareja](https://github.com/shwetathareja) | Amazon | +| Sorabh Hamirwasia | [sohami](https://github.com/sohami) | Amazon | +| Suraj Singh | [dreamer-89](https://github.com/dreamer-89) | Amazon | +| Tianli Feng | [tlfeng](https://github.com/tlfeng) | Amazon | +| Vacha Shah | [VachaShah](https://github.com/VachaShah) | Amazon | + +## Emeritus + +| Maintainer | GitHub ID | Affiliation | +|-------------------------|---------------------------------------------|-------------| +| Megha Sai Kavikondala | [meghasaik](https://github.com/meghasaik) | Amazon | +| Xue Zhou | [xuezhou25](https://github.com/xuezhou25) | Amazon | diff --git a/README.md b/README.md index 6a9a2a69d7367..1b7103191e22f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ +[![Chat](https://img.shields.io/badge/chat-on%20forums-blue)](https://forum.opensearch.org/c/opensearch/) +[![Documentation](https://img.shields.io/badge/documentation-reference-blue)](https://opensearch.org/docs/latest/opensearch/index/) +[![codecov](https://codecov.io/gh/opensearch-project/OpenSearch/branch/2.x/graph/badge.svg)](https://codecov.io/gh/opensearch-project/OpenSearch) +[![GHA gradle check](https://github.com/opensearch-project/OpenSearch/actions/workflows/gradle-check.yml/badge.svg)](https://github.com/opensearch-project/OpenSearch/actions/workflows/gradle-check.yml) +[![GHA validate pull request](https://github.com/opensearch-project/OpenSearch/actions/workflows/wrapper.yml/badge.svg)](https://github.com/opensearch-project/OpenSearch/actions/workflows/wrapper.yml) +[![GHA precommit](https://github.com/opensearch-project/OpenSearch/actions/workflows/precommit.yml/badge.svg)](https://github.com/opensearch-project/OpenSearch/actions/workflows/precommit.yml) +[![Jenkins gradle check job](https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fbuild.ci.opensearch.org%2Fjob%2Fgradle-check%2F&label=Jenkins%20Gradle%20Check)](https://build.ci.opensearch.org/job/gradle-check/) + + - [Welcome!](#welcome) - [Project Resources](#project-resources) - [Code of Conduct](#code-of-conduct) @@ -10,7 +19,7 @@ ## Welcome! -**OpenSearch** is [a community-driven, open source fork](https://aws.amazon.com/blogs/opensource/introducing-opensearch/) of [Elasticsearch](https://en.wikipedia.org/wiki/Elasticsearch) and [Kibana](https://en.wikipedia.org/wiki/Kibana) following the [licence change](https://opensource.org/node/1099) in early 2021. We're looking to sustain (and evolve!) a search and analytics suite for the multitude of businesses who are dependent on the rights granted by the original, [Apache v2.0 License](LICENSE.txt). +**OpenSearch** is [a community-driven, open source fork](https://aws.amazon.com/blogs/opensource/introducing-opensearch/) of [Elasticsearch](https://en.wikipedia.org/wiki/Elasticsearch) and [Kibana](https://en.wikipedia.org/wiki/Kibana) following the [license change](https://blog.opensource.org/the-sspl-is-not-an-open-source-license/) in early 2021. We're looking to sustain (and evolve!) a search and analytics suite for the multitude of businesses who are dependent on the rights granted by the original, [Apache v2.0 License](LICENSE.txt). ## Project Resources @@ -45,4 +54,4 @@ Copyright OpenSearch Contributors. See [NOTICE](NOTICE.txt) for details. OpenSearch is a registered trademark of Amazon Web Services. -OpenSearch includes certain Apache-licensed Elasticsearch code from Elasticsearch B.V. and other source code. Elasticsearch B.V. is not the source of that other source code. ELASTICSEARCH is a registered trademark of Elasticsearch B.V. \ No newline at end of file +OpenSearch includes certain Apache-licensed Elasticsearch code from Elasticsearch B.V. and other source code. Elasticsearch B.V. is not the source of that other source code. ELASTICSEARCH is a registered trademark of Elasticsearch B.V. diff --git a/RELEASING.md b/RELEASING.md index 50bb965b8d551..1ef59446f6e31 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,3 +1,3 @@ ## Releasing -This project follows [OpenSearch project branching, labelling, and releasing](https://github.com/opensearch-project/.github/blob/main/RELEASING.md). \ No newline at end of file +This project follows [OpenSearch project branching, labelling, and releasing](https://github.com/opensearch-project/.github/blob/main/RELEASING.md). diff --git a/SECURITY.md b/SECURITY.md index 0b85ca04ed260..b86292104335f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,3 +1,3 @@ ## Reporting a Vulnerability -If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to aws-security@amazon.com. Please do **not** create a public GitHub issue. \ No newline at end of file +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to aws-security@amazon.com. Please do **not** create a public GitHub issue. diff --git a/TESTING.md b/TESTING.md index 4a2a786469b67..06533cd5c170a 100644 --- a/TESTING.md +++ b/TESTING.md @@ -13,6 +13,7 @@ OpenSearch uses [jUnit](https://junit.org/junit5/) for testing, it also uses ran - [Test groups](#test-groups) - [Load balancing and caches](#load-balancing-and-caches) - [Test compatibility](#test-compatibility) + - [Retries](#retries) - [Miscellaneous](#miscellaneous) - [Running verification tasks](#running-verification-tasks) - [Testing the REST layer](#testing-the-rest-layer) @@ -160,6 +161,10 @@ It is possible to provide a version that allows to adapt the tests' behaviour to ./gradlew test -Dtests.compatibility=1.0.0 +## Retries + +The goal of tests is to be completely deterministic such that any test failure can be easily and reliably reproduced. However, the reality is that many OpenSearch integration tests have non-deterministic behavior which results in rare test failures that cannot be easily reproduced even using the same random test seed. To mitigate the pain of frequent non-reproducible test failures, limited retries have been introduced using the Gradle [test-retry](https://plugins.gradle.org/plugin/org.gradle.test-retry) plugin. The known flaky tests are explicitly listed in the test-retry configuration of the build.gradle file. This is intended as a temporary mitigation for existing flakiness, and as such new tests should not be added to the retry list. Any new addition to the retry list must provide a thorough rationale as to why adding retries is the right thing to do as opposed to fixing the underlying flakiness. Existing flaky tests are tracked in GitHub with the [Flaky Random Test Failure](https://github.com/opensearch-project/OpenSearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22flaky-test%22) label. + ## Miscellaneous Run all tests without stopping on errors (inspect log files). @@ -245,7 +250,7 @@ The YAML REST tests support all the options provided by the randomized runner, p - `tests.rest.suite`: comma separated paths of the test suites to be run (by default loaded from /rest-api-spec/test). It is possible to run only a subset of the tests providing a sub-folder or even a single yaml file (the default /rest-api-spec/test prefix is optional when files are loaded from classpath) e.g. `-Dtests.rest.suite=index,get,create/10_with_id` -- `tests.rest.blacklist`: comma separated globs that identify tests that are denylisted and need to be skipped e.g. `-Dtests.rest.blacklist=index/**/Index document,get/10_basic/**` +- `tests.rest.denylist`: comma separated globs that identify tests that are denylisted and need to be skipped e.g. `-Dtests.rest.denylist=index/**/Index document,get/10_basic/**` Java REST tests can be run with the "javaRestTest" task. @@ -383,6 +388,10 @@ Use -Dtest.class and -Dtests.method to run a specific bwcTest test. For example -Dtests.class=org.opensearch.upgrades.RecoveryIT \ -Dtests.method=testHistoryUUIDIsGenerated +Use `-PcustomDistributionDownloadType=bundle` to run the bwcTest against the test cluster with latest CI distribution bundle set up for the specified version; this property is default to min and exclusive choices between `bundle` and `min`: + + ./gradlew bwcTest -PcustomDistributionDownloadType=bundle + When running `./gradlew check`, minimal bwc checks are also run against compatible versions that are not yet released. ## BWC Testing against a specific remote/branch diff --git a/benchmarks/build.gradle b/benchmarks/build.gradle index faaeb33d80ff7..02aa9319cc583 100644 --- a/benchmarks/build.gradle +++ b/benchmarks/build.gradle @@ -32,10 +32,16 @@ import org.opensearch.gradle.info.BuildParams apply plugin: 'opensearch.build' apply plugin: 'application' -mainClassName = 'org.openjdk.jmh.Main' assemble.enabled = false -archivesBaseName = 'opensearch-benchmarks' + +application { + mainClass = 'org.openjdk.jmh.Main' +} + +base { + archivesBaseName = 'opensearch-benchmarks' +} test.enabled = false @@ -67,12 +73,6 @@ dependenciesInfo.enabled = false thirdPartyAudit.ignoreViolations( // these classes intentionally use JDK internal API (and this is ok since the project is maintained by Oracle employees) - 'org.openjdk.jmh.profile.AbstractHotspotProfiler', - 'org.openjdk.jmh.profile.HotspotThreadProfiler', - 'org.openjdk.jmh.profile.HotspotClassloadingProfiler', - 'org.openjdk.jmh.profile.HotspotCompilationProfiler', - 'org.openjdk.jmh.profile.HotspotMemoryProfiler', - 'org.openjdk.jmh.profile.HotspotRuntimeProfiler', 'org.openjdk.jmh.util.Utils' ) diff --git a/benchmarks/src/main/java/org/opensearch/benchmark/index/mapper/CustomBinaryDocValuesFieldBenchmark.java b/benchmarks/src/main/java/org/opensearch/benchmark/index/mapper/CustomBinaryDocValuesFieldBenchmark.java new file mode 100644 index 0000000000000..7307bec088d02 --- /dev/null +++ b/benchmarks/src/main/java/org/opensearch/benchmark/index/mapper/CustomBinaryDocValuesFieldBenchmark.java @@ -0,0 +1,81 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.benchmark.index.mapper; + +import org.apache.lucene.util.BytesRef; +import org.opensearch.index.mapper.BinaryFieldMapper; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 1) +@Measurement(iterations = 1) +@Fork(1) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +@SuppressWarnings("unused") // invoked by benchmarking framework +public class CustomBinaryDocValuesFieldBenchmark { + + static final String FIELD_NAME = "dummy"; + static final String SEED_VALUE = "seed"; + + @Benchmark + public void add(CustomBinaryDocValuesFieldBenchmark.BenchmarkParameters parameters, Blackhole blackhole) { + // Don't use the parameter binary doc values object. + // Start with a fresh object every call and add maximum number of entries + BinaryFieldMapper.CustomBinaryDocValuesField customBinaryDocValuesField = new BinaryFieldMapper.CustomBinaryDocValuesField( + FIELD_NAME, + new BytesRef(SEED_VALUE).bytes + ); + for (int i = 0; i < parameters.maximumNumberOfEntries; ++i) { + ThreadLocalRandom.current().nextBytes(parameters.bytes); + customBinaryDocValuesField.add(parameters.bytes); + } + } + + @Benchmark + public void binaryValue(CustomBinaryDocValuesFieldBenchmark.BenchmarkParameters parameters, Blackhole blackhole) { + blackhole.consume(parameters.customBinaryDocValuesField.binaryValue()); + } + + @State(Scope.Benchmark) + public static class BenchmarkParameters { + @Param({ "8", "32", "128", "512" }) + int maximumNumberOfEntries; + + @Param({ "8", "32", "128", "512" }) + int entrySize; + + BinaryFieldMapper.CustomBinaryDocValuesField customBinaryDocValuesField; + byte[] bytes; + + @Setup + public void setup() { + customBinaryDocValuesField = new BinaryFieldMapper.CustomBinaryDocValuesField(FIELD_NAME, new BytesRef(SEED_VALUE).bytes); + bytes = new byte[entrySize]; + for (int i = 0; i < maximumNumberOfEntries; ++i) { + ThreadLocalRandom.current().nextBytes(bytes); + customBinaryDocValuesField.add(bytes); + } + } + } +} diff --git a/benchmarks/src/main/java/org/opensearch/benchmark/routing/allocation/AllocationBenchmark.java b/benchmarks/src/main/java/org/opensearch/benchmark/routing/allocation/AllocationBenchmark.java index c89bf3d1b577c..1faf522f3a1c9 100644 --- a/benchmarks/src/main/java/org/opensearch/benchmark/routing/allocation/AllocationBenchmark.java +++ b/benchmarks/src/main/java/org/opensearch/benchmark/routing/allocation/AllocationBenchmark.java @@ -31,17 +31,6 @@ package org.opensearch.benchmark.routing.allocation; -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.Fork; -import org.openjdk.jmh.annotations.Measurement; -import org.openjdk.jmh.annotations.Mode; -import org.openjdk.jmh.annotations.OutputTimeUnit; -import org.openjdk.jmh.annotations.Param; -import org.openjdk.jmh.annotations.Scope; -import org.openjdk.jmh.annotations.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.Warmup; import org.opensearch.Version; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; @@ -53,6 +42,17 @@ import org.opensearch.cluster.routing.ShardRoutingState; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.common.settings.Settings; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; import java.util.HashMap; import java.util.Map; diff --git a/benchmarks/src/main/java/org/opensearch/benchmark/routing/allocation/Allocators.java b/benchmarks/src/main/java/org/opensearch/benchmark/routing/allocation/Allocators.java index d700b9dab2cf3..482a58f87086e 100644 --- a/benchmarks/src/main/java/org/opensearch/benchmark/routing/allocation/Allocators.java +++ b/benchmarks/src/main/java/org/opensearch/benchmark/routing/allocation/Allocators.java @@ -45,8 +45,8 @@ import org.opensearch.cluster.routing.allocation.decider.AllocationDeciders; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.set.Sets; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.gateway.GatewayAllocator; import org.opensearch.snapshots.EmptySnapshotsInfoService; diff --git a/benchmarks/src/main/java/org/opensearch/benchmark/search/aggregations/TermsReduceBenchmark.java b/benchmarks/src/main/java/org/opensearch/benchmark/search/aggregations/TermsReduceBenchmark.java index b549de555aca1..5073858848e05 100644 --- a/benchmarks/src/main/java/org/opensearch/benchmark/search/aggregations/TermsReduceBenchmark.java +++ b/benchmarks/src/main/java/org/opensearch/benchmark/search/aggregations/TermsReduceBenchmark.java @@ -40,14 +40,14 @@ import org.opensearch.action.search.SearchPhaseController; import org.opensearch.action.search.SearchProgressListener; import org.opensearch.action.search.SearchRequest; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.breaker.NoopCircuitBreaker; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.lucene.search.TopDocsAndMaxScore; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; -import org.opensearch.index.shard.ShardId; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.NoopCircuitBreaker; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; import org.opensearch.search.DocValueFormat; import org.opensearch.search.SearchModule; import org.opensearch.search.SearchShardTarget; @@ -57,6 +57,7 @@ import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.MultiBucketConsumerService; import org.opensearch.search.aggregations.bucket.terms.StringTerms; +import org.opensearch.search.aggregations.bucket.terms.TermsAggregator; import org.opensearch.search.aggregations.pipeline.PipelineAggregator; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.query.QuerySearchResult; @@ -170,15 +171,14 @@ private StringTerms newTerms(Random rand, BytesRef[] dict, boolean withNested) { "terms", BucketOrder.key(true), BucketOrder.count(false), - topNSize, - 1, Collections.emptyMap(), DocValueFormat.RAW, numShards, true, 0, buckets, - 0 + 0, + new TermsAggregator.BucketCountThresholds(1, 0, topNSize, numShards) ); } diff --git a/benchmarks/src/main/java/org/opensearch/benchmark/search/aggregations/bucket/terms/StringTermsSerializationBenchmark.java b/benchmarks/src/main/java/org/opensearch/benchmark/search/aggregations/bucket/terms/StringTermsSerializationBenchmark.java index 6d8721ce64090..b98a257dfbf48 100644 --- a/benchmarks/src/main/java/org/opensearch/benchmark/search/aggregations/bucket/terms/StringTermsSerializationBenchmark.java +++ b/benchmarks/src/main/java/org/opensearch/benchmark/search/aggregations/bucket/terms/StringTermsSerializationBenchmark.java @@ -34,12 +34,13 @@ import org.apache.lucene.util.BytesRef; import org.opensearch.common.io.stream.DelayableWriteable; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.search.DocValueFormat; import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.bucket.terms.StringTerms; +import org.opensearch.search.aggregations.bucket.terms.TermsAggregator; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Fork; @@ -64,9 +65,7 @@ @State(Scope.Benchmark) public class StringTermsSerializationBenchmark { private static final NamedWriteableRegistry REGISTRY = new NamedWriteableRegistry( - org.opensearch.common.collect.List.of( - new NamedWriteableRegistry.Entry(InternalAggregation.class, StringTerms.NAME, StringTerms::new) - ) + List.of(new NamedWriteableRegistry.Entry(InternalAggregation.class, StringTerms.NAME, StringTerms::new)) ); @Param(value = { "1000" }) private int buckets; @@ -75,30 +74,27 @@ public class StringTermsSerializationBenchmark { @Setup public void initResults() { - results = DelayableWriteable.referencing(InternalAggregations.from(org.opensearch.common.collect.List.of(newTerms(true)))); + results = DelayableWriteable.referencing(InternalAggregations.from(List.of(newTerms(true)))); } private StringTerms newTerms(boolean withNested) { List resultBuckets = new ArrayList<>(buckets); for (int i = 0; i < buckets; i++) { - InternalAggregations inner = withNested - ? InternalAggregations.from(org.opensearch.common.collect.List.of(newTerms(false))) - : InternalAggregations.EMPTY; + InternalAggregations inner = withNested ? InternalAggregations.from(List.of(newTerms(false))) : InternalAggregations.EMPTY; resultBuckets.add(new StringTerms.Bucket(new BytesRef("test" + i), i, inner, false, 0, DocValueFormat.RAW)); } return new StringTerms( "test", BucketOrder.key(true), BucketOrder.key(true), - buckets, - 1, null, DocValueFormat.RAW, buckets, false, 100000, resultBuckets, - 0 + 0, + new TermsAggregator.BucketCountThresholds(1, 0, buckets, buckets) ); } diff --git a/benchmarks/src/main/java/org/opensearch/benchmark/store/remote/filecache/FileCacheBenchmark.java b/benchmarks/src/main/java/org/opensearch/benchmark/store/remote/filecache/FileCacheBenchmark.java new file mode 100644 index 0000000000000..7cd8c672f45df --- /dev/null +++ b/benchmarks/src/main/java/org/opensearch/benchmark/store/remote/filecache/FileCacheBenchmark.java @@ -0,0 +1,131 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.benchmark.store.remote.filecache; + +import org.apache.lucene.store.IndexInput; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.NoopCircuitBreaker; +import org.opensearch.index.store.remote.filecache.CachedIndexInput; +import org.opensearch.index.store.remote.filecache.FileCache; +import org.opensearch.index.store.remote.filecache.FileCacheFactory; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +/** + * Simple benchmark test of {@link FileCache}. It uses a uniform random distribution + * of keys, which is very simple but unlikely to be representative of any real life + * workload. + */ +@Warmup(iterations = 1) +@Measurement(iterations = 1) +@Fork(1) +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Thread) +@Threads(8) +@SuppressWarnings("unused") // invoked by benchmarking framework +public class FileCacheBenchmark { + private static final CachedIndexInput INDEX_INPUT = new FixedSizeStubIndexInput(); + + @Benchmark + public void get(CacheParameters parameters, Blackhole blackhole) { + blackhole.consume(parameters.fileCache.get(randomKeyInCache(parameters))); + } + + @Benchmark + public void replace(CacheParameters parameters, Blackhole blackhole) { + blackhole.consume(parameters.fileCache.put(randomKeyInCache(parameters), INDEX_INPUT)); + } + + @Benchmark + public void put(CacheParameters parameters, Blackhole blackhole) { + blackhole.consume(parameters.fileCache.put(randomKeyNotInCache(parameters), INDEX_INPUT)); + } + + @Benchmark + public void remove(CacheParameters parameters) { + parameters.fileCache.remove(randomKeyInCache(parameters)); + } + + private static Path randomKeyInCache(CacheParameters parameters) { + int i = ThreadLocalRandom.current().nextInt(parameters.maximumNumberOfEntries); + return Paths.get(Integer.toString(i)); + } + + private static Path randomKeyNotInCache(CacheParameters parameters) { + int i = ThreadLocalRandom.current().nextInt(parameters.maximumNumberOfEntries, parameters.maximumNumberOfEntries * 2); + return Paths.get(Integer.toString(i)); + } + + @State(Scope.Benchmark) + public static class CacheParameters { + @Param({ "65536", "1048576" }) + int maximumNumberOfEntries; + + @Param({ "1", "8" }) + int concurrencyLevel; + + FileCache fileCache; + + @Setup + public void setup() { + fileCache = FileCacheFactory.createConcurrentLRUFileCache( + (long) maximumNumberOfEntries * INDEX_INPUT.length(), + concurrencyLevel, + new NoopCircuitBreaker(CircuitBreaker.REQUEST) + ); + for (long i = 0; i < maximumNumberOfEntries; i++) { + final Path key = Paths.get(Long.toString(i)); + fileCache.put(key, INDEX_INPUT); + fileCache.decRef(key); + } + } + } + + /** + * Stubbed out IndexInput that does nothing but report a fixed size + */ + private static class FixedSizeStubIndexInput implements CachedIndexInput { + @Override + public IndexInput getIndexInput() { + return null; + } + + @Override + public long length() { + return 1024 * 1024 * 8; // 8MiB + } + + @Override + public boolean isClosed() { + return false; + } + + @Override + public void close() throws Exception { + + } + } +} diff --git a/benchmarks/src/main/java/org/opensearch/benchmark/time/NanoTimeVsCurrentTimeMillisBenchmark.java b/benchmarks/src/main/java/org/opensearch/benchmark/time/NanoTimeVsCurrentTimeMillisBenchmark.java new file mode 100644 index 0000000000000..dfa0a8538108e --- /dev/null +++ b/benchmarks/src/main/java/org/opensearch/benchmark/time/NanoTimeVsCurrentTimeMillisBenchmark.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.benchmark.time; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.util.concurrent.TimeUnit; + +@Fork(3) +@Warmup(iterations = 10) +@Measurement(iterations = 20) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +@SuppressWarnings("unused") // invoked by benchmarking framework +public class NanoTimeVsCurrentTimeMillisBenchmark { + private volatile long var = 0; + + @Benchmark + public long currentTimeMillis() { + return System.currentTimeMillis(); + } + + @Benchmark + public long nanoTime() { + return System.nanoTime(); + } + + /* + * this acts as upper bound of how time is cached in org.opensearch.threadpool.ThreadPool + * */ + @Benchmark + public long accessLongVar() { + return var++; + } +} diff --git a/benchmarks/src/main/java/org/opensearch/common/ArrayRoundingBenchmark.java b/benchmarks/src/main/java/org/opensearch/common/ArrayRoundingBenchmark.java new file mode 100644 index 0000000000000..64c0a9e1d7aa6 --- /dev/null +++ b/benchmarks/src/main/java/org/opensearch/common/ArrayRoundingBenchmark.java @@ -0,0 +1,147 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.Random; +import java.util.function.Supplier; + +@Fork(value = 3) +@Warmup(iterations = 3, time = 1) +@Measurement(iterations = 1, time = 1) +@BenchmarkMode(Mode.Throughput) +public class ArrayRoundingBenchmark { + + @Benchmark + public void round(Blackhole bh, Options opts) { + Rounding.Prepared rounding = opts.supplier.get(); + for (long key : opts.queries) { + bh.consume(rounding.round(key)); + } + } + + @State(Scope.Benchmark) + public static class Options { + @Param({ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "12", + "14", + "16", + "18", + "20", + "22", + "24", + "26", + "29", + "32", + "37", + "41", + "45", + "49", + "54", + "60", + "64", + "74", + "83", + "90", + "98", + "108", + "118", + "128", + "144", + "159", + "171", + "187", + "204", + "229", + "256" }) + public Integer size; + + @Param({ "binary", "linear" }) + public String type; + + @Param({ "uniform", "skewed_edge", "skewed_center" }) + public String distribution; + + public long[] queries; + public Supplier supplier; + + @Setup + public void setup() { + Random random = new Random(size); + long[] values = new long[size]; + for (int i = 1; i < values.length; i++) { + values[i] = values[i - 1] + 100; + } + + long range = values[values.length - 1] - values[0] + 100; + long mean, stddev; + queries = new long[1000000]; + + switch (distribution) { + case "uniform": // all values equally likely. + for (int i = 0; i < queries.length; i++) { + queries[i] = values[0] + (nextPositiveLong(random) % range); + } + break; + case "skewed_edge": // distribution centered at p90 with ± 5% stddev. + mean = values[0] + (long) (range * 0.9); + stddev = (long) (range * 0.05); + for (int i = 0; i < queries.length; i++) { + queries[i] = Math.max(values[0], mean + (long) (random.nextGaussian() * stddev)); + } + break; + case "skewed_center": // distribution centered at p50 with ± 5% stddev. + mean = values[0] + (long) (range * 0.5); + stddev = (long) (range * 0.05); + for (int i = 0; i < queries.length; i++) { + queries[i] = Math.max(values[0], mean + (long) (random.nextGaussian() * stddev)); + } + break; + default: + throw new IllegalArgumentException("invalid distribution: " + distribution); + } + + switch (type) { + case "binary": + supplier = () -> new Rounding.BinarySearchArrayRounding(values, size, null); + break; + case "linear": + supplier = () -> new Rounding.BidirectionalLinearSearchArrayRounding(values, size, null); + break; + default: + throw new IllegalArgumentException("invalid type: " + type); + } + } + + private static long nextPositiveLong(Random random) { + return random.nextLong() & Long.MAX_VALUE; + } + } +} diff --git a/benchmarks/src/main/java/org/opensearch/common/hash/HashFunctionBenchmark.java b/benchmarks/src/main/java/org/opensearch/common/hash/HashFunctionBenchmark.java new file mode 100644 index 0000000000000..8842337a468a1 --- /dev/null +++ b/benchmarks/src/main/java/org/opensearch/common/hash/HashFunctionBenchmark.java @@ -0,0 +1,172 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.hash; + +import org.apache.lucene.util.StringHelper; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.Random; + +@Fork(value = 3) +@Warmup(iterations = 1, time = 1) +@Measurement(iterations = 3, time = 3) +@BenchmarkMode(Mode.Throughput) +public class HashFunctionBenchmark { + + @Benchmark + public void hash(Blackhole bh, Options opts) { + bh.consume(opts.type.hash(opts.data)); + } + + @State(Scope.Benchmark) + public static class Options { + @Param({ "MURMUR3", "T1HA1" }) + public Type type; + + @Param({ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "12", + "14", + "16", + "18", + "21", + "24", + "28", + "32", + "36", + "41", + "47", + "54", + "62", + "71", + "81", + "90", + "100", + "112", + "125", + "139", + "156", + "174", + "194", + "220", + "245", + "272", + "302", + "339", + "384", + "431", + "488", + "547", + "608", + "675", + "763", + "863", + "967", + "1084", + "1225", + "1372", + "1537", + "1737", + "1929", + "2142", + "2378", + "2664", + "3011", + "3343", + "3778", + "4232", + "4783", + "5310", + "5895", + "6662", + "7529", + "8508", + "9444", + "10483", + "11741", + "13150", + "14597", + "16495", + "18475", + "20877", + "23383", + "25956", + "29071", + "32560", + "36142", + "40841", + "46151", + "52151", + "57888", + "65414", + "72610", + "82050", + "91076", + "102006", + "114247", + "127957", + "143312", + "159077", + "176576", + "199531", + "223475", + "250292", + "277825", + "313943", + "351617", + "393812" }) + public Integer length; + public byte[] data; + + @Setup + public void setup() { + data = new byte[length]; + new Random(0).nextBytes(data); + } + } + + public enum Type { + MURMUR3((data, offset, length) -> StringHelper.murmurhash3_x86_32(data, offset, length, 0)), + T1HA1((data, offset, length) -> T1ha1.hash(data, offset, length, 0)); + + private final Hasher hasher; + + Type(Hasher hasher) { + this.hasher = hasher; + } + + public long hash(byte[] data) { + return hasher.hash(data, 0, data.length); + } + } + + @FunctionalInterface + interface Hasher { + long hash(byte[] data, int offset, int length); + } +} diff --git a/benchmarks/src/main/java/org/opensearch/common/util/BytesRefHashBenchmark.java b/benchmarks/src/main/java/org/opensearch/common/util/BytesRefHashBenchmark.java new file mode 100644 index 0000000000000..fef12b6d9f84a --- /dev/null +++ b/benchmarks/src/main/java/org/opensearch/common/util/BytesRefHashBenchmark.java @@ -0,0 +1,249 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.util; + +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.StringHelper; +import org.opensearch.common.hash.T1ha1; +import org.opensearch.common.lease.Releasable; +import org.opensearch.common.lease.Releasables; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.stream.Stream; + +@Fork(value = 3) +@Warmup(iterations = 1, time = 2) +@Measurement(iterations = 3, time = 5) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class BytesRefHashBenchmark { + private static final int NUM_TABLES = 20; // run across many tables so that caches aren't effective + private static final int NUM_HITS = 1_000_000; // num hits per table + + @Benchmark + public void add(Blackhole bh, Options opts) { + HashTable[] tables = Stream.generate(opts.type::create).limit(NUM_TABLES).toArray(HashTable[]::new); + + for (int hit = 0; hit < NUM_HITS; hit++) { + BytesRef key = opts.keys[hit % opts.keys.length]; + for (HashTable table : tables) { + bh.consume(table.add(key)); + } + } + + Releasables.close(tables); + } + + @State(Scope.Benchmark) + public static class Options { + @Param({ "MURMUR3", "T1HA1" }) + public Type type; + + @Param({ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "10", + "12", + "14", + "16", + "19", + "22", + "25", + "29", + "33", + "38", + "43", + "50", + "57", + "65", + "75", + "86", + "97", + "109", + "124", + "141", + "161", + "182", + "204", + "229", + "262", + "297", + "336", + "380", + "430", + "482", + "550", + "610", + "704", + "801", + "914", + "1042", + "1178", + "1343", + "1532", + "1716", + "1940", + "2173", + "2456", + "2751", + "3082", + "3514", + "4006", + "4487", + "5026", + "5730", + "6418", + "7317", + "8196", + "9180", + "10374", + "11723", + "13247", + "14837", + "16915", + "19114", + "21599", + "24623", + "28071", + "32001", + "36482", + "41590", + "46581", + "52637", + "58954", + "67208", + "76618", + "86579", + "97835", + "109576", + "122726", + "138681", + "156710", + "175516", + "198334", + "222135", + "248792", + "281135", + "320494", + "365364", + "409208", + "466498", + "527143", + "595672", + "667153", + "753883", + "851888", + "971153" }) + public Integer size; + + @Param({ "5", "28", "59", "105" }) + public Integer length; + + private BytesRef[] keys; + + @Setup + public void setup() { + assert size <= Math.pow(26, length) : "key length too small to generate the required number of keys"; + // Seeding with size will help produce deterministic results for the same size, and avoid similar + // looking clusters for different sizes, in case one hash function got unlucky. + Random random = new Random(size); + Set seen = new HashSet<>(); + keys = new BytesRef[size]; + for (int i = 0; i < size; i++) { + BytesRef key; + do { + key = new BytesRef( + random.ints(97, 123) + .limit(length) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString() + ); + } while (seen.contains(key)); + keys[i] = key; + seen.add(key); + } + } + } + + public enum Type { + MURMUR3(() -> new HashTable() { + private final BytesRefHash table = new BytesRefHash(1, 0.6f, key -> { + // Repeating the lower bits into upper bits to make the fingerprint work. + // Alternatively, use a 64-bit murmur3 hash, but that won't represent the baseline. + long h = StringHelper.murmurhash3_x86_32(key.bytes, key.offset, key.length, 0) & 0xFFFFFFFFL; + return h | (h << 32); + }, BigArrays.NON_RECYCLING_INSTANCE); + + @Override + public long add(BytesRef key) { + return table.add(key); + } + + @Override + public void close() { + table.close(); + } + }), + + T1HA1(() -> new HashTable() { + private final BytesRefHash table = new BytesRefHash( + 1, + 0.6f, + key -> T1ha1.hash(key.bytes, key.offset, key.length, 0), + BigArrays.NON_RECYCLING_INSTANCE + ); + + @Override + public long add(BytesRef key) { + return table.add(key); + } + + @Override + public void close() { + table.close(); + } + }); + + private final Supplier supplier; + + Type(Supplier supplier) { + this.supplier = supplier; + } + + public HashTable create() { + return supplier.get(); + } + } + + interface HashTable extends Releasable { + long add(BytesRef key); + } +} diff --git a/benchmarks/src/main/java/org/opensearch/common/util/LongHashBenchmark.java b/benchmarks/src/main/java/org/opensearch/common/util/LongHashBenchmark.java new file mode 100644 index 0000000000000..4d746b790a348 --- /dev/null +++ b/benchmarks/src/main/java/org/opensearch/common/util/LongHashBenchmark.java @@ -0,0 +1,425 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.util; + +import org.opensearch.common.lease.Releasable; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +@Fork(value = 3) +@Warmup(iterations = 1, time = 4) +@Measurement(iterations = 3, time = 2) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class LongHashBenchmark { + + @Benchmark + public void add(Blackhole bh, HashTableOptions tableOpts, WorkloadOptions workloadOpts) { + try (HashTable table = tableOpts.get(); WorkloadIterator iter = workloadOpts.iter()) { + while (iter.hasNext()) { + bh.consume(table.add(iter.next())); + } + } + } + + /** + * Creates a hash table with varying parameters. + */ + @State(Scope.Benchmark) + public static class HashTableOptions { + + @Param({ "LongHash", "ReorganizingLongHash" }) + public String type; + + @Param({ "1" }) + public long initialCapacity; + + @Param({ "0.6" }) + public float loadFactor; + + private Supplier supplier; + + @Setup + public void setup() { + switch (type) { + case "LongHash": + supplier = this::newLongHash; + break; + case "ReorganizingLongHash": + supplier = this::newReorganizingLongHash; + break; + default: + throw new IllegalArgumentException("invalid hash table type: " + type); + } + } + + public HashTable get() { + return supplier.get(); + } + + private HashTable newLongHash() { + return new HashTable() { + private final LongHash table = new LongHash(initialCapacity, loadFactor, BigArrays.NON_RECYCLING_INSTANCE); + + @Override + public long add(long key) { + return table.add(key); + } + + @Override + public void close() { + table.close(); + } + }; + } + + private HashTable newReorganizingLongHash() { + return new HashTable() { + private final ReorganizingLongHash table = new ReorganizingLongHash( + initialCapacity, + loadFactor, + BigArrays.NON_RECYCLING_INSTANCE + ); + + @Override + public long add(long key) { + return table.add(key); + } + + @Override + public void close() { + table.close(); + } + }; + } + } + + /** + * Creates a workload with varying parameters. + */ + @State(Scope.Benchmark) + public static class WorkloadOptions { + public static final int NUM_HITS = 20_000_000; + + /** + * Repeat the experiment with growing number of keys. + * These values are generated with an exponential growth pattern such that: + * value = ceil(previous_value * random_float_between(1.0, 1.14)) + */ + @Param({ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "13", + "15", + "17", + "18", + "19", + "20", + "21", + "23", + "26", + "27", + "30", + "32", + "35", + "41", + "45", + "50", + "53", + "54", + "55", + "57", + "63", + "64", + "69", + "74", + "80", + "84", + "91", + "98", + "101", + "111", + "114", + "124", + "128", + "139", + "148", + "161", + "162", + "176", + "190", + "204", + "216", + "240", + "257", + "269", + "291", + "302", + "308", + "327", + "341", + "374", + "402", + "412", + "438", + "443", + "488", + "505", + "558", + "612", + "621", + "623", + "627", + "642", + "717", + "765", + "787", + "817", + "915", + "962", + "1011", + "1083", + "1163", + "1237", + "1301", + "1424", + "1541", + "1716", + "1805", + "1817", + "1934", + "2024", + "2238", + "2281", + "2319", + "2527", + "2583", + "2639", + "2662", + "2692", + "2991", + "3201", + "3215", + "3517", + "3681", + "3710", + "4038", + "4060", + "4199", + "4509", + "4855", + "5204", + "5624", + "6217", + "6891", + "7569", + "8169", + "8929", + "9153", + "10005", + "10624", + "10931", + "12070", + "12370", + "13694", + "14227", + "15925", + "17295", + "17376", + "18522", + "19200", + "20108", + "21496", + "23427", + "24224", + "26759", + "29199", + "29897", + "32353", + "33104", + "36523", + "38480", + "38958", + "40020", + "44745", + "45396", + "47916", + "49745", + "49968", + "52231", + "53606" }) + public int size; + + @Param({ "correlated", "uncorrelated", "distinct" }) + public String dataset; + + private WorkloadIterator iterator; + + @Setup + public void setup() { + switch (dataset) { + case "correlated": + iterator = newCorrelatedWorkload(); + break; + case "uncorrelated": + iterator = newUncorrelatedWorkload(); + break; + case "distinct": + iterator = newDistinctWorkload(); + break; + default: + throw new IllegalArgumentException("invalid dataset: " + dataset); + } + } + + public WorkloadIterator iter() { + return iterator; + } + + /** + * Simulates monotonically increasing timestamp data with multiple hits mapping to the same key. + */ + private WorkloadIterator newCorrelatedWorkload() { + assert NUM_HITS >= size : "ensure hits >= size so that each key is used at least once"; + + final long[] data = new long[size]; + for (int i = 0; i < data.length; i++) { + data[i] = 1420070400000L + 3600000L * i; + } + + return new WorkloadIterator() { + private int count = 0; + private int index = 0; + private int remaining = NUM_HITS / data.length; + + @Override + public boolean hasNext() { + return count < NUM_HITS; + } + + @Override + public long next() { + if (--remaining <= 0) { + index = (index + 1) % data.length; + remaining = NUM_HITS / data.length; + } + count++; + return data[index]; + } + + @Override + public void reset() { + count = 0; + index = 0; + remaining = NUM_HITS / data.length; + } + }; + } + + /** + * Simulates uncorrelated data (such as travel distance / fare amount). + */ + private WorkloadIterator newUncorrelatedWorkload() { + assert NUM_HITS >= size : "ensure hits >= size so that each key is used at least once"; + + final Random random = new Random(0); // fixed seed for reproducible results + final long[] data = new long[size]; + for (int i = 0; i < data.length; i++) { + data[i] = Double.doubleToLongBits(20.0 + 80 * random.nextDouble()); + } + + return new WorkloadIterator() { + private int count = 0; + private int index = 0; + + @Override + public boolean hasNext() { + return count < NUM_HITS; + } + + @Override + public long next() { + count++; + index = (index + 1) % data.length; + return data[index]; + } + + @Override + public void reset() { + count = 0; + index = 0; + } + }; + } + + /** + * Simulates workload with high cardinality, i.e., each hit mapping to a different key. + */ + private WorkloadIterator newDistinctWorkload() { + return new WorkloadIterator() { + private int count = 0; + + @Override + public boolean hasNext() { + return count < size; + } + + @Override + public long next() { + return count++; + } + + @Override + public void reset() { + count = 0; + } + }; + } + } + + private interface HashTable extends Releasable { + long add(long key); + } + + private interface WorkloadIterator extends Releasable { + boolean hasNext(); + + long next(); + + void reset(); + + @Override + default void close() { + reset(); + } + } +} diff --git a/build.gradle b/build.gradle index be5766f327e0d..4b23a8d07dddd 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,9 @@ * under the License. */ +import java.nio.charset.StandardCharsets; +import java.io.ByteArrayOutputStream; + import com.avast.gradle.dockercompose.tasks.ComposePull import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin import de.thetaphi.forbiddenapis.gradle.ForbiddenApisPlugin @@ -37,18 +40,24 @@ import org.opensearch.gradle.Version import org.opensearch.gradle.VersionProperties import org.opensearch.gradle.info.BuildParams import org.opensearch.gradle.plugin.PluginBuildPlugin +import org.opensearch.gradle.tar.SymbolicLinkPreservingTar import org.gradle.plugins.ide.eclipse.model.AccessRule import org.gradle.plugins.ide.eclipse.model.EclipseJdt import org.gradle.plugins.ide.eclipse.model.SourceFolder -import org.gradle.util.DistributionLocator -import org.gradle.util.GradleVersion +import org.gradle.api.Project; +import org.gradle.process.ExecResult; +import org.opensearch.gradle.CheckCompatibilityTask + import static org.opensearch.gradle.util.GradleUtils.maybeConfigure plugins { id 'lifecycle-base' id 'opensearch.docker-support' id 'opensearch.global-build-info' - id "com.diffplug.spotless" version "6.3.0" apply false + id "com.diffplug.spotless" version "6.20.0" apply false + id "org.gradle.test-retry" version "1.5.4" apply false + id "test-report-aggregation" + id 'jacoco-report-aggregation' } apply from: 'gradle/build-complete.gradle' @@ -147,6 +156,30 @@ Map buildMetadataMap = buildMetadataValue.tokenize(';').collectE return [key, value] } + /** + * Using 'git' command line (if available), tries to fetch the commit date of the current revision + * @return commit date of the current revision or 0 if it is not available + */ +long gitRevisionDate = { + // Try to get last commit date as Unix timestamp + try (ByteArrayOutputStream stdout = new ByteArrayOutputStream()) { + ExecResult result = project.exec(spec -> { + spec.setIgnoreExitValue(true); + spec.setStandardOutput(stdout); + spec.commandLine("git", "log", "-1", "--format=%ct"); + }); + + if (result.getExitValue() == 0) { + return Long.parseLong(stdout.toString(StandardCharsets.UTF_8).replaceAll("\\s", "")) * 1000; /* seconds to millis */ + } + } catch (IOException | GradleException | NumberFormatException ex) { + /* fall back to default Unix epoch timestamp */ + } + + return 0; +}() + + // injecting groovy property variables into all projects allprojects { project.ext { @@ -230,9 +263,15 @@ tasks.register("branchConsistency") { allprojects { // configure compiler options tasks.withType(JavaCompile).configureEach { JavaCompile compile -> + options.fork = true + + configure(options.forkOptions) { + memoryMaximumSize = project.property('options.forkOptions.memoryMaximumSize') + } + // See please https://bugs.openjdk.java.net/browse/JDK-8209058 if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_11) { - compile.options.compilerArgs << '-Werror' + compile.options.compilerArgs << '-Werror' } compile.options.compilerArgs << '-Xlint:auxiliaryclass' compile.options.compilerArgs << '-Xlint:cast' @@ -275,14 +314,24 @@ allprojects { // see https://discuss.gradle.org/t/add-custom-javadoc-option-that-does-not-take-an-argument/5959 javadoc.options.encoding = 'UTF8' javadoc.options.addStringOption('Xdoclint:all,-missing', '-quiet') + boolean failOnJavadocWarning = project.ext.has('failOnJavadocWarning') ? project.ext.get('failOnJavadocWarning') : true + if (failOnJavadocWarning) { + javadoc.options.addStringOption('Xwerror', '-quiet') + } + javadoc.options.tags = ["opensearch.internal", "opensearch.api", "opensearch.experimental"] + javadoc.options.addStringOption("-release", java.targetCompatibility.majorVersion) } // support for reproducible builds - tasks.withType(AbstractArchiveTask).configureEach { + tasks.withType(AbstractArchiveTask).configureEach { task -> // ignore file timestamps // be consistent in archive file order - preserveFileTimestamps = false - reproducibleFileOrder = true + task.preserveFileTimestamps = false + task.reproducibleFileOrder = true + if (task instanceof SymbolicLinkPreservingTar) { + // Replace file timestamps with latest Git revision date (if available) + task.lastModifiedTimestamp = gitRevisionDate + } } project.afterEvaluate { @@ -318,9 +367,10 @@ allprojects { } else { // Link to non-shadowed dependant projects project.javadoc.dependsOn "${upstreamProject.path}:javadoc" - String externalLinkName = upstreamProject.archivesBaseName + String externalLinkName = upstreamProject.base.archivesBaseName String artifactPath = dep.group.replaceAll('\\.', '/') + '/' + externalLinkName.replaceAll('\\.', '/') + '/' + dep.version - project.javadoc.options.linksOffline artifactsHost + "/javadoc/" + artifactPath, "${upstreamProject.buildDir}/docs/javadoc/" + String projectRelativePath = project.relativePath(upstreamProject.buildDir) + project.javadoc.options.linksOffline artifactsHost + "/javadoc/" + artifactPath, "${projectRelativePath}/docs/javadoc/" } } boolean hasShadow = project.plugins.hasPlugin(ShadowPlugin) @@ -349,6 +399,10 @@ allprojects { // the dependency is added. gradle.projectsEvaluated { allprojects { + project.tasks.withType(JavaForkOptions) { + maxHeapSize project.property('options.forkOptions.memoryMaximumSize') + } + if (project.path == ':test:framework') { // :test:framework:test cannot run before and after :server:test return @@ -362,6 +416,9 @@ gradle.projectsEvaluated { if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_17) { task.jvmArgs += ["-Djava.security.manager=allow"] } + if (BuildParams.runtimeJavaVersion >= JavaVersion.VERSION_20) { + task.jvmArgs += ["--add-modules=jdk.incubator.vector"] + } } } @@ -384,11 +441,111 @@ gradle.projectsEvaluated { } } } + + dependencies { + subprojects.findAll { it.pluginManager.hasPlugin('java') }.forEach { + testReportAggregation it + } + subprojects.findAll { it.pluginManager.hasPlugin('jacoco') }.forEach { + jacocoAggregation it + } + } +} + +// test retry configuration +subprojects { + apply plugin: "org.gradle.test-retry" + tasks.withType(Test).configureEach { + retry { + if (BuildParams.isCi()) { + maxRetries = 3 + maxFailures = 10 + } + failOnPassedAfterRetry = false + filter { + includeClasses.add("org.opensearch.action.admin.cluster.node.tasks.ResourceAwareTasksTests") + includeClasses.add("org.opensearch.action.admin.cluster.tasks.PendingTasksBlocksIT") + includeClasses.add("org.opensearch.action.admin.indices.create.CreateIndexIT") + includeClasses.add("org.opensearch.action.admin.indices.create.ShrinkIndexIT") + includeClasses.add("org.opensearch.aliases.IndexAliasesIT") + includeClasses.add("org.opensearch.backwards.MixedClusterClientYamlTestSuiteIT") + includeClasses.add("org.opensearch.blocks.SimpleBlocksIT") + includeClasses.add("org.opensearch.client.PitIT") + includeClasses.add("org.opensearch.client.ReindexIT") + includeClasses.add("org.opensearch.cluster.ClusterHealthIT") + includeClasses.add("org.opensearch.cluster.allocation.AwarenessAllocationIT") + includeClasses.add("org.opensearch.cluster.allocation.ClusterRerouteIT") + includeClasses.add("org.opensearch.cluster.coordination.AwarenessAttributeDecommissionIT") + includeClasses.add("org.opensearch.cluster.metadata.IndexGraveyardTests") + includeClasses.add("org.opensearch.cluster.routing.MovePrimaryFirstTests") + includeClasses.add("org.opensearch.cluster.routing.allocation.decider.DiskThresholdDeciderIT") + includeClasses.add("org.opensearch.common.util.concurrent.QueueResizableOpenSearchThreadPoolExecutorTests") + includeClasses.add("org.opensearch.gateway.RecoveryFromGatewayIT") + includeClasses.add("org.opensearch.gateway.ReplicaShardAllocatorIT") + includeClasses.add("org.opensearch.http.SearchRestCancellationIT") + includeClasses.add("org.opensearch.http.netty4.Netty4HttpServerTransportTests") + includeClasses.add("org.opensearch.index.IndexServiceTests") + includeClasses.add("org.opensearch.index.IndexSettingsTests") + includeClasses.add("org.opensearch.index.SegmentReplicationPressureIT") + includeClasses.add("org.opensearch.index.ShardIndexingPressureIT") + includeClasses.add("org.opensearch.index.ShardIndexingPressureSettingsIT") + includeClasses.add("org.opensearch.index.reindex.BulkByScrollResponseTests") + includeClasses.add("org.opensearch.index.reindex.DeleteByQueryBasicTests") + includeClasses.add("org.opensearch.index.reindex.UpdateByQueryBasicTests") + includeClasses.add("org.opensearch.index.shard.IndexShardIT") + includeClasses.add("org.opensearch.index.shard.RemoteIndexShardTests") + includeClasses.add("org.opensearch.index.shard.RemoteStoreRefreshListenerTests") + includeClasses.add("org.opensearch.index.translog.RemoteFSTranslogTests") + includeClasses.add("org.opensearch.indices.DateMathIndexExpressionsIntegrationIT") + includeClasses.add("org.opensearch.indices.replication.RemoteStoreReplicationSourceTests") + includeClasses.add("org.opensearch.indices.replication.SegmentReplicationAllocationIT") + includeClasses.add("org.opensearch.indices.replication.SegmentReplicationIT") + includeClasses.add("org.opensearch.indices.replication.SegmentReplicationRelocationIT") + includeClasses.add("org.opensearch.indices.replication.SegmentReplicationTargetServiceTests") + includeClasses.add("org.opensearch.indices.state.CloseWhileRelocatingShardsIT") + includeClasses.add("org.opensearch.monitor.fs.FsHealthServiceTests") + includeClasses.add("org.opensearch.recovery.ReplicationCollectionTests") + includeClasses.add("org.opensearch.remotestore.CreateRemoteIndexClusterDefaultDocRep") + includeClasses.add("org.opensearch.remotestore.CreateRemoteIndexIT") + includeClasses.add("org.opensearch.remotestore.CreateRemoteIndexTranslogDisabledIT") + includeClasses.add("org.opensearch.remotestore.RemoteIndexPrimaryRelocationIT") + includeClasses.add("org.opensearch.remotestore.RemoteStoreBackpressureIT") + includeClasses.add("org.opensearch.remotestore.RemoteStoreIT") + includeClasses.add("org.opensearch.remotestore.RemoteStoreRefreshListenerIT") + includeClasses.add("org.opensearch.remotestore.RemoteStoreStatsIT") + includeClasses.add("org.opensearch.remotestore.SegmentReplicationRemoteStoreIT") + includeClasses.add("org.opensearch.remotestore.SegmentReplicationUsingRemoteStoreIT") + includeClasses.add("org.opensearch.remotestore.multipart.RemoteStoreMultipartIT") + includeClasses.add("org.opensearch.repositories.azure.AzureBlobContainerRetriesTests") + includeClasses.add("org.opensearch.repositories.azure.AzureBlobStoreRepositoryTests") + includeClasses.add("org.opensearch.repositories.gcs.GoogleCloudStorageBlobContainerRetriesTests") + includeClasses.add("org.opensearch.repositories.gcs.GoogleCloudStorageBlobStoreRepositoryTests") + includeClasses.add("org.opensearch.repositories.s3.S3BlobStoreRepositoryTests") + includeClasses.add("org.opensearch.search.ConcurrentSegmentSearchTimeoutIT") + includeClasses.add("org.opensearch.search.SearchTimeoutIT") + includeClasses.add("org.opensearch.search.SearchWeightedRoutingIT") + includeClasses.add("org.opensearch.search.aggregations.bucket.DoubleTermsIT") + includeClasses.add("org.opensearch.search.aggregations.bucket.terms.StringTermsIT") + includeClasses.add("org.opensearch.search.aggregations.metrics.CardinalityIT") + includeClasses.add("org.opensearch.search.backpressure.SearchBackpressureIT") + includeClasses.add("org.opensearch.search.basic.SearchWithRandomIOExceptionsIT") + includeClasses.add("org.opensearch.search.pit.DeletePitMultiNodeIT") + includeClasses.add("org.opensearch.smoketest.SmokeTestMultiNodeClientYamlTestSuiteIT") + includeClasses.add("org.opensearch.snapshots.CloneSnapshotIT") + includeClasses.add("org.opensearch.snapshots.DedicatedClusterSnapshotRestoreIT") + includeClasses.add("org.opensearch.snapshots.RestoreSnapshotIT") + includeClasses.add("org.opensearch.snapshots.SnapshotStatusApisIT") + includeClasses.add("org.opensearch.test.rest.ClientYamlTestSuiteIT") + includeClasses.add("org.opensearch.upgrade.DetectEsInstallationTaskTests") + } + } + } } // eclipse configuration allprojects { apply plugin: 'eclipse' + // Name all the non-root projects after their path so that paths get grouped together when imported into eclipse. if (path != ':') { eclipse.project.name = path @@ -445,25 +602,21 @@ allprojects { tasks.named('eclipse') { dependsOn 'cleanEclipse', 'copyEclipseSettings' } afterEvaluate { - tasks.findByName("eclipseJdt")?.configure { - dependsOn 'copyEclipseSettings' - } + tasks.findByName("eclipseJdt")?.configure { + dependsOn 'copyEclipseSettings' + } } } wrapper { distributionType = 'ALL' doLast { - final DistributionLocator locator = new DistributionLocator() - final GradleVersion version = GradleVersion.version(wrapper.gradleVersion) - final URI distributionUri = locator.getDistributionFor(version, wrapper.distributionType.name().toLowerCase(Locale.ENGLISH)) - final URI sha256Uri = new URI(distributionUri.toString() + ".sha256") - final String sha256Sum = new String(sha256Uri.toURL().bytes) - wrapper.getPropertiesFile() << "distributionSha256Sum=${sha256Sum}\n" + def sha256Sum = new String(new URL(getDistributionUrl() + ".sha256").bytes) + propertiesFile << "distributionSha256Sum=${sha256Sum}\n" println "Added checksum to wrapper properties" // Update build-tools to reflect the Gradle upgrade // TODO: we can remove this once we have tests to make sure older versions work. - project(':build-tools').file('src/main/resources/minimumGradleVersion').text = gradleVersion + project(':build-tools').file('src/main/resources/minimumGradleVersion').text = gradleVersion + "\n" println "Updated minimum Gradle Version" } } @@ -545,3 +698,32 @@ subprojects { } } } + +reporting { + reports { + testAggregateTestReport(AggregateTestReport) { + testType = TestSuiteType.UNIT_TEST + } + } +} + +// Enable XML test reports for Jenkins integration +tasks.withType(TestTaskReports).configureEach { + junitXml.enabled = true +} + +tasks.named(JavaBasePlugin.CHECK_TASK_NAME) { + dependsOn tasks.named('testAggregateTestReport', TestReport) +} + +tasks.register('checkCompatibility', CheckCompatibilityTask) { + description('Checks the compatibility with child components') +} + +allprojects { project -> + project.afterEvaluate { + if (project.tasks.findByName('publishToMavenLocal')) { + checkCompatibility.dependsOn(project.tasks.publishToMavenLocal) + } + } +} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index f940eec593306..66f99b93bcedb 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -61,7 +61,7 @@ Properties props = VersionPropertiesLoader.loadBuildSrcVersion(project.file('ver version = props.getProperty("opensearch") def generateVersionProperties = tasks.register("generateVersionProperties", WriteProperties) { - outputFile = "${buildDir}/version.properties" + destinationFile = file("${buildDir}/version.properties") comment = 'Generated version properties' properties(props) } @@ -102,31 +102,47 @@ dependencies { api localGroovy() - api 'commons-codec:commons-codec:1.15' - api 'org.apache.commons:commons-compress:1.21' - api 'org.apache.ant:ant:1.10.12' - api 'com.netflix.nebula:gradle-extra-configurations-plugin:7.0.0' - api 'com.netflix.nebula:nebula-publishing-plugin:4.4.4' - api 'com.netflix.nebula:gradle-info-plugin:7.1.3' - api 'org.apache.rat:apache-rat:0.13' - api 'commons-io:commons-io:2.7' - api "net.java.dev.jna:jna:5.10.0" - api 'gradle.plugin.com.github.johnrengelman:shadow:7.1.2' - api 'de.thetaphi:forbiddenapis:3.2' - api 'com.avast.gradle:gradle-docker-compose-plugin:0.14.12' - api 'org.apache.maven:maven-model:3.6.2' - api 'com.networknt:json-schema-validator:1.0.67' - api "com.fasterxml.jackson.core:jackson-databind:${props.getProperty('jackson')}" + api 'commons-codec:commons-codec:1.16.0' + api 'org.apache.commons:commons-compress:1.24.0' + api 'org.apache.ant:ant:1.10.13' + api 'com.netflix.nebula:gradle-extra-configurations-plugin:10.0.0' + api 'com.netflix.nebula:nebula-publishing-plugin:20.3.0' + api 'com.netflix.nebula:gradle-info-plugin:12.1.6' + api 'org.apache.rat:apache-rat:0.15' + api 'commons-io:commons-io:2.11.0' + api "net.java.dev.jna:jna:5.13.0" + api 'com.github.johnrengelman:shadow:8.1.1' + api 'org.jdom:jdom2:2.0.6.1' + api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${props.getProperty('kotlin')}" + api 'de.thetaphi:forbiddenapis:3.5.1' + api 'com.avast.gradle:gradle-docker-compose-plugin:0.16.11' + api "org.yaml:snakeyaml:${props.getProperty('snakeyaml')}" + api 'org.apache.maven:maven-model:3.9.4' + api 'com.networknt:json-schema-validator:1.0.86' + api 'org.jruby.jcodings:jcodings:1.0.58' + api 'org.jruby.joni:joni:2.2.1' + api "com.fasterxml.jackson.core:jackson-databind:${props.getProperty('jackson_databind')}" + api "org.ajoberstar.grgit:grgit-core:5.2.0" testFixturesApi "junit:junit:${props.getProperty('junit')}" testFixturesApi "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${props.getProperty('randomizedrunner')}" testFixturesApi gradleApi() testFixturesApi gradleTestKit() - testImplementation 'com.github.tomakehurst:wiremock-jre8-standalone:2.32.0' + testImplementation 'org.wiremock:wiremock-standalone:3.1.0' testImplementation "org.mockito:mockito-core:${props.getProperty('mockito')}" - integTestImplementation('org.spockframework:spock-core:2.1-groovy-3.0') { + integTestImplementation('org.spockframework:spock-core:2.3-groovy-3.0') { exclude module: "groovy" } + implementation('org.ajoberstar.grgit:grgit-core:5.2.0') { + exclude group: 'org.eclipse.jgit', module: 'org.eclipse.jgit' + } + implementation 'org.eclipse.jgit:org.eclipse.jgit:6.7.0.202309050840-r' +} + +configurations.all { + resolutionStrategy { + force "com.google.guava:guava:${props.getProperty('guava')}" + } } /***************************************************************************** @@ -158,8 +174,10 @@ if (project != rootProject) { apply plugin: 'opensearch.publish' allprojects { - targetCompatibility = JavaVersion.VERSION_11 - sourceCompatibility = JavaVersion.VERSION_11 + java { + targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_11 + } } // groovydoc succeeds, but has some weird internal exception... @@ -188,6 +206,7 @@ if (project != rootProject) { distribution project(':distribution:archives:linux-tar') distribution project(':distribution:archives:windows-zip') + integTestRuntimeOnly(project(":libs:opensearch-common")) integTestRuntimeOnly(project(":libs:opensearch-core")) } diff --git a/buildSrc/reaper/build.gradle b/buildSrc/reaper/build.gradle index d5e8d6ebc7099..58d06b02e9f4b 100644 --- a/buildSrc/reaper/build.gradle +++ b/buildSrc/reaper/build.gradle @@ -11,6 +11,11 @@ apply plugin: 'java' +java { + targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_11 +} + jar { archiveFileName = "${project.name}.jar" manifest { diff --git a/buildSrc/src/integTest/java/org/opensearch/gradle/tar/SymbolicLinkPreservingTarIT.java b/buildSrc/src/integTest/java/org/opensearch/gradle/tar/SymbolicLinkPreservingTarIT.java index b70574c507f70..61aa55b9c6b53 100644 --- a/buildSrc/src/integTest/java/org/opensearch/gradle/tar/SymbolicLinkPreservingTarIT.java +++ b/buildSrc/src/integTest/java/org/opensearch/gradle/tar/SymbolicLinkPreservingTarIT.java @@ -35,6 +35,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; +import org.apache.tools.ant.taskdefs.condition.Os; import org.opensearch.gradle.test.GradleIntegrationTestCase; import org.gradle.api.GradleException; import org.gradle.testkit.runner.GradleRunner; @@ -52,6 +53,7 @@ import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assume.assumeFalse; public class SymbolicLinkPreservingTarIT extends GradleIntegrationTestCase { @@ -60,6 +62,7 @@ public class SymbolicLinkPreservingTarIT extends GradleIntegrationTestCase { @Before public void before() throws IOException { + assumeFalse("Skip tar tests on windows.", Os.isFamily(Os.FAMILY_WINDOWS)); final Path realFolder = temporaryFolder.getRoot().toPath().resolve("real-folder"); Files.createDirectory(realFolder); Files.createFile(realFolder.resolve("file")); diff --git a/buildSrc/src/main/groovy/org/opensearch/gradle/CheckCompatibilityTask.groovy b/buildSrc/src/main/groovy/org/opensearch/gradle/CheckCompatibilityTask.groovy new file mode 100644 index 0000000000000..cfd3d04ecd16c --- /dev/null +++ b/buildSrc/src/main/groovy/org/opensearch/gradle/CheckCompatibilityTask.groovy @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle + +import groovy.json.JsonSlurper +import org.ajoberstar.grgit.Grgit +import org.ajoberstar.grgit.operation.BranchListOp +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction +import org.gradle.internal.os.OperatingSystem + +import java.nio.file.Paths + +class CheckCompatibilityTask extends DefaultTask { + + static final String REPO_URL = 'https://raw.githubusercontent.com/opensearch-project/opensearch-plugins/main/plugins/.meta' + + @Input + List repositoryUrls = project.hasProperty('repositoryUrls') ? project.property('repositoryUrls').split(',') : getRepoUrls() + + @Input + String ref = project.hasProperty('ref') ? project.property('ref') : '2.x' + + @Internal + List failedComponents = [] + + @Internal + List gitFailedComponents = [] + + @Internal + List compatibleComponents = [] + + @TaskAction + void checkCompatibility() { + repositoryUrls.parallelStream().forEach { repositoryUrl -> + logger.lifecycle("Checking compatibility for: $repositoryUrl with ref: $ref") + def tempDir = File.createTempDir() + def stdout = new ByteArrayOutputStream() + def errout = new ByteArrayOutputStream() + def skipped = false; + try { + if (cloneAndCheckout(repositoryUrl, tempDir)) { + if (repositoryUrl.toString().endsWithAny('notifications', 'notifications.git')) { + tempDir = Paths.get(tempDir.getAbsolutePath(), 'notifications') + } + project.exec { + workingDir = tempDir + executable = (OperatingSystem.current().isWindows()) ? 'gradlew.bat' : './gradlew' + args ('assemble') + standardOutput stdout + errorOutput errout + } + compatibleComponents.add(repositoryUrl) + } else { + skipped = true + } + } catch (ex) { + failedComponents.add(repositoryUrl) + logger.info("Gradle assemble failed for $repositoryUrl", ex) + } finally { + if (skipped) { + logger.lifecycle("Skipping compatibility check for $repositoryUrl") + } else { + logger.lifecycle("Finished compatibility check for $repositoryUrl") + logger.info("Standard output for $repositoryUrl build:\n\n" + stdout.toString()) + logger.error("Error output for $repositoryUrl build:\n\n" + errout.toString()) + } + tempDir.deleteDir() + } + } + if (!failedComponents.isEmpty()) { + logger.lifecycle("Incompatible components: $failedComponents") + } + if (!gitFailedComponents.isEmpty()) { + logger.lifecycle("Components skipped due to git failures: $gitFailedComponents") + } + if (!compatibleComponents.isEmpty()) { + logger.lifecycle("Compatible components: $compatibleComponents") + } + } + + protected static List getRepoUrls() { + def json = new JsonSlurper().parse(REPO_URL.toURL()) + def repository = json.projects.values() + def repoUrls = replaceSshWithHttps(repository as List) + return repoUrls + } + + protected static replaceSshWithHttps(List repoList) { + repoList.replaceAll { element -> + element.replace("git@github.com:", "https://github.com/") + } + return repoList + } + + protected boolean cloneAndCheckout(repoUrl, directory) { + try { + def grgit = Grgit.clone(dir: directory, uri: repoUrl) + def remoteBranches = grgit.branch.list(mode: BranchListOp.Mode.REMOTE) + String targetBranch = 'origin/' + ref + if (remoteBranches.find { it.name == targetBranch } == null) { + gitFailedComponents.add(repoUrl) + logger.info("$ref does not exist for $repoUrl. Skipping the compatibility check!!") + return false + } else { + logger.info("Checking out $targetBranch") + grgit.checkout(branch: targetBranch) + return true + } + } catch (ex) { + logger.error('Exception occurred during GitHub operations', ex) + gitFailedComponents.add(repoUrl) + return false + } + } +} diff --git a/buildSrc/src/main/groovy/org/opensearch/gradle/plugin/OptionalDependenciesPlugin.groovy b/buildSrc/src/main/groovy/org/opensearch/gradle/plugin/OptionalDependenciesPlugin.groovy new file mode 100644 index 0000000000000..2bd8835535881 --- /dev/null +++ b/buildSrc/src/main/groovy/org/opensearch/gradle/plugin/OptionalDependenciesPlugin.groovy @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +/* + * Copyright 2014-2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.opensearch.gradle.plugin + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.ivy.IvyPublication +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.publish.plugins.PublishingPlugin + +/** + * Including the support of `optional` dependencies: https://github.com/nebula-plugins/gradle-extra-configurations-plugin/blob/v9.0.0/src/main/groovy/nebula/plugin/extraconfigurations/OptionalBasePlugin.groovy + */ +class OptionalDependenciesPlugin implements Plugin { + static final String OPTIONAL_IDENTIFIER = 'optional' + + @Override + void apply(Project project) { + enhanceProjectModel(project) + configureMavenPublishPlugin(project) + configureIvyPublishPlugin(project) + } + + /** + * Enhances the Project domain object by adding + * + * a) a extra property List that holds optional dependencies + * b) a extra method that can be executed as parameter when declaring dependencies + * + * @param project Project + */ + private void enhanceProjectModel(Project project) { + project.ext.optionalDeps = [] + + project.ext.optional = { dep -> + project.ext.optionalDeps << dep + } + } + + /** + * Configures Maven Publishing plugin to ensure that published dependencies receive the optional element. + * + * @param project Project + */ + private void configureMavenPublishPlugin(Project project) { + project.plugins.withType(PublishingPlugin) { + project.publishing { + publications { + project.extensions.findByType(PublishingExtension)?.publications?.withType(MavenPublication) { MavenPublication pub -> + pub.pom.withXml { + project.ext.optionalDeps.each { dep -> + def foundDep = asNode().dependencies.dependency.find { + it.groupId.text() == dep.group && it.artifactId.text() == dep.name + } + + if (foundDep) { + if (foundDep.optional) { + foundDep.optional.value = 'true' + } else { + foundDep.appendNode(OPTIONAL_IDENTIFIER, 'true') + } + } + } + } + } + } + } + } + } + + /** + * Configures Ivy Publishing plugin to ensure that published dependencies receive the correct conf attribute value. + * + * @param project Project + */ + private void configureIvyPublishPlugin(Project project) { + project.plugins.withType(PublishingPlugin) { + project.publishing { + publications { + project.extensions.findByType(PublishingExtension)?.publications?.withType(IvyPublication) { IvyPublication pub -> + pub.descriptor.withXml { + def rootNode = asNode() + + // Add optional configuration if it doesn't exist yet + if (!rootNode.configurations.find { it.@name == OPTIONAL_IDENTIFIER }) { + rootNode.configurations[0].appendNode('conf', [name: OPTIONAL_IDENTIFIER, visibility: 'public']) + } + + // Replace dependency "runtime->default" conf attribute value with "optional" + project.ext.optionalDeps.each { dep -> + def foundDep = rootNode.dependencies.dependency.find { it.@name == dep.name } + foundDep?.@conf = OPTIONAL_IDENTIFIER + } + } + } + } + } + } + } +} diff --git a/buildSrc/src/main/groovy/org/opensearch/gradle/plugin/PluginBuildPlugin.groovy b/buildSrc/src/main/groovy/org/opensearch/gradle/plugin/PluginBuildPlugin.groovy index 31677965ab0d3..556763333d279 100644 --- a/buildSrc/src/main/groovy/org/opensearch/gradle/plugin/PluginBuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/opensearch/gradle/plugin/PluginBuildPlugin.groovy @@ -29,13 +29,13 @@ package org.opensearch.gradle.plugin import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin +import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.opensearch.gradle.BuildPlugin import org.opensearch.gradle.NoticeTask import org.opensearch.gradle.Version import org.opensearch.gradle.VersionProperties import org.opensearch.gradle.dependencies.CompileOnlyResolvePlugin import org.opensearch.gradle.info.BuildParams -import org.opensearch.gradle.plugin.PluginPropertiesExtension import org.opensearch.gradle.test.RestTestBasePlugin import org.opensearch.gradle.testclusters.RunTask import org.opensearch.gradle.util.Util @@ -44,6 +44,8 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.plugins.BasePlugin +import org.gradle.api.plugins.BasePluginExtension +import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.publish.maven.plugins.MavenPublishPlugin import org.gradle.api.publish.maven.tasks.GenerateMavenPom @@ -85,7 +87,9 @@ class PluginBuildPlugin implements Plugin { PluginPropertiesExtension extension1 = project.getExtensions().getByType(PluginPropertiesExtension.class) configurePublishing(project, extension1) String name = extension1.name - project.archivesBaseName = name + + BasePluginExtension base = project.getExtensions().findByType(BasePluginExtension.class) + base.archivesBaseName = name project.description = extension1.description if (extension1.name == null) { @@ -98,12 +102,13 @@ class PluginBuildPlugin implements Plugin { throw new InvalidUserDataException('classname is a required setting for opensearchplugin') } + JavaPluginExtension java = project.getExtensions().findByType(JavaPluginExtension.class) Map properties = [ 'name' : extension1.name, 'description' : extension1.description, 'version' : extension1.version, 'opensearchVersion' : Version.fromString(VersionProperties.getOpenSearch()).toString(), - 'javaVersion' : project.targetCompatibility as String, + 'javaVersion' : java.targetCompatibility as String, 'classname' : extension1.classname, 'customFolderName' : extension1.customFolderName, 'extendedPlugins' : extension1.extendedPlugins.join(','), @@ -134,6 +139,12 @@ class PluginBuildPlugin implements Plugin { } project.configurations.getByName('default') .extendsFrom(project.configurations.getByName('runtimeClasspath')) + project.tasks.withType(AbstractArchiveTask.class).configureEach { task -> + // ignore file timestamps + // be consistent in archive file order + task.preserveFileTimestamps = false + task.reproducibleFileOrder = true + } // allow running ES with this plugin in the foreground of a build project.tasks.register('run', RunTask) { dependsOn(project.tasks.bundlePlugin) @@ -143,15 +154,16 @@ class PluginBuildPlugin implements Plugin { private void configurePublishing(Project project, PluginPropertiesExtension extension) { // Only configure publishing if applied externally if (extension.hasClientJar) { - project.pluginManager.apply('nebula.maven-base-publish') + project.pluginManager.apply('com.netflix.nebula.maven-base-publish') // Only change Jar tasks, we don't want a -client zip so we can't change archivesBaseName project.tasks.withType(Jar) { archiveBaseName = archiveBaseName.get() + "-client" } // always configure publishing for client jars project.publishing.publications.nebula(MavenPublication).artifactId(extension.name + "-client") + final BasePluginExtension base = project.getExtensions().findByType(BasePluginExtension.class) project.tasks.withType(GenerateMavenPom.class).configureEach { GenerateMavenPom generatePOMTask -> - generatePOMTask.destination = "${project.buildDir}/distributions/${project.archivesBaseName}-client-${project.versions.opensearch}.pom" + generatePOMTask.destination = "${project.buildDir}/distributions/${base.archivesBaseName}-client-${project.versions.opensearch}.pom" } } else { if (project.plugins.hasPlugin(MavenPublishPlugin)) { diff --git a/buildSrc/src/main/groovy/org/opensearch/gradle/precommit/LicenseHeadersTask.groovy b/buildSrc/src/main/groovy/org/opensearch/gradle/precommit/LicenseHeadersTask.groovy index b330934ed2d26..b8d0ed2b9c43c 100644 --- a/buildSrc/src/main/groovy/org/opensearch/gradle/precommit/LicenseHeadersTask.groovy +++ b/buildSrc/src/main/groovy/org/opensearch/gradle/precommit/LicenseHeadersTask.groovy @@ -35,7 +35,10 @@ import org.opensearch.gradle.AntTask import org.gradle.api.file.FileCollection import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.SkipWhenEmpty import java.nio.file.Files @@ -78,6 +81,8 @@ class LicenseHeadersTask extends AntTask { */ @InputFiles @SkipWhenEmpty + @IgnoreEmptyDirectories + @PathSensitive(PathSensitivity.RELATIVE) List getJavaFiles() { return project.sourceSets.collect({it.allJava}) } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/Architecture.java b/buildSrc/src/main/java/org/opensearch/gradle/Architecture.java index 38d6db8c9916e..b16f35a9ad0eb 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/Architecture.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/Architecture.java @@ -35,7 +35,9 @@ public enum Architecture { X64, - ARM64; + ARM64, + S390X, + PPC64LE; public static Architecture current() { final String architecture = System.getProperty("os.arch", ""); @@ -45,6 +47,10 @@ public static Architecture current() { return X64; case "aarch64": return ARM64; + case "s390x": + return S390X; + case "ppc64le": + return PPC64LE; default: throw new IllegalArgumentException("can not determine architecture from [" + architecture + "]"); } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/ConcatFilesTask.java b/buildSrc/src/main/java/org/opensearch/gradle/ConcatFilesTask.java index 96e3702d5a729..e5cd767ba0e67 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/ConcatFilesTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/ConcatFilesTask.java @@ -31,6 +31,14 @@ package org.opensearch.gradle; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.FileTree; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -40,14 +48,6 @@ import java.util.LinkedHashSet; import java.util.List; -import org.gradle.api.DefaultTask; -import org.gradle.api.file.FileTree; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFiles; -import org.gradle.api.tasks.Optional; -import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.TaskAction; - /** * Concatenates a list of files into one and removes duplicate lines. */ diff --git a/buildSrc/src/main/java/org/opensearch/gradle/DistributionDownloadPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/DistributionDownloadPlugin.java index fccdc49ef6fc9..7b6aa6774b401 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/DistributionDownloadPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/DistributionDownloadPlugin.java @@ -80,6 +80,8 @@ public class DistributionDownloadPlugin implements Plugin { private static final String RELEASE_PATTERN_LAYOUT = "/core/opensearch/[revision]/[module]-min-[revision](-[classifier]).[ext]"; private static final String SNAPSHOT_PATTERN_LAYOUT = "/snapshots/core/opensearch/[revision]/[module]-min-[revision](-[classifier])-latest.[ext]"; + private static final String BUNDLE_PATTERN_LAYOUT = + "/ci/dbc/distribution-build-opensearch/[revision]/latest/linux/x64/tar/dist/opensearch/[module]-[revision](-[classifier]).[ext]"; private NamedDomainObjectContainer distributionsContainer; private NamedDomainObjectContainer distributionsResolutionStrategiesContainer; @@ -196,24 +198,45 @@ private static void setupDownloadServiceRepo(Project project) { return; } Object customDistributionUrl = project.findProperty("customDistributionUrl"); - // checks if custom Distribution Url has been passed by user from plugins + Object customDistributionDownloadType = project.findProperty("customDistributionDownloadType"); + // distributionDownloadType is default min if is not specified; download the distribution from CI if is bundle + String distributionDownloadType = customDistributionDownloadType != null + && customDistributionDownloadType.toString().equals("bundle") ? "bundle" : "min"; + + addIvyRepo2(project, DOWNLOAD_REPO_NAME_ES, "https://artifacts-no-kpi.elastic.co", FAKE_IVY_GROUP_ES); + addIvyRepo2(project, SNAPSHOT_REPO_NAME_ES, "https://snapshots-no-kpi.elastic.co", FAKE_SNAPSHOT_IVY_GROUP_ES); + if (customDistributionUrl != null) { addIvyRepo(project, DOWNLOAD_REPO_NAME, customDistributionUrl.toString(), FAKE_IVY_GROUP, ""); addIvyRepo(project, SNAPSHOT_REPO_NAME, customDistributionUrl.toString(), FAKE_SNAPSHOT_IVY_GROUP, ""); - } else { - addIvyRepo( - project, - DOWNLOAD_REPO_NAME, - "https://artifacts.opensearch.org", - FAKE_IVY_GROUP, - "/releases" + RELEASE_PATTERN_LAYOUT, - "/release-candidates" + RELEASE_PATTERN_LAYOUT - ); - addIvyRepo(project, SNAPSHOT_REPO_NAME, "https://artifacts.opensearch.org", FAKE_SNAPSHOT_IVY_GROUP, SNAPSHOT_PATTERN_LAYOUT); + return; + } + switch (distributionDownloadType) { + case "bundle": + addIvyRepo(project, DOWNLOAD_REPO_NAME, "https://ci.opensearch.org", FAKE_IVY_GROUP, BUNDLE_PATTERN_LAYOUT); + addIvyRepo(project, SNAPSHOT_REPO_NAME, "https://ci.opensearch.org", FAKE_SNAPSHOT_IVY_GROUP, BUNDLE_PATTERN_LAYOUT); + break; + case "min": + addIvyRepo( + project, + DOWNLOAD_REPO_NAME, + "https://artifacts.opensearch.org", + FAKE_IVY_GROUP, + "/releases" + RELEASE_PATTERN_LAYOUT, + "/release-candidates" + RELEASE_PATTERN_LAYOUT + ); + addIvyRepo( + project, + SNAPSHOT_REPO_NAME, + "https://artifacts.opensearch.org", + FAKE_SNAPSHOT_IVY_GROUP, + SNAPSHOT_PATTERN_LAYOUT + ); + break; + default: + throw new IllegalArgumentException("Unsupported property argument: " + distributionDownloadType); } - addIvyRepo2(project, DOWNLOAD_REPO_NAME_ES, "https://artifacts-no-kpi.elastic.co", FAKE_IVY_GROUP_ES); - addIvyRepo2(project, SNAPSHOT_REPO_NAME_ES, "https://snapshots-no-kpi.elastic.co", FAKE_SNAPSHOT_IVY_GROUP_ES); } /** @@ -247,6 +270,12 @@ private String dependencyNotation(OpenSearchDistribution distribution) { case X64: classifier = ":" + distribution.getPlatform() + "-x64"; break; + case S390X: + classifier = ":" + distribution.getPlatform() + "-s390x"; + break; + case PPC64LE: + classifier = ":" + distribution.getPlatform() + "-ppc64le"; + break; default: throw new IllegalArgumentException("Unsupported architecture: " + distribution.getArchitecture()); } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/EmptyDirTask.java b/buildSrc/src/main/java/org/opensearch/gradle/EmptyDirTask.java index f16b667f96ed4..96d7c69699c68 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/EmptyDirTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/EmptyDirTask.java @@ -31,16 +31,16 @@ package org.opensearch.gradle; -import java.io.File; - -import javax.inject.Inject; - import org.gradle.api.DefaultTask; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.TaskAction; import org.gradle.internal.file.Chmod; +import javax.inject.Inject; + +import java.io.File; + /** * Creates an empty directory. */ diff --git a/buildSrc/src/main/java/org/opensearch/gradle/JavaPackageType.java b/buildSrc/src/main/java/org/opensearch/gradle/JavaPackageType.java new file mode 100644 index 0000000000000..2acc335d80df0 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/JavaPackageType.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle; + +public enum JavaPackageType { + NONE, + JRE, + JDK +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/JavaVariant.java b/buildSrc/src/main/java/org/opensearch/gradle/JavaVariant.java new file mode 100644 index 0000000000000..5f576984627a8 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/JavaVariant.java @@ -0,0 +1,197 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle; + +import org.gradle.api.Buildable; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.TaskDependency; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +abstract class JavaVariant implements Buildable, Iterable { + + private static final List ALLOWED_ARCHITECTURES = Collections.unmodifiableList( + Arrays.asList("aarch64", "x64", "s390x", "ppc64le") + ); + private static final List ALLOWED_VENDORS = Collections.unmodifiableList(Arrays.asList("adoptium", "adoptopenjdk", "openjdk")); + private static final List ALLOWED_PLATFORMS = Collections.unmodifiableList( + Arrays.asList("darwin", "freebsd", "linux", "mac", "windows") + ); + private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)(\\.\\d+\\.\\d+)?\\+(\\d+(?:\\.\\d+)?)(@([a-f0-9]{32}))?"); + private static final Pattern LEGACY_VERSION_PATTERN = Pattern.compile("(\\d)(u\\d+)(?:\\+|\\-)(b\\d+?)(@([a-f0-9]{32}))?"); + + private final String name; + private final Configuration configuration; + + private final Property vendor; + private final Property version; + private final Property platform; + private final Property architecture; + private String baseVersion; + private String major; + private String build; + private String hash; + + JavaVariant(String name, Configuration configuration, ObjectFactory objectFactory) { + this.name = name; + this.configuration = configuration; + this.vendor = objectFactory.property(String.class); + this.version = objectFactory.property(String.class); + this.platform = objectFactory.property(String.class); + this.architecture = objectFactory.property(String.class); + } + + public String getName() { + return name; + } + + public String getVendor() { + return vendor.get(); + } + + public void setVendor(final String vendor) { + if (ALLOWED_VENDORS.contains(vendor) == false) { + throw new IllegalArgumentException("unknown vendor [" + vendor + "] for jdk [" + name + "], must be one of " + ALLOWED_VENDORS); + } + this.vendor.set(vendor); + } + + public String getVersion() { + return version.get(); + } + + public void setVersion(String version) { + if (VERSION_PATTERN.matcher(version).matches() == false && LEGACY_VERSION_PATTERN.matcher(version).matches() == false) { + throw new IllegalArgumentException("malformed version [" + version + "] for jdk [" + name + "]"); + } + parseVersion(version); + this.version.set(version); + } + + public String getPlatform() { + return platform.get(); + } + + public void setPlatform(String platform) { + if (ALLOWED_PLATFORMS.contains(platform) == false) { + throw new IllegalArgumentException( + "unknown platform [" + platform + "] for jdk [" + name + "], must be one of " + ALLOWED_PLATFORMS + ); + } + this.platform.set(platform); + } + + public String getArchitecture() { + return architecture.get(); + } + + public void setArchitecture(final String architecture) { + String jdkArchitecture = translateJdkArchitecture(architecture); + if (ALLOWED_ARCHITECTURES.contains(jdkArchitecture) == false) { + throw new IllegalArgumentException( + "unknown architecture [" + jdkArchitecture + "] for jdk [" + name + "], must be one of " + ALLOWED_ARCHITECTURES + ); + } + this.architecture.set(jdkArchitecture); + } + + public String getBaseVersion() { + return baseVersion; + } + + public String getMajor() { + return major; + } + + public String getBuild() { + return build; + } + + public String getHash() { + return hash; + } + + public String getPath() { + return configuration.getSingleFile().toString(); + } + + public String getConfigurationName() { + return configuration.getName(); + } + + @Override + public String toString() { + return getPath(); + } + + @Override + public TaskDependency getBuildDependencies() { + return configuration.getBuildDependencies(); + } + + // internal, make this jdks configuration unmodifiable + void finalizeValues() { + if (version.isPresent() == false) { + throw new IllegalArgumentException("version not specified for jdk [" + name + "]"); + } + if (platform.isPresent() == false) { + throw new IllegalArgumentException("platform not specified for jdk [" + name + "]"); + } + if (vendor.isPresent() == false) { + throw new IllegalArgumentException("vendor not specified for jdk [" + name + "]"); + } + if (architecture.isPresent() == false) { + throw new IllegalArgumentException("architecture not specified for jdk [" + name + "]"); + } + version.finalizeValue(); + platform.finalizeValue(); + vendor.finalizeValue(); + architecture.finalizeValue(); + } + + @Override + public Iterator iterator() { + return configuration.iterator(); + } + + private void parseVersion(String version) { + // decompose the bundled jdk version, broken into elements as: [feature, interim, update, build] + // Note the "patch" version is not yet handled here, as it has not yet been used by java. + Matcher jdkVersionMatcher = VERSION_PATTERN.matcher(version); + if (jdkVersionMatcher.matches() == false) { + // Try again with the pre-Java9 version format + jdkVersionMatcher = LEGACY_VERSION_PATTERN.matcher(version); + + if (jdkVersionMatcher.matches() == false) { + throw new IllegalArgumentException("Malformed jdk version [" + version + "]"); + } + } + + baseVersion = jdkVersionMatcher.group(1) + (jdkVersionMatcher.group(2) != null ? (jdkVersionMatcher.group(2)) : ""); + major = jdkVersionMatcher.group(1); + build = jdkVersionMatcher.group(3); + hash = jdkVersionMatcher.group(5); + } + + private String translateJdkArchitecture(String architecture) { + /* + * Jdk uses aarch64 from ARM. Translating from arm64 to aarch64 which Jdk understands. + */ + return "arm64".equals(architecture) ? "aarch64" : architecture; + } + +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/Jdk.java b/buildSrc/src/main/java/org/opensearch/gradle/Jdk.java index 792debe3f350a..3218abe726639 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/Jdk.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/Jdk.java @@ -32,144 +32,20 @@ package org.opensearch.gradle; -import org.gradle.api.Buildable; import org.gradle.api.artifacts.Configuration; import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.TaskDependency; - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Jdk implements Buildable, Iterable { - - private static final List ALLOWED_ARCHITECTURES = Collections.unmodifiableList(Arrays.asList("aarch64", "x64")); - private static final List ALLOWED_VENDORS = Collections.unmodifiableList(Arrays.asList("adoptium", "adoptopenjdk", "openjdk")); - private static final List ALLOWED_PLATFORMS = Collections.unmodifiableList( - Arrays.asList("darwin", "freebsd", "linux", "mac", "windows") - ); - private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)(\\.\\d+\\.\\d+)?\\+(\\d+(?:\\.\\d+)?)(@([a-f0-9]{32}))?"); - private static final Pattern LEGACY_VERSION_PATTERN = Pattern.compile("(\\d)(u\\d+)\\+(b\\d+?)(@([a-f0-9]{32}))?"); - - private final String name; - private final Configuration configuration; - - private final Property vendor; - private final Property version; - private final Property platform; - private final Property architecture; - private String baseVersion; - private String major; - private String build; - private String hash; +import org.gradle.internal.os.OperatingSystem; +public class Jdk extends JavaVariant { Jdk(String name, Configuration configuration, ObjectFactory objectFactory) { - this.name = name; - this.configuration = configuration; - this.vendor = objectFactory.property(String.class); - this.version = objectFactory.property(String.class); - this.platform = objectFactory.property(String.class); - this.architecture = objectFactory.property(String.class); - } - - public String getName() { - return name; - } - - public String getVendor() { - return vendor.get(); - } - - public void setVendor(final String vendor) { - if (ALLOWED_VENDORS.contains(vendor) == false) { - throw new IllegalArgumentException("unknown vendor [" + vendor + "] for jdk [" + name + "], must be one of " + ALLOWED_VENDORS); - } - this.vendor.set(vendor); - } - - public String getVersion() { - return version.get(); - } - - public void setVersion(String version) { - if (VERSION_PATTERN.matcher(version).matches() == false && LEGACY_VERSION_PATTERN.matcher(version).matches() == false) { - throw new IllegalArgumentException("malformed version [" + version + "] for jdk [" + name + "]"); - } - parseVersion(version); - this.version.set(version); - } - - public String getPlatform() { - return platform.get(); - } - - public void setPlatform(String platform) { - if (ALLOWED_PLATFORMS.contains(platform) == false) { - throw new IllegalArgumentException( - "unknown platform [" + platform + "] for jdk [" + name + "], must be one of " + ALLOWED_PLATFORMS - ); - } - this.platform.set(platform); - } - - public String getArchitecture() { - return architecture.get(); - } - - public void setArchitecture(final String architecture) { - String jdkArchitecture = translateJdkArchitecture(architecture); - if (ALLOWED_ARCHITECTURES.contains(jdkArchitecture) == false) { - throw new IllegalArgumentException( - "unknown architecture [" + jdkArchitecture + "] for jdk [" + name + "], must be one of " + ALLOWED_ARCHITECTURES - ); - } - this.architecture.set(architecture); - } - - public String getBaseVersion() { - return baseVersion; - } - - public String getMajor() { - return major; - } - - public String getBuild() { - return build; - } - - public String getHash() { - return hash; - } - - public String getPath() { - return configuration.getSingleFile().toString(); - } - - public String getConfigurationName() { - return configuration.getName(); - } - - @Override - public String toString() { - return getPath(); - } - - @Override - public TaskDependency getBuildDependencies() { - return configuration.getBuildDependencies(); + super(name, configuration, objectFactory); } public Object getBinJavaPath() { return new Object() { @Override public String toString() { - return getHomeRoot() + "/bin/java"; + return OperatingSystem.current().getExecutableName(getHomeRoot() + "/bin/java"); } }; } @@ -187,56 +63,4 @@ private String getHomeRoot() { boolean isOSX = "mac".equals(getPlatform()) || "darwin".equals(getPlatform()); return getPath() + (isOSX ? "/Contents/Home" : ""); } - - // internal, make this jdks configuration unmodifiable - void finalizeValues() { - if (version.isPresent() == false) { - throw new IllegalArgumentException("version not specified for jdk [" + name + "]"); - } - if (platform.isPresent() == false) { - throw new IllegalArgumentException("platform not specified for jdk [" + name + "]"); - } - if (vendor.isPresent() == false) { - throw new IllegalArgumentException("vendor not specified for jdk [" + name + "]"); - } - if (architecture.isPresent() == false) { - throw new IllegalArgumentException("architecture not specified for jdk [" + name + "]"); - } - version.finalizeValue(); - platform.finalizeValue(); - vendor.finalizeValue(); - architecture.finalizeValue(); - } - - @Override - public Iterator iterator() { - return configuration.iterator(); - } - - private void parseVersion(String version) { - // decompose the bundled jdk version, broken into elements as: [feature, interim, update, build] - // Note the "patch" version is not yet handled here, as it has not yet been used by java. - Matcher jdkVersionMatcher = VERSION_PATTERN.matcher(version); - if (jdkVersionMatcher.matches() == false) { - // Try again with the pre-Java9 version format - jdkVersionMatcher = LEGACY_VERSION_PATTERN.matcher(version); - - if (jdkVersionMatcher.matches() == false) { - throw new IllegalArgumentException("Malformed jdk version [" + version + "]"); - } - } - - baseVersion = jdkVersionMatcher.group(1) + (jdkVersionMatcher.group(2) != null ? (jdkVersionMatcher.group(2)) : ""); - major = jdkVersionMatcher.group(1); - build = jdkVersionMatcher.group(3); - hash = jdkVersionMatcher.group(5); - } - - private String translateJdkArchitecture(String architecture) { - /* - * Jdk uses aarch64 from ARM. Translating from arm64 to aarch64 which Jdk understands. - */ - return "arm64".equals(architecture) ? "aarch64" : architecture; - } - } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/JdkDownloadPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/JdkDownloadPlugin.java index b9f3036f7aad9..c359e3a497049 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/JdkDownloadPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/JdkDownloadPlugin.java @@ -110,22 +110,34 @@ private void setupRepository(Project project, Jdk jdk) { if (jdk.getVendor().equals(VENDOR_ADOPTIUM)) { repoUrl = "https://github.com/adoptium/temurin" + jdk.getMajor() + "-binaries/releases/download/"; - // JDK updates are suffixed with 'U' (fe OpenJDK17U), whereas GA releases are not (fe OpenJDK17). - // To distinguish between those, the GA releases have only major version component (fe 17+32), - // the updates always have minor/patch components (fe 17.0.1+12), checking for the presence of - // version separator '.' should be enough. - artifactPattern = "jdk-" - + jdk.getBaseVersion() - + "+" - + jdk.getBuild() - + "/OpenJDK" - + jdk.getMajor() - + (jdk.getBaseVersion().contains(".") ? "U" : "") - + "-jdk_[classifier]_[module]_hotspot_" - + jdk.getBaseVersion() - + "_" - + jdk.getBuild() - + ".[ext]"; + + if (jdk.getMajor().equals("8")) { + // JDK-8 updates are always suffixed with 'U' (fe OpenJDK8U). + artifactPattern = "jdk" + + jdk.getBaseVersion() + + "-" + + jdk.getBuild() + + "/OpenJDK" + + jdk.getMajor() + + "U" + + "-jdk_[classifier]_[module]_hotspot_" + + jdk.getBaseVersion() + + jdk.getBuild() + + ".[ext]"; + } else { + // JDK updates are suffixed with 'U' (fe OpenJDK17U), whereas GA releases are not (fe OpenJDK17). + // To distinguish between those, the GA releases have only major version component (fe 17+32), + // the updates always have minor/patch components (fe 17.0.1+12), checking for the presence of + // version separator '.' should be enough. + artifactPattern = "jdk-" + jdk.getBaseVersion() + "+" + jdk.getBuild() + "/OpenJDK" + jdk.getMajor() + // JDK-20 does use 'U' suffix all the time, no matter it is update or GA release + + (jdk.getBaseVersion().contains(".") || jdk.getBaseVersion().matches("^2\\d+$") ? "U" : "") + + "-jdk_[classifier]_[module]_hotspot_" + + jdk.getBaseVersion() + + "_" + + jdk.getBuild() + + ".[ext]"; + } } else if (jdk.getVendor().equals(VENDOR_ADOPTOPENJDK)) { repoUrl = "https://api.adoptopenjdk.net/v3/binary/version/"; if (jdk.getMajor().equals("8")) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/Jre.java b/buildSrc/src/main/java/org/opensearch/gradle/Jre.java new file mode 100644 index 0000000000000..473bfc4860b80 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/Jre.java @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle; + +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.model.ObjectFactory; + +public class Jre extends JavaVariant { + Jre(String name, Configuration configuration, ObjectFactory objectFactory) { + super(name, configuration, objectFactory); + } +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/JreDownloadPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/JreDownloadPlugin.java new file mode 100644 index 0000000000000..5a00f41f07a60 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/JreDownloadPlugin.java @@ -0,0 +1,147 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle; + +import org.opensearch.gradle.transform.SymbolicLinkPreservingUntarTransform; +import org.opensearch.gradle.transform.UnzipTransform; +import org.gradle.api.GradleException; +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.artifacts.repositories.IvyArtifactRepository; +import org.gradle.api.artifacts.type.ArtifactTypeDefinition; +import org.gradle.api.attributes.Attribute; +import org.gradle.api.internal.artifacts.ArtifactAttributes; + +public class JreDownloadPlugin implements Plugin { + public static final String VENDOR_ADOPTIUM = "adoptium"; + + private static final String REPO_NAME_PREFIX = "jre_repo_"; + private static final String EXTENSION_NAME = "jres"; + public static final String JRE_TRIMMED_PREFIX = "jdk-?\\d.*-jre"; + + @Override + public void apply(Project project) { + Attribute jreAttribute = Attribute.of("jre", Boolean.class); + project.getDependencies().getAttributesSchema().attribute(jreAttribute); + project.getDependencies().getArtifactTypes().maybeCreate(ArtifactTypeDefinition.ZIP_TYPE); + project.getDependencies().registerTransform(UnzipTransform.class, transformSpec -> { + transformSpec.getFrom() + .attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.ZIP_TYPE) + .attribute(jreAttribute, true); + transformSpec.getTo() + .attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE) + .attribute(jreAttribute, true); + transformSpec.parameters(parameters -> parameters.setTrimmedPrefixPattern(JRE_TRIMMED_PREFIX)); + }); + + ArtifactTypeDefinition tarArtifactTypeDefinition = project.getDependencies().getArtifactTypes().maybeCreate("tar.gz"); + project.getDependencies().registerTransform(SymbolicLinkPreservingUntarTransform.class, transformSpec -> { + transformSpec.getFrom() + .attribute(ArtifactAttributes.ARTIFACT_FORMAT, tarArtifactTypeDefinition.getName()) + .attribute(jreAttribute, true); + transformSpec.getTo() + .attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE) + .attribute(jreAttribute, true); + transformSpec.parameters(parameters -> parameters.setTrimmedPrefixPattern(JRE_TRIMMED_PREFIX)); + }); + + NamedDomainObjectContainer jresContainer = project.container(Jre.class, name -> { + Configuration configuration = project.getConfigurations().create("jre_" + name); + configuration.setCanBeConsumed(false); + configuration.getAttributes().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE); + configuration.getAttributes().attribute(jreAttribute, true); + Jre jre = new Jre(name, configuration, project.getObjects()); + configuration.defaultDependencies(dependencies -> { + jre.finalizeValues(); + setupRepository(project, jre); + dependencies.add(project.getDependencies().create(dependencyNotation(jre))); + }); + return jre; + }); + project.getExtensions().add(EXTENSION_NAME, jresContainer); + } + + private void setupRepository(Project project, Jre jre) { + RepositoryHandler repositories = project.getRepositories(); + + /* + * Define the appropriate repository for the given JRE vendor and version + * + * For Oracle/OpenJDK/AdoptOpenJDK we define a repository per-version. + */ + String repoName = REPO_NAME_PREFIX + jre.getVendor() + "_" + jre.getVersion(); + String repoUrl; + String artifactPattern; + + if (jre.getVendor().equals(VENDOR_ADOPTIUM)) { + repoUrl = "https://github.com/adoptium/temurin" + jre.getMajor() + "-binaries/releases/download/"; + + if (jre.getMajor().equals("8")) { + // JDK-8 updates are always suffixed with 'U' (fe OpenJDK8U). + artifactPattern = "jdk" + + jre.getBaseVersion() + + "-" + + jre.getBuild() + + "/OpenJDK" + + jre.getMajor() + + "U" + + "-jre_[classifier]_[module]_hotspot_" + + jre.getBaseVersion() + + jre.getBuild() + + ".[ext]"; + } else { + // JDK updates are suffixed with 'U' (fe OpenJDK17U), whereas GA releases are not (fe OpenJDK17). + // To distinguish between those, the GA releases have only major version component (fe 17+32), + // the updates always have minor/patch components (fe 17.0.1+12), checking for the presence of + // version separator '.' should be enough. + artifactPattern = "jdk-" + jre.getBaseVersion() + "+" + jre.getBuild() + "/OpenJDK" + jre.getMajor() + // JDK-20 does use 'U' suffix all the time, no matter it is update or GA release + + (jre.getBaseVersion().contains(".") || jre.getBaseVersion().matches("^2\\d+$") ? "U" : "") + + "-jre_[classifier]_[module]_hotspot_" + + jre.getBaseVersion() + + "_" + + jre.getBuild() + + ".[ext]"; + } + } else { + throw new GradleException("Unknown JDK vendor [" + jre.getVendor() + "]"); + } + + // Define the repository if we haven't already + if (repositories.findByName(repoName) == null) { + repositories.ivy(repo -> { + repo.setName(repoName); + repo.setUrl(repoUrl); + repo.metadataSources(IvyArtifactRepository.MetadataSources::artifact); + repo.patternLayout(layout -> layout.artifact(artifactPattern)); + repo.content(repositoryContentDescriptor -> repositoryContentDescriptor.includeGroup(groupName(jre))); + }); + } + } + + @SuppressWarnings("unchecked") + public static NamedDomainObjectContainer getContainer(Project project) { + return (NamedDomainObjectContainer) project.getExtensions().getByName(EXTENSION_NAME); + } + + private static String dependencyNotation(Jre jre) { + String platformDep = jre.getPlatform().equals("darwin") || jre.getPlatform().equals("mac") ? "mac" : jre.getPlatform(); + String extension = jre.getPlatform().equals("windows") ? "zip" : "tar.gz"; + + return groupName(jre) + ":" + platformDep + ":" + jre.getBaseVersion() + ":" + jre.getArchitecture() + "@" + extension; + } + + private static String groupName(Jre jre) { + return jre.getVendor() + "_" + jre.getMajor() + "_jre"; + } + +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/LoggedExec.java b/buildSrc/src/main/java/org/opensearch/gradle/LoggedExec.java index 0512ed72f5e47..1a78a7dbb2d10 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/LoggedExec.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/LoggedExec.java @@ -47,6 +47,7 @@ import org.gradle.process.JavaExecSpec; import javax.inject.Inject; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchDistribution.java b/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchDistribution.java index 968bd13bd4011..0575c23fee9f6 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchDistribution.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchDistribution.java @@ -101,7 +101,7 @@ public boolean shouldExtract() { private final Property version; private final Property type; private final Property platform; - private final Property bundledJdk; + private final Property bundledJdk; private final Property failIfUnavailable; private final Configuration extracted; @@ -120,7 +120,7 @@ public boolean shouldExtract() { this.type = objectFactory.property(Type.class); this.type.convention(Type.ARCHIVE); this.platform = objectFactory.property(Platform.class); - this.bundledJdk = objectFactory.property(Boolean.class); + this.bundledJdk = objectFactory.property(JavaPackageType.class); this.failIfUnavailable = objectFactory.property(Boolean.class).convention(true); this.extracted = extractedConfiguration; } @@ -154,8 +154,8 @@ public void setType(Type type) { this.type.set(type); } - public boolean getBundledJdk() { - return bundledJdk.getOrElse(true); + public JavaPackageType getBundledJdk() { + return bundledJdk.getOrElse(JavaPackageType.JDK); } public boolean isDocker() { @@ -163,7 +163,7 @@ public boolean isDocker() { return type == Type.DOCKER; } - public void setBundledJdk(Boolean bundledJdk) { + public void setBundledJdk(JavaPackageType bundledJdk) { this.bundledJdk.set(bundledJdk); } @@ -266,7 +266,7 @@ void finalizeValues() { } if (bundledJdk.isPresent() == false) { - bundledJdk.set(true); + bundledJdk.set(JavaPackageType.JDK); } version.finalizeValue(); diff --git a/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchJavaPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchJavaPlugin.java index c701c47f9e68c..018781c7b30c4 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchJavaPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchJavaPlugin.java @@ -34,6 +34,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar; import nebula.plugin.info.InfoBrokerPlugin; + import org.opensearch.gradle.info.BuildParams; import org.opensearch.gradle.info.GlobalBuildInfoPlugin; import org.opensearch.gradle.precommit.PrecommitTaskPlugin; @@ -256,10 +257,10 @@ private static void configureJarManifest(Project project) { ); }); - project.getPluginManager().apply("nebula.info-broker"); - project.getPluginManager().apply("nebula.info-basic"); - project.getPluginManager().apply("nebula.info-java"); - project.getPluginManager().apply("nebula.info-jar"); + project.getPluginManager().apply("com.netflix.nebula.info-broker"); + project.getPluginManager().apply("com.netflix.nebula.info-basic"); + project.getPluginManager().apply("com.netflix.nebula.info-java"); + project.getPluginManager().apply("com.netflix.nebula.info-jar"); } private static void configureJavadoc(Project project) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchTestBasePlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchTestBasePlugin.java index 9d6e78014916d..549e7f5c6d4df 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchTestBasePlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/OpenSearchTestBasePlugin.java @@ -33,8 +33,10 @@ package org.opensearch.gradle; import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin; + import org.opensearch.gradle.info.BuildParams; import org.opensearch.gradle.info.GlobalBuildInfoPlugin; +import org.opensearch.gradle.jvm.JvmTestSuiteHelper; import org.opensearch.gradle.test.ErrorReportingTestListener; import org.opensearch.gradle.util.Util; import org.gradle.api.Action; @@ -218,7 +220,15 @@ public void execute(Task t) { // Add the shadow JAR artifact itself FileCollection shadowJar = project.files(project.getTasks().named("shadowJar")); - test.setClasspath(test.getClasspath().minus(mainRuntime).plus(shadowConfig).plus(shadowJar)); + // See please https://docs.gradle.org/8.1/userguide/upgrading_version_8.html#test_task_default_classpath + test.setClasspath( + JvmTestSuiteHelper.getDefaultTestSuite(project) + .map(suite -> suite.getSources().getRuntimeClasspath()) + .orElseGet(() -> test.getClasspath()) + .minus(mainRuntime) + .plus(shadowConfig) + .plus(shadowJar) + ); }); }); } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java index d164b54c7506c..97e923c366598 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/PublishPlugin.java @@ -36,6 +36,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowExtension; import groovy.util.Node; import groovy.util.NodeList; + import org.opensearch.gradle.info.BuildParams; import org.opensearch.gradle.precommit.PomValidationPrecommitPlugin; import org.opensearch.gradle.util.Util; @@ -45,7 +46,7 @@ import org.gradle.api.XmlProvider; import org.gradle.api.artifacts.ProjectDependency; import org.gradle.api.plugins.BasePlugin; -import org.gradle.api.plugins.BasePluginConvention; +import org.gradle.api.plugins.BasePluginExtension; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.publish.PublishingExtension; import org.gradle.api.publish.maven.MavenPublication; @@ -55,6 +56,9 @@ import org.gradle.api.tasks.bundling.Jar; import org.gradle.language.base.plugins.LifecycleBasePlugin; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.concurrent.Callable; import static org.opensearch.gradle.util.GradleUtils.maybeConfigure; @@ -63,7 +67,8 @@ public class PublishPlugin implements Plugin { @Override public void apply(Project project) { - project.getPluginManager().apply("nebula.maven-base-publish"); + project.getPluginManager().apply("com.netflix.nebula.maven-base-publish"); + project.getPluginManager().apply("com.netflix.nebula.maven-nebula-publish"); project.getPluginManager().apply(PomValidationPrecommitPlugin.class); configureJavadocJar(project); @@ -72,7 +77,7 @@ public void apply(Project project) { } private static String getArchivesBaseName(Project project) { - return project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName(); + return project.getExtensions().getByType(BasePluginExtension.class).getArchivesBaseName(); } /**Configuration generation of maven poms. */ @@ -88,7 +93,7 @@ public String call() throws Exception { return String.format( "%s/distributions/%s-%s.pom", project.getBuildDir(), - getArchivesBaseName(project), + pomTask.getName().toLowerCase().contains("zip") ? project.getName() : getArchivesBaseName(project), project.getVersion() ); } @@ -104,8 +109,12 @@ public String call() throws Exception { // Here we manually add any project dependencies in the "shadow" configuration to our generated POM publication.getPom().withXml(xml -> { Node root = xml.asNode(); - root.appendNode("name", project.getName()); - root.appendNode("description", project.getDescription()); + if (((NodeList) root.get("name")).isEmpty()) { + root.appendNode("name", project.getName()); + } + if (((NodeList) root.get("description")).isEmpty()) { + root.appendNode("description", project.getDescription()); + } Node dependenciesNode = (Node) ((NodeList) root.get("dependencies")).get(0); project.getConfigurations().getByName(ShadowBasePlugin.CONFIGURATION_NAME).getAllDependencies().all(dependency -> { if (dependency instanceof ProjectDependency) { @@ -125,13 +134,17 @@ public String call() throws Exception { // Add git origin info to generated POM files publication.getPom().withXml(PublishPlugin::addScmInfo); - // have to defer this until archivesBaseName is set - project.afterEvaluate(p -> publication.setArtifactId(getArchivesBaseName(project))); - - // publish sources and javadoc for Java projects. - if (project.getPluginManager().hasPlugin("opensearch.java")) { - publication.artifact(project.getTasks().getByName("sourcesJar")); - publication.artifact(project.getTasks().getByName("javadocJar")); + if (!publication.getName().toLowerCase().contains("zip")) { + // have to defer this until archivesBaseName is set + project.afterEvaluate(p -> publication.setArtifactId(getArchivesBaseName(project))); + + // publish sources and javadoc for Java projects. + if (project.getPluginManager().hasPlugin("opensearch.java")) { + publication.artifact(project.getTasks().getByName("sourcesJar")); + publication.artifact(project.getTasks().getByName("javadocJar")); + } + } else { + project.afterEvaluate(p -> publication.setArtifactId(project.getName())); } generatePomTask.configure( @@ -143,9 +156,49 @@ public String call() throws Exception { private static void addScmInfo(XmlProvider xml) { Node root = xml.asNode(); - root.appendNode("url", Util.urlFromOrigin(BuildParams.getGitOrigin())); - Node scmNode = root.appendNode("scm"); - scmNode.appendNode("url", BuildParams.getGitOrigin()); + Node url = null, scm = null; + + for (final Object child : root.children()) { + if (child instanceof Node) { + final Node node = (Node) child; + final Object name = node.name(); + + try { + // For Gradle 6.8 and below, the class is groovy.xml.QName + // For Gradle 7.4 and above, the class is groovy.namespace.QName + if (name != null && name.getClass().getSimpleName().equals("QName")) { + final MethodHandle handle = MethodHandles.publicLookup() + .findVirtual(name.getClass(), "matches", MethodType.methodType(boolean.class, Object.class)) + .bindTo(name); + + if ((boolean) handle.invoke("url")) { + url = node; + } else if ((boolean) handle.invoke("scm")) { + scm = node; + } + } + } catch (final Throwable ex) { + // Not a suitable QName type we could use ... + } + + if ("url".equals(name)) { + url = node; + } else if ("scm".equals(name)) { + scm = node; + } + } + } + + // Only include URL section if it is not provided in the POM already + if (url == null) { + root.appendNode("url", Util.urlFromOrigin(BuildParams.getGitOrigin())); + } + + // Only include SCM section if it is not provided in the POM already + if (scm == null) { + Node scmNode = root.appendNode("scm"); + scmNode.appendNode("url", BuildParams.getGitOrigin()); + } } /** Adds a javadocJar task to generate a jar containing javadocs. */ diff --git a/buildSrc/src/main/java/org/opensearch/gradle/RepositoriesSetupPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/RepositoriesSetupPlugin.java index 30847f0648c5c..63b88f671c84c 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/RepositoriesSetupPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/RepositoriesSetupPlugin.java @@ -92,10 +92,9 @@ public static void configureRepositories(Project project) { throw new GradleException("Malformed lucene snapshot version: " + luceneVersion); } String revision = matcher.group(1); - // TODO(cleanup) - Setup own lucene snapshot repo MavenArtifactRepository luceneRepo = repos.maven(repo -> { repo.setName("lucene-snapshots"); - repo.setUrl("https://artifacts.opensearch.org/snapshots/lucene/"); + repo.setUrl("https://d1nvenhzbhpy0q.cloudfront.net/snapshots/lucene/"); }); repos.exclusiveContent(exclusiveRepo -> { exclusiveRepo.filter( diff --git a/buildSrc/src/main/java/org/opensearch/gradle/VersionProperties.java b/buildSrc/src/main/java/org/opensearch/gradle/VersionProperties.java index 2b50e59de04d3..4d8b62d95dff1 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/VersionProperties.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/VersionProperties.java @@ -31,6 +31,8 @@ package org.opensearch.gradle; +import org.apache.commons.lang3.StringUtils; + import java.io.IOException; import java.io.InputStream; import java.util.HashMap; @@ -54,7 +56,7 @@ public static String getLucene() { return lucene; } - public static String getBundledJdk(final String platform) { + public static String getBundledJdk(final String platform, final String arch) { switch (platform) { case "darwin": // fall trough case "mac": @@ -62,7 +64,7 @@ public static String getBundledJdk(final String platform) { case "freebsd": return bundledJdkFreeBSD; case "linux": - return bundledJdkLinux; + return getBundledJdkLinux(arch); case "windows": return bundledJdkWindows; default: @@ -70,6 +72,14 @@ public static String getBundledJdk(final String platform) { } } + public static String getBundledJdk(final String platform) { + return getBundledJdk(platform, null); + } + + public static String getBundledJre(final String platform, final String arch) { + return getBundledJdk(platform, arch); + } + public static String getBundledJdkVendor() { return bundledJdkVendor; } @@ -84,6 +94,10 @@ public static Map getVersions() { private static final String bundledJdkFreeBSD; private static final String bundledJdkLinux; private static final String bundledJdkWindows; + private static final String bundledJdkLinux_arm64; + private static final String bundledJdkLinux_x64; + private static final String bundledJdkLinux_s390x; + private static final String bundledJdkLinux_ppc64le; private static final String bundledJdkVendor; private static final Map versions = new HashMap(); @@ -98,6 +112,12 @@ public static Map getVersions() { bundledJdkLinux = props.getProperty("bundled_jdk_linux", bundledJdk); bundledJdkWindows = props.getProperty("bundled_jdk_windows", bundledJdk); + // Bundled JDKs per architecture (linux platform) + bundledJdkLinux_arm64 = props.getProperty("bundled_jdk_linux_arm64", bundledJdkLinux); + bundledJdkLinux_x64 = props.getProperty("bundled_jdk_linux_x64", bundledJdkLinux); + bundledJdkLinux_s390x = props.getProperty("bundled_jdk_linux_s390x", bundledJdkLinux); + bundledJdkLinux_ppc64le = props.getProperty("bundled_jdk_linux_ppc64le", bundledJdkLinux); + for (String property : props.stringPropertyNames()) { versions.put(property, props.getProperty(property)); } @@ -119,4 +139,24 @@ private static Properties getVersionProperties() { public static boolean isOpenSearchSnapshot() { return opensearch.endsWith("-SNAPSHOT"); } + + private static String getBundledJdkLinux(String arch) { + if (StringUtils.isBlank(arch)) { + return bundledJdkLinux; + } + + switch (arch) { + case "aarch64": + case "arm64": + return bundledJdkLinux_arm64; + case "x64": + return bundledJdkLinux_x64; + case "s390x": + return bundledJdkLinux_s390x; + case "ppc64le": + return bundledJdkLinux_ppc64le; + default: + throw new IllegalArgumentException("unknown platform [" + arch + "] for 'linux' platform"); + } + } } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerBuildTask.java b/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerBuildTask.java index bb2a6d37362e1..08f0e7488a43c 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerBuildTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerBuildTask.java @@ -52,6 +52,7 @@ import org.gradle.workers.WorkerExecutor; import javax.inject.Inject; + import java.io.IOException; import java.util.Arrays; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerSupportPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerSupportPlugin.java index 92777638982d2..5455875a96611 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerSupportPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerSupportPlugin.java @@ -56,13 +56,9 @@ public void apply(Project project) { Provider dockerSupportServiceProvider = project.getGradle() .getSharedServices() - .registerIfAbsent( - DOCKER_SUPPORT_SERVICE_NAME, - DockerSupportService.class, - spec -> spec.parameters( - params -> { params.setExclusionsFile(new File(project.getRootDir(), DOCKER_ON_LINUX_EXCLUSIONS_FILE)); } - ) - ); + .registerIfAbsent(DOCKER_SUPPORT_SERVICE_NAME, DockerSupportService.class, spec -> spec.parameters(params -> { + params.setExclusionsFile(new File(project.getRootDir(), DOCKER_ON_LINUX_EXCLUSIONS_FILE)); + })); // Ensure that if we are trying to run any DockerBuildTask tasks, we assert an available Docker installation exists project.getGradle().getTaskGraph().whenReady(graph -> { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerSupportService.java b/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerSupportService.java index 2eb2852e3e55e..fc78792bb3551 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerSupportService.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/docker/DockerSupportService.java @@ -31,6 +31,7 @@ package org.opensearch.gradle.docker; +import org.apache.tools.ant.taskdefs.condition.Os; import org.opensearch.gradle.Version; import org.opensearch.gradle.info.BuildParams; import org.gradle.api.GradleException; @@ -40,9 +41,9 @@ import org.gradle.api.services.BuildServiceParameters; import org.gradle.process.ExecOperations; import org.gradle.process.ExecResult; -import org.apache.tools.ant.taskdefs.condition.Os; import javax.inject.Inject; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -68,7 +69,9 @@ public abstract class DockerSupportService implements BuildService=" + numberOfNodes + "&wait_for_status=yellow")); } + public WaitForHttpResource(String protocol, String host, String username, String password, int numberOfNodes) + throws MalformedURLException { + this( + new URL( + protocol + + "://" + + username + + ":" + + password + + "@" + + host + + "/_cluster/health?wait_for_nodes=>=" + + numberOfNodes + + "&wait_for_status=yellow" + ) + ); + } + public WaitForHttpResource(URL url) { this.url = url; } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/info/BuildParams.java b/buildSrc/src/main/java/org/opensearch/gradle/info/BuildParams.java index 331794c9c2dd7..a9ccb75569002 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/info/BuildParams.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/info/BuildParams.java @@ -192,8 +192,8 @@ public void setRuntimeJavaHome(File runtimeJavaHome) { BuildParams.runtimeJavaHome = requireNonNull(runtimeJavaHome); } - public void setIsRutimeJavaHomeSet(boolean isRutimeJavaHomeSet) { - BuildParams.isRuntimeJavaHomeSet = isRutimeJavaHomeSet; + public void setIsRuntimeJavaHomeSet(boolean isRuntimeJavaHomeSet) { + BuildParams.isRuntimeJavaHomeSet = isRuntimeJavaHomeSet; } public void setJavaVersions(List javaVersions) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/info/GlobalBuildInfoPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/info/GlobalBuildInfoPlugin.java index ccd82372bb11b..448ba8a96ef02 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/info/GlobalBuildInfoPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/info/GlobalBuildInfoPlugin.java @@ -45,13 +45,16 @@ import org.gradle.internal.jvm.Jvm; import org.gradle.internal.jvm.inspection.JvmInstallationMetadata; import org.gradle.internal.jvm.inspection.JvmMetadataDetector; +import org.gradle.jvm.toolchain.internal.InstallationLocation; import org.gradle.util.GradleVersion; import javax.inject.Inject; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.UncheckedIOException; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -72,8 +75,8 @@ public class GlobalBuildInfoPlugin implements Plugin { private static final Logger LOGGER = Logging.getLogger(GlobalBuildInfoPlugin.class); - private static final String DEFAULT_LEGACY_VERSION_JAVA_FILE_PATH = "server/src/main/java/org/opensearch/LegacyESVersion.java"; - private static final String DEFAULT_VERSION_JAVA_FILE_PATH = "server/src/main/java/org/opensearch/Version.java"; + private static final String DEFAULT_LEGACY_VERSION_JAVA_FILE_PATH = "libs/core/src/main/java/org/opensearch/LegacyESVersion.java"; + private static final String DEFAULT_VERSION_JAVA_FILE_PATH = "libs/core/src/main/java/org/opensearch/Version.java"; private static Integer _defaultParallel = null; private final JvmMetadataDetector jvmMetadataDetector; @@ -113,7 +116,7 @@ public void apply(Project project) { params.reset(); params.setRuntimeJavaHome(runtimeJavaHome); params.setRuntimeJavaVersion(determineJavaVersion("runtime java.home", runtimeJavaHome, minimumRuntimeVersion)); - params.setIsRutimeJavaHomeSet(runtimeJavaHomeOpt.isPresent()); + params.setIsRuntimeJavaHomeSet(runtimeJavaHomeOpt.isPresent()); params.setRuntimeJavaDetails(getJavaInstallation(runtimeJavaHome).getDisplayName()); params.setJavaVersions(getAvailableJavaVersions(minimumCompilerVersion)); params.setMinimumCompilerVersion(minimumCompilerVersion); @@ -150,7 +153,7 @@ private static BwcVersions resolveBwcVersions(File root) { versionLines.addAll(IOUtils.readLines(fis2, "UTF-8")); return new BwcVersions(versionLines); } catch (IOException e) { - throw new IllegalStateException("Unable to resolve to resolve bwc versions from versionsFile.", e); + throw new IllegalStateException("Unable to resolve bwc versions from versionsFile.", e); } } @@ -196,7 +199,29 @@ private JavaVersion determineJavaVersion(String description, File javaHome, Java } private JvmInstallationMetadata getJavaInstallation(File javaHome) { - return jvmMetadataDetector.getMetadata(javaHome); + final InstallationLocation location = new InstallationLocation(javaHome, "Java home"); + + try { + try { + // The getMetadata(File) is used by Gradle pre-7.6 + return (JvmInstallationMetadata) MethodHandles.publicLookup() + .findVirtual(JvmMetadataDetector.class, "getMetadata", MethodType.methodType(JvmInstallationMetadata.class, File.class)) + .bindTo(jvmMetadataDetector) + .invokeExact(location.getLocation()); + } catch (NoSuchMethodException | IllegalAccessException ex) { + // The getMetadata(InstallationLocation) is used by Gradle post-7.6 + return (JvmInstallationMetadata) MethodHandles.publicLookup() + .findVirtual( + JvmMetadataDetector.class, + "getMetadata", + MethodType.methodType(JvmInstallationMetadata.class, InstallationLocation.class) + ) + .bindTo(jvmMetadataDetector) + .invokeExact(location); + } + } catch (Throwable ex) { + throw new IllegalStateException("Unable to find suitable JvmMetadataDetector::getMetadata", ex); + } } private List getAvailableJavaVersions(JavaVersion minimumCompilerVersion) { @@ -206,7 +231,7 @@ private List getAvailableJavaVersions(JavaVersion minimumCompilerVersi String javaHomeEnvVarName = getJavaHomeEnvVarName(Integer.toString(version)); if (System.getenv(javaHomeEnvVarName) != null) { File javaHomeDirectory = new File(findJavaHome(Integer.toString(version))); - JvmInstallationMetadata javaInstallation = jvmMetadataDetector.getMetadata(javaHomeDirectory); + JvmInstallationMetadata javaInstallation = getJavaInstallation(javaHomeDirectory); JavaHome javaHome = JavaHome.of(version, providers.provider(() -> { int actualVersion = Integer.parseInt(javaInstallation.getLanguageVersion().getMajorVersion()); if (actualVersion != version) { @@ -220,14 +245,6 @@ private List getAvailableJavaVersions(JavaVersion minimumCompilerVersi return javaVersions; } - private static boolean isCurrentJavaHome(File javaHome) { - try { - return Files.isSameFile(javaHome.toPath(), Jvm.current().getJavaHome().toPath()); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - private static String getTestSeed() { String testSeedProperty = System.getProperty("tests.seed"); final String testSeed; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalBwcGitPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalBwcGitPlugin.java index 11270e5c9a51d..159270d28e3d6 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalBwcGitPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalBwcGitPlugin.java @@ -50,10 +50,12 @@ import org.gradle.process.ExecSpec; import javax.inject.Inject; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; + import static java.util.Arrays.asList; public class InternalBwcGitPlugin implements Plugin { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionArchiveCheckPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionArchiveCheckPlugin.java index 2a162e5f12d7b..4949476038bdb 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionArchiveCheckPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionArchiveCheckPlugin.java @@ -44,6 +44,7 @@ import org.gradle.api.tasks.TaskProvider; import javax.inject.Inject; + import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -73,12 +74,14 @@ public void apply(Project project) { .create("distributionArchiveCheck", DistributionArchiveCheckExtension.class); File archiveExtractionDir = calculateArchiveExtractionDir(project); - // sanity checks if archives can be extracted TaskProvider checkExtraction = registerCheckExtractionTask(project, buildDistTask, archiveExtractionDir); + checkExtraction.configure(InternalDistributionArchiveSetupPlugin.configure(buildTaskName)); TaskProvider checkLicense = registerCheckLicenseTask(project, checkExtraction); + checkLicense.configure(InternalDistributionArchiveSetupPlugin.configure(buildTaskName)); TaskProvider checkNotice = registerCheckNoticeTask(project, checkExtraction); + checkNotice.configure(InternalDistributionArchiveSetupPlugin.configure(buildTaskName)); TaskProvider checkTask = project.getTasks().named("check"); checkTask.configure(task -> { task.dependsOn(checkExtraction); @@ -118,7 +121,7 @@ public void execute(Task task) { } private TaskProvider registerCheckLicenseTask(Project project, TaskProvider checkExtraction) { - TaskProvider checkLicense = project.getTasks().register("checkLicense", task -> { + return project.getTasks().register("checkLicense", task -> { task.dependsOn(checkExtraction); task.doLast(new Action() { @Override @@ -138,7 +141,6 @@ public void execute(Task task) { } }); }); - return checkLicense; } private TaskProvider registerCheckExtractionTask(Project project, TaskProvider buildDistTask, File archiveExtractionDir) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionArchiveSetupPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionArchiveSetupPlugin.java index 8adfbff424278..7ab91448252f2 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionArchiveSetupPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionArchiveSetupPlugin.java @@ -41,9 +41,9 @@ import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.type.ArtifactTypeDefinition; -import org.gradle.api.plugins.BasePlugin; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; +import org.gradle.api.plugins.BasePlugin; import org.gradle.api.tasks.AbstractCopyTask; import org.gradle.api.tasks.Sync; import org.gradle.api.tasks.TaskContainer; @@ -52,6 +52,7 @@ import org.gradle.api.tasks.bundling.Compression; import org.gradle.api.tasks.bundling.Zip; import org.gradle.internal.os.OperatingSystem; + import java.io.File; import static org.opensearch.gradle.util.Util.capitalize; @@ -87,7 +88,7 @@ public void apply(Project project) { configureTarDefaults(project); } - private Action configure(String name) { + static Action configure(String name) { return (Task task) -> task.onlyIf(s -> { if (OperatingSystem.current().isWindows()) { // On Windows, include only Windows distributions and integTestZip diff --git a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionBwcSetupPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionBwcSetupPlugin.java index dd2393702fe2b..6892af1b17f97 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionBwcSetupPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionBwcSetupPlugin.java @@ -48,6 +48,7 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin; import javax.inject.Inject; + import java.io.File; import java.util.ArrayList; import java.util.List; @@ -76,12 +77,9 @@ public InternalDistributionBwcSetupPlugin(ProviderFactory providerFactory) { @Override public void apply(Project project) { project.getRootProject().getPluginManager().apply(GlobalBuildInfoPlugin.class); - BuildParams.getBwcVersions() - .forPreviousUnreleased( - (BwcVersions.UnreleasedVersionInfo unreleasedVersion) -> { - configureBwcProject(project.project(unreleasedVersion.gradleProjectPath), unreleasedVersion); - } - ); + BuildParams.getBwcVersions().forPreviousUnreleased((BwcVersions.UnreleasedVersionInfo unreleasedVersion) -> { + configureBwcProject(project.project(unreleasedVersion.gradleProjectPath), unreleasedVersion); + }); } private void configureBwcProject(Project project, BwcVersions.UnreleasedVersionInfo versionInfo) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionDownloadPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionDownloadPlugin.java index f4368b1cecc59..6a54612320c6c 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionDownloadPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/internal/InternalDistributionDownloadPlugin.java @@ -37,6 +37,7 @@ import org.opensearch.gradle.DistributionDependency; import org.opensearch.gradle.DistributionDownloadPlugin; import org.opensearch.gradle.DistributionResolution; +import org.opensearch.gradle.JavaPackageType; import org.opensearch.gradle.OpenSearchDistribution; import org.opensearch.gradle.Version; import org.opensearch.gradle.VersionProperties; @@ -99,7 +100,7 @@ private void registerInternalDistributionResolutions(NamedDomainObjectContainer< resolutions.register("bwc", distributionResolution -> distributionResolution.setResolver((project, distribution) -> { BwcVersions.UnreleasedVersionInfo unreleasedInfo = bwcVersions.unreleasedInfo(Version.fromString(distribution.getVersion())); if (unreleasedInfo != null) { - if (!distribution.getBundledJdk()) { + if (distribution.getBundledJdk() == JavaPackageType.NONE) { throw new GradleException( "Configuring a snapshot bwc distribution ('" + distribution.getName() @@ -167,8 +168,10 @@ private static String distributionProjectName(OpenSearchDistribution distributio ? "" : "-" + architecture.toString().toLowerCase(); - if (distribution.getBundledJdk() == false) { + if (distribution.getBundledJdk() == JavaPackageType.NONE) { projectName += "no-jdk-"; + } else if (distribution.getBundledJdk() == JavaPackageType.JRE) { + projectName += "jre-"; } switch (distribution.getType()) { case ARCHIVE: diff --git a/buildSrc/src/main/java/org/opensearch/gradle/jvm/JvmTestSuiteHelper.java b/buildSrc/src/main/java/org/opensearch/gradle/jvm/JvmTestSuiteHelper.java new file mode 100644 index 0000000000000..3b8e88c9f1a8f --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/jvm/JvmTestSuiteHelper.java @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle.jvm; + +import org.gradle.api.Project; +import org.gradle.api.plugins.JvmTestSuitePlugin; +import org.gradle.api.plugins.jvm.JvmTestSuite; +import org.gradle.testing.base.TestSuite; +import org.gradle.testing.base.TestingExtension; + +import java.util.Optional; + +public final class JvmTestSuiteHelper { + private JvmTestSuiteHelper() {} + + /** + * Gets the default test suite. This method assumes the Java plugin is applied, + * adapted from {@link org.gradle.api.plugins.internal.JavaPluginHelper} since it is not + * available in Gradle releases predated 8.1. + */ + public static Optional getDefaultTestSuite(Project project) { + TestingExtension testing = project.getExtensions().findByType(TestingExtension.class); + if (testing == null) { + return Optional.empty(); + } + + TestSuite defaultTestSuite = testing.getSuites().findByName(JvmTestSuitePlugin.DEFAULT_TEST_SUITE_NAME); + if (!(defaultTestSuite instanceof JvmTestSuite)) { + return Optional.empty(); + } + + return Optional.of((JvmTestSuite) defaultTestSuite); + } +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/pluginzip/Publish.java b/buildSrc/src/main/java/org/opensearch/gradle/pluginzip/Publish.java new file mode 100644 index 0000000000000..a80471f6e6271 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/pluginzip/Publish.java @@ -0,0 +1,113 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.gradle.pluginzip; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.publish.maven.plugins.MavenPublishPlugin; + +import java.nio.file.Path; +import java.util.Set; +import java.util.stream.Collectors; + +public class Publish implements Plugin { + private final static String DEFAULT_GROUP_ID = "org.opensearch.plugin"; + + public final static String PUBLICATION_NAME = "pluginZip"; + public final static String STAGING_REPO = "zipStaging"; + public final static String LOCAL_STAGING_REPO_PATH = "/build/local-staging-repo"; + // TODO: Does the path ^^ need to use platform dependant file separators ? + + /** + * This method returns a "default" groupId value ("{@link #DEFAULT_GROUP_ID}"). + * It is possible to have the `group` property unspecified in which case the default value is used instead. + * See GitHub discussion + * for details. + * + * @deprecated This method will be removed in OpenSearch 3.x and `group` property will be required + * @return The default groupId value + */ + @Deprecated + public static String getDefaultGroupId(Project project) { + project.getLogger() + .warn( + String.format( + "The 'project.group' property is empty, a default value '%s' will be used instead. " + + "Please notice that in OpenSearch 3.x the 'project.group' property will be required.", + DEFAULT_GROUP_ID + ) + ); + return DEFAULT_GROUP_ID; + } + + private boolean isZipPublicationPresent(Project project) { + PublishingExtension pe = project.getExtensions().findByType(PublishingExtension.class); + if (pe == null) { + return false; + } + return pe.getPublications().findByName(PUBLICATION_NAME) != null; + } + + private void addLocalMavenRepo(Project project) { + final Path buildDirectory = project.getRootDir().toPath(); + project.getExtensions().configure(PublishingExtension.class, publishing -> { + publishing.repositories(repositories -> { + repositories.maven(maven -> { + maven.setName(STAGING_REPO); + maven.setUrl(buildDirectory.toString() + LOCAL_STAGING_REPO_PATH); + }); + }); + }); + } + + private void addZipArtifact(Project project) { + project.getExtensions().configure(PublishingExtension.class, publishing -> { + publishing.publications(publications -> { + MavenPublication mavenZip = (MavenPublication) publications.findByName(PUBLICATION_NAME); + if (mavenZip != null) { + mavenZip.artifact(project.getTasks().named("bundlePlugin")); + if (mavenZip.getGroupId().isEmpty()) { + mavenZip.setGroupId(getDefaultGroupId(project)); + } + } + }); + }); + } + + @Override + public void apply(Project project) { + project.getPluginManager().apply("com.netflix.nebula.maven-nebula-publish"); + project.getPluginManager().apply(MavenPublishPlugin.class); + project.afterEvaluate(evaluatedProject -> { + if (isZipPublicationPresent(project)) { + addLocalMavenRepo(project); + addZipArtifact(project); + Task validatePluginZipPom = project.getTasks().findByName("validatePluginZipPom"); + if (validatePluginZipPom != null) { + validatePluginZipPom.dependsOn("generatePomFileForNebulaPublication"); + } + + // There are number of tasks prefixed by 'publishPluginZipPublication', f.e.: + // publishPluginZipPublicationToZipStagingRepository, publishPluginZipPublicationToMavenLocal + final Set publishPluginZipPublicationToTasks = project.getTasks() + .stream() + .filter(t -> t.getName().startsWith("publishPluginZipPublicationTo")) + .collect(Collectors.toSet()); + if (!publishPluginZipPublicationToTasks.isEmpty()) { + publishPluginZipPublicationToTasks.forEach(t -> t.dependsOn("generatePomFileForNebulaPublication")); + } + } else { + project.getLogger() + .warn(String.format("Plugin 'opensearch.pluginzip' is applied but no '%s' publication is defined.", PUBLICATION_NAME)); + } + }); + } +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/FilePermissionsTask.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/FilePermissionsTask.java index 9ffd472151b4b..2c17666d8ee0c 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/FilePermissionsTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/FilePermissionsTask.java @@ -31,29 +31,32 @@ package org.opensearch.gradle.precommit; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.attribute.PosixFileAttributeView; -import java.nio.file.attribute.PosixFilePermission; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - import org.apache.tools.ant.taskdefs.condition.Os; import org.opensearch.gradle.util.GradleUtils; import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileTree; +import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.StopExecutionException; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.util.PatternFilterable; import org.gradle.api.tasks.util.PatternSet; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFilePermission; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + /** * Checks source files for correct file permissions. */ @@ -92,6 +95,8 @@ private static boolean isExecutableFile(File file) { */ @InputFiles @SkipWhenEmpty + @IgnoreEmptyDirectories + @PathSensitive(PathSensitivity.RELATIVE) public FileCollection getFiles() { return GradleUtils.getJavaSourceSets(getProject()) .stream() diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ForbiddenApisPrecommitPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ForbiddenApisPrecommitPlugin.java index 328edda8b1787..6b89aa8b60197 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ForbiddenApisPrecommitPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ForbiddenApisPrecommitPlugin.java @@ -34,7 +34,9 @@ import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis; import de.thetaphi.forbiddenapis.gradle.ForbiddenApisPlugin; + import groovy.lang.Closure; + import org.opensearch.gradle.ExportOpenSearchBuildResourcesTask; import org.opensearch.gradle.info.BuildParams; import org.opensearch.gradle.util.GradleUtils; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ForbiddenPatternsTask.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ForbiddenPatternsTask.java index f57c190496452..6ef1e77f5138f 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ForbiddenPatternsTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ForbiddenPatternsTask.java @@ -36,10 +36,13 @@ import org.gradle.api.InvalidUserDataException; import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileTree; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.util.PatternFilterable; @@ -100,9 +103,11 @@ public ForbiddenPatternsTask() { @InputFiles @SkipWhenEmpty + @IgnoreEmptyDirectories + @PathSensitive(PathSensitivity.RELATIVE) public FileCollection getFiles() { - return getProject().getConvention() - .getPlugin(JavaPluginConvention.class) + return getProject().getExtensions() + .getByType(JavaPluginExtension.class) .getSourceSets() .stream() .map(sourceSet -> sourceSet.getAllSource().matching(filesFilter)) diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/JarHellPrecommitPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/JarHellPrecommitPlugin.java index 87bf03cec3587..429028c9bf841 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/JarHellPrecommitPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/JarHellPrecommitPlugin.java @@ -44,11 +44,11 @@ public class JarHellPrecommitPlugin extends PrecommitPlugin { @Override public TaskProvider createTask(Project project) { Configuration jarHellConfig = project.getConfigurations().create("jarHell"); - if (BuildParams.isInternal() && project.getPath().equals(":libs:opensearch-core") == false) { + if (BuildParams.isInternal() && project.getPath().equals(":libs:opensearch-common") == false) { // External plugins will depend on this already via transitive dependencies. // Internal projects are not all plugins, so make sure the check is available // we are not doing this for this project itself to avoid jar hell with itself - project.getDependencies().add("jarHell", project.project(":libs:opensearch-core")); + project.getDependencies().add("jarHell", project.project(":libs:opensearch-common")); } TaskProvider jarHell = project.getTasks().register("jarHell", JarHellTask.class); diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/JarHellTask.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/JarHellTask.java index c6a0d7abf7da1..7726133562e9f 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/JarHellTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/JarHellTask.java @@ -33,7 +33,6 @@ package org.opensearch.gradle.precommit; import org.opensearch.gradle.LoggedExec; - import org.gradle.api.file.FileCollection; import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.CompileClasspath; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/LoggerUsageTask.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/LoggerUsageTask.java index 1fd092b7f268f..db215fb65ef95 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/LoggerUsageTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/LoggerUsageTask.java @@ -34,9 +34,10 @@ import org.opensearch.gradle.LoggedExec; import org.gradle.api.file.FileCollection; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.PathSensitive; import org.gradle.api.tasks.PathSensitivity; @@ -79,9 +80,10 @@ public void setClasspath(FileCollection classpath) { @InputFiles @PathSensitive(PathSensitivity.RELATIVE) @SkipWhenEmpty + @IgnoreEmptyDirectories public FileCollection getClassDirectories() { - return getProject().getConvention() - .getPlugin(JavaPluginConvention.class) + return getProject().getExtensions() + .getByType(JavaPluginExtension.class) .getSourceSets() .stream() // Don't pick up all source sets like the java9 ones as logger-check doesn't support the class format diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/TestingConventionsTasks.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/TestingConventionsTasks.java index 5e42cc99dd79e..d66b1f9d25cdd 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/TestingConventionsTasks.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/TestingConventionsTasks.java @@ -32,6 +32,8 @@ package org.opensearch.gradle.precommit; import groovy.lang.Closure; + +import org.opensearch.gradle.jvm.JvmTestSuiteHelper; import org.opensearch.gradle.util.GradleUtils; import org.opensearch.gradle.util.Util; import org.gradle.api.DefaultTask; @@ -39,6 +41,7 @@ import org.gradle.api.Task; import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileTree; +import org.gradle.api.plugins.jvm.JvmTestSuite; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.OutputFile; @@ -46,6 +49,11 @@ import org.gradle.api.tasks.SourceSetContainer; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.testing.Test; +import org.gradle.api.tasks.util.PatternFilterable; +import org.gradle.api.tasks.util.PatternSet; +import org.gradle.internal.Factory; + +import javax.inject.Inject; import java.io.File; import java.io.IOException; @@ -85,13 +93,39 @@ public TestingConventionsTasks() { naming = getProject().container(TestingConventionRule.class); } + @Inject + protected Factory getPatternSetFactory() { + throw new UnsupportedOperationException(); + } + @Input public Map> getClassFilesPerEnabledTask() { return getProject().getTasks() .withType(Test.class) .stream() .filter(Task::getEnabled) - .collect(Collectors.toMap(Task::getPath, task -> task.getCandidateClassFiles().getFiles())); + .collect(Collectors.toMap(Task::getPath, task -> { + // See please https://docs.gradle.org/8.1/userguide/upgrading_version_8.html#test_task_default_classpath + final JvmTestSuite jvmTestSuite = JvmTestSuiteHelper.getDefaultTestSuite(getProject()).orElse(null); + if (jvmTestSuite != null) { + final PatternFilterable patternSet = getPatternSetFactory().create() + .include(task.getIncludes()) + .exclude(task.getExcludes()); + + final Set files = jvmTestSuite.getSources() + .getOutput() + .getClassesDirs() + .getAsFileTree() + .matching(patternSet) + .getFiles(); + + if (!files.isEmpty()) { + return files; + } + } + + return task.getCandidateClassFiles().getFiles(); + })); } @Input diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ThirdPartyAuditPrecommitPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ThirdPartyAuditPrecommitPlugin.java index 5d707ce2b9f28..d83f1b01ee043 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ThirdPartyAuditPrecommitPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ThirdPartyAuditPrecommitPlugin.java @@ -51,7 +51,7 @@ public class ThirdPartyAuditPrecommitPlugin extends PrecommitPlugin { public TaskProvider createTask(Project project) { project.getPlugins().apply(CompileOnlyResolvePlugin.class); project.getConfigurations().create("forbiddenApisCliJar"); - project.getDependencies().add("forbiddenApisCliJar", "de.thetaphi:forbiddenapis:3.2"); + project.getDependencies().add("forbiddenApisCliJar", "de.thetaphi:forbiddenapis:3.5.1"); Configuration jdkJarHellConfig = project.getConfigurations().create(JDK_JAR_HELL_CONFIG_NAME); if (BuildParams.isInternal() && project.getPath().equals(":libs:opensearch-core") == false) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ThirdPartyAuditTask.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ThirdPartyAuditTask.java index ee68d2740e279..9e740b4e061cf 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ThirdPartyAuditTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ThirdPartyAuditTask.java @@ -32,6 +32,7 @@ package org.opensearch.gradle.precommit; import de.thetaphi.forbiddenapis.cli.CliMain; + import org.apache.commons.io.output.NullOutputStream; import org.opensearch.gradle.LoggedExec; import org.opensearch.gradle.OS; @@ -47,6 +48,7 @@ import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.CompileClasspath; +import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.InputFiles; @@ -78,9 +80,7 @@ @CacheableTask public class ThirdPartyAuditTask extends DefaultTask { - private static final Pattern MISSING_CLASS_PATTERN = Pattern.compile( - "WARNING: Class '(.*)' cannot be loaded \\(.*\\)\\. Please fix the classpath!" - ); + private static final Pattern MISSING_CLASS_PATTERN = Pattern.compile("DEBUG: Class '(.*)' cannot be loaded \\(.*\\)\\."); private static final Pattern VIOLATION_PATTERN = Pattern.compile("\\s\\sin ([a-zA-Z0-9$.]+) \\(.*\\)"); private static final int SIG_KILL_EXIT_VALUE = 137; @@ -105,6 +105,8 @@ public class ThirdPartyAuditTask extends DefaultTask { private final Property targetCompatibility = getProject().getObjects().property(JavaVersion.class); + public boolean jarHellEnabled = true; + @Input public Property getTargetCompatibility() { return targetCompatibility; @@ -195,6 +197,7 @@ public Set getMissingClassExcludes() { @Classpath @SkipWhenEmpty + @IgnoreEmptyDirectories public Set getJarsToScan() { // These are SelfResolvingDependency, and some of them backed by file collections, like the Gradle API files, // or dependencies added as `files(...)`, we can't be sure if those are third party or not. @@ -232,9 +235,13 @@ public void runThirdPartyAudit() throws IOException { violationsClasses.add(violationMatcher.group(1)); } - Set jdkJarHellClasses = runJdkJarHellCheck(); + Set jdkJarHellClasses = null; + if (this.jarHellEnabled) { + jdkJarHellClasses = runJdkJarHellCheck(); + } if (missingClassExcludes != null) { + assertNoPointlessExclusions("are not missing", missingClassExcludes, missingClasses); long bogousExcludesCount = Stream.concat(missingClassExcludes.stream(), violationsExcludes.stream()) .filter(each -> missingClasses.contains(each) == false) .filter(each -> violationsClasses.contains(each) == false) @@ -245,11 +252,12 @@ public void runThirdPartyAudit() throws IOException { "All excluded classes seem to have no issues. " + "This is sometimes an indication that the check silently failed" ); } - assertNoPointlessExclusions("are not missing", missingClassExcludes, missingClasses); missingClasses.removeAll(missingClassExcludes); } assertNoPointlessExclusions("have no violations", violationsExcludes, violationsClasses); - assertNoPointlessExclusions("do not generate jar hell with the JDK", jdkJarHellExcludes, jdkJarHellClasses); + if (this.jarHellEnabled) { + assertNoPointlessExclusions("do not generate jar hell with the JDK", jdkJarHellExcludes, jdkJarHellClasses); + } if (missingClassExcludes == null && (missingClasses.isEmpty() == false)) { getLogger().info("Found missing classes, but task is configured to ignore all of them:\n {}", formatClassList(missingClasses)); @@ -270,7 +278,9 @@ public void runThirdPartyAudit() throws IOException { throw new IllegalStateException("Audit of third party dependencies failed"); } - assertNoJarHell(jdkJarHellClasses); + if (this.jarHellEnabled) { + assertNoJarHell(jdkJarHellClasses); + } // Mark successful third party audit check getSuccessMarker().getParentFile().mkdirs(); @@ -357,7 +367,7 @@ private String runForbiddenAPIsCli() throws IOException { spec.jvmArgs("-Xmx1g"); spec.jvmArgs(LoggedExec.shortLivedArgs()); spec.getMainClass().set("de.thetaphi.forbiddenapis.cli.CliMain"); - spec.args("-f", getSignatureFile().getAbsolutePath(), "-d", getJarExpandDir(), "--allowmissingclasses"); + spec.args("-f", getSignatureFile().getAbsolutePath(), "-d", getJarExpandDir(), "--debug", "--allowmissingclasses"); spec.setErrorOutput(errorOut); if (getLogger().isInfoEnabled() == false) { spec.setStandardOutput(new NullOutputStream()); diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ValidateJsonAgainstSchemaTask.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ValidateJsonAgainstSchemaTask.java index ff28197feb02b..d829071c07e3c 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ValidateJsonAgainstSchemaTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ValidateJsonAgainstSchemaTask.java @@ -33,12 +33,14 @@ package org.opensearch.gradle.precommit; import com.fasterxml.jackson.databind.ObjectMapper; + import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaException; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SchemaValidatorsConfig; import com.networknt.schema.SpecVersion; import com.networknt.schema.ValidationMessage; + import org.gradle.api.DefaultTask; import org.gradle.api.UncheckedIOException; import org.gradle.api.file.FileCollection; @@ -103,6 +105,7 @@ public void validate(InputChanges inputChanges) throws IOException { File jsonSchemaOnDisk = getJsonSchema(); getLogger().debug("JSON schema : [{}]", jsonSchemaOnDisk.getAbsolutePath()); SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + config.setEcma262Validator(true); JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); JsonSchema jsonSchema = factory.getSchema(mapper.readTree(jsonSchemaOnDisk), config); Map> errors = new LinkedHashMap<>(); diff --git a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ValidateJsonNoKeywordsTask.java b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ValidateJsonNoKeywordsTask.java index b3ac804566e29..c69420f2216b2 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/precommit/ValidateJsonNoKeywordsTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/precommit/ValidateJsonNoKeywordsTask.java @@ -36,6 +36,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; + import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; import org.gradle.api.file.FileCollection; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/tar/SymbolicLinkPreservingTar.java b/buildSrc/src/main/java/org/opensearch/gradle/tar/SymbolicLinkPreservingTar.java index 7b8e9c9c925ba..1423b52c443d9 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/tar/SymbolicLinkPreservingTar.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/tar/SymbolicLinkPreservingTar.java @@ -65,6 +65,11 @@ * This task is necessary because the built-in task {@link org.gradle.api.tasks.bundling.Tar} does not preserve symbolic links. */ public class SymbolicLinkPreservingTar extends Tar { + private long lastModifiedTimestamp = 0; + + public void setLastModifiedTimestamp(long lastModifiedTimestamp) { + this.lastModifiedTimestamp = lastModifiedTimestamp; + } @Override protected CopyAction createCopyAction() { @@ -80,7 +85,7 @@ protected CopyAction createCopyAction() { compressor = new SimpleCompressor(); break; } - return new SymbolicLinkPreservingTarCopyAction(getArchiveFile(), compressor, isPreserveFileTimestamps()); + return new SymbolicLinkPreservingTarCopyAction(getArchiveFile(), compressor, isPreserveFileTimestamps(), lastModifiedTimestamp); } private static class SymbolicLinkPreservingTarCopyAction implements CopyAction { @@ -88,15 +93,18 @@ private static class SymbolicLinkPreservingTarCopyAction implements CopyAction { private final Provider tarFile; private final ArchiveOutputStreamFactory compressor; private final boolean isPreserveFileTimestamps; + private final long lastModifiedTimestamp; SymbolicLinkPreservingTarCopyAction( final Provider tarFile, final ArchiveOutputStreamFactory compressor, - final boolean isPreserveFileTimestamps + final boolean isPreserveFileTimestamps, + final long lastModifiedTimestamp ) { this.tarFile = tarFile; this.compressor = compressor; this.isPreserveFileTimestamps = isPreserveFileTimestamps; + this.lastModifiedTimestamp = lastModifiedTimestamp; } @Override @@ -219,7 +227,7 @@ private void handleProcessingException(final FileCopyDetailsInternal details, fi } private long getModTime(final FileCopyDetails details) { - return isPreserveFileTimestamps ? details.getLastModified() : 0; + return isPreserveFileTimestamps ? details.getLastModified() : lastModifiedTimestamp; } } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/test/DistroTestPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/test/DistroTestPlugin.java index a77155aacf723..a420c8b63b02c 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/test/DistroTestPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/test/DistroTestPlugin.java @@ -34,9 +34,10 @@ import org.opensearch.gradle.Architecture; import org.opensearch.gradle.DistributionDownloadPlugin; -import org.opensearch.gradle.OpenSearchDistribution; +import org.opensearch.gradle.JavaPackageType; import org.opensearch.gradle.Jdk; import org.opensearch.gradle.JdkDownloadPlugin; +import org.opensearch.gradle.OpenSearchDistribution; import org.opensearch.gradle.SystemPropertyCommandLineArgumentProvider; import org.opensearch.gradle.Version; import org.opensearch.gradle.VersionProperties; @@ -48,6 +49,7 @@ import org.opensearch.gradle.util.Util; import org.opensearch.gradle.vagrant.VagrantBasePlugin; import org.opensearch.gradle.vagrant.VagrantExtension; +import org.opensearch.gradle.vagrant.VagrantMachine; import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Plugin; @@ -61,7 +63,6 @@ import org.gradle.api.tasks.Copy; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.testing.Test; -import org.opensearch.gradle.vagrant.VagrantMachine; import java.io.File; import java.util.ArrayList; @@ -71,13 +72,14 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.function.Supplier; import java.util.stream.Stream; public class DistroTestPlugin implements Plugin { - private static final String SYSTEM_JDK_VERSION = "8u242+b08"; - private static final String SYSTEM_JDK_VENDOR = "adoptopenjdk"; - private static final String GRADLE_JDK_VERSION = "17.0.2+8"; + private static final String SYSTEM_JDK_VERSION = "11.0.20+8"; + private static final String SYSTEM_JDK_VENDOR = "adoptium"; + private static final String GRADLE_JDK_VERSION = "17.0.8+7"; private static final String GRADLE_JDK_VENDOR = "adoptium"; // all distributions used by distro tests. this is temporary until tests are per distribution @@ -119,8 +121,8 @@ public void apply(Project project) { TaskProvider depsTask = project.getTasks().register(taskname + "#deps"); depsTask.configure(t -> t.dependsOn(distribution, examplePlugin)); depsTasks.put(taskname, depsTask); - // TODO - suppressing failure temporarily where duplicate tasks are created for docker. - try { + // Avoid duplicate tasks such as docker registered in lifecycleTasks + if (project.getTasksByName(taskname, false).isEmpty()) { TaskProvider destructiveTask = configureTestTask(project, taskname, distribution, t -> { t.onlyIf(t2 -> distribution.isDocker() == false || dockerSupport.get().getDockerAvailability().isAvailable); addSysprop(t, DISTRIBUTION_SYSPROP, distribution::getFilepath); @@ -134,12 +136,10 @@ public void apply(Project project) { } destructiveDistroTest.configure(t -> t.dependsOn(destructiveTask)); lifecycleTasks.get(distribution.getType()).configure(t -> t.dependsOn(destructiveTask)); - } catch (Exception ex) { - System.out.println(ex.getMessage()); } if ((distribution.getType() == OpenSearchDistribution.Type.DEB || distribution.getType() == OpenSearchDistribution.Type.RPM) - && distribution.getBundledJdk()) { + && distribution.getBundledJdk() != JavaPackageType.NONE) { for (Version version : BuildParams.getBwcVersions().getIndexCompatible()) { if (version.before("6.3.0")) { continue; // before opening xpack @@ -197,13 +197,9 @@ public void apply(Project project) { // windows boxes get windows distributions, and linux boxes get linux distributions if (isWindows(vmProject)) { - configureVMWrapperTasks( - vmProject, - windowsTestTasks, - depsTasks, - wrapperTask -> { vmLifecyleTasks.get(OpenSearchDistribution.Type.ARCHIVE).configure(t -> t.dependsOn(wrapperTask)); }, - vmDependencies - ); + configureVMWrapperTasks(vmProject, windowsTestTasks, depsTasks, wrapperTask -> { + vmLifecyleTasks.get(OpenSearchDistribution.Type.ARCHIVE).configure(t -> t.dependsOn(wrapperTask)); + }, vmDependencies); } else { for (Entry>> entry : linuxTestTasks.entrySet()) { OpenSearchDistribution.Type type = entry.getKey(); @@ -385,8 +381,8 @@ private List configureDistributions(Project project) { OpenSearchDistribution.Type.RPM, OpenSearchDistribution.Type.DOCKER )) { - for (boolean bundledJdk : Arrays.asList(true, false)) { - if (bundledJdk == false) { + for (JavaPackageType bundledJdk : Set.of(JavaPackageType.NONE, JavaPackageType.JDK)) { + if (bundledJdk == JavaPackageType.NONE) { // We'll never publish an ARM (arm64) build without a bundled JDK. if (architecture == Architecture.ARM64) { continue; @@ -409,8 +405,8 @@ private List configureDistributions(Project project) { OpenSearchDistribution.Platform.LINUX, OpenSearchDistribution.Platform.WINDOWS )) { - for (boolean bundledJdk : Arrays.asList(true, false)) { - if (bundledJdk == false && architecture != Architecture.X64) { + for (JavaPackageType bundledJdk : Set.of(JavaPackageType.NONE, JavaPackageType.JDK)) { + if (bundledJdk == JavaPackageType.NONE && architecture != Architecture.X64) { // We will never publish distributions for non-x86 (amd64) platforms // without a bundled JDK continue; @@ -438,7 +434,7 @@ private static OpenSearchDistribution createDistro( Architecture architecture, OpenSearchDistribution.Type type, OpenSearchDistribution.Platform platform, - boolean bundledJdk, + JavaPackageType bundledJdk, String version ) { String name = distroId(type, platform, bundledJdk, architecture) + "-" + version; @@ -472,11 +468,12 @@ private static boolean isWindows(Project project) { private static String distroId( OpenSearchDistribution.Type type, OpenSearchDistribution.Platform platform, - boolean bundledJdk, + JavaPackageType bundledJdk, Architecture architecture ) { - return (type == OpenSearchDistribution.Type.ARCHIVE ? platform + "-" : "") + type + (bundledJdk ? "" : "-no-jdk") - + (architecture == Architecture.X64 ? "" : "-" + architecture.toString().toLowerCase()); + return (type == OpenSearchDistribution.Type.ARCHIVE ? platform + "-" : "") + type + (bundledJdk != JavaPackageType.NONE + ? (bundledJdk == JavaPackageType.JDK ? "" : "-jre") + : "-no-jdk") + (architecture == Architecture.X64 ? "" : "-" + architecture.toString().toLowerCase()); } private static String destructiveDistroTestTaskName(OpenSearchDistribution distro) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/test/GradleDistroTestTask.java b/buildSrc/src/main/java/org/opensearch/gradle/test/GradleDistroTestTask.java index 2443a30fc05fb..fa417da1a1007 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/test/GradleDistroTestTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/test/GradleDistroTestTask.java @@ -32,10 +32,10 @@ package org.opensearch.gradle.test; +import org.opensearch.gradle.vagrant.VagrantMachine; import org.opensearch.gradle.vagrant.VagrantShellTask; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.options.Option; -import org.opensearch.gradle.vagrant.VagrantMachine; import java.util.ArrayList; import java.util.Collections; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/test/JNAKernel32Library.java b/buildSrc/src/main/java/org/opensearch/gradle/test/JNAKernel32Library.java index 9d575364f5546..1484dcba1e290 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/test/JNAKernel32Library.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/test/JNAKernel32Library.java @@ -34,6 +34,7 @@ import com.sun.jna.Native; import com.sun.jna.WString; + import org.apache.tools.ant.taskdefs.condition.Os; public class JNAKernel32Library { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/test/RestIntegTestTask.java b/buildSrc/src/main/java/org/opensearch/gradle/test/RestIntegTestTask.java index d1671146d0b77..aec31d02b9bee 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/test/RestIntegTestTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/test/RestIntegTestTask.java @@ -32,7 +32,10 @@ package org.opensearch.gradle.test; +import groovy.lang.Closure; + import org.opensearch.gradle.testclusters.StandaloneRestIntegTestTask; +import org.gradle.api.Task; import org.gradle.api.tasks.CacheableTask; /** @@ -41,4 +44,12 @@ * conventional configured tasks of {@link RestIntegTestTask} */ @CacheableTask -public class RestIntegTestTask extends StandaloneRestIntegTestTask {} +public abstract class RestIntegTestTask extends StandaloneRestIntegTestTask implements TestSuiteConventionMappings { + @SuppressWarnings("rawtypes") + @Override + public Task configure(Closure closure) { + final Task t = super.configure(closure); + applyConventionMapping(getProject(), getConventionMapping()); + return t; + } +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/test/TestSuiteConventionMappings.java b/buildSrc/src/main/java/org/opensearch/gradle/test/TestSuiteConventionMappings.java new file mode 100644 index 0000000000000..f03b59509a2f9 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/test/TestSuiteConventionMappings.java @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle.test; + +import org.opensearch.gradle.jvm.JvmTestSuiteHelper; +import org.gradle.api.Project; +import org.gradle.api.internal.ConventionMapping; + +// Temporary workaround for https://docs.gradle.org/8.1/userguide/upgrading_version_8.html#test_task_default_classpath +interface TestSuiteConventionMappings { + default void applyConventionMapping(Project project, ConventionMapping conventionMapping) { + JvmTestSuiteHelper.getDefaultTestSuite(project).ifPresent(defaultTestSuite -> { + conventionMapping.map("testClassesDirs", () -> { return defaultTestSuite.getSources().getOutput().getClassesDirs(); }); + + conventionMapping.map("classpath", () -> { return defaultTestSuite.getSources().getRuntimeClasspath(); }); + }); + } +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/test/TestTask.java b/buildSrc/src/main/java/org/opensearch/gradle/test/TestTask.java new file mode 100644 index 0000000000000..f7511a2ac7f1c --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/test/TestTask.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle.test; + +import groovy.lang.Closure; + +import org.gradle.api.Task; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.testing.Test; + +@CacheableTask +public abstract class TestTask extends Test implements TestSuiteConventionMappings { + @SuppressWarnings("rawtypes") + @Override + public Task configure(Closure closure) { + final Task t = super.configure(closure); + applyConventionMapping(getProject(), getConventionMapping()); + return t; + } +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/test/rest/CopyRestApiTask.java b/buildSrc/src/main/java/org/opensearch/gradle/test/rest/CopyRestApiTask.java index 399cd39d236d7..485561a305291 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/test/rest/CopyRestApiTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/test/rest/CopyRestApiTask.java @@ -41,11 +41,14 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileSystemOperations; import org.gradle.api.file.FileTree; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.ListProperty; +import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskAction; @@ -54,6 +57,7 @@ import org.gradle.internal.Factory; import javax.inject.Inject; + import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -112,8 +116,10 @@ public boolean isSkipHasRestTestCheck() { return skipHasRestTestCheck; } + @IgnoreEmptyDirectories @SkipWhenEmpty @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) public FileTree getInputDir() { FileTree coreFileTree = null; boolean projectHasYamlRestTests = skipHasRestTestCheck || projectHasYamlRestTests(); @@ -235,7 +241,7 @@ private File getTestOutputResourceDir() { private Optional getSourceSet() { Project project = getProject(); - return project.getConvention().findPlugin(JavaPluginConvention.class) == null + return project.getExtensions().findByType(JavaPluginExtension.class) == null ? Optional.empty() : Optional.ofNullable(GradleUtils.getJavaSourceSets(project).findByName(getSourceSetName())); } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/test/rest/CopyRestTestsTask.java b/buildSrc/src/main/java/org/opensearch/gradle/test/rest/CopyRestTestsTask.java index 56ce449f4cf6f..0d5af7ca06b50 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/test/rest/CopyRestTestsTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/test/rest/CopyRestTestsTask.java @@ -41,11 +41,14 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileSystemOperations; import org.gradle.api.file.FileTree; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.ListProperty; +import org.gradle.api.tasks.IgnoreEmptyDirectories; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskAction; @@ -54,6 +57,7 @@ import org.gradle.internal.Factory; import javax.inject.Inject; + import java.io.File; import java.util.Objects; import java.util.Optional; @@ -104,8 +108,10 @@ String getSourceSetName() { return sourceSetName; } + @IgnoreEmptyDirectories @SkipWhenEmpty @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) public FileTree getInputDir() { FileTree coreFileTree = null; if (includeCore.get().isEmpty() == false) { @@ -173,7 +179,7 @@ void copy() { private Optional getSourceSet() { Project project = getProject(); - return project.getConvention().findPlugin(JavaPluginConvention.class) == null + return project.getExtensions().findByType(JavaPluginExtension.class) == null ? Optional.empty() : Optional.ofNullable(GradleUtils.getJavaSourceSets(project).findByName(getSourceSetName())); } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/ExtensionsProperties.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/ExtensionsProperties.java new file mode 100644 index 0000000000000..10a79e8325d0b --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/ExtensionsProperties.java @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle.testclusters; + +public class ExtensionsProperties { + private String name; + private String uniqueId; + private String hostAddress; + private String port; + private String version; + private String opensearchVersion; + private String minimumCompatibleVersion; + + public ExtensionsProperties( + String name, + String uniqueId, + String hostAddress, + String port, + String version, + String opensearchVersion, + String minimumCompatibleVersion + ) { + this.name = name; + this.uniqueId = uniqueId; + this.hostAddress = hostAddress; + this.port = port; + this.version = version; + this.opensearchVersion = opensearchVersion; + this.minimumCompatibleVersion = minimumCompatibleVersion; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUniqueId() { + return uniqueId; + } + + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + + public String getHostAddress() { + return hostAddress; + } + + public void setHostAddress(String hostAddress) { + this.hostAddress = hostAddress; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getOpensearchVersion() { + return opensearchVersion; + } + + public void setOpensearchVersion(String opensearchVersion) { + this.opensearchVersion = opensearchVersion; + } + + public String getMinimumCompatibleVersion() { + return minimumCompatibleVersion; + } + + public void setMinimumCompatibleVersion(String minimumCompatibleVersion) { + this.minimumCompatibleVersion = minimumCompatibleVersion; + } +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchCluster.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchCluster.java index ef52adab6377a..287be63692ab2 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchCluster.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchCluster.java @@ -180,6 +180,11 @@ public void setTestDistribution(TestDistribution distribution) { nodes.all(each -> each.setTestDistribution(distribution)); } + @Override + public void extension(boolean extensionsEnabled) { + nodes.all(each -> each.extension(extensionsEnabled)); + } + @Override public void plugin(Provider plugin) { nodes.all(each -> each.plugin(plugin)); @@ -235,6 +240,11 @@ public void keystorePassword(String password) { nodes.all(each -> each.keystorePassword(password)); } + @Override + public void setSecure(boolean secure) { + nodes.all(each -> each.setSecure(secure)); + } + @Override public void cliSetup(String binTool, CharSequence... args) { nodes.all(each -> each.cliSetup(binTool, args)); @@ -524,12 +534,25 @@ public OpenSearchNode singleNode() { private void addWaitForClusterHealth() { waitConditions.put("cluster health yellow", (node) -> { try { - WaitForHttpResource wait = new WaitForHttpResource("http", getFirstNode().getHttpSocketURI(), nodes.size()); - - List> credentials = getFirstNode().getCredentials(); - if (getFirstNode().getCredentials().isEmpty() == false) { - wait.setUsername(credentials.get(0).get("useradd")); - wait.setPassword(credentials.get(0).get("-p")); + WaitForHttpResource wait; + if (!getFirstNode().isSecure()) { + wait = new WaitForHttpResource("http", getFirstNode().getHttpSocketURI(), nodes.size()); + List> credentials = getFirstNode().getCredentials(); + if (getFirstNode().getCredentials().isEmpty() == false) { + wait.setUsername(credentials.get(0).get("useradd")); + wait.setPassword(credentials.get(0).get("-p")); + } + } else { + wait = new WaitForHttpResource( + "https", + getFirstNode().getHttpSocketURI(), + getFirstNode().getCredentials().get(0).get("username"), + getFirstNode().getCredentials().get(0).get("password"), + nodes.size() + ); + wait.setUsername(getFirstNode().getCredentials().get(0).get("username")); + wait.setPassword(getFirstNode().getCredentials().get(0).get("password")); + wait.setCertificateAuthorities(getFirstNode().getExtraConfigFilesMap().get("root-ca.pem")); } return wait.wait(500); } catch (IOException e) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java index b051c15e81d6d..e5132c3afb518 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java @@ -34,13 +34,13 @@ import org.apache.commons.io.FileUtils; import org.opensearch.gradle.Architecture; import org.opensearch.gradle.DistributionDownloadPlugin; -import org.opensearch.gradle.OpenSearchDistribution; import org.opensearch.gradle.FileSupplier; import org.opensearch.gradle.Jdk; import org.opensearch.gradle.LazyPropertyList; import org.opensearch.gradle.LazyPropertyMap; import org.opensearch.gradle.LoggedExec; import org.opensearch.gradle.OS; +import org.opensearch.gradle.OpenSearchDistribution; import org.opensearch.gradle.PropertyNormalization; import org.opensearch.gradle.ReaperService; import org.opensearch.gradle.Version; @@ -142,6 +142,7 @@ public class OpenSearchNode implements TestClusterConfiguration { private final Map pluginAndModuleConfigurations = new HashMap<>(); private final List> plugins = new ArrayList<>(); private final List> modules = new ArrayList<>(); + private boolean extensionsEnabled = false; final LazyPropertyMap settings = new LazyPropertyMap<>("Settings", this); private final LazyPropertyMap keystoreSettings = new LazyPropertyMap<>("Keystore", this); private final LazyPropertyMap keystoreFiles = new LazyPropertyMap<>("Keystore files", this, FileEntry::new); @@ -160,6 +161,7 @@ public class OpenSearchNode implements TestClusterConfiguration { private final Path httpPortsFile; private final Path tmpDir; + private boolean secure = false; private int currentDistro = 0; private TestDistribution testDistribution; private List distributions = new ArrayList<>(); @@ -205,6 +207,7 @@ public class OpenSearchNode implements TestClusterConfiguration { opensearchConfig = Config.getOpenSearchConfig(workingDir); legacyESConfig = Config.getLegacyESConfig(workingDir); currentConfig = opensearchConfig; + this.credentials.add(new HashMap<>()); } /* @@ -305,6 +308,11 @@ public String getName() { return nameCustomization.apply(name); } + @Internal + public boolean isSecure() { + return secure; + } + @Internal public Version getVersion() { return Version.fromString(distributions.get(currentDistro).getVersion()); @@ -432,6 +440,11 @@ public void module(String moduleProjectPath) { module(maybeCreatePluginOrModuleDependency(moduleProjectPath)); } + @Override + public void extension(boolean extensionsEnabled) { + this.extensionsEnabled = extensionsEnabled; + } + @Override public void keystore(String key, String value) { keystoreSettings.put(key, value); @@ -537,6 +550,11 @@ public void setPreserveDataDir(boolean preserveDataDir) { this.preserveDataDir = preserveDataDir; } + @Override + public void setSecure(boolean secure) { + this.secure = secure; + } + @Override public void freeze() { requireNonNull(testDistribution, "null testDistribution passed when configuring test cluster `" + this + "`"); @@ -556,6 +574,18 @@ public Stream logLines() throws IOException { @Override public synchronized void start() { LOGGER.info("Starting `{}`", this); + if (System.getProperty("tests.opensearch.secure") != null + && System.getProperty("tests.opensearch.secure").equalsIgnoreCase("true")) { + secure = true; + } + if (System.getProperty("tests.opensearch.username") != null) { + this.credentials.get(0).put("username", System.getProperty("tests.opensearch.username")); + LOGGER.info("Overwriting username to: " + this.getCredentials().get(0).get("username")); + } + if (System.getProperty("tests.opensearch.password") != null) { + this.credentials.get(0).put("password", System.getProperty("tests.opensearch.password")); + LOGGER.info("Overwriting password to: " + this.getCredentials().get(0).get("password")); + } if (Files.exists(getExtractedDistributionDir()) == false) { throw new TestClustersException("Can not start " + this + ", missing: " + getExtractedDistributionDir()); } @@ -609,6 +639,7 @@ public synchronized void start() { } logToProcessStdout("Creating " + currentConfig.command + " keystore with password set to [" + keystorePassword + "]"); + if (keystorePassword.length() > 0) { runOpenSearchBinScriptWithInput(keystorePassword + "\n" + keystorePassword, currentConfig.keystoreTool, "create", "-p"); } else { @@ -892,6 +923,10 @@ private void startOpenSearchProcess() { environment.clear(); environment.putAll(getOpenSearchEnvironment()); + if (extensionsEnabled) { + environment.put("OPENSEARCH_JAVA_OPTS", "-Dopensearch.experimental.feature.extensions.enabled=true"); + } + // don't buffer all in memory, make sure we don't block on the default pipes processBuilder.redirectError(ProcessBuilder.Redirect.appendTo(currentConfig.stderrFile.toFile())); processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(currentConfig.stdoutFile.toFile())); @@ -1467,6 +1502,11 @@ public List getExtraConfigFiles() { return extraConfigFiles.getNormalizedCollection(); } + @Internal + public Map getExtraConfigFilesMap() { + return extraConfigFiles; + } + @Override @Internal public boolean isProcessAlive() { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/RunTask.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/RunTask.java index 01680050cf18f..c5035f3b082fe 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/RunTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/RunTask.java @@ -66,22 +66,40 @@ public class RunTask extends DefaultTestClustersTask { private Boolean debug = false; + private Boolean debugServer = false; + private Boolean preserveData = false; private Path dataDir = null; private String keystorePassword = ""; - @Option(option = "debug-jvm", description = "Enable debugging configuration, to allow attaching a debugger to opensearch.") + @Option(option = "debug-jvm", description = "Run OpenSearch as a debug client, where it will try to connect to a debugging server at startup.") public void setDebug(boolean enabled) { + if (debugServer != null && debugServer == true) { + throw new IllegalStateException("Either --debug-jvm or --debug-server-jvm option should be specified (but not both)"); + } this.debug = enabled; } + @Option(option = "debug-server-jvm", description = "Run OpenSearch as a debug server that will accept connections from a debugging client.") + public void setDebugServer(boolean enabled) { + if (debug != null && debug == true) { + throw new IllegalStateException("Either --debug-jvm or --debug-server-jvm option should be specified (but not both)"); + } + this.debugServer = enabled; + } + @Input public Boolean getDebug() { return debug; } + @Input + public Boolean getDebugServer() { + return debugServer; + } + @Option(option = "data-dir", description = "Override the base data directory used by the testcluster") public void setDataDir(String dataDirStr) { dataDir = Paths.get(dataDirStr).toAbsolutePath(); @@ -162,9 +180,17 @@ public void beforeStart() { node.setDataPath(getDataPath.apply(node)); } if (debug) { - logger.lifecycle("Running opensearch in debug mode, {} expecting running debug server on port {}", node, debugPort); + logger.lifecycle( + "Running opensearch in debug mode (client), {} expecting running debug server on port {}", + node, + debugPort + ); node.jvmArgs("-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=" + debugPort); debugPort += 1; + } else if (debugServer) { + logger.lifecycle("Running opensearch in debug mode (server), {} running server with debug port {}", node, debugPort); + node.jvmArgs("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=" + debugPort); + debugPort += 1; } if (keystorePassword.length() > 0) { node.keystorePassword(keystorePassword); diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/StandaloneRestIntegTestTask.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/StandaloneRestIntegTestTask.java index bf17daa6e2e6f..ddcbf77b0d5e6 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/StandaloneRestIntegTestTask.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/StandaloneRestIntegTestTask.java @@ -32,11 +32,13 @@ package org.opensearch.gradle.testclusters; import groovy.lang.Closure; + import org.opensearch.gradle.FileSystemOperationsAware; import org.opensearch.gradle.test.Fixture; import org.opensearch.gradle.util.GradleUtils; import org.gradle.api.Task; import org.gradle.api.provider.Provider; +import org.gradle.api.services.internal.BuildServiceProvider; import org.gradle.api.services.internal.BuildServiceRegistryInternal; import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.Internal; @@ -46,6 +48,8 @@ import org.gradle.internal.resources.ResourceLock; import org.gradle.internal.resources.SharedResource; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -58,7 +62,7 @@ * {@link Nested} inputs. */ @CacheableTask -public class StandaloneRestIntegTestTask extends Test implements TestClustersAware, FileSystemOperationsAware { +public abstract class StandaloneRestIntegTestTask extends Test implements TestClustersAware, FileSystemOperationsAware { private Collection clusters = new HashSet<>(); private Closure beforeStart; @@ -120,16 +124,67 @@ public List getSharedResources() { serviceRegistry, TestClustersPlugin.THROTTLE_SERVICE_NAME ); - SharedResource resource = serviceRegistry.forService(throttleProvider); + final SharedResource resource = getSharedResource(serviceRegistry, throttleProvider); int nodeCount = clusters.stream().mapToInt(cluster -> cluster.getNodes().size()).sum(); if (nodeCount > 0) { - locks.add(resource.getResourceLock(Math.min(nodeCount, resource.getMaxUsages()))); + locks.add(getResourceLock(resource, Math.min(nodeCount, resource.getMaxUsages()))); } return Collections.unmodifiableList(locks); } + private SharedResource getSharedResource( + BuildServiceRegistryInternal serviceRegistry, + Provider throttleProvider + ) { + try { + try { + // The forService(Provider) is used by Gradle pre-8.0 + return (SharedResource) MethodHandles.publicLookup() + .findVirtual( + BuildServiceRegistryInternal.class, + "forService", + MethodType.methodType(SharedResource.class, Provider.class) + ) + .bindTo(serviceRegistry) + .invokeExact(throttleProvider); + } catch (NoSuchMethodException | IllegalAccessException ex) { + // The forService(BuildServiceProvider) is used by Gradle post-8.0 + return (SharedResource) MethodHandles.publicLookup() + .findVirtual( + BuildServiceRegistryInternal.class, + "forService", + MethodType.methodType(SharedResource.class, BuildServiceProvider.class) + ) + .bindTo(serviceRegistry) + .invokeExact((BuildServiceProvider) throttleProvider); + } + } catch (Throwable ex) { + throw new IllegalStateException("Unable to find suitable BuildServiceRegistryInternal::forService", ex); + } + } + + private ResourceLock getResourceLock(SharedResource resource, int nUsages) { + try { + try { + // The getResourceLock(int) is used by Gradle pre-7.5 + return (ResourceLock) MethodHandles.publicLookup() + .findVirtual(SharedResource.class, "getResourceLock", MethodType.methodType(ResourceLock.class, int.class)) + .bindTo(resource) + .invokeExact(nUsages); + } catch (NoSuchMethodException | IllegalAccessException ex) { + // The getResourceLock() is used by Gradle post-7.4 + return (ResourceLock) MethodHandles.publicLookup() + .findVirtual(SharedResource.class, "getResourceLock", MethodType.methodType(ResourceLock.class)) + .bindTo(resource) + .invokeExact(); + } + } catch (Throwable ex) { + throw new IllegalStateException("Unable to find suitable ResourceLock::getResourceLock", ex); + } + } + @Override public Task dependsOn(Object... dependencies) { super.dependsOn(dependencies); diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestClusterConfiguration.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestClusterConfiguration.java index b27f205291269..22c4185a39a98 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestClusterConfiguration.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestClusterConfiguration.java @@ -36,7 +36,6 @@ import org.gradle.api.file.RegularFile; import org.gradle.api.logging.Logging; import org.gradle.api.provider.Provider; -import org.slf4j.Logger; import java.io.File; import java.util.LinkedHashMap; @@ -47,6 +46,8 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import org.slf4j.Logger; + public interface TestClusterConfiguration { void setVersion(String version); @@ -55,6 +56,8 @@ public interface TestClusterConfiguration { void setTestDistribution(TestDistribution distribution); + void extension(boolean extensionsEnabled); + void plugin(Provider plugin); void plugin(String pluginProjectPath); @@ -105,6 +108,8 @@ public interface TestClusterConfiguration { void setPreserveDataDir(boolean preserveDataDir); + void setSecure(boolean secure); + void freeze(); void start(); diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestClustersPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestClustersPlugin.java index 2ef14a39b6669..b8ba1e6a90976 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestClustersPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestClustersPlugin.java @@ -57,6 +57,7 @@ import org.gradle.api.tasks.TaskState; import javax.inject.Inject; + import java.io.File; import static org.opensearch.gradle.util.GradleUtils.noop; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestExtensionsList.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestExtensionsList.java new file mode 100644 index 0000000000000..ea601e26672a2 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/TestExtensionsList.java @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle.testclusters; + +import java.util.List; + +public class TestExtensionsList { + private List extensions; + + public TestExtensionsList(List extensionsList) { + extensions = extensionsList; + } + + public List getExtensions() { + return extensions; + } + + public void setExtensions(List extensionsList) { + extensions = extensionsList; + } +} diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testfixtures/TestFixturesPlugin.java b/buildSrc/src/main/java/org/opensearch/gradle/testfixtures/TestFixturesPlugin.java index ae1db26fbc48d..3aba941875115 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testfixtures/TestFixturesPlugin.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testfixtures/TestFixturesPlugin.java @@ -37,6 +37,8 @@ import com.avast.gradle.dockercompose.tasks.ComposeDown; import com.avast.gradle.dockercompose.tasks.ComposePull; import com.avast.gradle.dockercompose.tasks.ComposeUp; + +import org.apache.tools.ant.taskdefs.condition.Os; import org.opensearch.gradle.SystemPropertyCommandLineArgumentProvider; import org.opensearch.gradle.docker.DockerSupportPlugin; import org.opensearch.gradle.docker.DockerSupportService; @@ -57,17 +59,17 @@ import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.testing.Test; -import org.apache.tools.ant.taskdefs.condition.Os; import javax.inject.Inject; + import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.util.Arrays; import java.util.Collections; -import java.util.function.BiConsumer; import java.util.Optional; +import java.util.function.BiConsumer; public class TestFixturesPlugin implements Plugin { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/util/GradleUtils.java b/buildSrc/src/main/java/org/opensearch/gradle/util/GradleUtils.java index 054f01788d126..031fee2d1127f 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/util/GradleUtils.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/util/GradleUtils.java @@ -40,7 +40,7 @@ import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; import org.gradle.api.plugins.JavaBasePlugin; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Provider; import org.gradle.api.services.BuildService; import org.gradle.api.services.BuildServiceRegistration; @@ -68,7 +68,7 @@ public static Action noop() { } public static SourceSetContainer getJavaSourceSets(Project project) { - return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets(); + return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets(); } public static TaskProvider maybeRegister(TaskContainer tasks, String name, Class clazz, Action action) { diff --git a/buildSrc/src/main/java/org/opensearch/gradle/util/Util.java b/buildSrc/src/main/java/org/opensearch/gradle/util/Util.java index 71b1e5040340d..a2fd1e247b660 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/util/Util.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/util/Util.java @@ -37,11 +37,12 @@ import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.file.FileTree; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.util.PatternFilterable; import javax.annotation.Nullable; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -149,7 +150,7 @@ public static FileTree getJavaTestAndMainSourceResources(Project project, Action * @return An Optional that contains the Java test SourceSet if it exists. */ public static Optional getJavaTestSourceSet(Project project) { - return project.getConvention().findPlugin(JavaPluginConvention.class) == null + return project.getExtensions().findByType(JavaPluginExtension.class) == null ? Optional.empty() : Optional.ofNullable(GradleUtils.getJavaSourceSets(project).findByName(SourceSet.TEST_SOURCE_SET_NAME)); } @@ -159,7 +160,7 @@ public static Optional getJavaTestSourceSet(Project project) { * @return An Optional that contains the Java main SourceSet if it exists. */ public static Optional getJavaMainSourceSet(Project project) { - return project.getConvention().findPlugin(JavaPluginConvention.class) == null + return project.getExtensions().findByType(JavaPluginExtension.class) == null ? Optional.empty() : Optional.ofNullable(GradleUtils.getJavaSourceSets(project).findByName(SourceSet.MAIN_SOURCE_SET_NAME)); } diff --git a/buildSrc/src/main/java/org/opensearch/gradle/vagrant/VagrantMachine.java b/buildSrc/src/main/java/org/opensearch/gradle/vagrant/VagrantMachine.java index 75827922fa007..2d71b9361963b 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/vagrant/VagrantMachine.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/vagrant/VagrantMachine.java @@ -43,6 +43,7 @@ import org.gradle.internal.logging.progress.ProgressLoggerFactory; import javax.inject.Inject; + import java.io.File; import java.io.OutputStream; import java.nio.file.Paths; diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.jre-download.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.jre-download.properties new file mode 100644 index 0000000000000..e9253488ffbeb --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.jre-download.properties @@ -0,0 +1,12 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. +# + +implementation-class=org.opensearch.gradle.JreDownloadPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.optional-dependencies.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.optional-dependencies.properties new file mode 100644 index 0000000000000..8b6f6f307cd5d --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.optional-dependencies.properties @@ -0,0 +1,12 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. +# + +implementation-class=org.opensearch.gradle.plugin.OptionalDependenciesPlugin diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.pluginzip.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.pluginzip.properties new file mode 100644 index 0000000000000..600218ff76835 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/opensearch.pluginzip.properties @@ -0,0 +1 @@ +implementation-class=org.opensearch.gradle.pluginzip.Publish diff --git a/buildSrc/src/main/resources/deb/postinst.ftl b/buildSrc/src/main/resources/deb/postinst.ftl index 605f620e16444..1fe98263a0fdf 100644 --- a/buildSrc/src/main/resources/deb/postinst.ftl +++ b/buildSrc/src/main/resources/deb/postinst.ftl @@ -1,2 +1,3 @@ -#!/usr/bin/env bash -e +#!/usr/bin/env bash +set -e -o pipefail <% commands.each {command -> %><%= command %><% } %> diff --git a/buildSrc/src/main/resources/deb/preinst.ftl b/buildSrc/src/main/resources/deb/preinst.ftl index 605f620e16444..1fe98263a0fdf 100644 --- a/buildSrc/src/main/resources/deb/preinst.ftl +++ b/buildSrc/src/main/resources/deb/preinst.ftl @@ -1,2 +1,3 @@ -#!/usr/bin/env bash -e +#!/usr/bin/env bash +set -e -o pipefail <% commands.each {command -> %><%= command %><% } %> diff --git a/buildSrc/src/main/resources/eclipse.settings/org.eclipse.core.resources.prefs b/buildSrc/src/main/resources/eclipse.settings/org.eclipse.core.resources.prefs index 6fd0a9aab1327..29abf99956411 100644 --- a/buildSrc/src/main/resources/eclipse.settings/org.eclipse.core.resources.prefs +++ b/buildSrc/src/main/resources/eclipse.settings/org.eclipse.core.resources.prefs @@ -3,4 +3,4 @@ encoding//src/main/java=UTF-8 encoding//src/main/resources=UTF-8 encoding//src/test/java=UTF-8 encoding//src/test/resources=UTF-8 -encoding/=UTF-8 \ No newline at end of file +encoding/=UTF-8 diff --git a/buildSrc/src/main/resources/forbidden/opensearch-all-signatures.txt b/buildSrc/src/main/resources/forbidden/opensearch-all-signatures.txt index 1e4e669e4722f..f9f24fd1e2367 100644 --- a/buildSrc/src/main/resources/forbidden/opensearch-all-signatures.txt +++ b/buildSrc/src/main/resources/forbidden/opensearch-all-signatures.txt @@ -50,6 +50,7 @@ java.nio.channels.SocketChannel#connect(java.net.SocketAddress) java.lang.Boolean#getBoolean(java.lang.String) org.apache.lucene.util.IOUtils @ use @org.opensearch.core.internal.io instead +org.apache.lucene.util.SetOnce @ use @org.opensearch.common.SetOnce instead @defaultMessage use executors from org.opensearch.common.util.concurrent.OpenSearchExecutors instead which will properly bubble up Errors java.util.concurrent.AbstractExecutorService#() diff --git a/buildSrc/src/main/resources/forbidden/opensearch-test-signatures.txt b/buildSrc/src/main/resources/forbidden/opensearch-test-signatures.txt index aeb5e25decf62..43568b3209baf 100644 --- a/buildSrc/src/main/resources/forbidden/opensearch-test-signatures.txt +++ b/buildSrc/src/main/resources/forbidden/opensearch-test-signatures.txt @@ -19,11 +19,10 @@ com.carrotsearch.randomizedtesting.annotations.Seed @ Don't commit hardcoded see com.carrotsearch.randomizedtesting.annotations.Repeat @ Don't commit hardcoded repeats org.apache.lucene.codecs.Codec#setDefault(org.apache.lucene.codecs.Codec) @ Use the SuppressCodecs("*") annotation instead -org.apache.lucene.tests.util.LuceneTestCase$Slow @ Don't write slow tests org.junit.Ignore @ Use AwaitsFix instead org.apache.lucene.tests.util.LuceneTestCase$Nightly @ We don't run nightly tests at this point! com.carrotsearch.randomizedtesting.annotations.Nightly @ We don't run nightly tests at this point! org.junit.Test @defaultMessage Just name your test method testFooBar -java.lang.Math#random() @ Use one of the various randomization methods from LuceneTestCase or ESTestCase for reproducibility +java.lang.Math#random() @ Use one of the various randomization methods from LuceneTestCase or OpenSearchTestCase for reproducibility diff --git a/buildSrc/src/main/resources/minimumGradleVersion b/buildSrc/src/main/resources/minimumGradleVersion index ba92e72f5775b..815da58b7a9ed 100644 --- a/buildSrc/src/main/resources/minimumGradleVersion +++ b/buildSrc/src/main/resources/minimumGradleVersion @@ -1 +1 @@ -6.6.1 \ No newline at end of file +7.4.1 diff --git a/buildSrc/src/test/java/org/opensearch/gradle/ArchitectureTests.java b/buildSrc/src/test/java/org/opensearch/gradle/ArchitectureTests.java new file mode 100644 index 0000000000000..2df8c1995c834 --- /dev/null +++ b/buildSrc/src/test/java/org/opensearch/gradle/ArchitectureTests.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle; + +import org.opensearch.gradle.test.GradleUnitTestCase; + +public class ArchitectureTests extends GradleUnitTestCase { + + final String architecture = System.getProperty("os.arch", ""); + + public void testCurrentArchitecture() { + assertEquals(Architecture.X64, currentArchitecture("amd64")); + assertEquals(Architecture.X64, currentArchitecture("x86_64")); + assertEquals(Architecture.ARM64, currentArchitecture("aarch64")); + assertEquals(Architecture.S390X, currentArchitecture("s390x")); + assertEquals(Architecture.PPC64LE, currentArchitecture("ppc64le")); + } + + public void testInvalidCurrentArchitecture() { + assertThrows("can not determine architecture from [", IllegalArgumentException.class, () -> currentArchitecture("fooBar64")); + } + + /** + * Determines the return value of {@link Architecture#current()} based on a string representing a potential OS Architecture. + * + * @param osArchToTest An expected value of the {@code os.arch} system property on another architecture. + * @return the value of the {@link Architecture} enum which would have resulted with the given value. + * @throws IllegalArgumentException if the string is not mapped to a value of the {@link Architecture} enum. + */ + private Architecture currentArchitecture(String osArchToTest) throws IllegalArgumentException { + // Test new architecture + System.setProperty("os.arch", osArchToTest); + try { + return Architecture.current(); + } finally { + // Restore actual architecture property value + System.setProperty("os.arch", this.architecture); + } + } +} diff --git a/buildSrc/src/test/java/org/opensearch/gradle/ConcatFilesTaskTests.java b/buildSrc/src/test/java/org/opensearch/gradle/ConcatFilesTaskTests.java index 3b9c1c81e0345..0484939ca4698 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/ConcatFilesTaskTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/ConcatFilesTaskTests.java @@ -31,16 +31,16 @@ package org.opensearch.gradle; +import org.opensearch.gradle.test.GradleUnitTestCase; +import org.gradle.api.Project; +import org.gradle.testfixtures.ProjectBuilder; + import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Arrays; -import org.opensearch.gradle.test.GradleUnitTestCase; -import org.gradle.api.Project; -import org.gradle.testfixtures.ProjectBuilder; - public class ConcatFilesTaskTests extends GradleUnitTestCase { public void testHeaderAdded() throws IOException { diff --git a/buildSrc/src/test/java/org/opensearch/gradle/DistributionDownloadPluginTests.java b/buildSrc/src/test/java/org/opensearch/gradle/DistributionDownloadPluginTests.java index 446c94acc7ad4..514e169700fd9 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/DistributionDownloadPluginTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/DistributionDownloadPluginTests.java @@ -32,19 +32,20 @@ package org.opensearch.gradle; -import org.gradle.api.internal.artifacts.repositories.DefaultIvyArtifactRepository; import org.opensearch.gradle.OpenSearchDistribution.Platform; import org.opensearch.gradle.OpenSearchDistribution.Type; import org.opensearch.gradle.info.BuildParams; import org.opensearch.gradle.test.GradleUnitTestCase; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; +import org.gradle.api.internal.artifacts.repositories.DefaultIvyArtifactRepository; import org.gradle.testfixtures.ProjectBuilder; import java.io.File; import java.util.Arrays; import java.util.TreeSet; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.core.StringContains.containsString; public class DistributionDownloadPluginTests extends GradleUnitTestCase { @@ -76,7 +77,14 @@ public class DistributionDownloadPluginTests extends GradleUnitTestCase { ); public void testVersionDefault() { - OpenSearchDistribution distro = checkDistro(createProject(null, false), "testdistro", null, Type.ARCHIVE, Platform.LINUX, true); + OpenSearchDistribution distro = checkDistro( + createProject(null, false), + "testdistro", + null, + Type.ARCHIVE, + Platform.LINUX, + JavaPackageType.JDK + ); assertEquals(distro.getVersion(), VersionProperties.getOpenSearch()); } @@ -140,18 +148,32 @@ public void testBadVersionFormat() { "badversion", Type.ARCHIVE, Platform.LINUX, - true, + JavaPackageType.JDK, "Invalid version format: 'badversion'" ); } public void testTypeDefault() { - OpenSearchDistribution distro = checkDistro(createProject(null, false), "testdistro", "5.0.0", null, Platform.LINUX, true); + OpenSearchDistribution distro = checkDistro( + createProject(null, false), + "testdistro", + "5.0.0", + null, + Platform.LINUX, + JavaPackageType.JDK + ); assertEquals(distro.getType(), Type.ARCHIVE); } public void testPlatformDefault() { - OpenSearchDistribution distro = checkDistro(createProject(null, false), "testdistro", "5.0.0", Type.ARCHIVE, null, true); + OpenSearchDistribution distro = checkDistro( + createProject(null, false), + "testdistro", + "5.0.0", + Type.ARCHIVE, + null, + JavaPackageType.JDK + ); assertEquals(distro.getPlatform(), OpenSearchDistribution.CURRENT_PLATFORM); } @@ -168,8 +190,15 @@ public void testPlatformForIntegTest() { } public void testBundledJdkDefault() { - OpenSearchDistribution distro = checkDistro(createProject(null, false), "testdistro", "5.0.0", Type.ARCHIVE, Platform.LINUX, true); - assertTrue(distro.getBundledJdk()); + OpenSearchDistribution distro = checkDistro( + createProject(null, false), + "testdistro", + "5.0.0", + Type.ARCHIVE, + Platform.LINUX, + JavaPackageType.JDK + ); + assertThat(distro.getBundledJdk(), equalTo(JavaPackageType.JDK)); } public void testBundledJdkForIntegTest() { @@ -179,7 +208,7 @@ public void testBundledJdkForIntegTest() { "5.0.0", Type.INTEG_TEST_ZIP, null, - true, + JavaPackageType.JDK, "bundledJdk cannot be set on opensearch distribution [testdistro]" ); } @@ -195,7 +224,7 @@ public void testLocalCurrentVersionIntegTestZip() { public void testLocalCurrentVersionArchives() { for (Platform platform : Platform.values()) { - for (boolean bundledJdk : new boolean[] { true, false }) { + for (JavaPackageType bundledJdk : JavaPackageType.values()) { for (Architecture architecture : Architecture.values()) { // create a new project in each iteration, so that we know we are resolving the only additional project being created Project project = createProject(BWC_MINOR, true); @@ -221,7 +250,7 @@ public void testLocalCurrentVersionArchives() { public void testLocalCurrentVersionPackages() { for (Type packageType : new Type[] { Type.RPM, Type.DEB }) { - for (boolean bundledJdk : new boolean[] { true, false }) { + for (JavaPackageType bundledJdk : JavaPackageType.values()) { Project project = createProject(BWC_MINOR, true); String projectName = projectName(packageType.toString(), bundledJdk); Project packageProject = ProjectBuilder.builder().withParent(packagesProject).withName(projectName).build(); @@ -236,7 +265,7 @@ public void testLocalCurrentVersionPackages() { public void testLocalBwcArchives() { for (Platform platform : Platform.values()) { // note: no non bundled jdk for bwc - String configName = projectName(platform.toString(), true); + String configName = projectName(platform.toString(), JavaPackageType.JDK); configName += (platform == Platform.WINDOWS ? "-zip" : "-tar"); checkBwc("minor", configName, BWC_MINOR_VERSION, Type.ARCHIVE, platform, BWC_MINOR, true); @@ -249,7 +278,7 @@ public void testLocalBwcArchives() { public void testLocalBwcPackages() { for (Type packageType : new Type[] { Type.RPM, Type.DEB }) { // note: no non bundled jdk for bwc - String configName = projectName(packageType.toString(), true); + String configName = projectName(packageType.toString(), JavaPackageType.JDK); checkBwc("minor", configName, BWC_MINOR_VERSION, packageType, null, BWC_MINOR, true); checkBwc("staged", configName, BWC_STAGED_VERSION, packageType, null, BWC_STAGED, true); @@ -264,7 +293,7 @@ private void assertDistroError( String version, Type type, Platform platform, - Boolean bundledJdk, + JavaPackageType bundledJdk, String message ) { IllegalArgumentException e = expectThrows( @@ -280,7 +309,7 @@ private OpenSearchDistribution createDistro( String version, Type type, Platform platform, - Boolean bundledJdk + JavaPackageType bundledJdk ) { NamedDomainObjectContainer distros = DistributionDownloadPlugin.getContainer(project); return distros.create(name, distro -> { @@ -306,7 +335,7 @@ private OpenSearchDistribution checkDistro( String version, Type type, Platform platform, - Boolean bundledJdk + JavaPackageType bundledJdk ) { OpenSearchDistribution distribution = createDistro(project, name, version, type, platform, bundledJdk); distribution.finalizeValues(); @@ -332,7 +361,8 @@ private void checkBwc( Project archiveProject = ProjectBuilder.builder().withParent(bwcProject).withName(projectName).build(); archiveProject.getConfigurations().create(config); archiveProject.getArtifacts().add(config, new File("doesnotmatter")); - createDistro(project, "distro", version.toString(), type, platform, true); + final OpenSearchDistribution distro = createDistro(project, "distro", version.toString(), type, platform, JavaPackageType.JDK); + distro.setArchitecture(Architecture.current()); checkPlugin(project); } @@ -351,7 +381,7 @@ private Project createProject(BwcVersions bwcVersions, boolean isInternal) { return project; } - private static String projectName(String base, boolean bundledJdk) { - return bundledJdk ? base : ("no-jdk-" + base); + private static String projectName(String base, JavaPackageType bundledJdk) { + return (bundledJdk == JavaPackageType.JDK) ? base : ((bundledJdk == JavaPackageType.NONE) ? ("no-jdk-" + base) : "jre-" + base); } } diff --git a/buildSrc/src/test/java/org/opensearch/gradle/EmptyDirTaskTests.java b/buildSrc/src/test/java/org/opensearch/gradle/EmptyDirTaskTests.java index b4eae42c625ac..ad5b8385abaf8 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/EmptyDirTaskTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/EmptyDirTaskTests.java @@ -31,15 +31,19 @@ package org.opensearch.gradle; -import java.io.File; -import java.io.IOException; - import com.carrotsearch.randomizedtesting.RandomizedTest; + import org.apache.tools.ant.taskdefs.condition.Os; import org.opensearch.gradle.test.GradleUnitTestCase; import org.gradle.api.Project; import org.gradle.testfixtures.ProjectBuilder; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; + public class EmptyDirTaskTests extends GradleUnitTestCase { public void testCreateEmptyDir() throws Exception { @@ -64,7 +68,11 @@ public void testCreateEmptyDir() throws Exception { } public void testCreateEmptyDirNoPermissions() throws Exception { - RandomizedTest.assumeFalse("Functionality is Unix specific", Os.isFamily(Os.FAMILY_WINDOWS)); + // Test depends on Posix file permissions + RandomizedTest.assumeFalse("Functionality is Unix-like OS specific", Os.isFamily(Os.FAMILY_WINDOWS)); + // Java's Files.setPosixFilePermissions is a NOOP inside a Docker container as + // files are created by default with UID and GID = 0 (root). + RandomizedTest.assumeFalse("Functionality doesn't work in Docker", isRunningInDocker()); Project project = ProjectBuilder.builder().build(); EmptyDirTask emptyDirTask = project.getTasks().create("emptyDirTask", EmptyDirTask.class); @@ -92,4 +100,20 @@ private File getNewNonExistingTempFolderFile(Project project) throws IOException return newEmptyFolder; } + private static boolean isRunningInDocker() { + // Only reliable existing method but may be removed in future + if (new File("/.dockerenv").exists()) { + return true; + } + try { + // Backup 1: look for 'docker' in one of the paths in /proc/1/cgroup + if (Files.lines(Path.of("/proc/1/cgroup")).anyMatch(line -> line.contains("docker"))) { + return true; + } + // Backup 2: look for 'docker' in overlay fs + return Files.lines(Path.of("/proc/1/mounts")).anyMatch(line -> line.startsWith("overlay") && line.contains("docker")); + } catch (InvalidPathException | IOException e) { + return false; + } + } } diff --git a/buildSrc/src/test/java/org/opensearch/gradle/JdkDownloadPluginTests.java b/buildSrc/src/test/java/org/opensearch/gradle/JdkDownloadPluginTests.java index 4dcc65cca4c62..bb394cf51429f 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/JdkDownloadPluginTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/JdkDownloadPluginTests.java @@ -38,6 +38,8 @@ import org.gradle.testfixtures.ProjectBuilder; import org.junit.BeforeClass; +import java.util.UUID; + import static org.hamcrest.CoreMatchers.equalTo; public class JdkDownloadPluginTests extends GradleUnitTestCase { @@ -108,7 +110,7 @@ public void testUnknownArchitecture() { "11.0.2+33", "linux", "unknown", - "unknown architecture [unknown] for jdk [testjdk], must be one of [aarch64, x64]" + "unknown architecture [unknown] for jdk [testjdk], must be one of [aarch64, x64, s390x, ppc64le]" ); } @@ -148,7 +150,8 @@ private void createJdk(Project project, String name, String vendor, String versi } private Project createProject() { - Project project = ProjectBuilder.builder().withParent(rootProject).build(); + final String name = UUID.randomUUID().toString(); + Project project = ProjectBuilder.builder().withName(name).withParent(rootProject).build(); project.getPlugins().apply("opensearch.jdk-download"); return project; } diff --git a/buildSrc/src/test/java/org/opensearch/gradle/plugin/OptionalDependenciesPluginTests.java b/buildSrc/src/test/java/org/opensearch/gradle/plugin/OptionalDependenciesPluginTests.java new file mode 100644 index 0000000000000..76b669a73610e --- /dev/null +++ b/buildSrc/src/test/java/org/opensearch/gradle/plugin/OptionalDependenciesPluginTests.java @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle.plugin; + +import org.apache.maven.model.Dependency; +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.opensearch.gradle.test.GradleUnitTestCase; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; +import org.junit.After; +import org.junit.Before; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.is; +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; + +public class OptionalDependenciesPluginTests extends GradleUnitTestCase { + private TemporaryFolder projectDir; + + @Before + public void setUp() throws Exception { + projectDir = new TemporaryFolder(); + projectDir.create(); + } + + @After + public void tearDown() { + projectDir.delete(); + } + + public void testApply() throws FileNotFoundException, IOException, XmlPullParserException { + final File mavenRepoDir = new File(projectDir.getRoot(), "mavenrepo"); + + try (InputStream in = getClass().getClassLoader().getResourceAsStream("plugin/optional-dependencies.gradle")) { + try (OutputStream out = new FileOutputStream(projectDir.newFile("build.gradle"))) { + in.transferTo(out); + } + } + + GradleRunner runner = GradleRunner.create() + .forwardOutput() + .withPluginClasspath() + .withArguments("publish", "-PrepoUrl=" + mavenRepoDir.toURI().toURL()) + .withProjectDir(projectDir.getRoot()); + + BuildResult result = runner.build(); + assertEquals(SUCCESS, result.task(":" + "publish").getOutcome()); + + final String name = projectDir.getRoot().getName(); + assertDependency(mavenRepoDir, name); + } + + private void assertDependency(File repoUrl, String name) throws FileNotFoundException, IOException, XmlPullParserException { + final File pom = new File(repoUrl, "org/custom/group/" + name + "/1.0.0/" + name + "-1.0.0.pom"); + assertThat(pom.exists(), is(true)); + + final MavenXpp3Reader reader = new MavenXpp3Reader(); + final Model model = reader.read(new FileReader(pom)); + + final Optional dependecyOpt = model.getDependencies() + .stream() + .filter(d -> d.getArtifactId().equals("commons-lang3")) + .findAny(); + + assertThat(dependecyOpt.isPresent(), is(true)); + assertThat(dependecyOpt.get().isOptional(), is(true)); + } +} diff --git a/buildSrc/src/test/java/org/opensearch/gradle/plugin/PluginBuildPluginTests.java b/buildSrc/src/test/java/org/opensearch/gradle/plugin/PluginBuildPluginTests.java index 9ed0e3e494992..fa0693a258222 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/plugin/PluginBuildPluginTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/plugin/PluginBuildPluginTests.java @@ -36,13 +36,15 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.internal.project.ProjectInternal; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; import org.gradle.testfixtures.ProjectBuilder; import org.junit.Before; import org.junit.Ignore; -import org.mockito.Mockito; import java.util.stream.Collectors; +import org.mockito.Mockito; + public class PluginBuildPluginTests extends GradleUnitTestCase { private Project project; @@ -64,6 +66,10 @@ public void testApply() { assertNotNull("plugin extensions has the right type", project.getExtensions().findByType(PluginPropertiesExtension.class)); assertNull("plugin should not create the integTest task", project.getTasks().findByName("integTest")); + project.getTasks().withType(AbstractArchiveTask.class).forEach(t -> { + assertFalse(String.format("task '%s' should not preserve timestamps", t.getName()), t.isPreserveFileTimestamps()); + assertTrue(String.format("task '%s' should have reproducible file order", t.getName()), t.isReproducibleFileOrder()); + }); } @Ignore("https://github.com/elastic/elasticsearch/issues/47123") diff --git a/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java b/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java new file mode 100644 index 0000000000000..5c26472cbd33e --- /dev/null +++ b/buildSrc/src/test/java/org/opensearch/gradle/pluginzip/PublishTests.java @@ -0,0 +1,590 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gradle.pluginzip; + +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.opensearch.gradle.test.GradleUnitTestCase; +import org.gradle.api.Project; +import org.gradle.testfixtures.ProjectBuilder; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; +import org.gradle.testkit.runner.UnexpectedBuildFailure; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; + +public class PublishTests extends GradleUnitTestCase { + private TemporaryFolder projectDir; + private static final String TEMPLATE_RESOURCE_FOLDER = "pluginzip"; + private final String PROJECT_NAME = "sample-plugin"; + private final String ZIP_PUBLISH_TASK = "publishPluginZipPublicationToZipStagingRepository"; + + @Before + public void setUp() throws IOException { + projectDir = new TemporaryFolder(); + projectDir.create(); + } + + @After + public void tearDown() { + projectDir.delete(); + } + + /** + * This test is used to verify that adding the 'opensearch.pluginzip' to the project + * adds some other transitive plugins and tasks under the hood. This is basically + * a behavioral test of the {@link Publish#apply(Project)} method. + * + * This is equivalent of having a build.gradle script with just the following section: + *
+     *     plugins {
+     *       id 'opensearch.pluginzip'
+     *     }
+     * 
+ */ + @Test + public void applyZipPublicationPluginNoConfig() { + // All we do here is creating an empty project and applying the Publish plugin. + Project project = ProjectBuilder.builder().build(); + project.getPluginManager().apply(Publish.class); + + // WARNING: ===================================================================== + // All the following tests will work only before the gradle project is evaluated. + // There are some methods that will cause the project to be evaluated, such as: + // project.getTasksByName() + // After the project is evaluated there are more tasks found in the project, like + // the [assemble, build, ...] and other standard tasks. + // This can potentially break in future gradle versions (?) + // =============================================================================== + + assertEquals( + "The Publish plugin is applied which adds total of five tasks from Nebula and MavenPublishing plugins.", + 5, + project.getTasks().size() + ); + + // Tasks applied from "com.netflix.nebula.maven-base-publish" + assertTrue( + project.getTasks() + .findByName("generateMetadataFileForNebulaPublication") instanceof org.gradle.api.publish.tasks.GenerateModuleMetadata + ); + assertTrue( + project.getTasks() + .findByName("generatePomFileForNebulaPublication") instanceof org.gradle.api.publish.maven.tasks.GenerateMavenPom + ); + assertTrue( + project.getTasks() + .findByName("publishNebulaPublicationToMavenLocal") instanceof org.gradle.api.publish.maven.tasks.PublishToMavenLocal + ); + + // Tasks applied from MavenPublishPlugin + assertTrue(project.getTasks().findByName("publishToMavenLocal") instanceof org.gradle.api.DefaultTask); + assertTrue(project.getTasks().findByName("publish") instanceof org.gradle.api.DefaultTask); + + // And we miss the pluginzip publication task (because no publishing was defined for it) + assertNull(project.getTasks().findByName(ZIP_PUBLISH_TASK)); + + // We have the following publishing plugins + assertEquals(4, project.getPlugins().size()); + // ... of the following types: + assertNotNull( + "Project is expected to have OpenSearch pluginzip Publish plugin", + project.getPlugins().findPlugin(org.opensearch.gradle.pluginzip.Publish.class) + ); + assertNotNull( + "Project is expected to have MavenPublishPlugin (applied from OpenSearch pluginzip plugin)", + project.getPlugins().findPlugin(org.gradle.api.publish.maven.plugins.MavenPublishPlugin.class) + ); + assertNotNull( + "Project is expected to have Publishing plugin (applied from MavenPublishPublish plugin)", + project.getPlugins().findPlugin(org.gradle.api.publish.plugins.PublishingPlugin.class) + ); + assertNotNull( + "Project is expected to have nebula MavenNebulaPublishPlugin plugin (applied from OpenSearch pluginzip plugin)", + project.getPlugins().findPlugin(nebula.plugin.publishing.maven.MavenNebulaPublishPlugin.class) + ); + } + + /** + * Verify that if the zip publication is configured then relevant tasks are chained correctly. + * This test that the dependsOn() is applied correctly. + */ + @Test + public void applyZipPublicationPluginWithConfig() throws IOException, URISyntaxException, InterruptedException { + + /* ------------------------------- + // The ideal approach would be to create a project (via ProjectBuilder) with publishzip plugin, + // have it evaluated (API call) and then check if there are tasks that the plugin uses to hookup into + // and how these tasks are chained. The problem is that there is a known gradle issue (#20301) that does + // not allow for it ATM. If, however, it is fixed in the future the following is the code that can + // be used... + + Project project = ProjectBuilder.builder().build(); + project.getPluginManager().apply(Publish.class); + // add publications via API + + // evaluate the project + ((DefaultProject)project).evaluate(); + + // - Check that "validatePluginZipPom" and/or "publishPluginZipPublicationToZipStagingRepository" + // tasks have dependencies on "generatePomFileForNebulaPublication". + // - Check that there is the staging repository added. + + // However, due to known issue(1): https://github.com/gradle/gradle/issues/20301 + // it is impossible to reach to individual tasks and work with them. + // (1): https://docs.gradle.org/7.4/release-notes.html#known-issues + + // I.e.: The following code throws exception, basically any access to individual tasks fails. + project.getTasks().getByName("validatePluginZipPom"); + ------------------------------- */ + + // Instead, we run the gradle project via GradleRunner (this way we get fully evaluated project) + // and using the minimal possible configuration (missingPOMEntity) we test that as soon as the zip publication + // configuration is specified then all the necessary tasks are hooked up and executed correctly. + // However, this does not test execution order of the tasks. + GradleRunner runner = prepareGradleRunnerFromTemplate("missingPOMEntity.gradle", ZIP_PUBLISH_TASK/*, "-m"*/); + BuildResult result = runner.build(); + + assertEquals(SUCCESS, result.task(":" + "bundlePlugin").getOutcome()); + assertEquals(SUCCESS, result.task(":" + "generatePomFileForNebulaPublication").getOutcome()); + assertEquals(SUCCESS, result.task(":" + "generatePomFileForPluginZipPublication").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + } + + /** + * If the plugin is used but relevant publication is not defined then a message is printed. + */ + @Test + public void missingPublications() throws IOException, URISyntaxException { + GradleRunner runner = prepareGradleRunnerFromTemplate("missingPublications.gradle", "build", "-m"); + BuildResult result = runner.build(); + + assertTrue(result.getOutput().contains("Plugin 'opensearch.pluginzip' is applied but no 'pluginZip' publication is defined.")); + } + + /** + * In OpenSearch 3.x the `project.group` property will be mandatory. + * But in 2.x (2.4 and above) the `project.group` property can be empty in which case it falls back to default value. + */ + @Test + public void missingGroupValue() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("missingGroupValue.gradle", "build", ZIP_PUBLISH_TASK); + BuildResult result = runner.build(); + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // Parse the maven file and validate default values + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "opensearch", + "plugin", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) + ) + ); + assertEquals(model.getVersion(), "2.0.0.0"); + assertEquals(model.getGroupId(), "org.opensearch.plugin"); + assertEquals(model.getArtifactId(), PROJECT_NAME); + } + + /** + * This would be the most common use case where user declares Maven publication entity with minimal info + * and the resulting POM file will use artifactId, groupId and version values based on the Gradle project object. + */ + @Test + public void useDefaultValues() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("useDefaultValues.gradle", "build", ZIP_PUBLISH_TASK); + BuildResult result = runner.build(); + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // check if both the zip and pom files have been published to local staging repo + assertTrue( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ).exists() + ); + assertTrue( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.zip" + ) + ).exists() + ); + + // Parse the maven file and validate default values + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) + ) + ); + assertEquals(model.getVersion(), "2.0.0.0"); + assertEquals(model.getGroupId(), "org.custom.group"); + assertEquals(model.getArtifactId(), PROJECT_NAME); + assertNull(model.getName()); + assertNull(model.getDescription()); + + assertEquals(model.getUrl(), "https://github.com/doe/sample-plugin"); + } + + /** + * If the `group` is defined in gradle's allprojects section then it does not have to defined in publications. + */ + @Test + public void allProjectsGroup() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("allProjectsGroup.gradle", "build", ZIP_PUBLISH_TASK); + BuildResult result = runner.build(); + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // Parse the maven file and validate default values + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "opensearch", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) + ) + ); + assertEquals(model.getVersion(), "2.0.0.0"); + assertEquals(model.getGroupId(), "org.opensearch"); + } + + /** + * The groupId value can be defined on several levels. This tests that the most internal level outweighs other levels. + */ + @Test + public void groupPriorityLevel() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("groupPriorityLevel.gradle", "build", ZIP_PUBLISH_TASK); + BuildResult result = runner.build(); + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // Parse the maven file and validate default values + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "level", + "3", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) + ) + ); + assertEquals(model.getVersion(), "2.0.0.0"); + assertEquals(model.getGroupId(), "level.3"); + } + + /** + * In this case the Publication entity is completely missing but still the POM file is generated using the default + * values including the groupId and version values obtained from the Gradle project object. + */ + @Test + public void missingPOMEntity() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("missingPOMEntity.gradle", "build", ZIP_PUBLISH_TASK); + BuildResult result = runner.build(); + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // Parse the maven file and validate it + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) + ) + ); + + assertEquals(model.getArtifactId(), PROJECT_NAME); + assertEquals(model.getGroupId(), "org.custom.group"); + assertEquals(model.getVersion(), "2.0.0.0"); + assertEquals(model.getPackaging(), "zip"); + + assertNull(model.getName()); + assertNull(model.getDescription()); + + assertEquals(0, model.getDevelopers().size()); + assertEquals(0, model.getContributors().size()); + assertEquals(0, model.getLicenses().size()); + } + + /** + * In some cases we need the POM groupId value to be different from the Gradle "project.group" value hence we + * allow for groupId customization (it will override whatever the Gradle "project.group" value is). + */ + @Test + public void customizedGroupValue() throws IOException, URISyntaxException, XmlPullParserException { + GradleRunner runner = prepareGradleRunnerFromTemplate("customizedGroupValue.gradle", "build", ZIP_PUBLISH_TASK); + BuildResult result = runner.build(); + + /** Check if build and {@value ZIP_PUBLISH_TASK} tasks have run well */ + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + ZIP_PUBLISH_TASK).getOutcome()); + + // Parse the maven file and validate the groupID + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "I", + "am", + "customized", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) + ) + ); + + assertEquals(model.getGroupId(), "I.am.customized"); + } + + /** + * If the customized groupId value is invalid (from the Maven POM perspective) then we need to be sure it is + * caught and reported properly. + */ + @Test + public void customizedInvalidGroupValue() throws IOException, URISyntaxException { + GradleRunner runner = prepareGradleRunnerFromTemplate("customizedInvalidGroupValue.gradle", "build", ZIP_PUBLISH_TASK); + Exception e = assertThrows(UnexpectedBuildFailure.class, runner::build); + assertTrue( + e.getMessage().contains("Invalid publication 'pluginZip': groupId ( ) is not a valid Maven identifier ([A-Za-z0-9_\\-.]+).") + ); + } + + /** + * This test verifies that use of the pluginZip does not clash with other maven publication plugins. + * It covers the case when user calls the "publishToMavenLocal" task. + */ + @Test + public void publishToMavenLocal() throws IOException, URISyntaxException, XmlPullParserException { + // By default, the "publishToMavenLocal" publishes artifacts to a local m2 repo, typically + // found in `~/.m2/repository`. But this is not practical for this unit test at all. We need to point + // the 'maven-publish' plugin to a custom m2 repo located in temporary directory associated with this + // test case instead. + // + // According to Gradle documentation this should be possible by proper configuration of the publishing + // task (https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:install). + // But for some reason this never worked as expected and artifacts created during this test case + // were always pushed into the default local m2 repository (ie: `~/.m2/repository`). + // The only workaround that seems to work is to pass "-Dmaven.repo.local" property via runner argument. + // (Kudos to: https://stackoverflow.com/questions/72265294/gradle-publishtomavenlocal-specify-custom-directory) + // + // The temporary directory that is used as the local m2 repository is created via in task "prepareLocalMVNRepo". + GradleRunner runner = prepareGradleRunnerFromTemplate( + "publishToMavenLocal.gradle", + String.join(File.separator, "-Dmaven.repo.local=" + projectDir.getRoot(), "build", "local-staging-repo"), + "build", + "prepareLocalMVNRepo", + "publishToMavenLocal" + ); + BuildResult result = runner.build(); + + assertEquals(SUCCESS, result.task(":" + "build").getOutcome()); + assertEquals(SUCCESS, result.task(":" + "publishToMavenLocal").getOutcome()); + + // Parse the maven file and validate it + MavenXpp3Reader reader = new MavenXpp3Reader(); + Model model = reader.read( + new FileReader( + new File( + projectDir.getRoot(), + String.join( + File.separator, + "build", + "local-staging-repo", + "org", + "custom", + "group", + PROJECT_NAME, + "2.0.0.0", + PROJECT_NAME + "-2.0.0.0.pom" + ) + ) + ) + ); + + // The "publishToMavenLocal" task will run ALL maven publications, hence we can expect the ZIP publication + // present as well: https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:tasks + assertEquals(model.getArtifactId(), PROJECT_NAME); + assertEquals(model.getGroupId(), "org.custom.group"); + assertEquals(model.getVersion(), "2.0.0.0"); + assertEquals(model.getPackaging(), "zip"); + + // We have two publications in the build.gradle file, both are "MavenPublication" based. + // Both the mavenJava and pluginZip publications publish to the same location (coordinates) and + // artifacts (the POM file) overwrite each other. However, we can verify that the Zip plugin is + // the last one and "wins" over the mavenJava. + assertEquals(model.getDescription(), "pluginZip publication"); + } + + /** + * A helper method for use cases + * + * @param templateName The name of the file (from "pluginzip" folder) to use as a build.gradle for the test + * @param gradleArguments Optional CLI parameters to pass into Gradle runner + */ + private GradleRunner prepareGradleRunnerFromTemplate(String templateName, String... gradleArguments) throws IOException, + URISyntaxException { + useTemplateFile(projectDir.newFile("build.gradle"), templateName); + prepareGradleFilesAndSources(); + + GradleRunner runner = GradleRunner.create() + .forwardOutput() + .withPluginClasspath() + .withArguments(gradleArguments) + .withProjectDir(projectDir.getRoot()); + + return runner; + } + + private void prepareGradleFilesAndSources() throws IOException { + // A dummy "source" file that is processed with bundlePlugin and put into a ZIP artifact file + File bundleFile = new File(projectDir.getRoot(), PROJECT_NAME + "-source.txt"); + Files.createFile(bundleFile.toPath()); + // Setting a project name via settings.gradle file + writeString(projectDir.newFile("settings.gradle"), "rootProject.name = '" + PROJECT_NAME + "'"); + } + + private void writeString(File file, String string) throws IOException { + try (Writer writer = new FileWriter(file)) { + writer.write(string); + } + } + + /** + * Write the content of the "template" file into the target file. + * The template file must be located in the {@value TEMPLATE_RESOURCE_FOLDER} folder. + * @param targetFile A target file + * @param templateFile A name of the template file located under {@value TEMPLATE_RESOURCE_FOLDER} folder + */ + private void useTemplateFile(File targetFile, String templateFile) throws IOException, URISyntaxException { + + URL resource = getClass().getClassLoader().getResource(String.join(File.separator, TEMPLATE_RESOURCE_FOLDER, templateFile)); + Path resPath = Paths.get(resource.toURI()).toAbsolutePath(); + List lines = Files.readAllLines(resPath, StandardCharsets.UTF_8); + + try (Writer writer = new FileWriter(targetFile)) { + for (String line : lines) { + writer.write(line); + writer.write(System.lineSeparator()); + } + } + } + +} diff --git a/buildSrc/src/test/java/org/opensearch/gradle/precommit/DependencyLicensesTaskTests.java b/buildSrc/src/test/java/org/opensearch/gradle/precommit/DependencyLicensesTaskTests.java index 6bb6b90116394..bb216b27128e1 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/precommit/DependencyLicensesTaskTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/precommit/DependencyLicensesTaskTests.java @@ -35,6 +35,7 @@ import org.gradle.api.Action; import org.gradle.api.GradleException; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; import org.gradle.api.file.FileCollection; import org.gradle.api.plugins.JavaPlugin; @@ -95,7 +96,7 @@ public void givenProjectWithoutLicensesDirButWithDependenciesThenShouldThrowExce expectedException.expect(GradleException.class); expectedException.expectMessage(containsString("does not exist, but there are dependencies")); - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); task.get().checkDependencies(); } @@ -113,7 +114,7 @@ public void givenProjectWithDependencyButNoShaFileThenShouldReturnException() th createFileIn(licensesDir, "groovy-LICENSE.txt", PERMISSIVE_LICENSE_TEXT); createFileIn(licensesDir, "groovy-NOTICE.txt", ""); - project.getDependencies().add("compileClasspath", project.getDependencies().localGroovy()); + project.getDependencies().add("someCompileConfiguration", project.getDependencies().localGroovy()); task.get().checkDependencies(); } @@ -122,7 +123,7 @@ public void givenProjectWithDependencyButNoLicenseFileThenShouldReturnException( expectedException.expect(GradleException.class); expectedException.expectMessage(containsString("Missing LICENSE for ")); - project.getDependencies().add("compileClasspath", project.getDependencies().localGroovy()); + project.getDependencies().add("someCompileConfiguration", project.getDependencies().localGroovy()); getLicensesDir(project).mkdir(); updateShas.updateShas(); @@ -134,7 +135,7 @@ public void givenProjectWithDependencyButNoNoticeFileThenShouldReturnException() expectedException.expect(GradleException.class); expectedException.expectMessage(containsString("Missing NOTICE for ")); - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); createFileIn(getLicensesDir(project), "groovy-LICENSE.txt", PERMISSIVE_LICENSE_TEXT); @@ -147,7 +148,7 @@ public void givenProjectWithStrictDependencyButNoSourcesFileThenShouldReturnExce expectedException.expect(GradleException.class); expectedException.expectMessage(containsString("Missing SOURCES for ")); - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); createFileIn(getLicensesDir(project), "groovy-LICENSE.txt", STRICT_LICENSE_TEXT); createFileIn(getLicensesDir(project), "groovy-NOTICE.txt", ""); @@ -158,7 +159,7 @@ public void givenProjectWithStrictDependencyButNoSourcesFileThenShouldReturnExce @Test public void givenProjectWithStrictDependencyAndEverythingInOrderThenShouldReturnSilently() throws Exception { - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); createFileIn(getLicensesDir(project), "groovy-LICENSE.txt", STRICT_LICENSE_TEXT); createFileIn(getLicensesDir(project), "groovy-NOTICE.txt", ""); @@ -174,7 +175,7 @@ public void givenProjectWithStrictDependencyAndEverythingInOrderThenShouldReturn @Test public void givenProjectWithDependencyAndEverythingInOrderThenShouldReturnSilently() throws Exception { - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); File licensesDir = getLicensesDir(project); @@ -187,7 +188,7 @@ public void givenProjectWithALicenseButWithoutTheDependencyThenShouldThrowExcept expectedException.expect(GradleException.class); expectedException.expectMessage(containsString("Unused license ")); - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); File licensesDir = getLicensesDir(project); createAllDefaultDependencyFiles(licensesDir, "groovy", "javaparser-core"); @@ -201,7 +202,7 @@ public void givenProjectWithANoticeButWithoutTheDependencyThenShouldThrowExcepti expectedException.expect(GradleException.class); expectedException.expectMessage(containsString("Unused notice ")); - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); File licensesDir = getLicensesDir(project); createAllDefaultDependencyFiles(licensesDir, "groovy", "javaparser-core"); @@ -215,7 +216,7 @@ public void givenProjectWithAShaButWithoutTheDependencyThenShouldThrowException( expectedException.expect(GradleException.class); expectedException.expectMessage(containsString("Unused sha files found: \n")); - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); File licensesDir = getLicensesDir(project); createAllDefaultDependencyFiles(licensesDir, "groovy", "javaparser-core"); @@ -229,7 +230,7 @@ public void givenProjectWithADependencyWithWrongShaThenShouldThrowException() th expectedException.expect(GradleException.class); expectedException.expectMessage(containsString("SHA has changed! Expected ")); - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); File licensesDir = getLicensesDir(project); createAllDefaultDependencyFiles(licensesDir, "groovy"); @@ -243,7 +244,7 @@ public void givenProjectWithADependencyWithWrongShaThenShouldThrowException() th @Test public void givenProjectWithADependencyMappingThenShouldReturnSilently() throws Exception { - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); File licensesDir = getLicensesDir(project); createAllDefaultDependencyFiles(licensesDir, "groovy", "javaparser"); @@ -258,7 +259,7 @@ public void givenProjectWithADependencyMappingThenShouldReturnSilently() throws @Test public void givenProjectWithAIgnoreShaConfigurationAndNoShaFileThenShouldReturnSilently() throws Exception { - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); File licensesDir = getLicensesDir(project); createFileIn(licensesDir, "groovy-LICENSE.txt", PERMISSIVE_LICENSE_TEXT); @@ -297,6 +298,11 @@ private Project createProject() { Project project = ProjectBuilder.builder().build(); project.getPlugins().apply(JavaPlugin.class); + Configuration compileClasspath = project.getConfigurations().getByName("compileClasspath"); + Configuration someCompileConfiguration = project.getConfigurations().create("someCompileConfiguration"); + // Declare a configuration that is going to resolve the compile classpath of the application + project.getConfigurations().add(compileClasspath.extendsFrom(someCompileConfiguration)); + return project; } diff --git a/buildSrc/src/test/java/org/opensearch/gradle/precommit/FilePermissionsTaskTests.java b/buildSrc/src/test/java/org/opensearch/gradle/precommit/FilePermissionsTaskTests.java index f47964dd17a7a..0a8cff734ea97 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/precommit/FilePermissionsTaskTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/precommit/FilePermissionsTaskTests.java @@ -31,12 +31,8 @@ package org.opensearch.gradle.precommit; -import java.io.File; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.util.List; - import com.carrotsearch.randomizedtesting.RandomizedTest; + import org.apache.tools.ant.taskdefs.condition.Os; import org.opensearch.gradle.test.GradleUnitTestCase; import org.gradle.api.GradleException; @@ -45,6 +41,11 @@ import org.gradle.testfixtures.ProjectBuilder; import org.junit.Assert; +import java.io.File; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.List; + public class FilePermissionsTaskTests extends GradleUnitTestCase { public void testCheckPermissionsWhenAnExecutableFileExists() throws Exception { diff --git a/buildSrc/src/test/java/org/opensearch/gradle/precommit/UpdateShasTaskTests.java b/buildSrc/src/test/java/org/opensearch/gradle/precommit/UpdateShasTaskTests.java index a4f3dfb92a04f..2deabb752017a 100644 --- a/buildSrc/src/test/java/org/opensearch/gradle/precommit/UpdateShasTaskTests.java +++ b/buildSrc/src/test/java/org/opensearch/gradle/precommit/UpdateShasTaskTests.java @@ -36,6 +36,7 @@ import org.gradle.api.Action; import org.gradle.api.GradleException; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; import org.gradle.api.file.FileCollection; import org.gradle.api.plugins.JavaPlugin; @@ -86,7 +87,7 @@ public void whenDependencyDoesntExistThenShouldDeleteDependencySha() throws IOEx @Test public void whenDependencyExistsButShaNotThenShouldCreateNewShaFile() throws IOException, NoSuchAlgorithmException { - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); getLicensesDir(project).mkdir(); task.updateShas(); @@ -99,7 +100,7 @@ public void whenDependencyExistsButShaNotThenShouldCreateNewShaFile() throws IOE @Test public void whenDependencyAndWrongShaExistsThenShouldNotOverwriteShaFile() throws IOException, NoSuchAlgorithmException { - project.getDependencies().add("compileClasspath", dependency); + project.getDependencies().add("someCompileConfiguration", dependency); File groovyJar = task.getParentTask().getDependencies().getFiles().iterator().next(); String groovyShaName = groovyJar.getName() + ".sha1"; @@ -122,6 +123,11 @@ private Project createProject() { Project project = ProjectBuilder.builder().build(); project.getPlugins().apply(JavaPlugin.class); + Configuration compileClasspath = project.getConfigurations().getByName("compileClasspath"); + Configuration someCompileConfiguration = project.getConfigurations().create("someCompileConfiguration"); + // Declare a configuration that is going to resolve the compile classpath of the application + project.getConfigurations().add(compileClasspath.extendsFrom(someCompileConfiguration)); + return project; } diff --git a/buildSrc/src/test/resources/plugin/optional-dependencies.gradle b/buildSrc/src/test/resources/plugin/optional-dependencies.gradle new file mode 100644 index 0000000000000..ebcf758f578d8 --- /dev/null +++ b/buildSrc/src/test/resources/plugin/optional-dependencies.gradle @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +plugins { + id 'java' + id 'maven-publish' + id 'opensearch.optional-dependencies' +} +group = "org.custom.group" +version = '1.0.0' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.apache.commons:commons-lang3:3.3.2', optional +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + repositories { + maven { + url "${repoUrl}" + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/allProjectsGroup.gradle b/buildSrc/src/test/resources/pluginzip/allProjectsGroup.gradle new file mode 100644 index 0000000000000..80638107c86e1 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/allProjectsGroup.gradle @@ -0,0 +1,28 @@ +plugins { + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +allprojects { + group = 'org.opensearch' +} + +publishing { + publications { + pluginZip(MavenPublication) { publication -> + pom { + name = "sample-plugin" + description = "pluginDescription" + } + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/customizedGroupValue.gradle b/buildSrc/src/test/resources/pluginzip/customizedGroupValue.gradle new file mode 100644 index 0000000000000..94f03132faa80 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/customizedGroupValue.gradle @@ -0,0 +1,44 @@ +plugins { + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + groupId = "I.am.customized" + pom { + name = "sample-plugin" + description = "pluginDescription" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + developers { + developer { + name = "John Doe" + url = "https://github.com/john-doe/" + organization = "Doe.inc" + organizationUrl = "https://doe.inc/" + } + } + url = "https://github.com/doe/sample-plugin" + scm { + url = "https://github.com/doe/sample-plugin" + } + } + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/customizedInvalidGroupValue.gradle b/buildSrc/src/test/resources/pluginzip/customizedInvalidGroupValue.gradle new file mode 100644 index 0000000000000..6f2abbdacd6d4 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/customizedInvalidGroupValue.gradle @@ -0,0 +1,44 @@ +plugins { + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + groupId = " " // <-- User provides invalid value + pom { + name = "sample-plugin" + description = "pluginDescription" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + developers { + developer { + name = "John Doe" + url = "https://github.com/john-doe/" + organization = "Doe.inc" + organizationUrl = "https://doe.inc/" + } + } + url = "https://github.com/doe/sample-plugin" + scm { + url = "https://github.com/doe/sample-plugin" + } + } + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/groupPriorityLevel.gradle b/buildSrc/src/test/resources/pluginzip/groupPriorityLevel.gradle new file mode 100644 index 0000000000000..4da02c9f191d8 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/groupPriorityLevel.gradle @@ -0,0 +1,30 @@ +plugins { + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +allprojects { + group = 'level.1' +} + +publishing { + publications { + pluginZip(MavenPublication) { publication -> + groupId = "level.2" + pom { + name = "sample-plugin" + description = "pluginDescription" + groupId = "level.3" + } + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/missingGroupValue.gradle b/buildSrc/src/test/resources/pluginzip/missingGroupValue.gradle new file mode 100644 index 0000000000000..8fcd1d6600b5a --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/missingGroupValue.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +//group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/missingPOMEntity.gradle b/buildSrc/src/test/resources/pluginzip/missingPOMEntity.gradle new file mode 100644 index 0000000000000..394bc53622769 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/missingPOMEntity.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/missingPublications.gradle b/buildSrc/src/test/resources/pluginzip/missingPublications.gradle new file mode 100644 index 0000000000000..ba6b33ad86463 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/missingPublications.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +//publishing { +// publications { +// pluginZip(MavenPublication) { +// } +// } +//} diff --git a/buildSrc/src/test/resources/pluginzip/publishToMavenLocal.gradle b/buildSrc/src/test/resources/pluginzip/publishToMavenLocal.gradle new file mode 100644 index 0000000000000..8d248dbe08a42 --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/publishToMavenLocal.gradle @@ -0,0 +1,47 @@ +plugins { + // The java-gradle-plugin adds a new publication called 'pluginMaven' that causes some warnings because it + // clashes a bit with other publications defined in this file. If you are running at the --info level then you can + // expect some warning like the following: + // "Multiple publications with coordinates 'org.custom.group:sample-plugin:2.0.0.0' are published to repository 'mavenLocal'." + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +// A task to prepare directory for a temporary maven local repository +tasks.register('prepareLocalMVNRepo') { + dependsOn ':bundlePlugin' + doFirst { + File localMVNRepo = new File (layout.buildDirectory.get().getAsFile().getPath(), 'local-staging-repo') + System.out.println('Creating temporary folder for mavenLocal repo: '+ localMVNRepo.toString()) + System.out.println("Success: " + localMVNRepo.mkdir()) + } +} + +publishing { + publications { + // Plugin zip publication + pluginZip(MavenPublication) { + pom { + url = 'http://www.example.com/library' + description = 'pluginZip publication' + } + } + // Standard maven publication + mavenJava(MavenPublication) { + pom { + url = 'http://www.example.com/library' + description = 'mavenJava publication' + } + } + } +} diff --git a/buildSrc/src/test/resources/pluginzip/useDefaultValues.gradle b/buildSrc/src/test/resources/pluginzip/useDefaultValues.gradle new file mode 100644 index 0000000000000..52f1c042fd47c --- /dev/null +++ b/buildSrc/src/test/resources/pluginzip/useDefaultValues.gradle @@ -0,0 +1,43 @@ +plugins { + id 'java-gradle-plugin' + id 'opensearch.pluginzip' +} + +group="org.custom.group" +version='2.0.0.0' + +// A bundlePlugin task mockup +tasks.register('bundlePlugin', Zip.class) { + archiveFileName = "sample-plugin-${version}.zip" + destinationDirectory = layout.buildDirectory.dir('distributions') + from layout.projectDirectory.file('sample-plugin-source.txt') +} + +publishing { + publications { + pluginZip(MavenPublication) { + pom { +// name = "plugin name" +// description = "plugin description" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + developers { + developer { + name = "John Doe" + url = "https://github.com/john-doe/" + organization = "Doe.inc" + organizationUrl = "https://doe.inc/" + } + } + url = "https://github.com/doe/sample-plugin" + scm { + url = "https://github.com/doe/sample-plugin" + } + } + } + } +} diff --git a/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/BaseTestCase.java b/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/BaseTestCase.java index 8e06a1cad0241..f285dcbfd3bd2 100644 --- a/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/BaseTestCase.java +++ b/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/BaseTestCase.java @@ -35,10 +35,12 @@ import com.carrotsearch.randomizedtesting.RandomizedRunner; import com.carrotsearch.randomizedtesting.annotations.TestMethodProviders; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering; -import junit.framework.AssertionFailedError; + import org.junit.Assert; import org.junit.runner.RunWith; +import junit.framework.AssertionFailedError; + @RunWith(RandomizedRunner.class) @TestMethodProviders({ JUnit4MethodProvider.class, JUnit3MethodProvider.class }) @ThreadLeakLingering(linger = 5000) // wait for "Connection worker" to die diff --git a/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/GradleUnitTestCase.java b/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/GradleUnitTestCase.java index a50a14a0ea932..f8032ba53df4c 100644 --- a/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/GradleUnitTestCase.java +++ b/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/GradleUnitTestCase.java @@ -35,6 +35,7 @@ import com.carrotsearch.randomizedtesting.RandomizedRunner; import com.carrotsearch.randomizedtesting.annotations.TestMethodProviders; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; + import org.junit.runner.RunWith; @RunWith(RandomizedRunner.class) diff --git a/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/JUnit3MethodProvider.java b/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/JUnit3MethodProvider.java index 0c01b6d519d62..163a903d31832 100644 --- a/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/JUnit3MethodProvider.java +++ b/buildSrc/src/testFixtures/java/org/opensearch/gradle/test/JUnit3MethodProvider.java @@ -59,7 +59,7 @@ public Collection getTestMethods(Class suiteClass, ClassModel classMo if (m.getName().startsWith("test") && Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers()) - && m.getParameterTypes().length == 0) { + && m.getParameterCount() == 0) { result.add(m); } } diff --git a/buildSrc/src/testKit/testingConventions/build.gradle b/buildSrc/src/testKit/testingConventions/build.gradle index 418e833e8cb14..676960bcc8b70 100644 --- a/buildSrc/src/testKit/testingConventions/build.gradle +++ b/buildSrc/src/testKit/testingConventions/build.gradle @@ -88,6 +88,3 @@ project(':valid_setup_with_base') { } } } - - - diff --git a/buildSrc/src/testKit/testingConventions/settings.gradle b/buildSrc/src/testKit/testingConventions/settings.gradle index c4206edd63ff7..bb64f39e020c5 100644 --- a/buildSrc/src/testKit/testingConventions/settings.gradle +++ b/buildSrc/src/testKit/testingConventions/settings.gradle @@ -16,4 +16,4 @@ include 'all_classes_in_tasks' include 'not_implementing_base' include 'valid_setup_no_base' include 'valid_setup_with_base' -include 'tests_in_main' \ No newline at end of file +include 'tests_in_main' diff --git a/buildSrc/src/testKit/thirdPartyAudit/build.gradle b/buildSrc/src/testKit/thirdPartyAudit/build.gradle index 41e699db94dcf..553ff5d8e6ed2 100644 --- a/buildSrc/src/testKit/thirdPartyAudit/build.gradle +++ b/buildSrc/src/testKit/thirdPartyAudit/build.gradle @@ -40,7 +40,6 @@ repositories { } dependencies { - forbiddenApisCliJar 'de.thetaphi:forbiddenapis:3.2' jdkJarHell 'org.opensearch:opensearch-core:current' compileOnly "org.${project.properties.compileOnlyGroup}:${project.properties.compileOnlyVersion}" implementation "org.${project.properties.compileGroup}:${project.properties.compileVersion}" diff --git a/buildSrc/src/testKit/thirdPartyAudit/sample_jars/build.gradle b/buildSrc/src/testKit/thirdPartyAudit/sample_jars/build.gradle index c8c89fb5e4273..cb8050d1718c4 100644 --- a/buildSrc/src/testKit/thirdPartyAudit/sample_jars/build.gradle +++ b/buildSrc/src/testKit/thirdPartyAudit/sample_jars/build.gradle @@ -16,7 +16,7 @@ repositories { mavenCentral() } dependencies { - implementation 'org.apache.logging.log4j:log4j-core:2.17.2' + implementation "org.apache.logging.log4j:log4j-core:2.20.0" } ["0.0.1", "0.0.2"].forEach { v -> @@ -41,7 +41,7 @@ dependencies { ["0.0.1"].forEach { v -> ["opensearch", "other"].forEach { p -> tasks.register("broken-log4j-${p}-${v}", Jar) { - destinationDir = file("${buildDir}/testrepo/org/${p}/gradle/broken-log4j/${v}/") + destinationDirectory = file("${buildDir}/testrepo/org/${p}/gradle/broken-log4j/${v}/") archiveFileName = "broken-log4j-${v}.jar" from sourceSets.main.output include "**/TestingLog4j.class" @@ -51,7 +51,7 @@ dependencies { } tasks.register("jarhellJdk", Jar) { - destinationDir = file("${buildDir}/testrepo/org/other/gradle/jarhellJdk/0.0.1/") + destinationDirectory = file("${buildDir}/testrepo/org/other/gradle/jarhellJdk/0.0.1/") archiveFileName = "jarhellJdk-0.0.1.jar" from sourceSets.main.output include "**/String.class" diff --git a/buildSrc/src/testKit/thirdPartyAudit/settings.gradle b/buildSrc/src/testKit/thirdPartyAudit/settings.gradle index 582faadddaef1..603d8b7da6e5d 100644 --- a/buildSrc/src/testKit/thirdPartyAudit/settings.gradle +++ b/buildSrc/src/testKit/thirdPartyAudit/settings.gradle @@ -9,4 +9,4 @@ * GitHub history for details. */ -include 'sample_jars' \ No newline at end of file +include 'sample_jars' diff --git a/buildSrc/version.properties b/buildSrc/version.properties index b5e14cd24bd93..c5db4da86165a 100644 --- a/buildSrc/version.properties +++ b/buildSrc/version.properties @@ -1,43 +1,67 @@ -opensearch = 2.0.0 -lucene = 9.1.0-snapshot-ea989fe8f30 +opensearch = 2.11.0 +lucene = 9.7.0 bundled_jdk_vendor = adoptium -bundled_jdk = 17.0.2+8 - - +bundled_jdk = 17.0.8+7 # optional dependencies spatial4j = 0.7 jts = 1.15.0 -jackson = 2.13.2 -snakeyaml = 1.26 +jackson = 2.15.2 +jackson_databind = 2.15.2 +snakeyaml = 2.1 icu4j = 70.1 supercsv = 2.4.0 -log4j = 2.17.1 -slf4j = 1.6.2 +log4j = 2.20.0 +slf4j = 1.7.36 +asm = 9.6 +jettison = 1.5.4 +woodstox = 6.4.0 +kotlin = 1.7.10 +antlr4 = 4.11.1 +guava = 32.1.1-jre +protobuf = 3.22.3 +jakarta_annotation = 1.3.5 # when updating the JNA version, also update the version in buildSrc/build.gradle -jna = 5.5.0 +jna = 5.13.0 -netty = 4.1.73.Final -joda = 2.10.12 +netty = 4.1.99.Final +joda = 2.12.2 + +# client dependencies +httpclient = 4.5.14 +httpcore = 4.4.16 +httpasyncclient = 4.1.5 +commonslogging = 1.2 +commonscodec = 1.15 +commonslang = 3.13.0 +commonscompress = 1.24.0 +# plugin dependencies +aws = 2.20.55 +reactivestreams = 1.0.4 # when updating this version, you need to ensure compatibility with: # - plugins/ingest-attachment (transitive dependency, check the upstream POM) # - distribution/tools/plugin-cli -bouncycastle=1.70 +bouncycastle=1.76 # test dependencies randomizedrunner = 2.7.1 junit = 4.13.2 -httpclient = 4.5.13 -httpcore = 4.4.12 -httpasyncclient = 4.1.4 -commonslogging = 1.1.3 -commonscodec = 1.13 hamcrest = 2.1 -mockito = 4.3.1 +mockito = 5.5.0 objenesis = 3.2 -bytebuddy = 1.12.7 +bytebuddy = 1.14.7 # benchmark dependencies -jmh = 1.19 +jmh = 1.35 + +# compression +zstd = 1.5.5-5 + +jzlib = 1.1.3 + +resteasy = 6.2.4.Final + +# opentelemetry dependencies +opentelemetry = 1.30.1 diff --git a/client/benchmark/README.md b/client/benchmark/README.md index ee99a1384d27e..2732586b9e575 100644 --- a/client/benchmark/README.md +++ b/client/benchmark/README.md @@ -29,7 +29,7 @@ Example invocation: wget http://benchmarks.elasticsearch.org.s3.amazonaws.com/corpora/geonames/documents-2.json.bz2 bzip2 -d documents-2.json.bz2 mv documents-2.json client/benchmark/build -gradlew -p client/benchmark run --args ' rest bulk localhost build/documents-2.json geonames type 8647880 5000' +gradlew -p client/benchmark run --args ' rest bulk localhost build/documents-2.json geonames 8647880 5000' ``` The parameters are all in the `'`s and are in order: @@ -39,7 +39,6 @@ The parameters are all in the `'`s and are in order: * Benchmark target host IP (the host where OpenSearch is running) * full path to the file that should be bulk indexed * name of the index -* name of the (sole) type in the index * number of documents in the file * bulk size diff --git a/client/benchmark/build.gradle b/client/benchmark/build.gradle index 4aa4d7171e366..6fd5262f0ab4f 100644 --- a/client/benchmark/build.gradle +++ b/client/benchmark/build.gradle @@ -31,13 +31,17 @@ apply plugin: 'opensearch.build' apply plugin: 'application' -group = 'org.opensearch.client' +base { + group = 'org.opensearch.client' + archivesBaseName = 'client-benchmarks' +} // Not published so no need to assemble assemble.enabled = true -archivesBaseName = 'client-benchmarks' -mainClassName = 'org.opensearch.client.benchmark.BenchmarkMain' +application { + mainClass = 'org.opensearch.client.benchmark.BenchmarkMain' +} // never try to invoke tests on the benchmark project - there aren't any test.enabled = false diff --git a/client/benchmark/src/main/java/org/opensearch/client/benchmark/AbstractBenchmark.java b/client/benchmark/src/main/java/org/opensearch/client/benchmark/AbstractBenchmark.java index de9d075cb9a16..ab0a0d6b8a19c 100644 --- a/client/benchmark/src/main/java/org/opensearch/client/benchmark/AbstractBenchmark.java +++ b/client/benchmark/src/main/java/org/opensearch/client/benchmark/AbstractBenchmark.java @@ -49,7 +49,7 @@ public abstract class AbstractBenchmark { protected abstract T client(String benchmarkTargetHost) throws Exception; - protected abstract BulkRequestExecutor bulkRequestExecutor(T client, String indexName, String typeName); + protected abstract BulkRequestExecutor bulkRequestExecutor(T client, String indexName); protected abstract SearchRequestExecutor searchRequestExecutor(T client, String indexName); @@ -76,16 +76,15 @@ public final void run(String[] args) throws Exception { @SuppressForbidden(reason = "system out is ok for a command line tool") private void runBulkIndexBenchmark(String[] args) throws Exception { - if (args.length != 7) { - System.err.println("usage: 'bulk' benchmarkTargetHostIp indexFilePath indexName typeName numberOfDocuments bulkSize"); + if (args.length != 6) { + System.err.println("usage: 'bulk' benchmarkTargetHostIp indexFilePath indexName numberOfDocuments bulkSize"); System.exit(1); } String benchmarkTargetHost = args[1]; String indexFilePath = args[2]; String indexName = args[3]; - String typeName = args[4]; - int totalDocs = Integer.valueOf(args[5]); - int bulkSize = Integer.valueOf(args[6]); + int totalDocs = Integer.valueOf(args[4]); + int bulkSize = Integer.valueOf(args[5]); int totalIterationCount = (int) Math.floor(totalDocs / bulkSize); // consider 40% of all iterations as warmup iterations @@ -97,7 +96,7 @@ private void runBulkIndexBenchmark(String[] args) throws Exception { BenchmarkRunner benchmark = new BenchmarkRunner( warmupIterations, iterations, - new BulkBenchmarkTask(bulkRequestExecutor(client, indexName, typeName), indexFilePath, warmupIterations, iterations, bulkSize) + new BulkBenchmarkTask(bulkRequestExecutor(client, indexName), indexFilePath, warmupIterations, iterations, bulkSize) ); try { diff --git a/client/benchmark/src/main/java/org/opensearch/client/benchmark/ops/bulk/BulkBenchmarkTask.java b/client/benchmark/src/main/java/org/opensearch/client/benchmark/ops/bulk/BulkBenchmarkTask.java index 459cba1ce5f23..5d2b9cb764a6f 100644 --- a/client/benchmark/src/main/java/org/opensearch/client/benchmark/ops/bulk/BulkBenchmarkTask.java +++ b/client/benchmark/src/main/java/org/opensearch/client/benchmark/ops/bulk/BulkBenchmarkTask.java @@ -31,8 +31,8 @@ package org.opensearch.client.benchmark.ops.bulk; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.opensearch.OpenSearchException; import org.opensearch.client.benchmark.BenchmarkTask; import org.opensearch.client.benchmark.metrics.Sample; diff --git a/client/benchmark/src/main/java/org/opensearch/client/benchmark/rest/RestClientBenchmark.java b/client/benchmark/src/main/java/org/opensearch/client/benchmark/rest/RestClientBenchmark.java index 073fd5eab5c46..d2d7163b8dee2 100644 --- a/client/benchmark/src/main/java/org/opensearch/client/benchmark/rest/RestClientBenchmark.java +++ b/client/benchmark/src/main/java/org/opensearch/client/benchmark/rest/RestClientBenchmark.java @@ -65,8 +65,8 @@ protected RestClient client(String benchmarkTargetHost) { } @Override - protected BulkRequestExecutor bulkRequestExecutor(RestClient client, String indexName, String typeName) { - return new RestBulkRequestExecutor(client, indexName, typeName); + protected BulkRequestExecutor bulkRequestExecutor(RestClient client, String indexName) { + return new RestBulkRequestExecutor(client, indexName); } @Override @@ -78,9 +78,9 @@ private static final class RestBulkRequestExecutor implements BulkRequestExecuto private final RestClient client; private final String actionMetadata; - RestBulkRequestExecutor(RestClient client, String index, String type) { + RestBulkRequestExecutor(RestClient client, String index) { this.client = client; - this.actionMetadata = String.format(Locale.ROOT, "{ \"index\" : { \"_index\" : \"%s\", \"_type\" : \"%s\" } }%n", index, type); + this.actionMetadata = String.format(Locale.ROOT, "{ \"index\" : { \"_index\" : \"%s\" } }%n", index); } @Override @@ -91,7 +91,7 @@ public boolean bulkIndex(List bulkData) { bulkRequestBody.append(bulkItem); bulkRequestBody.append("\n"); } - Request request = new Request("POST", "/geonames/type/_noop_bulk"); + Request request = new Request("POST", "/geonames/_noop_bulk"); request.setJsonEntity(bulkRequestBody.toString()); try { Response response = client.performRequest(request); diff --git a/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/NoopPlugin.java b/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/NoopPlugin.java index e9d0fff2a9dc9..56bf91d1b2360 100644 --- a/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/NoopPlugin.java +++ b/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/NoopPlugin.java @@ -31,17 +31,17 @@ package org.opensearch.plugin.noop; -import org.opensearch.plugin.noop.action.bulk.NoopBulkAction; -import org.opensearch.plugin.noop.action.bulk.RestNoopBulkAction; -import org.opensearch.plugin.noop.action.bulk.TransportNoopBulkAction; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.plugin.noop.action.bulk.NoopBulkAction; +import org.opensearch.plugin.noop.action.bulk.RestNoopBulkAction; +import org.opensearch.plugin.noop.action.bulk.TransportNoopBulkAction; import org.opensearch.plugin.noop.action.search.NoopSearchAction; import org.opensearch.plugin.noop.action.search.RestNoopSearchAction; import org.opensearch.plugin.noop.action.search.TransportNoopSearchAction; diff --git a/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/bulk/RestNoopBulkAction.java b/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/bulk/RestNoopBulkAction.java index 1e94939bed7b5..8bd35a0bfed6a 100644 --- a/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/bulk/RestNoopBulkAction.java +++ b/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/bulk/RestNoopBulkAction.java @@ -40,8 +40,8 @@ import org.opensearch.action.update.UpdateResponse; import org.opensearch.client.Requests; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; @@ -54,9 +54,9 @@ import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; +import static org.opensearch.core.rest.RestStatus.OK; import static org.opensearch.rest.RestRequest.Method.POST; import static org.opensearch.rest.RestRequest.Method.PUT; -import static org.opensearch.rest.RestStatus.OK; public class RestNoopBulkAction extends BaseRestHandler { @@ -99,7 +99,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC defaultPipeline, defaultRequireAlias, true, - request.getXContentType() + request.getMediaType() ); // short circuit the call to the transport layer diff --git a/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/bulk/TransportNoopBulkAction.java b/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/bulk/TransportNoopBulkAction.java index 22dd96a0bdfc5..77d4d3d095b29 100644 --- a/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/bulk/TransportNoopBulkAction.java +++ b/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/bulk/TransportNoopBulkAction.java @@ -31,7 +31,6 @@ package org.opensearch.plugin.noop.action.bulk; -import org.opensearch.action.ActionListener; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.bulk.BulkItemResponse; @@ -41,7 +40,8 @@ import org.opensearch.action.support.HandledTransportAction; import org.opensearch.action.update.UpdateResponse; import org.opensearch.common.inject.Inject; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; diff --git a/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/search/TransportNoopSearchAction.java b/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/search/TransportNoopSearchAction.java index 77668a030ee7f..99efd31dfcaa5 100644 --- a/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/search/TransportNoopSearchAction.java +++ b/client/client-benchmark-noop-api-plugin/src/main/java/org/opensearch/plugin/noop/action/search/TransportNoopSearchAction.java @@ -32,14 +32,14 @@ package org.opensearch.plugin.noop.action.search; import org.apache.lucene.search.TotalHits; -import org.opensearch.action.ActionListener; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.aggregations.InternalAggregations; diff --git a/client/rest-high-level/build.gradle b/client/rest-high-level/build.gradle index 07147ce81b72e..cc8262ff6d9ad 100644 --- a/client/rest-high-level/build.gradle +++ b/client/rest-high-level/build.gradle @@ -37,8 +37,10 @@ apply plugin: 'opensearch.rest-test' apply plugin: 'opensearch.publish' apply plugin: 'opensearch.rest-resources' -group = 'org.opensearch.client' -archivesBaseName = 'opensearch-rest-high-level-client' +base { + group = 'org.opensearch.client' + archivesBaseName = 'opensearch-rest-high-level-client' +} restResources { //we need to copy the yaml spec so we can check naming (see RestHighlevelClientTests#testApiNamingConventions) @@ -103,4 +105,5 @@ testClusters.all { extraConfigFile nodeCert.name, nodeCert extraConfigFile nodeTrustStore.name, nodeTrustStore extraConfigFile pkiTrustCert.name, pkiTrustCert + } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/ClusterClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/ClusterClient.java index 10cfec9497862..5bd5a5d0e308e 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/ClusterClient.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/ClusterClient.java @@ -32,7 +32,6 @@ package org.opensearch.client; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.cluster.settings.ClusterGetSettingsRequest; @@ -47,7 +46,8 @@ import org.opensearch.client.indices.GetComponentTemplatesRequest; import org.opensearch.client.indices.GetComponentTemplatesResponse; import org.opensearch.client.indices.PutComponentTemplateRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/ClusterRequestConverters.java b/client/rest-high-level/src/main/java/org/opensearch/client/ClusterRequestConverters.java index 1cf52ac4169ba..8014eb946b144 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/ClusterRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/ClusterRequestConverters.java @@ -45,7 +45,7 @@ import org.opensearch.client.indices.DeleteComponentTemplateRequest; import org.opensearch.client.indices.GetComponentTemplatesRequest; import org.opensearch.client.indices.PutComponentTemplateRequest; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import java.io.IOException; @@ -58,7 +58,7 @@ static Request clusterPutSettings(ClusterUpdateSettingsRequest clusterUpdateSett RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(clusterUpdateSettingsRequest.timeout()); - parameters.withMasterTimeout(clusterUpdateSettingsRequest.masterNodeTimeout()); + parameters.withMasterTimeout(clusterUpdateSettingsRequest.clusterManagerNodeTimeout()); request.addParameters(parameters.asMap()); request.setEntity(RequestConverters.createEntity(clusterUpdateSettingsRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); return request; @@ -69,7 +69,7 @@ static Request clusterGetSettings(ClusterGetSettingsRequest clusterGetSettingsRe RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withLocal(clusterGetSettingsRequest.local()); parameters.withIncludeDefaults(clusterGetSettingsRequest.includeDefaults()); - parameters.withMasterTimeout(clusterGetSettingsRequest.masterNodeTimeout()); + parameters.withMasterTimeout(clusterGetSettingsRequest.clusterManagerNodeTimeout()); request.addParameters(parameters.asMap()); return request; } @@ -88,7 +88,7 @@ static Request clusterHealth(ClusterHealthRequest healthRequest) { .withWaitForNodes(healthRequest.waitForNodes()) .withWaitForEvents(healthRequest.waitForEvents()) .withTimeout(healthRequest.timeout()) - .withMasterTimeout(healthRequest.masterNodeTimeout()) + .withMasterTimeout(healthRequest.clusterManagerNodeTimeout()) .withLocal(healthRequest.local()) .withLevel(healthRequest.level()); request.addParameters(params.asMap()); @@ -105,7 +105,7 @@ static Request putComponentTemplate(PutComponentTemplateRequest putComponentTemp .build(); Request request = new Request(HttpPut.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(putComponentTemplateRequest.masterNodeTimeout()); + params.withMasterTimeout(putComponentTemplateRequest.clusterManagerNodeTimeout()); if (putComponentTemplateRequest.create()) { params.putParam("create", Boolean.TRUE.toString()); } @@ -146,7 +146,7 @@ static Request deleteComponentTemplate(DeleteComponentTemplateRequest deleteComp String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_component_template").addPathPart(name).build(); Request request = new Request(HttpDelete.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(deleteComponentTemplateRequest.masterNodeTimeout()); + params.withMasterTimeout(deleteComponentTemplateRequest.clusterManagerNodeTimeout()); request.addParameters(params.asMap()); return request; } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/GetAliasesResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/GetAliasesResponse.java index 81fd040ae6dee..c79be0a668896 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/GetAliasesResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/GetAliasesResponse.java @@ -35,11 +35,11 @@ import org.opensearch.OpenSearchException; import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import java.io.IOException; import java.util.Collections; @@ -48,7 +48,7 @@ import java.util.Map; import java.util.Set; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * Response obtained from the get aliases API. diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/IndicesClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/IndicesClient.java index 9b4586ec6bf89..281f020533d51 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/IndicesClient.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/IndicesClient.java @@ -32,7 +32,6 @@ package org.opensearch.client; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; import org.opensearch.action.admin.indices.alias.get.GetAliasesRequest; import org.opensearch.action.admin.indices.cache.clear.ClearIndicesCacheRequest; @@ -88,7 +87,8 @@ import org.opensearch.client.indices.SimulateIndexTemplateResponse; import org.opensearch.client.indices.rollover.RolloverRequest; import org.opensearch.client.indices.rollover.RolloverResponse; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; import java.util.Collections; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/IndicesRequestConverters.java b/client/rest-high-level/src/main/java/org/opensearch/client/IndicesRequestConverters.java index c50ea58982e4e..75a82a752281a 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/IndicesRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/IndicesRequestConverters.java @@ -52,28 +52,28 @@ import org.opensearch.action.admin.indices.validate.query.ValidateQueryRequest; import org.opensearch.client.indices.AnalyzeRequest; import org.opensearch.client.indices.CloseIndexRequest; +import org.opensearch.client.indices.ComposableIndexTemplateExistRequest; import org.opensearch.client.indices.CreateDataStreamRequest; import org.opensearch.client.indices.CreateIndexRequest; import org.opensearch.client.indices.DataStreamsStatsRequest; -import org.opensearch.client.indices.GetDataStreamRequest; import org.opensearch.client.indices.DeleteAliasRequest; import org.opensearch.client.indices.DeleteComposableIndexTemplateRequest; import org.opensearch.client.indices.DeleteDataStreamRequest; +import org.opensearch.client.indices.GetComposableIndexTemplateRequest; +import org.opensearch.client.indices.GetDataStreamRequest; import org.opensearch.client.indices.GetFieldMappingsRequest; import org.opensearch.client.indices.GetIndexRequest; -import org.opensearch.client.indices.GetComposableIndexTemplateRequest; import org.opensearch.client.indices.GetIndexTemplatesRequest; import org.opensearch.client.indices.GetMappingsRequest; -import org.opensearch.client.indices.ComposableIndexTemplateExistRequest; import org.opensearch.client.indices.IndexTemplatesExistRequest; -import org.opensearch.client.indices.PutIndexTemplateRequest; import org.opensearch.client.indices.PutComposableIndexTemplateRequest; +import org.opensearch.client.indices.PutIndexTemplateRequest; import org.opensearch.client.indices.PutMappingRequest; import org.opensearch.client.indices.ResizeRequest; import org.opensearch.client.indices.SimulateIndexTemplateRequest; import org.opensearch.client.indices.rollover.RolloverRequest; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import java.io.IOException; import java.util.Locale; @@ -119,7 +119,7 @@ static Request deleteIndex(DeleteIndexRequest deleteIndexRequest) { RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(deleteIndexRequest.timeout()); - parameters.withMasterTimeout(deleteIndexRequest.masterNodeTimeout()); + parameters.withMasterTimeout(deleteIndexRequest.clusterManagerNodeTimeout()); parameters.withIndicesOptions(deleteIndexRequest.indicesOptions()); request.addParameters(parameters.asMap()); return request; @@ -131,7 +131,7 @@ static Request openIndex(OpenIndexRequest openIndexRequest) { RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(openIndexRequest.timeout()); - parameters.withMasterTimeout(openIndexRequest.masterNodeTimeout()); + parameters.withMasterTimeout(openIndexRequest.clusterManagerNodeTimeout()); parameters.withWaitForActiveShards(openIndexRequest.waitForActiveShards()); parameters.withIndicesOptions(openIndexRequest.indicesOptions()); request.addParameters(parameters.asMap()); @@ -144,7 +144,7 @@ static Request closeIndex(CloseIndexRequest closeIndexRequest) { RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(closeIndexRequest.timeout()); - parameters.withMasterTimeout(closeIndexRequest.masterNodeTimeout()); + parameters.withMasterTimeout(closeIndexRequest.clusterManagerNodeTimeout()); parameters.withIndicesOptions(closeIndexRequest.indicesOptions()); request.addParameters(parameters.asMap()); return request; @@ -156,7 +156,7 @@ static Request createIndex(CreateIndexRequest createIndexRequest) throws IOExcep RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(createIndexRequest.timeout()); - parameters.withMasterTimeout(createIndexRequest.masterNodeTimeout()); + parameters.withMasterTimeout(createIndexRequest.clusterManagerNodeTimeout()); parameters.withWaitForActiveShards(createIndexRequest.waitForActiveShards()); request.addParameters(parameters.asMap()); request.setEntity(RequestConverters.createEntity(createIndexRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); @@ -168,7 +168,7 @@ static Request updateAliases(IndicesAliasesRequest indicesAliasesRequest) throws RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(indicesAliasesRequest.timeout()); - parameters.withMasterTimeout(indicesAliasesRequest.masterNodeTimeout()); + parameters.withMasterTimeout(indicesAliasesRequest.clusterManagerNodeTimeout()); request.addParameters(parameters.asMap()); request.setEntity(RequestConverters.createEntity(indicesAliasesRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); return request; @@ -179,7 +179,7 @@ static Request putMapping(PutMappingRequest putMappingRequest) throws IOExceptio RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(putMappingRequest.timeout()); - parameters.withMasterTimeout(putMappingRequest.masterNodeTimeout()); + parameters.withMasterTimeout(putMappingRequest.clusterManagerNodeTimeout()); parameters.withIndicesOptions(putMappingRequest.indicesOptions()); request.addParameters(parameters.asMap()); request.setEntity(RequestConverters.createEntity(putMappingRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); @@ -192,7 +192,7 @@ static Request getMappings(GetMappingsRequest getMappingsRequest) { Request request = new Request(HttpGet.METHOD_NAME, RequestConverters.endpoint(indices, "_mapping")); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(getMappingsRequest.masterNodeTimeout()); + parameters.withMasterTimeout(getMappingsRequest.clusterManagerNodeTimeout()); parameters.withIndicesOptions(getMappingsRequest.indicesOptions()); parameters.withLocal(getMappingsRequest.local()); request.addParameters(parameters.asMap()); @@ -261,6 +261,7 @@ static Request clearCache(ClearIndicesCacheRequest clearIndicesCacheRequest) { parameters.withIndicesOptions(clearIndicesCacheRequest.indicesOptions()); parameters.putParam("query", Boolean.toString(clearIndicesCacheRequest.queryCache())); parameters.putParam("fielddata", Boolean.toString(clearIndicesCacheRequest.fieldDataCache())); + parameters.putParam("file", Boolean.toString(clearIndicesCacheRequest.fileCache())); parameters.putParam("request", Boolean.toString(clearIndicesCacheRequest.requestCache())); parameters.putParam("fields", String.join(",", clearIndicesCacheRequest.fields())); request.addParameters(parameters.asMap()); @@ -332,7 +333,7 @@ private static Request resize(ResizeRequest resizeRequest, ResizeType type) thro RequestConverters.Params params = new RequestConverters.Params(); params.withTimeout(resizeRequest.timeout()); - params.withMasterTimeout(resizeRequest.masterNodeTimeout()); + params.withMasterTimeout(resizeRequest.clusterManagerNodeTimeout()); params.withWaitForActiveShards(resizeRequest.getWaitForActiveShards()); request.addParameters(params.asMap()); request.setEntity(RequestConverters.createEntity(resizeRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); @@ -349,7 +350,7 @@ private static Request resize(org.opensearch.action.admin.indices.shrink.ResizeR RequestConverters.Params params = new RequestConverters.Params(); params.withTimeout(resizeRequest.timeout()); - params.withMasterTimeout(resizeRequest.masterNodeTimeout()); + params.withMasterTimeout(resizeRequest.clusterManagerNodeTimeout()); params.withWaitForActiveShards(resizeRequest.getTargetIndexRequest().waitForActiveShards()); request.addParameters(params.asMap()); request.setEntity(RequestConverters.createEntity(resizeRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); @@ -365,7 +366,7 @@ static Request rollover(RolloverRequest rolloverRequest) throws IOException { RequestConverters.Params params = new RequestConverters.Params(); params.withTimeout(rolloverRequest.timeout()); - params.withMasterTimeout(rolloverRequest.masterNodeTimeout()); + params.withMasterTimeout(rolloverRequest.clusterManagerNodeTimeout()); params.withWaitForActiveShards(rolloverRequest.getCreateIndexRequest().waitForActiveShards()); if (rolloverRequest.isDryRun()) { params.putParam("dry_run", Boolean.TRUE.toString()); @@ -386,7 +387,7 @@ static Request getSettings(GetSettingsRequest getSettingsRequest) { params.withIndicesOptions(getSettingsRequest.indicesOptions()); params.withLocal(getSettingsRequest.local()); params.withIncludeDefaults(getSettingsRequest.includeDefaults()); - params.withMasterTimeout(getSettingsRequest.masterNodeTimeout()); + params.withMasterTimeout(getSettingsRequest.clusterManagerNodeTimeout()); request.addParameters(params.asMap()); return request; } @@ -402,7 +403,7 @@ static Request getIndex(GetIndexRequest getIndexRequest) { params.withLocal(getIndexRequest.local()); params.withIncludeDefaults(getIndexRequest.includeDefaults()); params.withHuman(getIndexRequest.humanReadable()); - params.withMasterTimeout(getIndexRequest.masterNodeTimeout()); + params.withMasterTimeout(getIndexRequest.clusterManagerNodeTimeout()); request.addParameters(params.asMap()); return request; } @@ -429,7 +430,7 @@ static Request indexPutSettings(UpdateSettingsRequest updateSettingsRequest) thr RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(updateSettingsRequest.timeout()); - parameters.withMasterTimeout(updateSettingsRequest.masterNodeTimeout()); + parameters.withMasterTimeout(updateSettingsRequest.clusterManagerNodeTimeout()); parameters.withIndicesOptions(updateSettingsRequest.indicesOptions()); parameters.withPreserveExisting(updateSettingsRequest.isPreserveExisting()); request.addParameters(parameters.asMap()); @@ -443,7 +444,7 @@ static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) thro .build(); Request request = new Request(HttpPut.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(putIndexTemplateRequest.masterNodeTimeout()); + params.withMasterTimeout(putIndexTemplateRequest.clusterManagerNodeTimeout()); if (putIndexTemplateRequest.create()) { params.putParam("create", Boolean.TRUE.toString()); } @@ -461,7 +462,7 @@ static Request putIndexTemplate(PutComposableIndexTemplateRequest putIndexTempla .build(); Request request = new Request(HttpPut.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(putIndexTemplateRequest.masterNodeTimeout()); + params.withMasterTimeout(putIndexTemplateRequest.clusterManagerNodeTimeout()); if (putIndexTemplateRequest.create()) { params.putParam("create", Boolean.TRUE.toString()); } @@ -479,7 +480,7 @@ static Request simulateIndexTemplate(SimulateIndexTemplateRequest simulateIndexT .build(); Request request = new Request(HttpPost.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(simulateIndexTemplateRequest.masterNodeTimeout()); + params.withMasterTimeout(simulateIndexTemplateRequest.clusterManagerNodeTimeout()); PutComposableIndexTemplateRequest putComposableIndexTemplateRequest = simulateIndexTemplateRequest.indexTemplateV2Request(); if (putComposableIndexTemplateRequest != null) { if (putComposableIndexTemplateRequest.create()) { @@ -587,7 +588,7 @@ static Request deleteTemplate(DeleteIndexTemplateRequest deleteIndexTemplateRequ String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_template").addPathPart(name).build(); Request request = new Request(HttpDelete.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(deleteIndexTemplateRequest.masterNodeTimeout()); + params.withMasterTimeout(deleteIndexTemplateRequest.clusterManagerNodeTimeout()); request.addParameters(params.asMap()); return request; } @@ -597,7 +598,7 @@ static Request deleteIndexTemplate(DeleteComposableIndexTemplateRequest deleteIn String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_index_template").addPathPart(name).build(); Request request = new Request(HttpDelete.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(deleteIndexTemplateRequest.masterNodeTimeout()); + params.withMasterTimeout(deleteIndexTemplateRequest.clusterManagerNodeTimeout()); request.addParameters(params.asMap()); return request; } @@ -610,7 +611,7 @@ static Request deleteAlias(DeleteAliasRequest deleteAliasRequest) { Request request = new Request(HttpDelete.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(deleteAliasRequest.timeout()); - parameters.withMasterTimeout(deleteAliasRequest.masterNodeTimeout()); + parameters.withMasterTimeout(deleteAliasRequest.clusterManagerNodeTimeout()); request.addParameters(parameters.asMap()); return request; } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/IngestClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/IngestClient.java index cd304019e771c..29e5c5369f184 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/IngestClient.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/IngestClient.java @@ -32,7 +32,6 @@ package org.opensearch.client; -import org.opensearch.action.ActionListener; import org.opensearch.action.ingest.DeletePipelineRequest; import org.opensearch.action.ingest.GetPipelineRequest; import org.opensearch.action.ingest.GetPipelineResponse; @@ -40,6 +39,7 @@ import org.opensearch.action.ingest.SimulatePipelineRequest; import org.opensearch.action.ingest.SimulatePipelineResponse; import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.action.ActionListener; import java.io.IOException; import java.util.Collections; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/IngestRequestConverters.java b/client/rest-high-level/src/main/java/org/opensearch/client/IngestRequestConverters.java index e2ede61f38ee9..a80a2b57fe2f7 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/IngestRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/IngestRequestConverters.java @@ -54,7 +54,7 @@ static Request getPipeline(GetPipelineRequest getPipelineRequest) { Request request = new Request(HttpGet.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(getPipelineRequest.masterNodeTimeout()); + parameters.withMasterTimeout(getPipelineRequest.clusterManagerNodeTimeout()); request.addParameters(parameters.asMap()); return request; } @@ -67,7 +67,7 @@ static Request putPipeline(PutPipelineRequest putPipelineRequest) throws IOExcep RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(putPipelineRequest.timeout()); - parameters.withMasterTimeout(putPipelineRequest.masterNodeTimeout()); + parameters.withMasterTimeout(putPipelineRequest.clusterManagerNodeTimeout()); request.addParameters(parameters.asMap()); request.setEntity(RequestConverters.createEntity(putPipelineRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); return request; @@ -81,7 +81,7 @@ static Request deletePipeline(DeletePipelineRequest deletePipelineRequest) { RequestConverters.Params parameters = new RequestConverters.Params(); parameters.withTimeout(deletePipelineRequest.timeout()); - parameters.withMasterTimeout(deletePipelineRequest.masterNodeTimeout()); + parameters.withMasterTimeout(deletePipelineRequest.clusterManagerNodeTimeout()); request.addParameters(parameters.asMap()); return request; } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/NodesResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/NodesResponse.java index 0f05f3e360232..589ab92a6de39 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/NodesResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/NodesResponse.java @@ -32,8 +32,8 @@ package org.opensearch.client; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; /** * Base class for responses that are node responses. These responses always contain the cluster diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/NodesResponseHeader.java b/client/rest-high-level/src/main/java/org/opensearch/client/NodesResponseHeader.java index 7b09467798606..3b2e7ce7edd00 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/NodesResponseHeader.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/NodesResponseHeader.java @@ -34,11 +34,11 @@ import org.opensearch.OpenSearchException; import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.rest.action.RestActions; import java.io.IOException; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/opensearch/client/RequestConverters.java index 3e43963db519f..0dd74e663ba71 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/RequestConverters.java @@ -54,6 +54,8 @@ import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.ClearScrollRequest; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.DeletePitRequest; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchScrollRequest; @@ -70,20 +72,21 @@ import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Nullable; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.CollectionUtils; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.rankeval.RankEvalRequest; @@ -92,6 +95,7 @@ import org.opensearch.index.reindex.ReindexRequest; import org.opensearch.index.reindex.UpdateByQueryRequest; import org.opensearch.index.seqno.SequenceNumbers; +import org.opensearch.rest.action.search.RestCreatePitAction; import org.opensearch.rest.action.search.RestSearchAction; import org.opensearch.script.mustache.MultiSearchTemplateRequest; import org.opensearch.script.mustache.SearchTemplateRequest; @@ -109,8 +113,13 @@ import java.util.Map; import java.util.StringJoiner; +/** + * Converts OpenSearch writeable requests to an HTTP Request + * + * @opensearch.api + */ final class RequestConverters { - static final XContentType REQUEST_BODY_CONTENT_TYPE = XContentType.JSON; + static final MediaType REQUEST_BODY_CONTENT_TYPE = MediaTypeRegistry.JSON; private RequestConverters() { // Contains only status utility methods @@ -148,7 +157,7 @@ static Request bulk(BulkRequest bulkRequest) throws IOException { // Bulk API only supports newline delimited JSON or Smile. Before executing // the bulk, we need to check that all requests have the same content-type // and this content-type is supported by the Bulk API. - XContentType bulkContentType = null; + MediaType bulkContentType = null; for (int i = 0; i < bulkRequest.numberOfActions(); i++) { DocWriteRequest action = bulkRequest.requests().get(i); @@ -168,7 +177,7 @@ static Request bulk(BulkRequest bulkRequest) throws IOException { } if (bulkContentType == null) { - bulkContentType = XContentType.JSON; + bulkContentType = MediaTypeRegistry.JSON; } final byte separator = bulkContentType.xContent().streamSeparator(); @@ -236,7 +245,7 @@ static Request bulk(BulkRequest bulkRequest) throws IOException { if (opType == DocWriteRequest.OpType.INDEX || opType == DocWriteRequest.OpType.CREATE) { IndexRequest indexRequest = (IndexRequest) action; BytesReference indexSource = indexRequest.source(); - XContentType indexXContentType = indexRequest.getContentType(); + MediaType mediaType = indexRequest.getContentType(); try ( XContentParser parser = XContentHelper.createParser( @@ -248,7 +257,7 @@ static Request bulk(BulkRequest bulkRequest) throws IOException { NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, indexSource, - indexXContentType + mediaType ) ) { try (XContentBuilder builder = XContentBuilder.builder(bulkContentType.xContent())) { @@ -257,7 +266,12 @@ static Request bulk(BulkRequest bulkRequest) throws IOException { } } } else if (opType == DocWriteRequest.OpType.UPDATE) { - source = XContentHelper.toXContent((UpdateRequest) action, bulkContentType, false).toBytesRef(); + source = org.opensearch.core.xcontent.XContentHelper.toXContent( + (UpdateRequest) action, + bulkContentType, + ToXContent.EMPTY_PARAMS, + false + ).toBytesRef(); } if (source != null) { @@ -383,30 +397,30 @@ static Request update(UpdateRequest updateRequest) throws IOException { // set for the partial document and the upsert document. This client // only accepts update requests that have the same content types set // for both doc and upsert. - XContentType xContentType = null; + MediaType mediaType = null; if (updateRequest.doc() != null) { - xContentType = updateRequest.doc().getContentType(); + mediaType = updateRequest.doc().getContentType(); } if (updateRequest.upsertRequest() != null) { - XContentType upsertContentType = updateRequest.upsertRequest().getContentType(); - if ((xContentType != null) && (xContentType != upsertContentType)) { + MediaType upsertContentType = updateRequest.upsertRequest().getContentType(); + if ((mediaType != null) && (mediaType != upsertContentType)) { throw new IllegalStateException( "Update request cannot have different content types for doc [" - + xContentType + + mediaType + "]" + " and upsert [" + upsertContentType + "] documents" ); } else { - xContentType = upsertContentType; + mediaType = upsertContentType; } } - if (xContentType == null) { - xContentType = Requests.INDEX_CONTENT_TYPE; + if (mediaType == null) { + mediaType = Requests.INDEX_CONTENT_TYPE; } request.addParameters(parameters.asMap()); - request.setEntity(createEntity(updateRequest, xContentType)); + request.setEntity(createEntity(updateRequest, mediaType)); return request; } @@ -433,9 +447,19 @@ static void addSearchRequestParams(Params params, SearchRequest searchRequest) { params.putParam(RestSearchAction.TYPED_KEYS_PARAM, "true"); params.withRouting(searchRequest.routing()); params.withPreference(searchRequest.preference()); - params.withIndicesOptions(searchRequest.indicesOptions()); + if (searchRequest.pointInTimeBuilder() == null) { + params.withIndicesOptions(searchRequest.indicesOptions()); + } params.withSearchType(searchRequest.searchType().name().toLowerCase(Locale.ROOT)); - params.putParam("ccs_minimize_roundtrips", Boolean.toString(searchRequest.isCcsMinimizeRoundtrips())); + /** + * Merging search responses as part of CCS flow to reduce roundtrips is not supported for point in time - + * refer to org.opensearch.action.search.SearchResponseMerger + */ + if (searchRequest.pointInTimeBuilder() != null) { + params.putParam("ccs_minimize_roundtrips", "false"); + } else { + params.putParam("ccs_minimize_roundtrips", Boolean.toString(searchRequest.isCcsMinimizeRoundtrips())); + } if (searchRequest.getPreFilterShardSize() != null) { params.putParam("pre_filter_shard_size", Integer.toString(searchRequest.getPreFilterShardSize())); } @@ -464,6 +488,31 @@ static Request clearScroll(ClearScrollRequest clearScrollRequest) throws IOExcep return request; } + static Request createPit(CreatePitRequest createPitRequest) throws IOException { + Params params = new Params(); + params.putParam(RestCreatePitAction.ALLOW_PARTIAL_PIT_CREATION, Boolean.toString(createPitRequest.shouldAllowPartialPitCreation())); + params.putParam(RestCreatePitAction.KEEP_ALIVE, createPitRequest.getKeepAlive()); + params.withIndicesOptions(createPitRequest.indicesOptions()); + Request request = new Request(HttpPost.METHOD_NAME, endpoint(createPitRequest.indices(), "_search/point_in_time")); + request.addParameters(params.asMap()); + request.setEntity(createEntity(createPitRequest, REQUEST_BODY_CONTENT_TYPE)); + return request; + } + + static Request deletePit(DeletePitRequest deletePitRequest) throws IOException { + Request request = new Request(HttpDelete.METHOD_NAME, "/_search/point_in_time"); + request.setEntity(createEntity(deletePitRequest, REQUEST_BODY_CONTENT_TYPE)); + return request; + } + + static Request deleteAllPits() { + return new Request(HttpDelete.METHOD_NAME, "/_search/point_in_time/_all"); + } + + static Request getAllPits() { + return new Request(HttpGet.METHOD_NAME, "/_search/point_in_time/_all"); + } + static Request multiSearch(MultiSearchRequest multiSearchRequest) throws IOException { Request request = new Request(HttpPost.METHOD_NAME, "/_msearch"); @@ -476,7 +525,7 @@ static Request multiSearch(MultiSearchRequest multiSearchRequest) throws IOExcep XContent xContent = REQUEST_BODY_CONTENT_TYPE.xContent(); byte[] source = MultiSearchRequest.writeMultiLineFormat(multiSearchRequest, xContent); request.addParameters(params.asMap()); - request.setEntity(new NByteArrayEntity(source, createContentType(xContent.type()))); + request.setEntity(new NByteArrayEntity(source, createContentType(xContent.mediaType()))); return request; } @@ -511,7 +560,7 @@ static Request multiSearchTemplate(MultiSearchTemplateRequest multiSearchTemplat XContent xContent = REQUEST_BODY_CONTENT_TYPE.xContent(); byte[] source = MultiSearchTemplateRequest.writeMultiLineFormat(multiSearchTemplateRequest, xContent); - request.setEntity(new NByteArrayEntity(source, createContentType(xContent.type()))); + request.setEntity(new NByteArrayEntity(source, createContentType(xContent.mediaType()))); return request; } @@ -702,7 +751,7 @@ static Request putScript(PutStoredScriptRequest putStoredScriptRequest) throws I Request request = new Request(HttpPost.METHOD_NAME, endpoint); Params params = new Params(); params.withTimeout(putStoredScriptRequest.timeout()); - params.withMasterTimeout(putStoredScriptRequest.masterNodeTimeout()); + params.withMasterTimeout(putStoredScriptRequest.clusterManagerNodeTimeout()); if (Strings.hasText(putStoredScriptRequest.context())) { params.putParam("context", putStoredScriptRequest.context()); } @@ -757,7 +806,7 @@ static Request getScript(GetStoredScriptRequest getStoredScriptRequest) { String endpoint = new EndpointBuilder().addPathPartAsIs("_scripts").addPathPart(getStoredScriptRequest.id()).build(); Request request = new Request(HttpGet.METHOD_NAME, endpoint); Params params = new Params(); - params.withMasterTimeout(getStoredScriptRequest.masterNodeTimeout()); + params.withMasterTimeout(getStoredScriptRequest.clusterManagerNodeTimeout()); request.addParameters(params.asMap()); return request; } @@ -767,19 +816,19 @@ static Request deleteScript(DeleteStoredScriptRequest deleteStoredScriptRequest) Request request = new Request(HttpDelete.METHOD_NAME, endpoint); Params params = new Params(); params.withTimeout(deleteStoredScriptRequest.timeout()); - params.withMasterTimeout(deleteStoredScriptRequest.masterNodeTimeout()); + params.withMasterTimeout(deleteStoredScriptRequest.clusterManagerNodeTimeout()); request.addParameters(params.asMap()); return request; } - static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException { - return createEntity(toXContent, xContentType, ToXContent.EMPTY_PARAMS); + static HttpEntity createEntity(ToXContent toXContent, MediaType mediaType) throws IOException { + return createEntity(toXContent, mediaType, ToXContent.EMPTY_PARAMS); } - static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType, ToXContent.Params toXContentParams) - throws IOException { - BytesRef source = XContentHelper.toXContent(toXContent, xContentType, toXContentParams, false).toBytesRef(); - return new NByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType)); + static HttpEntity createEntity(ToXContent toXContent, MediaType mediaType, ToXContent.Params toXContentParams) throws IOException { + BytesRef source = org.opensearch.core.xcontent.XContentHelper.toXContent(toXContent, mediaType, toXContentParams, false) + .toBytesRef(); + return new NByteArrayEntity(source.bytes, source.offset, source.length, createContentType(mediaType)); } static String endpoint(String index, String id) { @@ -825,14 +874,14 @@ static String endpoint(String[] indices, String endpoint, String type) { } /** - * Returns a {@link ContentType} from a given {@link XContentType}. + * Returns a {@link ContentType} from a given {@link MediaType}. * - * @param xContentType the {@link XContentType} + * @param mediaType the {@link MediaType} * @return the {@link ContentType} */ - @SuppressForbidden(reason = "Only allowed place to convert a XContentType to a ContentType") - public static ContentType createContentType(final XContentType xContentType) { - return ContentType.create(xContentType.mediaTypeWithoutParameters(), (Charset) null); + @SuppressForbidden(reason = "Only allowed place to convert a MediaType to a ContentType") + public static ContentType createContentType(final MediaType mediaType) { + return ContentType.create(mediaType.mediaTypeWithoutParameters(), (Charset) null); } /** @@ -1128,14 +1177,14 @@ Params withActions(List actions) { return this; } - Params withTaskId(org.opensearch.tasks.TaskId taskId) { + Params withTaskId(org.opensearch.core.tasks.TaskId taskId) { if (taskId != null && taskId.isSet()) { return putParam("task_id", taskId.toString()); } return this; } - Params withParentTaskId(org.opensearch.tasks.TaskId parentTaskId) { + Params withParentTaskId(org.opensearch.core.tasks.TaskId parentTaskId) { if (parentTaskId != null && parentTaskId.isSet()) { return putParam("parent_task_id", parentTaskId.toString()); } @@ -1199,28 +1248,28 @@ Params withWaitForEvents(Priority waitForEvents) { * * @return the {@link IndexRequest}'s content type */ - static XContentType enforceSameContentType(IndexRequest indexRequest, @Nullable XContentType xContentType) { - XContentType requestContentType = indexRequest.getContentType(); - if (requestContentType != XContentType.JSON && requestContentType != XContentType.SMILE) { + static MediaType enforceSameContentType(IndexRequest indexRequest, @Nullable MediaType mediaType) { + MediaType requestContentType = indexRequest.getContentType(); + if (requestContentType != MediaTypeRegistry.JSON && requestContentType != MediaTypeRegistry.fromFormat("smile")) { throw new IllegalArgumentException( "Unsupported content-type found for request with content-type [" + requestContentType + "], only JSON and SMILE are supported" ); } - if (xContentType == null) { + if (mediaType == null) { return requestContentType; } - if (requestContentType != xContentType) { + if (requestContentType != mediaType) { throw new IllegalArgumentException( "Mismatching content-type found for request with content-type [" + requestContentType + "], previous requests have content-type [" - + xContentType + + mediaType + "]" ); } - return xContentType; + return mediaType; } /** diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java index 3eebb361fd9c4..94303097c772d 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/RestHighLevelClient.java @@ -35,7 +35,6 @@ import org.apache.http.HttpEntity; import org.opensearch.OpenSearchException; import org.opensearch.OpenSearchStatusException; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; @@ -59,6 +58,11 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.action.search.ClearScrollRequest; import org.opensearch.action.search.ClearScrollResponse; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.action.search.GetAllPitNodesResponse; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.SearchRequest; @@ -80,12 +84,14 @@ import org.opensearch.client.tasks.TaskSubmissionResponse; import org.opensearch.common.CheckedConsumer; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ContextParser; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.rankeval.RankEvalRequest; import org.opensearch.index.rankeval.RankEvalResponse; import org.opensearch.index.reindex.BulkByScrollResponse; @@ -94,7 +100,6 @@ import org.opensearch.index.reindex.UpdateByQueryRequest; import org.opensearch.plugins.spi.NamedXContentProvider; import org.opensearch.rest.BytesRestResponse; -import org.opensearch.rest.RestStatus; import org.opensearch.script.mustache.MultiSearchTemplateRequest; import org.opensearch.script.mustache.MultiSearchTemplateResponse; import org.opensearch.script.mustache.SearchTemplateRequest; @@ -108,10 +113,6 @@ import org.opensearch.search.aggregations.bucket.filter.FiltersAggregationBuilder; import org.opensearch.search.aggregations.bucket.filter.ParsedFilter; import org.opensearch.search.aggregations.bucket.filter.ParsedFilters; -import org.opensearch.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.ParsedGeoHashGrid; -import org.opensearch.search.aggregations.bucket.geogrid.ParsedGeoTileGrid; import org.opensearch.search.aggregations.bucket.global.GlobalAggregationBuilder; import org.opensearch.search.aggregations.bucket.global.ParsedGlobal; import org.opensearch.search.aggregations.bucket.histogram.AutoDateHistogramAggregationBuilder; @@ -138,24 +139,27 @@ import org.opensearch.search.aggregations.bucket.range.RangeAggregationBuilder; import org.opensearch.search.aggregations.bucket.sampler.InternalSampler; import org.opensearch.search.aggregations.bucket.sampler.ParsedSampler; +import org.opensearch.search.aggregations.bucket.terms.DoubleTerms; import org.opensearch.search.aggregations.bucket.terms.LongRareTerms; +import org.opensearch.search.aggregations.bucket.terms.LongTerms; +import org.opensearch.search.aggregations.bucket.terms.MultiTermsAggregationBuilder; +import org.opensearch.search.aggregations.bucket.terms.ParsedDoubleTerms; import org.opensearch.search.aggregations.bucket.terms.ParsedLongRareTerms; +import org.opensearch.search.aggregations.bucket.terms.ParsedLongTerms; +import org.opensearch.search.aggregations.bucket.terms.ParsedMultiTerms; import org.opensearch.search.aggregations.bucket.terms.ParsedSignificantLongTerms; import org.opensearch.search.aggregations.bucket.terms.ParsedSignificantStringTerms; import org.opensearch.search.aggregations.bucket.terms.ParsedStringRareTerms; +import org.opensearch.search.aggregations.bucket.terms.ParsedStringTerms; +import org.opensearch.search.aggregations.bucket.terms.ParsedUnsignedLongTerms; import org.opensearch.search.aggregations.bucket.terms.SignificantLongTerms; import org.opensearch.search.aggregations.bucket.terms.SignificantStringTerms; -import org.opensearch.search.aggregations.bucket.terms.DoubleTerms; -import org.opensearch.search.aggregations.bucket.terms.LongTerms; -import org.opensearch.search.aggregations.bucket.terms.ParsedDoubleTerms; -import org.opensearch.search.aggregations.bucket.terms.ParsedLongTerms; -import org.opensearch.search.aggregations.bucket.terms.ParsedStringTerms; import org.opensearch.search.aggregations.bucket.terms.StringRareTerms; import org.opensearch.search.aggregations.bucket.terms.StringTerms; +import org.opensearch.search.aggregations.bucket.terms.UnsignedLongTerms; import org.opensearch.search.aggregations.metrics.AvgAggregationBuilder; import org.opensearch.search.aggregations.metrics.CardinalityAggregationBuilder; import org.opensearch.search.aggregations.metrics.ExtendedStatsAggregationBuilder; -import org.opensearch.search.aggregations.metrics.GeoBoundsAggregationBuilder; import org.opensearch.search.aggregations.metrics.GeoCentroidAggregationBuilder; import org.opensearch.search.aggregations.metrics.InternalHDRPercentileRanks; import org.opensearch.search.aggregations.metrics.InternalHDRPercentiles; @@ -167,7 +171,6 @@ import org.opensearch.search.aggregations.metrics.ParsedAvg; import org.opensearch.search.aggregations.metrics.ParsedCardinality; import org.opensearch.search.aggregations.metrics.ParsedExtendedStats; -import org.opensearch.search.aggregations.metrics.ParsedGeoBounds; import org.opensearch.search.aggregations.metrics.ParsedGeoCentroid; import org.opensearch.search.aggregations.metrics.ParsedHDRPercentileRanks; import org.opensearch.search.aggregations.metrics.ParsedHDRPercentiles; @@ -267,6 +270,7 @@ public class RestHighLevelClient implements Closeable { private final IngestClient ingestClient = new IngestClient(this); private final SnapshotClient snapshotClient = new SnapshotClient(this); private final TasksClient tasksClient = new TasksClient(this); + private final SearchPipelineClient searchPipelineClient = new SearchPipelineClient(this); /** * Creates a {@link RestHighLevelClient} given the low level {@link RestClientBuilder} that allows to build the @@ -353,6 +357,10 @@ public final TasksClient tasks() { return tasksClient; } + public final SearchPipelineClient searchPipeline() { + return searchPipelineClient; + } + /** * Executes a bulk request using the Bulk API. * @param bulkRequest the request @@ -1254,6 +1262,154 @@ public final Cancellable scrollAsync( ); } + /** + * Create PIT context using create PIT API + * + * @param createPitRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + */ + public final CreatePitResponse createPit(CreatePitRequest createPitRequest, RequestOptions options) throws IOException { + return performRequestAndParseEntity( + createPitRequest, + RequestConverters::createPit, + options, + CreatePitResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously Create PIT context using create PIT API + * + * @param createPitRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return the response + */ + public final Cancellable createPitAsync( + CreatePitRequest createPitRequest, + RequestOptions options, + ActionListener listener + ) { + return performRequestAsyncAndParseEntity( + createPitRequest, + RequestConverters::createPit, + options, + CreatePitResponse::fromXContent, + listener, + emptySet() + ); + } + + /** + * Delete point in time searches using delete PIT API + * + * @param deletePitRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + */ + public final DeletePitResponse deletePit(DeletePitRequest deletePitRequest, RequestOptions options) throws IOException { + return performRequestAndParseEntity( + deletePitRequest, + RequestConverters::deletePit, + options, + DeletePitResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously Delete point in time searches using delete PIT API + * + * @param deletePitRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return the response + */ + public final Cancellable deletePitAsync( + DeletePitRequest deletePitRequest, + RequestOptions options, + ActionListener listener + ) { + return performRequestAsyncAndParseEntity( + deletePitRequest, + RequestConverters::deletePit, + options, + DeletePitResponse::fromXContent, + listener, + emptySet() + ); + } + + /** + * Delete all point in time searches using delete all PITs API + * + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + */ + public final DeletePitResponse deleteAllPits(RequestOptions options) throws IOException { + return performRequestAndParseEntity( + new MainRequest(), + (request) -> RequestConverters.deleteAllPits(), + options, + DeletePitResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously Delete all point in time searches using delete all PITs API + * + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return the response + */ + public final Cancellable deleteAllPitsAsync(RequestOptions options, ActionListener listener) { + return performRequestAsyncAndParseEntity( + new MainRequest(), + (request) -> RequestConverters.deleteAllPits(), + options, + DeletePitResponse::fromXContent, + listener, + emptySet() + ); + } + + /** + * Get all point in time searches using list all PITs API + * + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + */ + public final GetAllPitNodesResponse getAllPits(RequestOptions options) throws IOException { + return performRequestAndParseEntity( + new MainRequest(), + (request) -> RequestConverters.getAllPits(), + options, + GetAllPitNodesResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously get all point in time searches using list all PITs API + * + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return the response + */ + public final Cancellable getAllPitsAsync(RequestOptions options, ActionListener listener) { + return performRequestAsyncAndParseEntity( + new MainRequest(), + (request) -> RequestConverters.getAllPits(), + options, + GetAllPitNodesResponse::fromXContent, + listener, + emptySet() + ); + } + /** * Clears one or more scroll ids using the Clear Scroll API. * @@ -1915,6 +2071,10 @@ private Cancellable internalPerformRequestAsync( ActionListener listener, Set ignores ) { + if (listener == null) { + throw new IllegalArgumentException("The listener is required and cannot be null"); + } + Request req; try { req = requestConverter.apply(request); @@ -2067,11 +2227,11 @@ protected final Resp parseEntity(final HttpEntity entity, final CheckedFu if (entity.getContentType() == null) { throw new IllegalStateException("OpenSearch didn't return the [Content-Type] header, unable to parse response body"); } - XContentType xContentType = XContentType.fromMediaTypeOrFormat(entity.getContentType().getValue()); - if (xContentType == null) { + MediaType medaiType = MediaType.fromMediaType(entity.getContentType().getValue()); + if (medaiType == null) { throw new IllegalStateException("Unsupported Content-Type: " + entity.getContentType().getValue()); } - try (XContentParser parser = xContentType.xContent().createParser(registry, DEPRECATION_HANDLER, entity.getContent())) { + try (XContentParser parser = medaiType.xContent().createParser(registry, DEPRECATION_HANDLER, entity.getContent())) { return entityParser.apply(parser); } } @@ -2110,13 +2270,13 @@ static List getDefaultNamedXContents() { map.put(StatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedStatsBucket.fromXContent(p, (String) c)); map.put(ExtendedStatsAggregationBuilder.NAME, (p, c) -> ParsedExtendedStats.fromXContent(p, (String) c)); map.put(ExtendedStatsBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedExtendedStatsBucket.fromXContent(p, (String) c)); - map.put(GeoBoundsAggregationBuilder.NAME, (p, c) -> ParsedGeoBounds.fromXContent(p, (String) c)); map.put(GeoCentroidAggregationBuilder.NAME, (p, c) -> ParsedGeoCentroid.fromXContent(p, (String) c)); map.put(HistogramAggregationBuilder.NAME, (p, c) -> ParsedHistogram.fromXContent(p, (String) c)); map.put(DateHistogramAggregationBuilder.NAME, (p, c) -> ParsedDateHistogram.fromXContent(p, (String) c)); map.put(AutoDateHistogramAggregationBuilder.NAME, (p, c) -> ParsedAutoDateHistogram.fromXContent(p, (String) c)); map.put(VariableWidthHistogramAggregationBuilder.NAME, (p, c) -> ParsedVariableWidthHistogram.fromXContent(p, (String) c)); map.put(StringTerms.NAME, (p, c) -> ParsedStringTerms.fromXContent(p, (String) c)); + map.put(UnsignedLongTerms.NAME, (p, c) -> ParsedUnsignedLongTerms.fromXContent(p, (String) c)); map.put(LongTerms.NAME, (p, c) -> ParsedLongTerms.fromXContent(p, (String) c)); map.put(DoubleTerms.NAME, (p, c) -> ParsedDoubleTerms.fromXContent(p, (String) c)); map.put(LongRareTerms.NAME, (p, c) -> ParsedLongRareTerms.fromXContent(p, (String) c)); @@ -2127,8 +2287,6 @@ static List getDefaultNamedXContents() { map.put(GlobalAggregationBuilder.NAME, (p, c) -> ParsedGlobal.fromXContent(p, (String) c)); map.put(FilterAggregationBuilder.NAME, (p, c) -> ParsedFilter.fromXContent(p, (String) c)); map.put(InternalSampler.PARSER_NAME, (p, c) -> ParsedSampler.fromXContent(p, (String) c)); - map.put(GeoHashGridAggregationBuilder.NAME, (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c)); - map.put(GeoTileGridAggregationBuilder.NAME, (p, c) -> ParsedGeoTileGrid.fromXContent(p, (String) c)); map.put(RangeAggregationBuilder.NAME, (p, c) -> ParsedRange.fromXContent(p, (String) c)); map.put(DateRangeAggregationBuilder.NAME, (p, c) -> ParsedDateRange.fromXContent(p, (String) c)); map.put(GeoDistanceAggregationBuilder.NAME, (p, c) -> ParsedGeoDistance.fromXContent(p, (String) c)); @@ -2140,6 +2298,7 @@ static List getDefaultNamedXContents() { map.put(IpRangeAggregationBuilder.NAME, (p, c) -> ParsedBinaryRange.fromXContent(p, (String) c)); map.put(TopHitsAggregationBuilder.NAME, (p, c) -> ParsedTopHits.fromXContent(p, (String) c)); map.put(CompositeAggregationBuilder.NAME, (p, c) -> ParsedComposite.fromXContent(p, (String) c)); + map.put(MultiTermsAggregationBuilder.NAME, (p, c) -> ParsedMultiTerms.fromXContent(p, (String) c)); List entries = map.entrySet() .stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/RethrottleRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/RethrottleRequest.java index 958e6ce4cda1c..6e453a5c7f343 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/RethrottleRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/RethrottleRequest.java @@ -32,7 +32,7 @@ package org.opensearch.client; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.tasks.TaskId; import java.util.Objects; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/SearchPipelineClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/SearchPipelineClient.java new file mode 100644 index 0000000000000..0014bdb8c8182 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/opensearch/client/SearchPipelineClient.java @@ -0,0 +1,149 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client; + +import org.opensearch.action.search.DeleteSearchPipelineRequest; +import org.opensearch.action.search.GetSearchPipelineRequest; +import org.opensearch.action.search.GetSearchPipelineResponse; +import org.opensearch.action.search.PutSearchPipelineRequest; +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.action.ActionListener; + +import java.io.IOException; +import java.util.Collections; + +import static java.util.Collections.emptySet; + +public final class SearchPipelineClient { + private final RestHighLevelClient restHighLevelClient; + + SearchPipelineClient(RestHighLevelClient restHighLevelClient) { + this.restHighLevelClient = restHighLevelClient; + } + + /** + * Add a pipeline or update an existing pipeline. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public AcknowledgedResponse put(PutSearchPipelineRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity( + request, + SearchPipelineRequestConverters::putPipeline, + options, + AcknowledgedResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously add a pipeline or update an existing pipeline. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return cancellable that may be used to cancel the request + */ + public Cancellable putAsync(PutSearchPipelineRequest request, RequestOptions options, ActionListener listener) { + return restHighLevelClient.performRequestAsyncAndParseEntity( + request, + SearchPipelineRequestConverters::putPipeline, + options, + AcknowledgedResponse::fromXContent, + listener, + emptySet() + ); + } + + /** + * Get existing pipelines. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public GetSearchPipelineResponse get(GetSearchPipelineRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity( + request, + SearchPipelineRequestConverters::getPipeline, + options, + GetSearchPipelineResponse::fromXContent, + Collections.singleton(404) + ); + } + + /** + * Asynchronously get existing pipelines. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return cancellable that may be used to cancel the request + */ + public Cancellable getAsync( + GetSearchPipelineRequest request, + RequestOptions options, + ActionListener listener + ) { + return restHighLevelClient.performRequestAsyncAndParseEntity( + request, + SearchPipelineRequestConverters::getPipeline, + options, + GetSearchPipelineResponse::fromXContent, + listener, + Collections.singleton(404) + ); + } + + /** + * Delete an existing pipeline. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public AcknowledgedResponse delete(DeleteSearchPipelineRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity( + request, + SearchPipelineRequestConverters::deletePipeline, + options, + AcknowledgedResponse::fromXContent, + emptySet() + ); + } + + /** + * Asynchronously delete an existing pipeline. + * + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + * @return cancellable that may be used to cancel the request + */ + public Cancellable deleteAsync( + DeleteSearchPipelineRequest request, + RequestOptions options, + ActionListener listener + ) { + return restHighLevelClient.performRequestAsyncAndParseEntity( + request, + SearchPipelineRequestConverters::deletePipeline, + options, + AcknowledgedResponse::fromXContent, + listener, + emptySet() + ); + } + +} diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/SearchPipelineRequestConverters.java b/client/rest-high-level/src/main/java/org/opensearch/client/SearchPipelineRequestConverters.java new file mode 100644 index 0000000000000..655e3728edd71 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/opensearch/client/SearchPipelineRequestConverters.java @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client; + +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPut; +import org.opensearch.action.search.DeleteSearchPipelineRequest; +import org.opensearch.action.search.GetSearchPipelineRequest; +import org.opensearch.action.search.PutSearchPipelineRequest; + +import java.io.IOException; + +final class SearchPipelineRequestConverters { + private SearchPipelineRequestConverters() {} + + static Request putPipeline(PutSearchPipelineRequest putPipelineRequest) throws IOException { + String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_search/pipeline") + .addPathPart(putPipelineRequest.getId()) + .build(); + Request request = new Request(HttpPut.METHOD_NAME, endpoint); + + RequestConverters.Params params = new RequestConverters.Params(); + params.withTimeout(putPipelineRequest.timeout()); + params.withMasterTimeout(putPipelineRequest.clusterManagerNodeTimeout()); + request.addParameters(params.asMap()); + request.setEntity(RequestConverters.createEntity(putPipelineRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); + return request; + } + + static Request deletePipeline(DeleteSearchPipelineRequest deletePipelineRequest) { + String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_search/pipeline") + .addPathPart(deletePipelineRequest.getId()) + .build(); + Request request = new Request(HttpDelete.METHOD_NAME, endpoint); + + RequestConverters.Params parameters = new RequestConverters.Params(); + parameters.withTimeout(deletePipelineRequest.timeout()); + parameters.withMasterTimeout(deletePipelineRequest.clusterManagerNodeTimeout()); + request.addParameters(parameters.asMap()); + return request; + } + + static Request getPipeline(GetSearchPipelineRequest getPipelineRequest) { + String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_search/pipeline") + .addCommaSeparatedPathParts(getPipelineRequest.getIds()) + .build(); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + + RequestConverters.Params parameters = new RequestConverters.Params(); + parameters.withMasterTimeout(getPipelineRequest.clusterManagerNodeTimeout()); + request.addParameters(parameters.asMap()); + return request; + } +} diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/SnapshotClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/SnapshotClient.java index 85a793dec24ce..87a0e45eafe49 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/SnapshotClient.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/SnapshotClient.java @@ -32,7 +32,6 @@ package org.opensearch.client; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryRequest; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryResponse; import org.opensearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; @@ -52,6 +51,7 @@ import org.opensearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest; import org.opensearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse; import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.action.ActionListener; import java.io.IOException; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/SnapshotRequestConverters.java b/client/rest-high-level/src/main/java/org/opensearch/client/SnapshotRequestConverters.java index 3c92bb5ec2ab8..357bb75d5fadb 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/SnapshotRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/SnapshotRequestConverters.java @@ -47,7 +47,7 @@ import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest; import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest; import org.opensearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import java.io.IOException; @@ -63,7 +63,7 @@ static Request getRepositories(GetRepositoriesRequest getRepositoriesRequest) { Request request = new Request(HttpGet.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(getRepositoriesRequest.masterNodeTimeout()); + parameters.withMasterTimeout(getRepositoriesRequest.clusterManagerNodeTimeout()); parameters.withLocal(getRepositoriesRequest.local()); request.addParameters(parameters.asMap()); return request; @@ -74,7 +74,7 @@ static Request createRepository(PutRepositoryRequest putRepositoryRequest) throw Request request = new Request(HttpPut.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(putRepositoryRequest.masterNodeTimeout()); + parameters.withMasterTimeout(putRepositoryRequest.clusterManagerNodeTimeout()); parameters.withTimeout(putRepositoryRequest.timeout()); if (putRepositoryRequest.verify() == false) { parameters.putParam("verify", "false"); @@ -91,7 +91,7 @@ static Request deleteRepository(DeleteRepositoryRequest deleteRepositoryRequest) Request request = new Request(HttpDelete.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(deleteRepositoryRequest.masterNodeTimeout()); + parameters.withMasterTimeout(deleteRepositoryRequest.clusterManagerNodeTimeout()); parameters.withTimeout(deleteRepositoryRequest.timeout()); request.addParameters(parameters.asMap()); return request; @@ -105,7 +105,7 @@ static Request verifyRepository(VerifyRepositoryRequest verifyRepositoryRequest) Request request = new Request(HttpPost.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(verifyRepositoryRequest.masterNodeTimeout()); + parameters.withMasterTimeout(verifyRepositoryRequest.clusterManagerNodeTimeout()); parameters.withTimeout(verifyRepositoryRequest.timeout()); request.addParameters(parameters.asMap()); return request; @@ -119,7 +119,7 @@ static Request cleanupRepository(CleanupRepositoryRequest cleanupRepositoryReque Request request = new Request(HttpPost.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(cleanupRepositoryRequest.masterNodeTimeout()); + parameters.withMasterTimeout(cleanupRepositoryRequest.clusterManagerNodeTimeout()); parameters.withTimeout(cleanupRepositoryRequest.timeout()); request.addParameters(parameters.asMap()); return request; @@ -132,7 +132,7 @@ static Request createSnapshot(CreateSnapshotRequest createSnapshotRequest) throw .build(); Request request = new Request(HttpPut.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(createSnapshotRequest.masterNodeTimeout()); + params.withMasterTimeout(createSnapshotRequest.clusterManagerNodeTimeout()); params.withWaitForCompletion(createSnapshotRequest.waitForCompletion()); request.addParameters(params.asMap()); request.setEntity(RequestConverters.createEntity(createSnapshotRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); @@ -148,7 +148,7 @@ static Request cloneSnapshot(CloneSnapshotRequest cloneSnapshotRequest) throws I .build(); Request request = new Request(HttpPut.METHOD_NAME, endpoint); RequestConverters.Params params = new RequestConverters.Params(); - params.withMasterTimeout(cloneSnapshotRequest.masterNodeTimeout()); + params.withMasterTimeout(cloneSnapshotRequest.clusterManagerNodeTimeout()); request.addParameters(params.asMap()); request.setEntity(RequestConverters.createEntity(cloneSnapshotRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); return request; @@ -167,7 +167,7 @@ static Request getSnapshots(GetSnapshotsRequest getSnapshotsRequest) { Request request = new Request(HttpGet.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(getSnapshotsRequest.masterNodeTimeout()); + parameters.withMasterTimeout(getSnapshotsRequest.clusterManagerNodeTimeout()); parameters.putParam("ignore_unavailable", Boolean.toString(getSnapshotsRequest.ignoreUnavailable())); parameters.putParam("verbose", Boolean.toString(getSnapshotsRequest.verbose())); request.addParameters(parameters.asMap()); @@ -183,7 +183,7 @@ static Request snapshotsStatus(SnapshotsStatusRequest snapshotsStatusRequest) { Request request = new Request(HttpGet.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(snapshotsStatusRequest.masterNodeTimeout()); + parameters.withMasterTimeout(snapshotsStatusRequest.clusterManagerNodeTimeout()); parameters.withIgnoreUnavailable(snapshotsStatusRequest.ignoreUnavailable()); request.addParameters(parameters.asMap()); return request; @@ -197,7 +197,7 @@ static Request restoreSnapshot(RestoreSnapshotRequest restoreSnapshotRequest) th .build(); Request request = new Request(HttpPost.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(restoreSnapshotRequest.masterNodeTimeout()); + parameters.withMasterTimeout(restoreSnapshotRequest.clusterManagerNodeTimeout()); parameters.withWaitForCompletion(restoreSnapshotRequest.waitForCompletion()); request.addParameters(parameters.asMap()); request.setEntity(RequestConverters.createEntity(restoreSnapshotRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE)); @@ -212,7 +212,7 @@ static Request deleteSnapshot(DeleteSnapshotRequest deleteSnapshotRequest) { Request request = new Request(HttpDelete.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(); - parameters.withMasterTimeout(deleteSnapshotRequest.masterNodeTimeout()); + parameters.withMasterTimeout(deleteSnapshotRequest.clusterManagerNodeTimeout()); request.addParameters(parameters.asMap()); return request; } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/TasksClient.java b/client/rest-high-level/src/main/java/org/opensearch/client/TasksClient.java index 51764e3339394..ec862aead794a 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/TasksClient.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/TasksClient.java @@ -32,13 +32,13 @@ package org.opensearch.client; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.client.tasks.CancelTasksRequest; import org.opensearch.client.tasks.CancelTasksResponse; import org.opensearch.client.tasks.GetTaskRequest; import org.opensearch.client.tasks.GetTaskResponse; +import org.opensearch.core.action.ActionListener; import java.io.IOException; import java.util.Optional; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/TimedRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/TimedRequest.java index 3310425df4662..dad5b6a3679ec 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/TimedRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/TimedRequest.java @@ -44,10 +44,13 @@ public abstract class TimedRequest implements Validatable { public static final TimeValue DEFAULT_ACK_TIMEOUT = timeValueSeconds(30); - public static final TimeValue DEFAULT_MASTER_NODE_TIMEOUT = TimeValue.timeValueSeconds(30); + public static final TimeValue DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT = TimeValue.timeValueSeconds(30); + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT} */ + @Deprecated + public static final TimeValue DEFAULT_MASTER_NODE_TIMEOUT = DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; private TimeValue timeout = DEFAULT_ACK_TIMEOUT; - private TimeValue masterTimeout = DEFAULT_MASTER_NODE_TIMEOUT; + private TimeValue clusterManagerTimeout = DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; /** * Sets the timeout to wait for the all the nodes to acknowledge @@ -58,11 +61,21 @@ public void setTimeout(TimeValue timeout) { } /** - * Sets the timeout to connect to the master node - * @param masterTimeout timeout as a {@link TimeValue} + * Sets the timeout to connect to the cluster-manager node + * @param clusterManagerTimeout timeout as a {@link TimeValue} */ - public void setMasterTimeout(TimeValue masterTimeout) { - this.masterTimeout = masterTimeout; + public void setClusterManagerTimeout(TimeValue clusterManagerTimeout) { + this.clusterManagerTimeout = clusterManagerTimeout; + } + + /** + * Sets the timeout to connect to the cluster-manager node + * @param clusterManagerTimeout timeout as a {@link TimeValue} + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #setClusterManagerTimeout(TimeValue)} + */ + @Deprecated + public void setMasterTimeout(TimeValue clusterManagerTimeout) { + setClusterManagerTimeout(clusterManagerTimeout); } /** @@ -73,9 +86,18 @@ public TimeValue timeout() { } /** - * Returns the timeout for the request to be completed on the master node + * Returns the timeout for the request to be completed on the cluster-manager node + */ + public TimeValue clusterManagerNodeTimeout() { + return clusterManagerTimeout; + } + + /** + * Returns the timeout for the request to be completed on the cluster-manager node + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #clusterManagerNodeTimeout()} */ + @Deprecated public TimeValue masterNodeTimeout() { - return masterTimeout; + return clusterManagerNodeTimeout(); } } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/cluster/RemoteConnectionInfo.java b/client/rest-high-level/src/main/java/org/opensearch/client/cluster/RemoteConnectionInfo.java index 4f91d32452d26..28eeae7b52554 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/cluster/RemoteConnectionInfo.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/cluster/RemoteConnectionInfo.java @@ -32,16 +32,16 @@ package org.opensearch.client.cluster; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.List; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * This class encapsulates all remote cluster information to be rendered on diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/cluster/RemoteInfoResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/cluster/RemoteInfoResponse.java index 388bcd3be51e4..d1fa2eb1dff0d 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/cluster/RemoteInfoResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/cluster/RemoteInfoResponse.java @@ -31,14 +31,14 @@ package org.opensearch.client.cluster; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * A response to _remote/info API request. diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/AcknowledgedResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/AcknowledgedResponse.java index c54bef15d4876..73e6e4a638020 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/AcknowledgedResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/AcknowledgedResponse.java @@ -32,15 +32,15 @@ package org.opensearch.client.core; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; import java.util.function.Function; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; public class AcknowledgedResponse { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/BroadcastResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/BroadcastResponse.java index dc46b3fbcc941..a91d1461685f8 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/BroadcastResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/BroadcastResponse.java @@ -32,10 +32,10 @@ package org.opensearch.client.core; -import org.opensearch.action.support.DefaultShardOperationFailedException; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collection; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/CountRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/CountRequest.java index 88d0fee69a870..22be6c2e7ac9d 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/CountRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/CountRequest.java @@ -36,9 +36,9 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.internal.SearchContext; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/CountResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/CountResponse.java index 1d67a50f68f40..eeb99cbca2b5f 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/CountResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/CountResponse.java @@ -33,16 +33,16 @@ package org.opensearch.client.core; import org.opensearch.action.search.ShardSearchFailure; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * A response to _count API request. diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/GetSourceResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/GetSourceResponse.java index 7d3b327d15902..57a88426f6f95 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/GetSourceResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/GetSourceResponse.java @@ -32,7 +32,7 @@ package org.opensearch.client.core; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Map; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/IndexerJobStats.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/IndexerJobStats.java index b0cc46b80afc8..38e30c08a04e1 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/IndexerJobStats.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/IndexerJobStats.java @@ -32,7 +32,7 @@ package org.opensearch.client.core; -import org.opensearch.common.ParseField; +import org.opensearch.core.ParseField; import java.util.Objects; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/MainResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/MainResponse.java index b15ef2e699ca5..6df51a6a4ff3e 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/MainResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/MainResponse.java @@ -32,9 +32,9 @@ package org.opensearch.client.core; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.util.Objects; @@ -43,7 +43,9 @@ public class MainResponse { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( MainResponse.class.getName(), true, - args -> { return new MainResponse((String) args[0], (Version) args[1], (String) args[2], (String) args[3]); } + args -> { + return new MainResponse((String) args[0], (Version) args[1], (String) args[2], (String) args[3]); + } ); static { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/MultiTermVectorsRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/MultiTermVectorsRequest.java index fdb8967dfa605..7c70d977d320c 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/MultiTermVectorsRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/MultiTermVectorsRequest.java @@ -33,8 +33,8 @@ package org.opensearch.client.core; import org.opensearch.client.Validatable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/MultiTermVectorsResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/MultiTermVectorsResponse.java index 490c5e314a513..1dd5bdb6f30f8 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/MultiTermVectorsResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/MultiTermVectorsResponse.java @@ -32,14 +32,14 @@ package org.opensearch.client.core; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.util.List; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; public class MultiTermVectorsResponse { private final List responses; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/PageParams.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/PageParams.java index 38b287a87d412..7f684624b8aa7 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/PageParams.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/PageParams.java @@ -32,10 +32,10 @@ package org.opensearch.client.core; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Objects; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/ShardsAcknowledgedResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/ShardsAcknowledgedResponse.java index df228d3d84f60..3d097de47afb3 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/ShardsAcknowledgedResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/ShardsAcknowledgedResponse.java @@ -31,13 +31,13 @@ package org.opensearch.client.core; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; public class ShardsAcknowledgedResponse extends AcknowledgedResponse { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/TermVectorsRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/TermVectorsRequest.java index b346667f462c4..c58114299c065 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/TermVectorsRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/TermVectorsRequest.java @@ -34,9 +34,9 @@ import org.opensearch.client.Validatable; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.io.InputStream; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/core/TermVectorsResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/core/TermVectorsResponse.java index fa13abf72207e..ad2e3dd550880 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/core/TermVectorsResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/core/TermVectorsResponse.java @@ -33,17 +33,18 @@ package org.opensearch.client.core; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.util.Collections; -import java.util.List; import java.util.Comparator; +import java.util.List; import java.util.Objects; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; + public class TermVectorsResponse { private final String index; private final String id; @@ -240,7 +241,9 @@ public static final class FieldStatistics { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "field_statistics", true, - args -> { return new FieldStatistics((long) args[0], (int) args[1], (long) args[2]); } + args -> { + return new FieldStatistics((long) args[0], (int) args[1], (long) args[2]); + } ); static { @@ -411,11 +414,9 @@ public int hashCode() { public static final class Token { - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - "token", - true, - args -> { return new Token((Integer) args[0], (Integer) args[1], (Integer) args[2], (String) args[3]); } - ); + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("token", true, args -> { + return new Token((Integer) args[0], (Integer) args[1], (Integer) args[2], (String) args[3]); + }); static { PARSER.declareInt(optionalConstructorArg(), new ParseField("start_offset")); PARSER.declareInt(optionalConstructorArg(), new ParseField("end_offset")); diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/AnalyzeRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/AnalyzeRequest.java index b614e30d63ddd..a2b69962c392e 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/AnalyzeRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/AnalyzeRequest.java @@ -33,11 +33,11 @@ package org.opensearch.client.indices; import org.opensearch.client.Validatable; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/AnalyzeResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/AnalyzeResponse.java index cc173a5b8ab6f..359e6e6617c98 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/AnalyzeResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/AnalyzeResponse.java @@ -32,10 +32,10 @@ package org.opensearch.client.indices; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; @@ -43,7 +43,7 @@ import java.util.Map; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; public class AnalyzeResponse { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/CloseIndexResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/CloseIndexResponse.java index 817d1c08532c6..e32c33484140d 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/CloseIndexResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/CloseIndexResponse.java @@ -32,21 +32,21 @@ package org.opensearch.client.indices; import org.opensearch.OpenSearchException; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import java.util.List; import java.util.Objects; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; -import static org.opensearch.common.xcontent.ObjectParser.ValueType; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ObjectParser.ValueType; public class CloseIndexResponse extends ShardsAcknowledgedResponse { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/ComponentTemplatesExistRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/ComponentTemplatesExistRequest.java index 4a518d851cdd8..98395626ef392 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/ComponentTemplatesExistRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/ComponentTemplatesExistRequest.java @@ -32,7 +32,7 @@ package org.opensearch.client.indices; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; /** * A request to check for the existence of component templates diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/ComposableIndexTemplateExistRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/ComposableIndexTemplateExistRequest.java index 4efe0e6b33aac..a9297d0dd0559 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/ComposableIndexTemplateExistRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/ComposableIndexTemplateExistRequest.java @@ -32,7 +32,7 @@ package org.opensearch.client.indices; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; /** * A request to check for the existence of index templates diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/CreateIndexRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/CreateIndexRequest.java index 5bf65a6ea1989..7805a7853b003 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/CreateIndexRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/CreateIndexRequest.java @@ -38,19 +38,19 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.client.TimedRequest; import org.opensearch.client.Validatable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.InputStream; @@ -64,6 +64,8 @@ /** * A request to create an index. + * + * @opensearch.api */ public class CreateIndexRequest extends TimedRequest implements Validatable, ToXContentObject { static final ParseField MAPPINGS = new ParseField("mappings"); @@ -74,7 +76,7 @@ public class CreateIndexRequest extends TimedRequest implements Validatable, ToX private Settings settings = EMPTY_SETTINGS; private BytesReference mappings; - private XContentType mappingsXContentType; + private MediaType mappingsMediaType; private final Set aliases = new HashSet<>(); @@ -123,8 +125,8 @@ public CreateIndexRequest settings(Settings settings) { /** * The settings to create the index with (either json or yaml format) */ - public CreateIndexRequest settings(String source, XContentType xContentType) { - this.settings = Settings.builder().loadFromSource(source, xContentType).build(); + public CreateIndexRequest settings(String source, MediaType mediaType) { + this.settings = Settings.builder().loadFromSource(source, mediaType).build(); return this; } @@ -132,7 +134,7 @@ public CreateIndexRequest settings(String source, XContentType xContentType) { * Allows to set the settings using a json builder. */ public CreateIndexRequest settings(XContentBuilder builder) { - settings(Strings.toString(builder), builder.contentType()); + settings(builder.toString(), builder.contentType()); return this; } @@ -148,8 +150,8 @@ public BytesReference mappings() { return mappings; } - public XContentType mappingsXContentType() { - return mappingsXContentType; + public MediaType mappingsMediaType() { + return mappingsMediaType; } /** @@ -158,10 +160,10 @@ public XContentType mappingsXContentType() { * Note that the definition should *not* be nested under a type name. * * @param source The mapping source - * @param xContentType The content type of the source + * @param mediaType The media type of the source */ - public CreateIndexRequest mapping(String source, XContentType xContentType) { - return mapping(new BytesArray(source), xContentType); + public CreateIndexRequest mapping(String source, MediaType mediaType) { + return mapping(new BytesArray(source), mediaType); } /** @@ -184,7 +186,7 @@ public CreateIndexRequest mapping(XContentBuilder source) { */ public CreateIndexRequest mapping(Map source) { try { - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(MediaTypeRegistry.getDefaultMediaType()); builder.map(source); return mapping(BytesReference.bytes(builder), builder.contentType()); } catch (IOException e) { @@ -198,12 +200,12 @@ public CreateIndexRequest mapping(Map source) { * Note that the definition should *not* be nested under a type name. * * @param source The mapping source - * @param xContentType the content type of the mapping source + * @param mediaType the content type of the mapping source */ - public CreateIndexRequest mapping(BytesReference source, XContentType xContentType) { - Objects.requireNonNull(xContentType); + public CreateIndexRequest mapping(BytesReference source, MediaType mediaType) { + Objects.requireNonNull(mediaType); mappings = source; - mappingsXContentType = xContentType; + mappingsMediaType = mediaType; return this; } @@ -234,14 +236,14 @@ public CreateIndexRequest aliases(XContentBuilder source) { /** * Sets the aliases that will be associated with the index when it gets created */ - public CreateIndexRequest aliases(String source, XContentType contentType) { - return aliases(new BytesArray(source), contentType); + public CreateIndexRequest aliases(String source, MediaType mediaType) { + return aliases(new BytesArray(source), mediaType); } /** * Sets the aliases that will be associated with the index when it gets created */ - public CreateIndexRequest aliases(BytesReference source, XContentType contentType) { + public CreateIndexRequest aliases(BytesReference source, MediaType contentType) { // EMPTY is safe here because we never call namedObject try ( XContentParser parser = XContentHelper.createParser( @@ -283,8 +285,8 @@ public CreateIndexRequest aliases(Collection aliases) { * * Note that the mapping definition should *not* be nested under a type name. */ - public CreateIndexRequest source(String source, XContentType xContentType) { - return source(new BytesArray(source), xContentType); + public CreateIndexRequest source(String source, MediaType mediaType) { + return source(new BytesArray(source), mediaType); } /** @@ -301,9 +303,9 @@ public CreateIndexRequest source(XContentBuilder source) { * * Note that the mapping definition should *not* be nested under a type name. */ - public CreateIndexRequest source(BytesReference source, XContentType xContentType) { - Objects.requireNonNull(xContentType); - source(XContentHelper.convertToMap(source, false, xContentType).v2()); + public CreateIndexRequest source(BytesReference source, MediaType mediaType) { + Objects.requireNonNull(mediaType); + source(XContentHelper.convertToMap(source, false, mediaType).v2()); return this; } @@ -365,7 +367,7 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t if (mappings != null) { try (InputStream stream = mappings.streamInput()) { - builder.rawField(MAPPINGS.getPreferredName(), stream, mappingsXContentType); + builder.rawField(MAPPINGS.getPreferredName(), stream, mappingsMediaType); } } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/CreateIndexResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/CreateIndexResponse.java index 7e1ea2894961d..21e2ba4b342a6 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/CreateIndexResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/CreateIndexResponse.java @@ -33,14 +33,14 @@ package org.opensearch.client.indices; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * A response for a create index action. diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/DataStream.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/DataStream.java index e87a77dafdde1..0166dad4a4077 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/DataStream.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/DataStream.java @@ -33,9 +33,9 @@ import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.List; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/DataStreamsStatsResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/DataStreamsStatsResponse.java index fc3a7b5b4fac5..327836160cceb 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/DataStreamsStatsResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/DataStreamsStatsResponse.java @@ -33,11 +33,11 @@ package org.opensearch.client.indices; import org.opensearch.client.core.BroadcastResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; @@ -45,7 +45,7 @@ import java.util.Map; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; public class DataStreamsStatsResponse extends BroadcastResponse { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/DetailAnalyzeResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/DetailAnalyzeResponse.java index 00fa6fb046cf2..92ab2d7b744b2 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/DetailAnalyzeResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/DetailAnalyzeResponse.java @@ -32,18 +32,18 @@ package org.opensearch.client.indices; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; public class DetailAnalyzeResponse { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComponentTemplatesRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComponentTemplatesRequest.java index f70682fee3763..6326e8edf763b 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComponentTemplatesRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComponentTemplatesRequest.java @@ -44,7 +44,7 @@ public class GetComponentTemplatesRequest implements Validatable { private final String name; - private TimeValue masterNodeTimeout = TimedRequest.DEFAULT_MASTER_NODE_TIMEOUT; + private TimeValue clusterManagerNodeTimeout = TimedRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; private boolean local = false; /** @@ -65,23 +65,47 @@ public String name() { } /** - * @return the timeout for waiting for the master node to respond + * @return the timeout for waiting for the cluster-manager node to respond */ + public TimeValue getClusterManagerNodeTimeout() { + return clusterManagerNodeTimeout; + } + + /** + * @return the timeout for waiting for the cluster-manager node to respond + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #getMasterNodeTimeout()} + */ + @Deprecated public TimeValue getMasterNodeTimeout() { - return masterNodeTimeout; + return getClusterManagerNodeTimeout(); + } + + public void setClusterManagerNodeTimeout(@Nullable TimeValue clusterManagerNodeTimeout) { + this.clusterManagerNodeTimeout = clusterManagerNodeTimeout; + } + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #setClusterManagerNodeTimeout(TimeValue)} */ + @Deprecated + public void setMasterNodeTimeout(@Nullable TimeValue clusterManagerNodeTimeout) { + setClusterManagerNodeTimeout(clusterManagerNodeTimeout); } - public void setMasterNodeTimeout(@Nullable TimeValue masterNodeTimeout) { - this.masterNodeTimeout = masterNodeTimeout; + public void setClusterManagerNodeTimeout(String clusterManagerNodeTimeout) { + final TimeValue timeValue = TimeValue.parseTimeValue( + clusterManagerNodeTimeout, + getClass().getSimpleName() + ".clusterManagerNodeTimeout" + ); + setClusterManagerNodeTimeout(timeValue); } - public void setMasterNodeTimeout(String masterNodeTimeout) { - final TimeValue timeValue = TimeValue.parseTimeValue(masterNodeTimeout, getClass().getSimpleName() + ".masterNodeTimeout"); - setMasterNodeTimeout(timeValue); + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #setClusterManagerNodeTimeout(String)} */ + @Deprecated + public void setMasterNodeTimeout(String clusterManagerNodeTimeout) { + setClusterManagerNodeTimeout(clusterManagerNodeTimeout); } /** - * @return true if this request is to read from the local cluster state, rather than the master node - false otherwise + * @return true if this request is to read from the local cluster state, rather than the cluster-manager node - false otherwise */ public boolean isLocal() { return local; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComponentTemplatesResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComponentTemplatesResponse.java index 8f6ebbc8349d5..a5117c5db4edb 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComponentTemplatesResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComponentTemplatesResponse.java @@ -32,9 +32,9 @@ package org.opensearch.client.indices; import org.opensearch.cluster.metadata.ComponentTemplate; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collections; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComposableIndexTemplateRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComposableIndexTemplateRequest.java index 572a5eeec2d23..73f6f15fc7a78 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComposableIndexTemplateRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComposableIndexTemplateRequest.java @@ -44,7 +44,7 @@ public class GetComposableIndexTemplateRequest implements Validatable { private final String name; - private TimeValue masterNodeTimeout = TimedRequest.DEFAULT_MASTER_NODE_TIMEOUT; + private TimeValue clusterManagerNodeTimeout = TimedRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; private boolean local = false; /** @@ -65,23 +65,47 @@ public String name() { } /** - * @return the timeout for waiting for the master node to respond + * @return the timeout for waiting for the cluster-manager node to respond */ + public TimeValue getClusterManagerNodeTimeout() { + return clusterManagerNodeTimeout; + } + + /** + * @return the timeout for waiting for the cluster-manager node to respond + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #getMasterNodeTimeout()} + */ + @Deprecated public TimeValue getMasterNodeTimeout() { - return masterNodeTimeout; + return getClusterManagerNodeTimeout(); + } + + public void setClusterManagerNodeTimeout(@Nullable TimeValue clusterManagerNodeTimeout) { + this.clusterManagerNodeTimeout = clusterManagerNodeTimeout; + } + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #setClusterManagerNodeTimeout(TimeValue)} */ + @Deprecated + public void setMasterNodeTimeout(@Nullable TimeValue clusterManagerNodeTimeout) { + setClusterManagerNodeTimeout(clusterManagerNodeTimeout); } - public void setMasterNodeTimeout(@Nullable TimeValue masterNodeTimeout) { - this.masterNodeTimeout = masterNodeTimeout; + public void setClusterManagerNodeTimeout(String clusterManagerNodeTimeout) { + final TimeValue timeValue = TimeValue.parseTimeValue( + clusterManagerNodeTimeout, + getClass().getSimpleName() + ".clusterManagerNodeTimeout" + ); + setClusterManagerNodeTimeout(timeValue); } - public void setMasterNodeTimeout(String masterNodeTimeout) { - final TimeValue timeValue = TimeValue.parseTimeValue(masterNodeTimeout, getClass().getSimpleName() + ".masterNodeTimeout"); - setMasterNodeTimeout(timeValue); + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #setClusterManagerNodeTimeout(String)} */ + @Deprecated + public void setMasterNodeTimeout(String clusterManagerNodeTimeout) { + setClusterManagerNodeTimeout(clusterManagerNodeTimeout); } /** - * @return true if this request is to read from the local cluster state, rather than the master node - false otherwise + * @return true if this request is to read from the local cluster state, rather than the cluster-manager node - false otherwise */ public boolean isLocal() { return local; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComposableIndexTemplatesResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComposableIndexTemplatesResponse.java index 96ba7e675253a..9e05725660e96 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComposableIndexTemplatesResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetComposableIndexTemplatesResponse.java @@ -32,9 +32,9 @@ package org.opensearch.client.indices; import org.opensearch.cluster.metadata.ComposableIndexTemplate; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collections; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetDataStreamResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetDataStreamResponse.java index 81fde3ebf2114..54633f9558f5c 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetDataStreamResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetDataStreamResponse.java @@ -31,7 +31,7 @@ package org.opensearch.client.indices; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetFieldMappingsRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetFieldMappingsRequest.java index 19792001170f3..70eae3a360005 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetFieldMappingsRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetFieldMappingsRequest.java @@ -34,7 +34,7 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.client.Validatable; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; /** Request the mappings of specific fields */ public class GetFieldMappingsRequest implements Validatable { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetFieldMappingsResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetFieldMappingsResponse.java index c3d9679fc485b..ec3101b7e7543 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetFieldMappingsResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetFieldMappingsResponse.java @@ -32,14 +32,14 @@ package org.opensearch.client.indices; -import org.opensearch.common.ParseField; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.Mapper; import java.io.IOException; @@ -47,9 +47,9 @@ import java.util.Map; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** Response object for {@link GetFieldMappingsRequest} API */ public class GetFieldMappingsResponse { @@ -150,7 +150,7 @@ public String fullName() { * Returns the mappings as a map. Note that the returned map has a single key which is always the field's {@link Mapper#name}. */ public Map sourceAsMap() { - return XContentHelper.convertToMap(source, true, XContentType.JSON).v2(); + return XContentHelper.convertToMap(source, true, MediaTypeRegistry.JSON).v2(); } // pkg-private for testing diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexRequest.java index 5e5ab6aeae305..c5ef5cb9c1795 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexRequest.java @@ -82,9 +82,9 @@ public final GetIndexRequest local(boolean local) { } /** - * Return local information, do not retrieve the state from master node (default: false). + * Return local information, do not retrieve the state from cluster-manager node (default: false). * @return true if local information is to be returned; - * false if information is to be retrieved from master node (default). + * false if information is to be retrieved from cluster-manager node (default). */ public final boolean local() { return local; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexResponse.java index 4fa87a28784aa..6ec1c312c9ba9 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexResponse.java @@ -36,8 +36,8 @@ import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.index.mapper.MapperService; import java.io.IOException; @@ -49,7 +49,7 @@ import java.util.List; import java.util.Map; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * A client side response for a get index action. diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexTemplatesRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexTemplatesRequest.java index 071bcc7a75a71..dc1759a7272e8 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexTemplatesRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexTemplatesRequest.java @@ -35,8 +35,8 @@ import org.opensearch.client.TimedRequest; import org.opensearch.client.Validatable; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; import java.util.Arrays; import java.util.List; @@ -51,7 +51,7 @@ public class GetIndexTemplatesRequest implements Validatable { private final List names; - private TimeValue masterNodeTimeout = TimedRequest.DEFAULT_MASTER_NODE_TIMEOUT; + private TimeValue clusterManagerNodeTimeout = TimedRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; private boolean local = false; /** @@ -84,23 +84,47 @@ public List names() { } /** - * @return the timeout for waiting for the master node to respond + * @return the timeout for waiting for the cluster-manager node to respond */ + public TimeValue getClusterManagerNodeTimeout() { + return clusterManagerNodeTimeout; + } + + /** + * @return the timeout for waiting for the cluster-manager node to respond + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #getMasterNodeTimeout()} + */ + @Deprecated public TimeValue getMasterNodeTimeout() { - return masterNodeTimeout; + return getClusterManagerNodeTimeout(); + } + + public void setClusterManagerNodeTimeout(@Nullable TimeValue clusterManagerNodeTimeout) { + this.clusterManagerNodeTimeout = clusterManagerNodeTimeout; + } + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #setClusterManagerNodeTimeout(TimeValue)} */ + @Deprecated + public void setMasterNodeTimeout(@Nullable TimeValue clusterManagerNodeTimeout) { + setClusterManagerNodeTimeout(clusterManagerNodeTimeout); } - public void setMasterNodeTimeout(@Nullable TimeValue masterNodeTimeout) { - this.masterNodeTimeout = masterNodeTimeout; + public void setClusterManagerNodeTimeout(String clusterManagerNodeTimeout) { + final TimeValue timeValue = TimeValue.parseTimeValue( + clusterManagerNodeTimeout, + getClass().getSimpleName() + ".clusterManagerNodeTimeout" + ); + setClusterManagerNodeTimeout(timeValue); } - public void setMasterNodeTimeout(String masterNodeTimeout) { - final TimeValue timeValue = TimeValue.parseTimeValue(masterNodeTimeout, getClass().getSimpleName() + ".masterNodeTimeout"); - setMasterNodeTimeout(timeValue); + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #setClusterManagerNodeTimeout(String)} */ + @Deprecated + public void setMasterNodeTimeout(String clusterManagerNodeTimeout) { + setClusterManagerNodeTimeout(clusterManagerNodeTimeout); } /** - * @return true if this request is to read from the local cluster state, rather than the master node - false otherwise + * @return true if this request is to read from the local cluster state, rather than the cluster-manager node - false otherwise */ public boolean isLocal() { return local; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexTemplatesResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexTemplatesResponse.java index 84ee4c25409e0..25bdc2191b6d3 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexTemplatesResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetIndexTemplatesResponse.java @@ -31,7 +31,7 @@ package org.opensearch.client.indices; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetMappingsRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetMappingsRequest.java index e049e70c7587c..2e5b0cb89cedb 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetMappingsRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetMappingsRequest.java @@ -34,7 +34,7 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.client.TimedRequest; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; public class GetMappingsRequest extends TimedRequest { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetMappingsResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetMappingsResponse.java index 85841d9e28dc7..8257eb02e9ac1 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetMappingsResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/GetMappingsResponse.java @@ -33,9 +33,9 @@ package org.opensearch.client.indices; import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import org.opensearch.index.mapper.MapperService; import java.io.IOException; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/IndexTemplateMetadata.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/IndexTemplateMetadata.java index 63ffae0bd4abb..017ad0089704e 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/IndexTemplateMetadata.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/IndexTemplateMetadata.java @@ -35,21 +35,22 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import java.io.IOException; import java.util.AbstractMap; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; public class IndexTemplateMetadata { @@ -59,9 +60,7 @@ public class IndexTemplateMetadata { true, (a, name) -> { List> alias = (List>) a[5]; - ImmutableOpenMap aliasMap = new ImmutableOpenMap.Builder().putAll( - alias.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) - ).build(); + final Map aliasMap = alias.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); return new IndexTemplateMetadata( name, (Integer) a[0], @@ -128,7 +127,7 @@ public class IndexTemplateMetadata { private final MappingMetadata mappings; - private final ImmutableOpenMap aliases; + private final Map aliases; public IndexTemplateMetadata( String name, @@ -137,7 +136,7 @@ public IndexTemplateMetadata( List patterns, Settings settings, MappingMetadata mappings, - ImmutableOpenMap aliases + final Map aliases ) { if (patterns == null || patterns.isEmpty()) { throw new IllegalArgumentException("Index patterns must not be null or empty; got " + patterns); @@ -148,7 +147,7 @@ public IndexTemplateMetadata( this.patterns = patterns; this.settings = settings; this.mappings = mappings; - this.aliases = aliases; + this.aliases = Collections.unmodifiableMap(aliases); } public String name() { @@ -176,7 +175,7 @@ public MappingMetadata mappings() { return this.mappings; } - public ImmutableOpenMap aliases() { + public Map aliases() { return this.aliases; } @@ -217,12 +216,12 @@ public static class Builder { private MappingMetadata mappings; - private final ImmutableOpenMap.Builder aliases; + private final Map aliases; public Builder(String name) { this.name = name; mappings = null; - aliases = ImmutableOpenMap.builder(); + aliases = new HashMap<>(); } public Builder(IndexTemplateMetadata indexTemplateMetadata) { @@ -233,7 +232,7 @@ public Builder(IndexTemplateMetadata indexTemplateMetadata) { settings(indexTemplateMetadata.settings()); mappings = indexTemplateMetadata.mappings(); - aliases = ImmutableOpenMap.builder(indexTemplateMetadata.aliases()); + aliases = new HashMap<>(indexTemplateMetadata.aliases()); } public Builder order(int order) { @@ -277,7 +276,7 @@ public Builder putAlias(AliasMetadata.Builder aliasMetadata) { } public IndexTemplateMetadata build() { - return new IndexTemplateMetadata(name, order, version, indexPatterns, settings, mappings, aliases.build()); + return new IndexTemplateMetadata(name, order, version, indexPatterns, settings, mappings, aliases); } public static IndexTemplateMetadata fromXContent(XContentParser parser, String templateName) throws IOException { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutComponentTemplateRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutComponentTemplateRequest.java index e895cc71c4471..cfcd594658ea1 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutComponentTemplateRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutComponentTemplateRequest.java @@ -33,9 +33,9 @@ import org.opensearch.client.TimedRequest; import org.opensearch.cluster.metadata.ComponentTemplate; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutComposableIndexTemplateRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutComposableIndexTemplateRequest.java index c8fa70e9f8738..309519d6f7b76 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutComposableIndexTemplateRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutComposableIndexTemplateRequest.java @@ -33,9 +33,9 @@ import org.opensearch.client.TimedRequest; import org.opensearch.cluster.metadata.ComposableIndexTemplate; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutIndexTemplateRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutIndexTemplateRequest.java index f248e8efa24f4..d090a469d2735 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutIndexTemplateRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutIndexTemplateRequest.java @@ -37,21 +37,23 @@ import org.opensearch.action.IndicesRequest; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.UncheckedIOException; @@ -69,7 +71,10 @@ /** * A request to create an index template. */ -public class PutIndexTemplateRequest extends MasterNodeRequest implements IndicesRequest, ToXContentFragment { +public class PutIndexTemplateRequest extends ClusterManagerNodeRequest + implements + IndicesRequest, + ToXContentFragment { private String name; @@ -212,10 +217,10 @@ public Settings settings() { * Adds mapping that will be added when the index gets created. * * @param source The mapping source - * @param xContentType The type of content contained within the source + * @param mediaType The type of content contained within the source */ - public PutIndexTemplateRequest mapping(String source, XContentType xContentType) { - internalMapping(XContentHelper.convertToMap(new BytesArray(source), true, xContentType).v2()); + public PutIndexTemplateRequest mapping(String source, MediaType mediaType) { + internalMapping(XContentHelper.convertToMap(new BytesArray(source), true, mediaType).v2()); return this; } @@ -263,11 +268,12 @@ public PutIndexTemplateRequest mapping(Map source) { private PutIndexTemplateRequest internalMapping(Map source) { try { - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + XContentBuilder builder = MediaTypeRegistry.JSON.contentBuilder(); builder.map(source); - Objects.requireNonNull(builder.contentType()); + MediaType mediaType = builder.contentType(); + Objects.requireNonNull(mediaType); try { - mappings = new BytesArray(XContentHelper.convertToJson(BytesReference.bytes(builder), false, false, builder.contentType())); + mappings = new BytesArray(XContentHelper.convertToJson(BytesReference.bytes(builder), false, false, mediaType)); return this; } catch (IOException e) { throw new UncheckedIOException("failed to convert source to json", e); @@ -339,7 +345,10 @@ public PutIndexTemplateRequest source(Map templateSource) { /** * The template source definition. + * + * @deprecated use {@link #source(String, MediaType)} instead */ + @Deprecated public PutIndexTemplateRequest source(String templateSource, XContentType xContentType) { return source(XContentHelper.convertToMap(xContentType.xContent(), templateSource, true)); } @@ -347,6 +356,16 @@ public PutIndexTemplateRequest source(String templateSource, XContentType xConte /** * The template source definition. */ + public PutIndexTemplateRequest source(String templateSource, MediaType mediaType) { + return source(XContentHelper.convertToMap(mediaType.xContent(), templateSource, true)); + } + + /** + * The template source definition. + * + * @deprecated use {@link #source(byte[], MediaType)} instead + */ + @Deprecated public PutIndexTemplateRequest source(byte[] source, XContentType xContentType) { return source(source, 0, source.length, xContentType); } @@ -354,6 +373,16 @@ public PutIndexTemplateRequest source(byte[] source, XContentType xContentType) /** * The template source definition. */ + public PutIndexTemplateRequest source(byte[] source, MediaType mediaType) { + return source(source, 0, source.length, mediaType); + } + + /** + * The template source definition. + * + * @deprecated use {@link #source(byte[], int, int, MediaType)} instead + */ + @Deprecated public PutIndexTemplateRequest source(byte[] source, int offset, int length, XContentType xContentType) { return source(new BytesArray(source, offset, length), xContentType); } @@ -361,10 +390,27 @@ public PutIndexTemplateRequest source(byte[] source, int offset, int length, XCo /** * The template source definition. */ + public PutIndexTemplateRequest source(byte[] source, int offset, int length, MediaType mediaType) { + return source(new BytesArray(source, offset, length), mediaType); + } + + /** + * The template source definition. + * + * @deprecated use {@link #source(BytesReference, MediaType)} instead + */ + @Deprecated public PutIndexTemplateRequest source(BytesReference source, XContentType xContentType) { return source(XContentHelper.convertToMap(source, true, xContentType).v2()); } + /** + * The template source definition. + */ + public PutIndexTemplateRequest source(BytesReference source, MediaType mediaType) { + return source(XContentHelper.convertToMap(source, true, mediaType).v2()); + } + public Set aliases() { return this.aliases; } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutMappingRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutMappingRequest.java index c8d5e4c95782a..6d7e95d191ba6 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutMappingRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/PutMappingRequest.java @@ -36,13 +36,13 @@ import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; import org.opensearch.client.TimedRequest; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.io.InputStream; @@ -52,6 +52,8 @@ * Put a mapping definition into one or more indices. If an index already contains mappings, * the new mappings will be merged with the existing one. If there are elements that cannot * be merged, the request will be rejected. + * + * @opensearch.api */ public class PutMappingRequest extends TimedRequest implements IndicesRequest, ToXContentObject { @@ -59,7 +61,7 @@ public class PutMappingRequest extends TimedRequest implements IndicesRequest, T private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, false, true, true); private BytesReference source; - private XContentType xContentType; + private MediaType mediaType; /** * Constructs a new put mapping request against one or more indices. If no indices @@ -95,10 +97,10 @@ public BytesReference source() { } /** - * The {@link XContentType} of the mapping source. + * The {@link MediaType} of the mapping source. */ - public XContentType xContentType() { - return xContentType; + public MediaType mediaType() { + return mediaType; } /** @@ -108,7 +110,7 @@ public XContentType xContentType() { */ public PutMappingRequest source(Map mappingSource) { try { - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(MediaTypeRegistry.getDefaultMediaType()); builder.map(mappingSource); return source(builder); } catch (IOException e) { @@ -121,9 +123,9 @@ public PutMappingRequest source(Map mappingSource) { * * Note that the definition should *not* be nested under a type name. */ - public PutMappingRequest source(String mappingSource, XContentType xContentType) { + public PutMappingRequest source(String mappingSource, MediaType mediaType) { this.source = new BytesArray(mappingSource); - this.xContentType = xContentType; + this.mediaType = mediaType; return this; } @@ -134,7 +136,7 @@ public PutMappingRequest source(String mappingSource, XContentType xContentType) */ public PutMappingRequest source(XContentBuilder builder) { this.source = BytesReference.bytes(builder); - this.xContentType = builder.contentType(); + this.mediaType = builder.contentType(); return this; } @@ -143,9 +145,9 @@ public PutMappingRequest source(XContentBuilder builder) { * * Note that the definition should *not* be nested under a type name. */ - public PutMappingRequest source(BytesReference source, XContentType xContentType) { + public PutMappingRequest source(BytesReference source, MediaType mediaType) { this.source = source; - this.xContentType = xContentType; + this.mediaType = mediaType; return this; } @@ -153,7 +155,7 @@ public PutMappingRequest source(BytesReference source, XContentType xContentType public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { if (source != null) { try (InputStream stream = source.streamInput()) { - builder.rawValue(stream, xContentType); + builder.rawValue(stream, mediaType); } } else { builder.startObject().endObject(); diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/ResizeRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/ResizeRequest.java index 2a22c8d7d19e9..e53bdd99b3cee 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/ResizeRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/ResizeRequest.java @@ -37,8 +37,9 @@ import org.opensearch.client.Validatable; import org.opensearch.client.ValidationException; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Collections; @@ -58,6 +59,7 @@ public class ResizeRequest extends TimedRequest implements Validatable, ToXConte private final String targetIndex; private Settings settings = Settings.EMPTY; private Set aliases = new HashSet<>(); + private ByteSizeValue maxShardSize; /** * Creates a new resize request @@ -155,6 +157,24 @@ public ActiveShardCount getWaitForActiveShards() { return waitForActiveShards; } + /** + * Sets the maximum size of a primary shard in the new shrunken index. + * This parameter can be used to calculate the lowest factor of the source index's shards number + * which satisfies the maximum shard size requirement. + * + * @param maxShardSize the maximum size of a primary shard in the new shrunken index + */ + public void setMaxShardSize(ByteSizeValue maxShardSize) { + this.maxShardSize = maxShardSize; + } + + /** + * Returns the maximum size of a primary shard in the new shrunken index. + */ + public ByteSizeValue getMaxShardSize() { + return maxShardSize; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/ResizeResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/ResizeResponse.java index cd6346ea81d20..04d690c9922ec 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/ResizeResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/ResizeResponse.java @@ -34,14 +34,14 @@ import org.opensearch.client.core.AcknowledgedResponse; import org.opensearch.client.core.ShardsAcknowledgedResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * The response from a {@link ResizeRequest} call diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/SimulateIndexTemplateRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/SimulateIndexTemplateRequest.java index ab894dfcf7fa1..abc095b3ccac9 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/SimulateIndexTemplateRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/SimulateIndexTemplateRequest.java @@ -34,7 +34,7 @@ import org.opensearch.client.TimedRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; /** * A request to simulate matching a provided index name and an optional new index template against the existing index templates. diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/SimulateIndexTemplateResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/SimulateIndexTemplateResponse.java index a2156a32fa199..d1bc316cc3db4 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/SimulateIndexTemplateResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/SimulateIndexTemplateResponse.java @@ -33,9 +33,9 @@ import org.opensearch.cluster.metadata.Template; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.List; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/rollover/RolloverRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/rollover/RolloverRequest.java index 85f2836db5cba..45211864a3df3 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/rollover/RolloverRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/rollover/RolloverRequest.java @@ -37,10 +37,10 @@ import org.opensearch.action.admin.indices.rollover.MaxSizeCondition; import org.opensearch.client.TimedRequest; import org.opensearch.client.indices.CreateIndexRequest; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.HashMap; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/indices/rollover/RolloverResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/indices/rollover/RolloverResponse.java index 0303dba2535e7..b146d4f94e131 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/indices/rollover/RolloverResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/indices/rollover/RolloverResponse.java @@ -33,14 +33,14 @@ package org.opensearch.client.indices.rollover; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.util.Map; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * Response object for {@link RolloverRequest} API diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/ExecuteSnapshotLifecyclePolicyResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/ExecuteSnapshotLifecyclePolicyResponse.java index 7b22b83bcf533..48cc41653a8b1 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/ExecuteSnapshotLifecyclePolicyResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/ExecuteSnapshotLifecyclePolicyResponse.java @@ -32,11 +32,11 @@ package org.opensearch.client.slm; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/GetSnapshotLifecyclePolicyResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/GetSnapshotLifecyclePolicyResponse.java index 0ed4ea751b42b..c1329e2bacd59 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/GetSnapshotLifecyclePolicyResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/GetSnapshotLifecyclePolicyResponse.java @@ -32,16 +32,16 @@ package org.opensearch.client.slm; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; public class GetSnapshotLifecyclePolicyResponse implements ToXContentObject { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/GetSnapshotLifecycleStatsResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/GetSnapshotLifecycleStatsResponse.java index 5feda2ac49b2f..858a9ebe1ff40 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/GetSnapshotLifecycleStatsResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/GetSnapshotLifecycleStatsResponse.java @@ -32,9 +32,9 @@ package org.opensearch.client.slm; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/PutSnapshotLifecyclePolicyRequest.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/PutSnapshotLifecyclePolicyRequest.java index f6e59c0301b9c..30b271cab45c8 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/PutSnapshotLifecyclePolicyRequest.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/PutSnapshotLifecyclePolicyRequest.java @@ -33,8 +33,8 @@ package org.opensearch.client.slm; import org.opensearch.client.TimedRequest; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Objects; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotInvocationRecord.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotInvocationRecord.java index 6d60003302acd..ca2a33df9a850 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotInvocationRecord.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotInvocationRecord.java @@ -32,11 +32,11 @@ package org.opensearch.client.slm; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecyclePolicy.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecyclePolicy.java index a6e08fc6df018..fd101c575b97a 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecyclePolicy.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecyclePolicy.java @@ -33,12 +33,13 @@ package org.opensearch.client.slm; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Map; @@ -168,6 +169,6 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecyclePolicyMetadata.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecyclePolicyMetadata.java index 199805d348fd4..dd44d16f0d65e 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecyclePolicyMetadata.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecyclePolicyMetadata.java @@ -33,12 +33,13 @@ package org.opensearch.client.slm; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.snapshots.SnapshotId; import java.io.IOException; @@ -288,7 +289,7 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecycleStats.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecycleStats.java index 74d3d21952499..476533d9c91ca 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecycleStats.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotLifecycleStats.java @@ -32,14 +32,15 @@ package org.opensearch.client.slm; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collections; @@ -187,7 +188,7 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } public static class SnapshotPolicyStats implements ToXContentFragment { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotRetentionConfiguration.java b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotRetentionConfiguration.java index 2f2aa778b6958..3165b6bede19d 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotRetentionConfiguration.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/slm/SnapshotRetentionConfiguration.java @@ -33,13 +33,14 @@ package org.opensearch.client.slm; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; @@ -150,6 +151,6 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/CancelTasksResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/CancelTasksResponse.java index 9319e7aa5d292..1c43f9ec1535e 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/CancelTasksResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/CancelTasksResponse.java @@ -31,14 +31,14 @@ package org.opensearch.client.tasks; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.List; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * cancel tasks response that contains diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/GetTaskResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/GetTaskResponse.java index 09aed03f26628..9dfff63712250 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/GetTaskResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/GetTaskResponse.java @@ -31,12 +31,12 @@ package org.opensearch.client.tasks; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.tasks.TaskInfo; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; public class GetTaskResponse { private final boolean completed; diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/NodeData.java b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/NodeData.java index 5449039e1eea1..4ecdff354c37d 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/NodeData.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/NodeData.java @@ -31,14 +31,15 @@ package org.opensearch.client.tasks; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; class NodeData { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/OpenSearchException.java b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/OpenSearchException.java index 777da5fc7b685..2f341e5102a08 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/OpenSearchException.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/OpenSearchException.java @@ -31,8 +31,9 @@ package org.opensearch.client.tasks; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.XContentParser; + import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -40,7 +41,7 @@ import java.util.Map; import java.util.Objects; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * client side counterpart of server side diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskId.java b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskId.java index cd036a732957b..c2cf2c826b8bd 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskId.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskId.java @@ -34,7 +34,7 @@ import java.util.Objects; /** - * client side version of a {@link org.opensearch.tasks.TaskId} + * client side version of a {@link org.opensearch.core.tasks.TaskId} */ public class TaskId { diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskInfo.java b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskInfo.java index de8374b283ea6..75badc4e3dbf2 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskInfo.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskInfo.java @@ -31,9 +31,9 @@ package org.opensearch.client.tasks; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.util.HashMap; import java.util.Map; @@ -54,9 +54,11 @@ public class TaskInfo { private long runningTimeNanos; private boolean cancellable; private boolean cancelled; + private Long cancellationStartTime; private TaskId parentTaskId; private final Map status = new HashMap<>(); private final Map headers = new HashMap<>(); + private final Map resourceStats = new HashMap<>(); public TaskInfo(TaskId taskId) { this.taskId = taskId; @@ -126,6 +128,14 @@ void setCancelled(boolean cancelled) { this.cancelled = cancelled; } + public Long getCancellationStartTime() { + return this.cancellationStartTime; + } + + public void setCancellationStartTime(Long cancellationStartTime) { + this.cancellationStartTime = cancellationStartTime; + } + public TaskId getParentTaskId() { return parentTaskId; } @@ -150,6 +160,14 @@ public Map getStatus() { return status; } + void setResourceStats(Map resourceStats) { + this.resourceStats.putAll(resourceStats); + } + + public Map getResourceStats() { + return resourceStats; + } + private void noOpParse(Object s) {} public static final ObjectParser.NamedObjectParser PARSER; @@ -170,6 +188,8 @@ private void noOpParse(Object s) {} parser.declareBoolean(TaskInfo::setCancelled, new ParseField("cancelled")); parser.declareString(TaskInfo::setParentTaskId, new ParseField("parent_task_id")); parser.declareObject(TaskInfo::setHeaders, (p, c) -> p.mapStrings(), new ParseField("headers")); + parser.declareObject(TaskInfo::setResourceStats, (p, c) -> p.map(), new ParseField("resource_stats")); + parser.declareLong(TaskInfo::setCancellationStartTime, new ParseField("cancellation_time_millis")); PARSER = (XContentParser p, Void v, String name) -> parser.parse(p, new TaskInfo(new TaskId(name)), null); } @@ -188,7 +208,9 @@ && isCancelled() == taskInfo.isCancelled() && Objects.equals(getDescription(), taskInfo.getDescription()) && Objects.equals(getParentTaskId(), taskInfo.getParentTaskId()) && Objects.equals(status, taskInfo.status) - && Objects.equals(getHeaders(), taskInfo.getHeaders()); + && Objects.equals(getHeaders(), taskInfo.getHeaders()) + && Objects.equals(getResourceStats(), taskInfo.getResourceStats()) + && Objects.equals(getCancellationStartTime(), taskInfo.cancellationStartTime); } @Override @@ -204,7 +226,9 @@ public int hashCode() { isCancelled(), getParentTaskId(), status, - getHeaders() + getHeaders(), + getResourceStats(), + getCancellationStartTime() ); } @@ -236,6 +260,10 @@ public String toString() { + status + ", headers=" + headers + + ", resource_stats=" + + resourceStats + + ", cancellationStartTime=" + + cancellationStartTime + '}'; } } diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskOperationFailure.java b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskOperationFailure.java index f173da9daa8fc..c4395f5cb81d1 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskOperationFailure.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskOperationFailure.java @@ -31,13 +31,13 @@ package org.opensearch.client.tasks; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * client side counterpart of server side diff --git a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskSubmissionResponse.java b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskSubmissionResponse.java index 6077455224b7d..7ec3e974de468 100644 --- a/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskSubmissionResponse.java +++ b/client/rest-high-level/src/main/java/org/opensearch/client/tasks/TaskSubmissionResponse.java @@ -32,9 +32,9 @@ package org.opensearch.client.tasks; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; diff --git a/client/rest-high-level/src/main/resources/forbidden/rest-high-level-signatures.txt b/client/rest-high-level/src/main/resources/forbidden/rest-high-level-signatures.txt index 68dc509e5ff27..bee105f05e6b4 100644 --- a/client/rest-high-level/src/main/resources/forbidden/rest-high-level-signatures.txt +++ b/client/rest-high-level/src/main/resources/forbidden/rest-high-level-signatures.txt @@ -14,7 +14,7 @@ # either express or implied. See the License for the specific # language governing permissions and limitations under the License. -@defaultMessage Use Request#createContentType(XContentType) to be sure to pass the right MIME type +@defaultMessage Use Request#createContentType(MediaType) to be sure to pass the right MIME type org.apache.http.entity.ContentType#create(java.lang.String) org.apache.http.entity.ContentType#create(java.lang.String,java.lang.String) org.apache.http.entity.ContentType#create(java.lang.String,java.nio.charset.Charset) @@ -23,7 +23,7 @@ org.apache.http.entity.ContentType#create(java.lang.String,org.apache.http.NameV @defaultMessage ES's logging infrastructure uses log4j2 which we don't want to force on high level rest client users org.opensearch.common.logging.DeprecationLogger org.opensearch.common.logging.LogConfigurator -org.opensearch.common.logging.LoggerMessageFormat +org.opensearch.core.common.logging.LoggerMessageFormat org.opensearch.common.logging.Loggers org.opensearch.common.logging.NodeNamePatternConverter org.opensearch.common.logging.PrefixLogger diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/AbstractRequestTestCase.java b/client/rest-high-level/src/test/java/org/opensearch/client/AbstractRequestTestCase.java index e63375c34f97e..49bcb61b2dc3d 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/AbstractRequestTestCase.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/AbstractRequestTestCase.java @@ -31,14 +31,13 @@ package org.opensearch.client; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; @@ -60,7 +59,7 @@ public final void testFromXContent() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); final BytesReference bytes = toShuffledXContent(clientTestInstance, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean()); - final XContent xContent = XContentFactory.xContent(xContentType); + final XContent xContent = xContentType.xContent(); final XContentParser parser = xContent.createParser(xContentRegistry(), LoggingDeprecationHandler.INSTANCE, bytes.streamInput()); final S serverInstance = doParseToServerInstance(parser); assertInstances(serverInstance, clientTestInstance); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/AbstractResponseTestCase.java b/client/rest-high-level/src/test/java/org/opensearch/client/AbstractResponseTestCase.java index 95188ec0f8e96..27704b01560c4 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/AbstractResponseTestCase.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/AbstractResponseTestCase.java @@ -31,22 +31,16 @@ package org.opensearch.client; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; /** * Base class for HLRC response parsing tests. @@ -64,7 +58,7 @@ public final void testFromXContent() throws IOException { final S serverTestInstance = createServerTestInstance(xContentType); final BytesReference bytes = toShuffledXContent(serverTestInstance, xContentType, getParams(), randomBoolean()); - final XContent xContent = XContentFactory.xContent(xContentType); + final XContent xContent = xContentType.xContent(); final XContentParser parser = xContent.createParser( NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, @@ -103,17 +97,4 @@ public final void testFromXContent() throws IOException { protected ToXContent.Params getParams() { return ToXContent.EMPTY_PARAMS; } - - protected static void assertMapEquals(ImmutableOpenMap expected, Map actual) { - Set expectedKeys = new HashSet<>(); - Iterator keysIt = expected.keysIt(); - while (keysIt.hasNext()) { - expectedKeys.add(keysIt.next()); - } - - assertEquals(expectedKeys, actual.keySet()); - for (String key : expectedKeys) { - assertEquals(expected.get(key), actual.get(key)); - } - } } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/BulkProcessorIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/BulkProcessorIT.java index cc9abdccf4c9f..1d70778398e2e 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/BulkProcessorIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/BulkProcessorIT.java @@ -33,6 +33,7 @@ package org.opensearch.client; import com.carrotsearch.randomizedtesting.generators.RandomPicks; + import org.opensearch.action.bulk.BulkItemResponse; import org.opensearch.action.bulk.BulkProcessor; import org.opensearch.action.bulk.BulkRequest; @@ -42,13 +43,12 @@ import org.opensearch.action.get.MultiGetResponse; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.SearchRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.SearchHit; import org.hamcrest.Matcher; @@ -278,12 +278,12 @@ public void testBulkProcessorConcurrentRequestsReadOnlyIndex() throws Exception // let's make sure we get at least 1 item in the MultiGetRequest regardless of the randomising roulette if (randomBoolean() || multiGetRequest.getItems().size() == 0) { testDocs++; - processor.add(new IndexRequest("test").id(Integer.toString(testDocs)).source(XContentType.JSON, "field", "value")); + processor.add(new IndexRequest("test").id(Integer.toString(testDocs)).source(MediaTypeRegistry.JSON, "field", "value")); multiGetRequest.add("test", Integer.toString(testDocs)); } else { testReadOnlyDocs++; processor.add( - new IndexRequest("test-ro").id(Integer.toString(testReadOnlyDocs)).source(XContentType.JSON, "field", "value") + new IndexRequest("test-ro").id(Integer.toString(testReadOnlyDocs)).source(MediaTypeRegistry.JSON, "field", "value") ); } } @@ -334,9 +334,9 @@ public void testGlobalParametersAndSingleRequest() throws Exception { processor.add(new IndexRequest() // <1> - .source(XContentType.JSON, "user", "some user")); + .source(MediaTypeRegistry.JSON, "user", "some user")); processor.add(new IndexRequest("blogs").id("1") // <2> - .source(XContentType.JSON, "title", "some title")); + .source(MediaTypeRegistry.JSON, "title", "some title")); } // end::bulk-processor-mix-parameters latch.await(); @@ -400,11 +400,11 @@ private MultiGetRequest indexDocs(BulkProcessor processor, int numDocs, String l if (randomBoolean()) { processor.add( new IndexRequest(localIndex).id(Integer.toString(i)) - .source(XContentType.JSON, "field", randomRealisticUnicodeOfLengthBetween(1, 30)) + .source(MediaTypeRegistry.JSON, "field", randomRealisticUnicodeOfLengthBetween(1, 30)) ); } else { BytesArray data = bytesBulkRequest(localIndex, i); - processor.add(data, globalIndex, globalPipeline, XContentType.JSON); + processor.add(data, globalIndex, globalPipeline, MediaTypeRegistry.JSON); } multiGetRequest.add(localIndex, Integer.toString(i)); } @@ -423,7 +423,7 @@ private static BytesArray bytesBulkRequest(String localIndex, int id) throws IOE XContentBuilder source = jsonBuilder().startObject().field("field", randomRealisticUnicodeOfLengthBetween(1, 30)).endObject(); - String request = Strings.toString(action) + "\n" + Strings.toString(source) + "\n"; + String request = action + "\n" + source + "\n"; return new BytesArray(request); } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/BulkProcessorRetryIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/BulkProcessorRetryIT.java index 0744fe4e6db3e..b7f6328b3c88e 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/BulkProcessorRetryIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/BulkProcessorRetryIT.java @@ -40,8 +40,8 @@ import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.transport.RemoteTransportException; import java.util.Collections; @@ -170,7 +170,7 @@ private static MultiGetRequest indexDocs(BulkProcessor processor, int numDocs) { for (int i = 1; i <= numDocs; i++) { processor.add( new IndexRequest(INDEX_NAME).id(Integer.toString(i)) - .source(XContentType.JSON, "field", randomRealisticUnicodeOfCodepointLengthBetween(1, 30)) + .source(MediaTypeRegistry.JSON, "field", randomRealisticUnicodeOfCodepointLengthBetween(1, 30)) ); multiGetRequest.add(INDEX_NAME, Integer.toString(i)); } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/BulkRequestWithGlobalParametersIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/BulkRequestWithGlobalParametersIT.java index 35fc9d88e316c..d392aa842fb35 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/BulkRequestWithGlobalParametersIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/BulkRequestWithGlobalParametersIT.java @@ -36,7 +36,7 @@ import org.opensearch.action.bulk.BulkResponse; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.SearchRequest; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.search.SearchHit; import java.io.IOException; @@ -59,8 +59,8 @@ public void testGlobalPipelineOnBulkRequest() throws IOException { createFieldAddingPipleine("xyz", "fieldNameXYZ", "valueXYZ"); BulkRequest request = new BulkRequest(); - request.add(new IndexRequest("test").id("1").source(XContentType.JSON, "field", "bulk1")); - request.add(new IndexRequest("test").id("2").source(XContentType.JSON, "field", "bulk2")); + request.add(new IndexRequest("test").id("1").source(MediaTypeRegistry.JSON, "field", "bulk1")); + request.add(new IndexRequest("test").id("2").source(MediaTypeRegistry.JSON, "field", "bulk2")); request.pipeline("xyz"); bulk(request); @@ -76,8 +76,8 @@ public void testPipelineOnRequestOverridesGlobalPipeline() throws IOException { BulkRequest request = new BulkRequest(); request.pipeline("globalId"); - request.add(new IndexRequest("test").id("1").source(XContentType.JSON, "field", "bulk1").setPipeline("perIndexId")); - request.add(new IndexRequest("test").id("2").source(XContentType.JSON, "field", "bulk2").setPipeline("perIndexId")); + request.add(new IndexRequest("test").id("1").source(MediaTypeRegistry.JSON, "field", "bulk1").setPipeline("perIndexId")); + request.add(new IndexRequest("test").id("2").source(MediaTypeRegistry.JSON, "field", "bulk2").setPipeline("perIndexId")); bulk(request); @@ -96,11 +96,11 @@ public void testMixPipelineOnRequestAndGlobal() throws IOException { request.pipeline("globalId"); request.add(new IndexRequest("test").id("1") - .source(XContentType.JSON, "field", "bulk1") + .source(MediaTypeRegistry.JSON, "field", "bulk1") .setPipeline("perIndexId")); // <1> request.add(new IndexRequest("test").id("2") - .source(XContentType.JSON, "field", "bulk2")); // <2> + .source(MediaTypeRegistry.JSON, "field", "bulk2")); // <2> // end::bulk-request-mix-pipeline bulk(request); @@ -116,8 +116,8 @@ public void testMixPipelineOnRequestAndGlobal() throws IOException { public void testGlobalIndex() throws IOException { BulkRequest request = new BulkRequest("global_index"); - request.add(new IndexRequest().id("1").source(XContentType.JSON, "field", "bulk1")); - request.add(new IndexRequest().id("2").source(XContentType.JSON, "field", "bulk2")); + request.add(new IndexRequest().id("1").source(MediaTypeRegistry.JSON, "field", "bulk1")); + request.add(new IndexRequest().id("2").source(MediaTypeRegistry.JSON, "field", "bulk2")); bulk(request); @@ -128,10 +128,10 @@ public void testGlobalIndex() throws IOException { @SuppressWarnings("unchecked") public void testIndexGlobalAndPerRequest() throws IOException { BulkRequest request = new BulkRequest("global_index"); - request.add(new IndexRequest("local_index").id("1").source(XContentType.JSON, "field", "bulk1")); + request.add(new IndexRequest("local_index").id("1").source(MediaTypeRegistry.JSON, "field", "bulk1")); request.add( new IndexRequest().id("2") // will take global index - .source(XContentType.JSON, "field", "bulk2") + .source(MediaTypeRegistry.JSON, "field", "bulk2") ); bulk(request); @@ -143,8 +143,8 @@ public void testIndexGlobalAndPerRequest() throws IOException { public void testGlobalRouting() throws IOException { createIndexWithMultipleShards("index"); BulkRequest request = new BulkRequest((String) null); - request.add(new IndexRequest("index").id("1").source(XContentType.JSON, "field", "bulk1")); - request.add(new IndexRequest("index").id("2").source(XContentType.JSON, "field", "bulk1")); + request.add(new IndexRequest("index").id("1").source(MediaTypeRegistry.JSON, "field", "bulk1")); + request.add(new IndexRequest("index").id("2").source(MediaTypeRegistry.JSON, "field", "bulk1")); request.routing("1"); bulk(request); @@ -158,8 +158,8 @@ public void testGlobalRouting() throws IOException { public void testMixLocalAndGlobalRouting() throws IOException { BulkRequest request = new BulkRequest((String) null); request.routing("globalRouting"); - request.add(new IndexRequest("index").id("1").source(XContentType.JSON, "field", "bulk1")); - request.add(new IndexRequest("index").id("2").routing("localRouting").source(XContentType.JSON, "field", "bulk1")); + request.add(new IndexRequest("index").id("1").source(MediaTypeRegistry.JSON, "field", "bulk1")); + request.add(new IndexRequest("index").id("2").routing("localRouting").source(MediaTypeRegistry.JSON, "field", "bulk1")); bulk(request); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/ClusterClientIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/ClusterClientIT.java index 71b869fb59e7b..66e8d8c7f5c04 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/ClusterClientIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/ClusterClientIT.java @@ -60,12 +60,12 @@ import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.indices.recovery.RecoverySettings; -import org.opensearch.rest.RestStatus; import org.opensearch.transport.RemoteClusterService; import org.opensearch.transport.SniffConnectionStrategy; @@ -124,7 +124,7 @@ public void testClusterPutSettings() throws IOException { ClusterUpdateSettingsRequest resetRequest = new ClusterUpdateSettingsRequest(); resetRequest.transientSettings(Settings.builder().putNull(transientSettingKey)); - resetRequest.persistentSettings("{\"" + persistentSettingKey + "\": null }", XContentType.JSON); + resetRequest.persistentSettings("{\"" + persistentSettingKey + "\": null }", MediaTypeRegistry.JSON); ClusterUpdateSettingsResponse resetResponse = execute( resetRequest, diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/ClusterRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/ClusterRequestConvertersTests.java index 2af164a51dbab..da9de98cad141 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/ClusterRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/ClusterRequestConvertersTests.java @@ -42,7 +42,7 @@ import org.opensearch.client.cluster.RemoteInfoRequest; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Priority; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.util.CollectionUtils; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.CoreMatchers; import org.junit.Assert; @@ -91,7 +91,7 @@ public void testClusterHealth() { RequestConvertersTests.setRandomLocal(healthRequest::local, expectedParams); String timeoutType = OpenSearchTestCase.randomFrom("timeout", "masterTimeout", "both", "none"); String timeout = OpenSearchTestCase.randomTimeValue(); - String masterTimeout = OpenSearchTestCase.randomTimeValue(); + String clusterManagerTimeout = OpenSearchTestCase.randomTimeValue(); switch (timeoutType) { case "timeout": healthRequest.timeout(timeout); @@ -101,13 +101,13 @@ public void testClusterHealth() { break; case "masterTimeout": expectedParams.put("timeout", "30s"); - healthRequest.masterNodeTimeout(masterTimeout); - expectedParams.put("master_timeout", masterTimeout); + healthRequest.clusterManagerNodeTimeout(clusterManagerTimeout); + expectedParams.put("master_timeout", clusterManagerTimeout); break; case "both": healthRequest.timeout(timeout); expectedParams.put("timeout", timeout); - healthRequest.masterNodeTimeout(timeout); + healthRequest.clusterManagerNodeTimeout(timeout); expectedParams.put("master_timeout", timeout); break; case "none": diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/CrudIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/CrudIT.java index 999c2a0e7643b..da9f790215669 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/CrudIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/CrudIT.java @@ -58,17 +58,19 @@ import org.opensearch.client.core.TermVectorsRequest; import org.opensearch.client.core.TermVectorsResponse; import org.opensearch.client.indices.GetIndexRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.VersionType; import org.opensearch.index.get.GetResult; -import org.opensearch.rest.RestStatus; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.fetch.subphase.FetchSourceContext; @@ -202,7 +204,7 @@ public void testExists() throws IOException { assertFalse(execute(getRequest, highLevelClient()::exists, highLevelClient()::existsAsync)); } IndexRequest index = new IndexRequest("index").id("id"); - index.source("{\"field1\":\"value1\",\"field2\":\"value2\"}", XContentType.JSON); + index.source("{\"field1\":\"value1\",\"field2\":\"value2\"}", MediaTypeRegistry.JSON); index.setRefreshPolicy(RefreshPolicy.IMMEDIATE); highLevelClient().index(index, RequestOptions.DEFAULT); { @@ -227,7 +229,7 @@ public void testDeprecatedSourceExists() throws IOException { assertFalse(execute(getRequest, highLevelClient()::existsSource, highLevelClient()::existsSourceAsync)); } IndexRequest index = new IndexRequest("index").id("id"); - index.source("{\"field1\":\"value1\",\"field2\":\"value2\"}", XContentType.JSON); + index.source("{\"field1\":\"value1\",\"field2\":\"value2\"}", MediaTypeRegistry.JSON); index.setRefreshPolicy(RefreshPolicy.IMMEDIATE); highLevelClient().index(index, RequestOptions.DEFAULT); { @@ -250,7 +252,7 @@ public void testSourceExists() throws IOException { assertFalse(execute(getRequest, highLevelClient()::existsSource, highLevelClient()::existsSourceAsync)); } IndexRequest index = new IndexRequest("index").id("id"); - index.source("{\"field1\":\"value1\",\"field2\":\"value2\"}", XContentType.JSON); + index.source("{\"field1\":\"value1\",\"field2\":\"value2\"}", MediaTypeRegistry.JSON); index.setRefreshPolicy(RefreshPolicy.IMMEDIATE); highLevelClient().index(index, RequestOptions.DEFAULT); { @@ -274,9 +276,9 @@ public void testSourceDoesNotExist() throws IOException { RestStatus.OK, highLevelClient().bulk( new BulkRequest().add( - new IndexRequest(noSourceIndex).id("1").source(Collections.singletonMap("foo", 1), XContentType.JSON) + new IndexRequest(noSourceIndex).id("1").source(Collections.singletonMap("foo", 1), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(noSourceIndex).id("2").source(Collections.singletonMap("foo", 2), XContentType.JSON)) + .add(new IndexRequest(noSourceIndex).id("2").source(Collections.singletonMap("foo", 2), MediaTypeRegistry.JSON)) .setRefreshPolicy(RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT ).status() @@ -306,7 +308,7 @@ public void testGet() throws IOException { } IndexRequest index = new IndexRequest("index").id("id"); String document = "{\"field1\":\"value1\",\"field2\":\"value2\"}"; - index.source(document, XContentType.JSON); + index.source(document, MediaTypeRegistry.JSON); index.setRefreshPolicy(RefreshPolicy.IMMEDIATE); highLevelClient().index(index, RequestOptions.DEFAULT); { @@ -406,10 +408,10 @@ public void testMultiGet() throws IOException { BulkRequest bulk = new BulkRequest(); bulk.setRefreshPolicy(RefreshPolicy.IMMEDIATE); IndexRequest index = new IndexRequest("index").id("id1"); - index.source("{\"field\":\"value1\"}", XContentType.JSON); + index.source("{\"field\":\"value1\"}", MediaTypeRegistry.JSON); bulk.add(index); index = new IndexRequest("index").id("id2"); - index.source("{\"field\":\"value2\"}", XContentType.JSON); + index.source("{\"field\":\"value2\"}", MediaTypeRegistry.JSON); bulk.add(index); highLevelClient().bulk(bulk, RequestOptions.DEFAULT); { @@ -436,8 +438,8 @@ public void testMultiGet() throws IOException { public void testMultiGetWithIds() throws IOException { BulkRequest bulk = new BulkRequest(); bulk.setRefreshPolicy(RefreshPolicy.IMMEDIATE); - bulk.add(new IndexRequest("index").id("id1").source("{\"field\":\"value1\"}", XContentType.JSON)); - bulk.add(new IndexRequest("index").id("id2").source("{\"field\":\"value2\"}", XContentType.JSON)); + bulk.add(new IndexRequest("index").id("id1").source("{\"field\":\"value1\"}", MediaTypeRegistry.JSON)); + bulk.add(new IndexRequest("index").id("id2").source("{\"field\":\"value2\"}", MediaTypeRegistry.JSON)); MultiGetRequest multiGetRequest = new MultiGetRequest(); multiGetRequest.add("index", "id1"); @@ -457,7 +459,7 @@ public void testGetSource() throws IOException { } IndexRequest index = new IndexRequest("index").id("id"); String document = "{\"field1\":\"value1\",\"field2\":\"value2\"}"; - index.source(document, XContentType.JSON); + index.source(document, MediaTypeRegistry.JSON); index.setRefreshPolicy(RefreshPolicy.IMMEDIATE); highLevelClient().index(index, RequestOptions.DEFAULT); { @@ -628,10 +630,9 @@ public void testIndex() throws IOException { assertEquals("index", indexResponse.getIndex()); assertEquals("with_create_op_type", indexResponse.getId()); - OpenSearchStatusException exception = expectThrows( - OpenSearchStatusException.class, - () -> { execute(indexRequest, highLevelClient()::index, highLevelClient()::indexAsync); } - ); + OpenSearchStatusException exception = expectThrows(OpenSearchStatusException.class, () -> { + execute(indexRequest, highLevelClient()::index, highLevelClient()::indexAsync); + }); assertEquals(RestStatus.CONFLICT, exception.status()); assertEquals( @@ -816,7 +817,7 @@ public void testUpdate() throws IOException { { IllegalStateException exception = expectThrows(IllegalStateException.class, () -> { UpdateRequest updateRequest = new UpdateRequest("index", "id"); - updateRequest.doc(new IndexRequest().source(Collections.singletonMap("field", "doc"), XContentType.JSON)); + updateRequest.doc(new IndexRequest().source(Collections.singletonMap("field", "doc"), MediaTypeRegistry.JSON)); updateRequest.upsert(new IndexRequest().source(Collections.singletonMap("field", "upsert"), XContentType.YAML)); execute(updateRequest, highLevelClient()::update, highLevelClient()::updateAsync); }); @@ -828,7 +829,7 @@ public void testUpdate() throws IOException { { OpenSearchException exception = expectThrows(OpenSearchException.class, () -> { UpdateRequest updateRequest = new UpdateRequest("index", "require_alias").setRequireAlias(true); - updateRequest.doc(new IndexRequest().source(Collections.singletonMap("field", "doc"), XContentType.JSON)); + updateRequest.doc(new IndexRequest().source(Collections.singletonMap("field", "doc"), MediaTypeRegistry.JSON)); execute(updateRequest, highLevelClient()::update, highLevelClient()::updateAsync); }); assertEquals(RestStatus.NOT_FOUND, exception.status()); @@ -843,7 +844,7 @@ public void testBulk() throws IOException { int nbItems = randomIntBetween(10, 100); boolean[] errors = new boolean[nbItems]; - XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE); + MediaType mediaType = randomFrom(MediaTypeRegistry.JSON, XContentType.SMILE); BulkRequest bulkRequest = new BulkRequest(); for (int i = 0; i < nbItems; i++) { @@ -864,10 +865,10 @@ public void testBulk() throws IOException { } else { BytesReference source = BytesReference.bytes( - XContentBuilder.builder(xContentType.xContent()).startObject().field("id", i).endObject() + XContentBuilder.builder(mediaType.xContent()).startObject().field("id", i).endObject() ); if (opType == DocWriteRequest.OpType.INDEX) { - IndexRequest indexRequest = new IndexRequest("index").id(id).source(source, xContentType); + IndexRequest indexRequest = new IndexRequest("index").id(id).source(source, mediaType); if (erroneous) { indexRequest.setIfSeqNo(12L); indexRequest.setIfPrimaryTerm(12L); @@ -875,14 +876,14 @@ public void testBulk() throws IOException { bulkRequest.add(indexRequest); } else if (opType == DocWriteRequest.OpType.CREATE) { - IndexRequest createRequest = new IndexRequest("index").id(id).source(source, xContentType).create(true); + IndexRequest createRequest = new IndexRequest("index").id(id).source(source, mediaType).create(true); if (erroneous) { assertEquals(RestStatus.CREATED, highLevelClient().index(createRequest, RequestOptions.DEFAULT).status()); } bulkRequest.add(createRequest); } else if (opType == DocWriteRequest.OpType.UPDATE) { - UpdateRequest updateRequest = new UpdateRequest("index", id).doc(new IndexRequest().source(source, xContentType)); + UpdateRequest updateRequest = new UpdateRequest("index", id).doc(new IndexRequest().source(source, mediaType)); if (erroneous == false) { assertEquals( RestStatus.CREATED, @@ -906,7 +907,7 @@ public void testBulkProcessorIntegration() throws IOException { int nbItems = randomIntBetween(10, 100); boolean[] errors = new boolean[nbItems]; - XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE); + MediaType mediaType = randomFrom(MediaTypeRegistry.JSON, XContentType.SMILE); AtomicReference responseRef = new AtomicReference<>(); AtomicReference requestRef = new AtomicReference<>(); @@ -954,7 +955,7 @@ public void afterBulk(long executionId, BulkRequest request, Throwable failure) } else { if (opType == DocWriteRequest.OpType.INDEX) { - IndexRequest indexRequest = new IndexRequest("index").id(id).source(xContentType, "id", i); + IndexRequest indexRequest = new IndexRequest("index").id(id).source(mediaType, "id", i); if (erroneous) { indexRequest.setIfSeqNo(12L); indexRequest.setIfPrimaryTerm(12L); @@ -962,14 +963,14 @@ public void afterBulk(long executionId, BulkRequest request, Throwable failure) processor.add(indexRequest); } else if (opType == DocWriteRequest.OpType.CREATE) { - IndexRequest createRequest = new IndexRequest("index").id(id).source(xContentType, "id", i).create(true); + IndexRequest createRequest = new IndexRequest("index").id(id).source(mediaType, "id", i).create(true); if (erroneous) { assertEquals(RestStatus.CREATED, highLevelClient().index(createRequest, RequestOptions.DEFAULT).status()); } processor.add(createRequest); } else if (opType == DocWriteRequest.OpType.UPDATE) { - UpdateRequest updateRequest = new UpdateRequest("index", id).doc(new IndexRequest().source(xContentType, "id", i)); + UpdateRequest updateRequest = new UpdateRequest("index", id).doc(new IndexRequest().source(mediaType, "id", i)); if (erroneous == false) { assertEquals( RestStatus.CREATED, @@ -1107,9 +1108,12 @@ public void testTermvectors() throws IOException { RestStatus.OK, highLevelClient().bulk( new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("field", "value1"), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("field", "value1"), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("field", "value2"), XContentType.JSON)) + .add( + new IndexRequest(sourceIndex).id("2") + .source(Collections.singletonMap("field", "value2"), MediaTypeRegistry.JSON) + ) .setRefreshPolicy(RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT ).status() @@ -1202,8 +1206,8 @@ public void testMultiTermvectors() throws IOException { assertEquals( RestStatus.OK, highLevelClient().bulk( - new BulkRequest().add(new IndexRequest(sourceIndex).id("1").source(doc1, XContentType.JSON)) - .add(new IndexRequest(sourceIndex).id("2").source(doc2, XContentType.JSON)) + new BulkRequest().add(new IndexRequest(sourceIndex).id("1").source(doc1, MediaTypeRegistry.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(doc2, MediaTypeRegistry.JSON)) .setRefreshPolicy(RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT ).status() diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/CustomRestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/CustomRestHighLevelClientTests.java index 1d94f190c611c..5204108a1d2a6 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/CustomRestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/CustomRestHighLevelClientTests.java @@ -44,14 +44,14 @@ import org.apache.lucene.util.BytesRef; import org.opensearch.Build; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.main.MainRequest; import org.opensearch.action.main.MainResponse; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.cluster.ClusterName; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentHelper; import org.opensearch.test.OpenSearchTestCase; import org.junit.Before; @@ -175,7 +175,7 @@ private Response mockPerformRequest(Request request) throws IOException { when(mockResponse.getStatusLine()).thenReturn(new BasicStatusLine(protocol, 200, "OK")); MainResponse response = new MainResponse(httpHeader.getValue(), Version.CURRENT, ClusterName.DEFAULT, "_na", Build.CURRENT); - BytesRef bytesRef = XContentHelper.toXContent(response, XContentType.JSON, false).toBytesRef(); + BytesRef bytesRef = XContentHelper.toXContent(response, MediaTypeRegistry.JSON, false).toBytesRef(); when(mockResponse.getEntity()).thenReturn(new NByteArrayEntity(bytesRef.bytes, ContentType.APPLICATION_JSON)); RequestLine requestLine = new BasicRequestLine(HttpGet.METHOD_NAME, ENDPOINT, protocol); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/GetAliasesResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/GetAliasesResponseTests.java index 67b1c0bc3b9a9..245a720dc9825 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/GetAliasesResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/GetAliasesResponseTests.java @@ -33,9 +33,9 @@ package org.opensearch.client; import org.opensearch.cluster.metadata.AliasMetadata; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.AbstractXContentTestCase; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/IndicesClientIT.java index f9c8851f8839e..649e921b54cbe 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/IndicesClientIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/IndicesClientIT.java @@ -107,24 +107,24 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.cluster.metadata.Template; -import org.opensearch.common.Strings; import org.opensearch.common.ValidationException; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.rest.RestStatus; import java.io.IOException; import java.util.Arrays; @@ -134,6 +134,10 @@ import java.util.Map; import java.util.stream.Collectors; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.common.xcontent.support.XContentMapValues.extractRawValues; +import static org.opensearch.common.xcontent.support.XContentMapValues.extractValue; import static org.hamcrest.CoreMatchers.hasItem; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.contains; @@ -149,10 +153,6 @@ import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; -import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; -import static org.opensearch.common.xcontent.support.XContentMapValues.extractRawValues; -import static org.opensearch.common.xcontent.support.XContentMapValues.extractValue; public class IndicesClientIT extends OpenSearchRestHighLevelClientTestCase { @@ -1074,7 +1074,7 @@ public void testRollover() throws IOException { } { String mappings = "{\"properties\":{\"field2\":{\"type\":\"keyword\"}}}"; - rolloverRequest.getCreateIndexRequest().mapping(mappings, XContentType.JSON); + rolloverRequest.getCreateIndexRequest().mapping(mappings, MediaTypeRegistry.JSON); rolloverRequest.dryRun(false); rolloverRequest.addMaxIndexSizeCondition(new ByteSizeValue(1, ByteSizeUnit.MB)); RolloverResponse rolloverResponse = execute( @@ -1490,7 +1490,7 @@ public void testPutTemplate() throws Exception { .order(10) .create(randomBoolean()) .settings(Settings.builder().put("number_of_shards", "3").put("number_of_replicas", "0")) - .mapping("{ \"properties\": { \"host_name\": { \"type\": \"keyword\" } } }", XContentType.JSON) + .mapping("{ \"properties\": { \"host_name\": { \"type\": \"keyword\" } } }", MediaTypeRegistry.JSON) .alias(new Alias("alias-1").indexRouting("abc")) .alias(new Alias("alias-1").indexRouting("abc")) .alias(new Alias("{index}-write").searchRouting("xyz")); @@ -1559,7 +1559,7 @@ public void testPutTemplateWithTypesUsingUntypedAPI() throws Exception { + " }" + " }" + "}", - XContentType.JSON + MediaTypeRegistry.JSON ) .alias(new Alias("alias-1").indexRouting("abc")) .alias(new Alias("{index}-write").searchRouting("xyz")); @@ -1665,7 +1665,7 @@ public void testCRUDIndexTemplate() throws Exception { equalTo(true) ); PutIndexTemplateRequest putTemplate2 = new PutIndexTemplateRequest("template-2").patterns(Arrays.asList("pattern-2", "name-2")) - .mapping("{\"properties\": { \"name\": { \"type\": \"text\" }}}", XContentType.JSON) + .mapping("{\"properties\": { \"name\": { \"type\": \"text\" }}}", MediaTypeRegistry.JSON) .settings(Settings.builder().put("number_of_shards", "2").put("number_of_replicas", "0")); assertThat( execute(putTemplate2, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged(), @@ -2032,8 +2032,8 @@ public void testSimulateIndexTemplate() throws Exception { Settings settings = Settings.builder().put("index.number_of_shards", 1).build(); CompressedXContent mappings = new CompressedXContent("{\"properties\":{\"host_name\":{\"type\":\"keyword\"}}}"); AliasMetadata alias = AliasMetadata.builder("alias").writeIndex(true).build(); - Template template = new Template(settings, mappings, org.opensearch.common.collect.Map.of("alias", alias)); - List pattern = org.opensearch.common.collect.List.of("pattern"); + Template template = new Template(settings, mappings, Map.of("alias", alias)); + List pattern = List.of("pattern"); ComposableIndexTemplate indexTemplate = new ComposableIndexTemplate( pattern, template, @@ -2058,7 +2058,7 @@ public void testSimulateIndexTemplate() throws Exception { AliasMetadata simulationAlias = AliasMetadata.builder("simulation-alias").writeIndex(true).build(); ComposableIndexTemplate simulationTemplate = new ComposableIndexTemplate( pattern, - new Template(null, null, org.opensearch.common.collect.Map.of("simulation-alias", simulationAlias)), + new Template(null, null, Map.of("simulation-alias", simulationAlias)), Collections.emptyList(), 2L, 1L, diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/IndicesRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/IndicesRequestConvertersTests.java index f853378e789fa..9adef6981788e 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/IndicesRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/IndicesRequestConvertersTests.java @@ -72,11 +72,12 @@ import org.opensearch.client.indices.ResizeRequest; import org.opensearch.client.indices.rollover.RolloverRequest; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.CollectionUtils; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.test.OpenSearchTestCase; import org.junit.Assert; @@ -595,6 +596,10 @@ public void testClearCache() { clearIndicesCacheRequest.fields(RequestConvertersTests.randomIndicesNames(1, 5)); expectedParams.put("fields", String.join(",", clearIndicesCacheRequest.fields())); } + if (OpenSearchTestCase.randomBoolean()) { + clearIndicesCacheRequest.fileCache(OpenSearchTestCase.randomBoolean()); + } + expectedParams.put("file", Boolean.toString(clearIndicesCacheRequest.fileCache())); Request request = IndicesRequestConverters.clearCache(clearIndicesCacheRequest); StringJoiner endpoint = new StringJoiner("/", "/", ""); @@ -701,6 +706,8 @@ private void resizeTest(ResizeType resizeType, CheckedFunction expectedParams = new HashMap<>(); RequestConvertersTests.setRandomMasterTimeout(request, expectedParams); @@ -121,7 +121,6 @@ public void testSimulatePipeline() throws IOException { + " \"docs\": [" + " {" + " \"_index\": \"index\"," - + " \"_type\": \"_doc\"," + " \"_id\": \"id\"," + " \"_source\": {" + " \"foo\": \"rab\"" @@ -131,7 +130,7 @@ public void testSimulatePipeline() throws IOException { + "}"; SimulatePipelineRequest request = new SimulatePipelineRequest( new BytesArray(json.getBytes(StandardCharsets.UTF_8)), - XContentType.JSON + MediaTypeRegistry.JSON ); request.setId(pipelineId); request.setVerbose(verbose); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/NodesResponseHeaderTestUtils.java b/client/rest-high-level/src/test/java/org/opensearch/client/NodesResponseHeaderTestUtils.java index 9591cff16f121..32d9203480342 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/NodesResponseHeaderTestUtils.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/NodesResponseHeaderTestUtils.java @@ -32,8 +32,8 @@ package org.opensearch.client; import org.opensearch.OpenSearchException; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/OpenSearchRestHighLevelClientTestCase.java b/client/rest-high-level/src/test/java/org/opensearch/client/OpenSearchRestHighLevelClientTestCase.java index efac508cf6814..6701124759f9f 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/OpenSearchRestHighLevelClientTestCase.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/OpenSearchRestHighLevelClientTestCase.java @@ -33,7 +33,6 @@ package org.opensearch.client; import org.apache.http.util.EntityUtils; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.admin.cluster.node.tasks.list.TaskGroup; @@ -48,18 +47,20 @@ import org.opensearch.client.indices.CreateIndexRequest; import org.opensearch.common.Booleans; import org.opensearch.common.CheckedRunnable; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.ingest.Pipeline; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchModule; -import org.opensearch.tasks.TaskId; import org.opensearch.test.rest.OpenSearchRestTestCase; import org.junit.AfterClass; import org.junit.Before; @@ -221,7 +222,7 @@ protected static void createFieldAddingPipleine(String id, String fieldName, Str .endArray() .endObject(); - createPipeline(new PutPipelineRequest(id, BytesReference.bytes(pipeline), XContentType.JSON)); + createPipeline(new PutPipelineRequest(id, BytesReference.bytes(pipeline), MediaTypeRegistry.JSON)); } protected static void createPipeline(String pipelineId) throws IOException { diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/PitIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/PitIT.java new file mode 100644 index 0000000000000..5ec1da77a6795 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/opensearch/client/PitIT.java @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client; + +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.opensearch.OpenSearchStatusException; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitInfo; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.action.search.GetAllPitNodesResponse; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Tests point in time API with rest high level client + */ +public class PitIT extends OpenSearchRestHighLevelClientTestCase { + + @Before + public void indexDocuments() throws IOException { + Request doc1 = new Request(HttpPut.METHOD_NAME, "/index/_doc/1"); + doc1.setJsonEntity("{\"type\":\"type1\", \"id\":1, \"num\":10, \"num2\":50}"); + client().performRequest(doc1); + Request doc2 = new Request(HttpPut.METHOD_NAME, "/index/_doc/2"); + doc2.setJsonEntity("{\"type\":\"type1\", \"id\":2, \"num\":20, \"num2\":40}"); + client().performRequest(doc2); + Request doc3 = new Request(HttpPut.METHOD_NAME, "/index/_doc/3"); + doc3.setJsonEntity("{\"type\":\"type1\", \"id\":3, \"num\":50, \"num2\":35}"); + client().performRequest(doc3); + Request doc4 = new Request(HttpPut.METHOD_NAME, "/index/_doc/4"); + doc4.setJsonEntity("{\"type\":\"type2\", \"id\":4, \"num\":100, \"num2\":10}"); + client().performRequest(doc4); + Request doc5 = new Request(HttpPut.METHOD_NAME, "/index/_doc/5"); + doc5.setJsonEntity("{\"type\":\"type2\", \"id\":5, \"num\":100, \"num2\":10}"); + client().performRequest(doc5); + client().performRequest(new Request(HttpPost.METHOD_NAME, "/_refresh")); + } + + public void testCreateAndDeletePit() throws IOException { + CreatePitRequest pitRequest = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, "index"); + CreatePitResponse createPitResponse = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + assertTrue(createPitResponse.getId() != null); + assertEquals(1, createPitResponse.getTotalShards()); + assertEquals(1, createPitResponse.getSuccessfulShards()); + assertEquals(0, createPitResponse.getFailedShards()); + assertEquals(0, createPitResponse.getSkippedShards()); + GetAllPitNodesResponse getAllPitResponse = highLevelClient().getAllPits(RequestOptions.DEFAULT); + List pits = getAllPitResponse.getPitInfos().stream().map(r -> r.getPitId()).collect(Collectors.toList()); + assertTrue(pits.contains(createPitResponse.getId())); + List pitIds = new ArrayList<>(); + pitIds.add(createPitResponse.getId()); + DeletePitRequest deletePitRequest = new DeletePitRequest(pitIds); + DeletePitResponse deletePitResponse = execute(deletePitRequest, highLevelClient()::deletePit, highLevelClient()::deletePitAsync); + assertTrue(deletePitResponse.getDeletePitResults().get(0).isSuccessful()); + assertTrue(deletePitResponse.getDeletePitResults().get(0).getPitId().equals(createPitResponse.getId())); + } + + public void testDeleteAllAndListAllPits() throws IOException, InterruptedException { + CreatePitRequest pitRequest = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, "index"); + CreatePitResponse pitResponse = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + CreatePitResponse pitResponse1 = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + assertTrue(pitResponse.getId() != null); + assertTrue(pitResponse1.getId() != null); + DeletePitResponse deletePitResponse = highLevelClient().deleteAllPits(RequestOptions.DEFAULT); + for (DeletePitInfo deletePitInfo : deletePitResponse.getDeletePitResults()) { + assertTrue(deletePitInfo.isSuccessful()); + } + pitResponse = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + pitResponse1 = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + assertTrue(pitResponse.getId() != null); + assertTrue(pitResponse1.getId() != null); + GetAllPitNodesResponse getAllPitResponse = highLevelClient().getAllPits(RequestOptions.DEFAULT); + + List pits = getAllPitResponse.getPitInfos().stream().map(r -> r.getPitId()).collect(Collectors.toList()); + assertTrue(pits.contains(pitResponse.getId())); + assertTrue(pits.contains(pitResponse1.getId())); + CountDownLatch countDownLatch = new CountDownLatch(1); + ActionListener deletePitListener = new ActionListener<>() { + @Override + public void onResponse(DeletePitResponse response) { + countDownLatch.countDown(); + for (DeletePitInfo deletePitInfo : response.getDeletePitResults()) { + assertTrue(deletePitInfo.isSuccessful()); + } + } + + @Override + public void onFailure(Exception e) { + countDownLatch.countDown(); + if (!(e instanceof OpenSearchStatusException)) { + throw new AssertionError("Delete all failed"); + } + } + }; + final CreatePitResponse pitResponse3 = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + + ActionListener getPitsListener = new ActionListener() { + @Override + public void onResponse(GetAllPitNodesResponse response) { + List pits = response.getPitInfos().stream().map(r -> r.getPitId()).collect(Collectors.toList()); + assertTrue(pits.contains(pitResponse3.getId())); + } + + @Override + public void onFailure(Exception e) { + if (!(e instanceof OpenSearchStatusException)) { + throw new AssertionError("List all PITs failed", e); + } + } + }; + highLevelClient().getAllPitsAsync(RequestOptions.DEFAULT, getPitsListener); + highLevelClient().deleteAllPitsAsync(RequestOptions.DEFAULT, deletePitListener); + assertTrue(countDownLatch.await(10, TimeUnit.SECONDS)); + // validate no pits case + getAllPitResponse = highLevelClient().getAllPits(RequestOptions.DEFAULT); + assertTrue(getAllPitResponse.getPitInfos().size() == 0); + highLevelClient().deleteAllPitsAsync(RequestOptions.DEFAULT, deletePitListener); + } +} diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/RankEvalIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/RankEvalIT.java index 07e5b1627942e..47add92ecaccd 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/RankEvalIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/RankEvalIT.java @@ -32,7 +32,6 @@ package org.opensearch.client; -import org.junit.Before; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.support.IndicesOptions; import org.opensearch.index.query.MatchAllQueryBuilder; @@ -50,6 +49,7 @@ import org.opensearch.index.rankeval.RatedSearchHit; import org.opensearch.index.rankeval.RecallAtK; import org.opensearch.search.builder.SearchSourceBuilder; +import org.junit.Before; import java.io.IOException; import java.util.ArrayList; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/ReindexIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/ReindexIT.java index 78fb2aef02ee0..2457bafdc6a22 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/ReindexIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/ReindexIT.java @@ -33,7 +33,6 @@ package org.opensearch.client; import org.opensearch.OpenSearchStatusException; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.bulk.BulkItemResponse; import org.opensearch.action.bulk.BulkRequest; @@ -42,15 +41,16 @@ import org.opensearch.action.support.WriteRequest; import org.opensearch.client.tasks.TaskSubmissionResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.query.IdsQueryBuilder; import org.opensearch.index.reindex.BulkByScrollResponse; import org.opensearch.index.reindex.DeleteByQueryAction; import org.opensearch.index.reindex.DeleteByQueryRequest; import org.opensearch.index.reindex.ReindexRequest; -import org.opensearch.rest.RestStatus; import org.opensearch.tasks.RawTaskStatus; -import org.opensearch.tasks.TaskId; import java.io.IOException; import java.util.Collections; @@ -76,9 +76,9 @@ public void testReindex() throws IOException { createIndex(sourceIndex, settings); createIndex(destinationIndex, settings); BulkRequest bulkRequest = new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo2", "bar2"), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo2", "bar2"), MediaTypeRegistry.JSON)) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); assertEquals(RestStatus.OK, highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT).status()); } @@ -112,10 +112,9 @@ public void testReindex() throws IOException { reindexRequest.setRefresh(true); reindexRequest.setRequireAlias(true); - OpenSearchStatusException exception = expectThrows( - OpenSearchStatusException.class, - () -> { execute(reindexRequest, highLevelClient()::reindex, highLevelClient()::reindexAsync); } - ); + OpenSearchStatusException exception = expectThrows(OpenSearchStatusException.class, () -> { + execute(reindexRequest, highLevelClient()::reindex, highLevelClient()::reindexAsync); + }); assertEquals(RestStatus.NOT_FOUND, exception.status()); assertEquals( "OpenSearch exception [type=index_not_found_exception, reason=no such index [dest] and [require_alias] request flag is [true] and [dest] is not an alias]", @@ -133,9 +132,9 @@ public void testReindexTask() throws Exception { createIndex(sourceIndex, settings); createIndex(destinationIndex, settings); BulkRequest bulkRequest = new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo2", "bar2"), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo2", "bar2"), MediaTypeRegistry.JSON)) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); assertEquals(RestStatus.OK, highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT).status()); } @@ -164,9 +163,9 @@ public void testReindexConflict() throws IOException { createIndex(sourceIndex, settings); createIndex(destIndex, settings); final BulkRequest bulkRequest = new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", "bar"), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", "bar"), MediaTypeRegistry.JSON)) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); assertThat(highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT).status(), equalTo(RestStatus.OK)); @@ -206,10 +205,10 @@ public void testDeleteByQuery() throws Exception { RestStatus.OK, highLevelClient().bulk( new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", 1), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", 1), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", 2), XContentType.JSON)) - .add(new IndexRequest(sourceIndex).id("3").source(Collections.singletonMap("foo", 3), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", 2), MediaTypeRegistry.JSON)) + .add(new IndexRequest(sourceIndex).id("3").source(Collections.singletonMap("foo", 3), MediaTypeRegistry.JSON)) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT ).status() @@ -306,10 +305,10 @@ public void testDeleteByQueryTask() throws Exception { RestStatus.OK, highLevelClient().bulk( new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", 1), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", 1), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", 2), XContentType.JSON)) - .add(new IndexRequest(sourceIndex).id("3").source(Collections.singletonMap("foo", 3), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", 2), MediaTypeRegistry.JSON)) + .add(new IndexRequest(sourceIndex).id("3").source(Collections.singletonMap("foo", 3), MediaTypeRegistry.JSON)) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT ).status() diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/RequestConvertersTests.java index 32c6cde0725b4..2be3c27c6b5ce 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/RequestConvertersTests.java @@ -53,6 +53,8 @@ import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.ClearScrollRequest; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.DeletePitRequest; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchScrollRequest; @@ -60,8 +62,8 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.WriteRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.action.support.master.MasterNodeRequest; import org.opensearch.action.support.replication.ReplicationRequest; import org.opensearch.action.update.UpdateRequest; import org.opensearch.client.RequestConverters.EndpointBuilder; @@ -71,20 +73,22 @@ import org.opensearch.client.core.TermVectorsRequest; import org.opensearch.client.indices.AnalyzeRequest; import org.opensearch.common.CheckedBiConsumer; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.io.Streams; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.MatchAllQueryBuilder; @@ -115,7 +119,6 @@ import org.opensearch.search.rescore.QueryRescorerBuilder; import org.opensearch.search.suggest.SuggestBuilder; import org.opensearch.search.suggest.completion.CompletionSuggestionBuilder; -import org.opensearch.tasks.TaskId; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.RandomObjects; import org.hamcrest.Matchers; @@ -131,6 +134,7 @@ import java.util.Locale; import java.util.Map; import java.util.StringJoiner; +import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -806,7 +810,7 @@ public void testUpdate() throws IOException { UpdateRequest parsedUpdateRequest = new UpdateRequest(); - XContentType entityContentType = XContentType.fromMediaTypeOrFormat(entity.getContentType().getValue()); + MediaType entityContentType = MediaType.fromMediaType(entity.getContentType().getValue()); try (XContentParser parser = createParser(entityContentType.xContent(), entity.getContent())) { parsedUpdateRequest.fromXContent(parser); } @@ -848,7 +852,7 @@ private static void setRandomIfSeqNoAndTerm(DocWriteRequest request, Map { UpdateRequest updateRequest = new UpdateRequest(); - updateRequest.doc(new IndexRequest().source(singletonMap("field", "doc"), XContentType.JSON)); + updateRequest.doc(new IndexRequest().source(singletonMap("field", "doc"), MediaTypeRegistry.JSON)); updateRequest.upsert(new IndexRequest().source(singletonMap("field", "upsert"), XContentType.YAML)); RequestConverters.update(updateRequest); }); @@ -872,7 +876,7 @@ public void testBulk() throws IOException { setRandomRefreshPolicy(bulkRequest::setRefreshPolicy, expectedParams); - XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE); + MediaType mediaType = randomFrom(MediaTypeRegistry.JSON, XContentType.SMILE); int nbItems = randomIntBetween(10, 100); DocWriteRequest[] requests = new DocWriteRequest[nbItems]; @@ -880,21 +884,21 @@ public void testBulk() throws IOException { String index = randomAlphaOfLength(5); String id = randomAlphaOfLength(5); - BytesReference source = RandomObjects.randomSource(random(), xContentType); + BytesReference source = RandomObjects.randomSource(random(), mediaType); DocWriteRequest.OpType opType = randomFrom(DocWriteRequest.OpType.values()); DocWriteRequest docWriteRequest; if (opType == DocWriteRequest.OpType.INDEX) { - IndexRequest indexRequest = new IndexRequest(index).id(id).source(source, xContentType); + IndexRequest indexRequest = new IndexRequest(index).id(id).source(source, mediaType); docWriteRequest = indexRequest; if (randomBoolean()) { indexRequest.setPipeline(randomAlphaOfLength(5)); } } else if (opType == DocWriteRequest.OpType.CREATE) { - IndexRequest createRequest = new IndexRequest(index).id(id).source(source, xContentType).create(true); + IndexRequest createRequest = new IndexRequest(index).id(id).source(source, mediaType).create(true); docWriteRequest = createRequest; } else if (opType == DocWriteRequest.OpType.UPDATE) { - final UpdateRequest updateRequest = new UpdateRequest(index, id).doc(new IndexRequest().source(source, xContentType)); + final UpdateRequest updateRequest = new UpdateRequest(index, id).doc(new IndexRequest().source(source, mediaType)); docWriteRequest = updateRequest; if (randomBoolean()) { updateRequest.retryOnConflict(randomIntBetween(1, 5)); @@ -923,14 +927,14 @@ public void testBulk() throws IOException { assertEquals("/_bulk", request.getEndpoint()); assertEquals(expectedParams, request.getParameters()); assertEquals(HttpPost.METHOD_NAME, request.getMethod()); - assertEquals(xContentType.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); + assertEquals(mediaType.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); byte[] content = new byte[(int) request.getEntity().getContentLength()]; try (InputStream inputStream = request.getEntity().getContent()) { Streams.readFully(inputStream, content); } BulkRequest parsedBulkRequest = new BulkRequest(); - parsedBulkRequest.add(content, 0, content.length, xContentType); + parsedBulkRequest.add(content, 0, content.length, mediaType); assertEquals(bulkRequest.numberOfActions(), parsedBulkRequest.numberOfActions()); for (int i = 0; i < bulkRequest.numberOfActions(); i++) { @@ -952,7 +956,7 @@ public void testBulk() throws IOException { IndexRequest parsedIndexRequest = (IndexRequest) parsedRequest; assertEquals(indexRequest.getPipeline(), parsedIndexRequest.getPipeline()); - assertToXContentEquivalent(indexRequest.source(), parsedIndexRequest.source(), xContentType); + assertToXContentEquivalent(indexRequest.source(), parsedIndexRequest.source(), mediaType); } else if (opType == DocWriteRequest.OpType.UPDATE) { UpdateRequest updateRequest = (UpdateRequest) originalRequest; UpdateRequest parsedUpdateRequest = (UpdateRequest) parsedRequest; @@ -960,7 +964,7 @@ public void testBulk() throws IOException { assertEquals(updateRequest.retryOnConflict(), parsedUpdateRequest.retryOnConflict()); assertEquals(updateRequest.fetchSource(), parsedUpdateRequest.fetchSource()); if (updateRequest.doc() != null) { - assertToXContentEquivalent(updateRequest.doc().source(), parsedUpdateRequest.doc().source(), xContentType); + assertToXContentEquivalent(updateRequest.doc().source(), parsedUpdateRequest.doc().source(), mediaType); } else { assertNull(parsedUpdateRequest.doc()); } @@ -976,34 +980,34 @@ public void testBulkWithDifferentContentTypes() throws IOException { bulkRequest.add(new DeleteRequest("index", "2")); Request request = RequestConverters.bulk(bulkRequest); - assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); + assertEquals(MediaTypeRegistry.JSON.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); } { - XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE); + MediaType mediaType = randomFrom(MediaTypeRegistry.JSON, XContentType.SMILE); BulkRequest bulkRequest = new BulkRequest(); bulkRequest.add(new DeleteRequest("index", "0")); - bulkRequest.add(new IndexRequest("index").id("0").source(singletonMap("field", "value"), xContentType)); + bulkRequest.add(new IndexRequest("index").id("0").source(singletonMap("field", "value"), mediaType)); bulkRequest.add(new DeleteRequest("index", "2")); Request request = RequestConverters.bulk(bulkRequest); - assertEquals(xContentType.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); + assertEquals(mediaType.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); } { - XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE); + MediaType mediaType = randomFrom(MediaTypeRegistry.JSON, XContentType.SMILE); UpdateRequest updateRequest = new UpdateRequest("index", "0"); if (randomBoolean()) { - updateRequest.doc(new IndexRequest().source(singletonMap("field", "value"), xContentType)); + updateRequest.doc(new IndexRequest().source(singletonMap("field", "value"), mediaType)); } else { - updateRequest.upsert(new IndexRequest().source(singletonMap("field", "value"), xContentType)); + updateRequest.upsert(new IndexRequest().source(singletonMap("field", "value"), mediaType)); } Request request = RequestConverters.bulk(new BulkRequest().add(updateRequest)); - assertEquals(xContentType.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); + assertEquals(mediaType.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); } { BulkRequest bulkRequest = new BulkRequest(); bulkRequest.add(new IndexRequest("index").id("0").source(singletonMap("field", "value"), XContentType.SMILE)); - bulkRequest.add(new IndexRequest("index").id("1").source(singletonMap("field", "value"), XContentType.JSON)); + bulkRequest.add(new IndexRequest("index").id("1").source(singletonMap("field", "value"), MediaTypeRegistry.JSON)); IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> RequestConverters.bulk(bulkRequest)); assertEquals( "Mismatching content-type found for request with content-type [JSON], " + "previous requests have content-type [SMILE]", @@ -1012,10 +1016,10 @@ public void testBulkWithDifferentContentTypes() throws IOException { } { BulkRequest bulkRequest = new BulkRequest(); - bulkRequest.add(new IndexRequest("index").id("0").source(singletonMap("field", "value"), XContentType.JSON)); - bulkRequest.add(new IndexRequest("index").id("1").source(singletonMap("field", "value"), XContentType.JSON)); + bulkRequest.add(new IndexRequest("index").id("0").source(singletonMap("field", "value"), MediaTypeRegistry.JSON)); + bulkRequest.add(new IndexRequest("index").id("1").source(singletonMap("field", "value"), MediaTypeRegistry.JSON)); bulkRequest.add( - new UpdateRequest("index", "2").doc(new IndexRequest().source(singletonMap("field", "value"), XContentType.JSON)) + new UpdateRequest("index", "2").doc(new IndexRequest().source(singletonMap("field", "value"), MediaTypeRegistry.JSON)) .upsert(new IndexRequest().source(singletonMap("field", "value"), XContentType.SMILE)) ); IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> RequestConverters.bulk(bulkRequest)); @@ -1028,10 +1032,10 @@ public void testBulkWithDifferentContentTypes() throws IOException { XContentType xContentType = randomFrom(XContentType.CBOR, XContentType.YAML); BulkRequest bulkRequest = new BulkRequest(); bulkRequest.add(new DeleteRequest("index", "0")); - bulkRequest.add(new IndexRequest("index").id("1").source(singletonMap("field", "value"), XContentType.JSON)); + bulkRequest.add(new IndexRequest("index").id("1").source(singletonMap("field", "value"), MediaTypeRegistry.JSON)); bulkRequest.add(new DeleteRequest("index", "2")); bulkRequest.add(new DeleteRequest("index", "3")); - bulkRequest.add(new IndexRequest("index").id("4").source(singletonMap("field", "value"), XContentType.JSON)); + bulkRequest.add(new IndexRequest("index").id("4").source(singletonMap("field", "value"), MediaTypeRegistry.JSON)); bulkRequest.add(new IndexRequest("index").id("1").source(singletonMap("field", "value"), xContentType)); IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> RequestConverters.bulk(bulkRequest)); assertEquals( @@ -1044,9 +1048,9 @@ public void testBulkWithDifferentContentTypes() throws IOException { public void testGlobalPipelineOnBulkRequest() throws IOException { BulkRequest bulkRequest = new BulkRequest(); bulkRequest.pipeline("xyz"); - bulkRequest.add(new IndexRequest("test").id("11").source(XContentType.JSON, "field", "bulk1")); - bulkRequest.add(new IndexRequest("test").id("12").source(XContentType.JSON, "field", "bulk2")); - bulkRequest.add(new IndexRequest("test").id("13").source(XContentType.JSON, "field", "bulk3")); + bulkRequest.add(new IndexRequest("test").id("11").source(MediaTypeRegistry.JSON, "field", "bulk1")); + bulkRequest.add(new IndexRequest("test").id("12").source(MediaTypeRegistry.JSON, "field", "bulk2")); + bulkRequest.add(new IndexRequest("test").id("13").source(MediaTypeRegistry.JSON, "field", "bulk3")); Request request = RequestConverters.bulk(bulkRequest); @@ -1303,6 +1307,47 @@ public void testClearScroll() throws IOException { assertEquals(REQUEST_BODY_CONTENT_TYPE.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); } + public void testCreatePit() throws IOException { + String[] indices = randomIndicesNames(0, 5); + Map expectedParams = new HashMap<>(); + expectedParams.put("keep_alive", "1d"); + expectedParams.put("allow_partial_pit_creation", "true"); + CreatePitRequest createPitRequest = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, indices); + setRandomIndicesOptions(createPitRequest::indicesOptions, createPitRequest::indicesOptions, expectedParams); + Request request = RequestConverters.createPit(createPitRequest); + StringJoiner endpoint = new StringJoiner("/", "/", ""); + String index = String.join(",", indices); + if (Strings.hasLength(index)) { + endpoint.add(index); + } + endpoint.add("_search/point_in_time"); + assertEquals(HttpPost.METHOD_NAME, request.getMethod()); + assertEquals(endpoint.toString(), request.getEndpoint()); + assertEquals(expectedParams, request.getParameters()); + assertToXContentBody(createPitRequest, request.getEntity()); + assertEquals(REQUEST_BODY_CONTENT_TYPE.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); + } + + public void testDeletePit() throws IOException { + List pitIdsList = new ArrayList<>(); + pitIdsList.add("pitId1"); + pitIdsList.add("pitId2"); + DeletePitRequest deletePitRequest = new DeletePitRequest(pitIdsList); + Request request = RequestConverters.deletePit(deletePitRequest); + String endpoint = "/_search/point_in_time"; + assertEquals(HttpDelete.METHOD_NAME, request.getMethod()); + assertEquals(endpoint, request.getEndpoint()); + assertToXContentBody(deletePitRequest, request.getEntity()); + assertEquals(REQUEST_BODY_CONTENT_TYPE.mediaTypeWithoutParameters(), request.getEntity().getContentType().getValue()); + } + + public void testDeleteAllPits() { + Request request = RequestConverters.deleteAllPits(); + String endpoint = "/_search/point_in_time/_all"; + assertEquals(HttpDelete.METHOD_NAME, request.getMethod()); + assertEquals(endpoint, request.getEndpoint()); + } + public void testSearchTemplate() throws Exception { // Create a random request. String[] indices = randomIndicesNames(0, 5); @@ -1411,8 +1456,11 @@ public void testMultiSearchTemplate() throws Exception { assertEquals(expectedParams, multiRequest.getParameters()); HttpEntity actualEntity = multiRequest.getEntity(); - byte[] expectedBytes = MultiSearchTemplateRequest.writeMultiLineFormat(multiSearchTemplateRequest, XContentType.JSON.xContent()); - assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue()); + byte[] expectedBytes = MultiSearchTemplateRequest.writeMultiLineFormat( + multiSearchTemplateRequest, + MediaTypeRegistry.JSON.xContent() + ); + assertEquals(MediaTypeRegistry.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue()); assertEquals(new BytesArray(expectedBytes), new BytesArray(EntityUtils.toByteArray(actualEntity))); } @@ -1718,8 +1766,12 @@ public void testDeleteScriptRequest() { } static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException { - BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false); - assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue()); + BytesReference expectedBytes = org.opensearch.core.xcontent.XContentHelper.toXContent( + expectedBody, + REQUEST_BODY_CONTENT_TYPE, + false + ); + assertEquals(MediaTypeRegistry.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue()); assertEquals(expectedBytes, new BytesArray(EntityUtils.toByteArray(actualEntity))); } @@ -1868,12 +1920,12 @@ public void testCreateContentType() { } public void testEnforceSameContentType() { - XContentType xContentType = randomFrom(XContentType.JSON, XContentType.SMILE); - IndexRequest indexRequest = new IndexRequest().source(singletonMap("field", "value"), xContentType); - assertEquals(xContentType, enforceSameContentType(indexRequest, null)); - assertEquals(xContentType, enforceSameContentType(indexRequest, xContentType)); + MediaType mediaType = randomFrom(MediaTypeRegistry.JSON, XContentType.SMILE); + IndexRequest indexRequest = new IndexRequest().source(singletonMap("field", "value"), mediaType); + assertEquals(mediaType, enforceSameContentType(indexRequest, null)); + assertEquals(mediaType, enforceSameContentType(indexRequest, mediaType)); - XContentType bulkContentType = randomBoolean() ? xContentType : null; + MediaType bulkContentType = randomBoolean() ? mediaType : null; IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, @@ -1893,18 +1945,18 @@ public void testEnforceSameContentType() { exception.getMessage() ); - XContentType requestContentType = xContentType == XContentType.JSON ? XContentType.SMILE : XContentType.JSON; + MediaType requestContentType = mediaType == MediaTypeRegistry.JSON ? XContentType.SMILE : MediaTypeRegistry.JSON; exception = expectThrows( IllegalArgumentException.class, - () -> enforceSameContentType(new IndexRequest().source(singletonMap("field", "value"), requestContentType), xContentType) + () -> enforceSameContentType(new IndexRequest().source(singletonMap("field", "value"), requestContentType), mediaType) ); assertEquals( "Mismatching content-type found for request with content-type [" + requestContentType + "], " + "previous requests have content-type [" - + xContentType + + mediaType + "]", exception.getMessage() ); @@ -2105,8 +2157,8 @@ static void setRandomTimeoutTimeValue(Consumer setter, TimeValue defa } } - static void setRandomMasterTimeout(MasterNodeRequest request, Map expectedParams) { - setRandomMasterTimeout(request::masterNodeTimeout, expectedParams); + static void setRandomMasterTimeout(ClusterManagerNodeRequest request, Map expectedParams) { + setRandomMasterTimeout(request::clusterManagerNodeTimeout, expectedParams); } static void setRandomMasterTimeout(TimedRequest request, Map expectedParams) { @@ -2122,7 +2174,7 @@ static void setRandomMasterTimeout(Consumer setter, Map setter.accept(masterTimeout); expectedParams.put("master_timeout", masterTimeout); } else { - expectedParams.put("master_timeout", MasterNodeRequest.DEFAULT_MASTER_NODE_TIMEOUT.getStringRep()); + expectedParams.put("master_timeout", ClusterManagerNodeRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT.getStringRep()); } } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientExtTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientExtTests.java index dbdf7eba3dca4..7658aa3c2c765 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientExtTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientExtTests.java @@ -35,11 +35,11 @@ import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.nio.entity.NStringEntity; -import org.junit.Before; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; import java.io.IOException; import java.util.ArrayList; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java index 73ce7d1b2b794..b0476a65e6d8c 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/RestHighLevelClientTests.java @@ -33,6 +33,7 @@ package org.opensearch.client; import com.fasterxml.jackson.core.JsonParseException; + import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; @@ -47,7 +48,6 @@ import org.apache.http.nio.entity.NByteArrayEntity; import org.apache.http.nio.entity.NStringEntity; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.search.ClearScrollRequest; @@ -59,16 +59,18 @@ import org.opensearch.client.core.MainRequest; import org.opensearch.client.core.MainResponse; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.cbor.CborXContent; import org.opensearch.common.xcontent.smile.SmileXContent; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.rankeval.DiscountedCumulativeGain; import org.opensearch.index.rankeval.EvaluationMetric; import org.opensearch.index.rankeval.ExpectedReciprocalRank; @@ -77,14 +79,13 @@ import org.opensearch.index.rankeval.PrecisionAtK; import org.opensearch.index.rankeval.RecallAtK; import org.opensearch.join.aggregations.ChildrenAggregationBuilder; -import org.opensearch.rest.RestStatus; import org.opensearch.search.SearchHits; import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.matrix.stats.MatrixStatsAggregationBuilder; import org.opensearch.search.suggest.Suggest; -import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.InternalAggregationTestCase; +import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.rest.yaml.restspec.ClientYamlSuiteRestApi; import org.opensearch.test.rest.yaml.restspec.ClientYamlSuiteRestSpec; import org.hamcrest.Matchers; @@ -108,7 +109,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.opensearch.common.xcontent.XContentHelper.toXContent; +import static org.opensearch.core.xcontent.XContentHelper.toXContent; import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; @@ -134,6 +135,8 @@ public class RestHighLevelClientTests extends OpenSearchTestCase { // core "ping", "info", + "delete_all_pits", + "get_all_pits", // security "security.get_ssl_certificates", "security.authenticate", @@ -284,6 +287,20 @@ public ActionRequestValidationException validate() { } } + public void testNullableActionListener() { + ActionRequest request = new ActionRequest() { + @Override + public ActionRequestValidationException validate() { + return null; + } + }; + + assertThrows( + IllegalArgumentException.class, + () -> restHighLevelClient.performRequestAsync(request, null, RequestOptions.DEFAULT, null, null, null) + ); + } + public void testParseEntity() throws IOException { { IllegalStateException ise = expectThrows(IllegalStateException.class, () -> restHighLevelClient.parseEntity(null, null)); @@ -411,13 +428,9 @@ public void testPerformRequestOnSuccess() throws IOException { { IOException ioe = expectThrows( IOException.class, - () -> restHighLevelClient.performRequest( - mainRequest, - requestConverter, - RequestOptions.DEFAULT, - response -> { throw new IllegalStateException(); }, - Collections.emptySet() - ) + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> { + throw new IllegalStateException(); + }, Collections.emptySet()) ); assertEquals( "Unable to parse response body for Response{requestLine=GET / http/1.1, host=http://localhost:9200, " @@ -559,13 +572,9 @@ public void testPerformRequestOnResponseExceptionWithIgnoresErrorNoBody() throws when(restClient.performRequest(any(Request.class))).thenThrow(responseException); OpenSearchException openSearchException = expectThrows( OpenSearchException.class, - () -> restHighLevelClient.performRequest( - mainRequest, - requestConverter, - RequestOptions.DEFAULT, - response -> { throw new IllegalStateException(); }, - Collections.singleton(404) - ) + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> { + throw new IllegalStateException(); + }, Collections.singleton(404)) ); assertEquals(RestStatus.NOT_FOUND, openSearchException.status()); assertSame(responseException, openSearchException.getCause()); @@ -582,13 +591,9 @@ public void testPerformRequestOnResponseExceptionWithIgnoresErrorValidBody() thr when(restClient.performRequest(any(Request.class))).thenThrow(responseException); OpenSearchException openSearchException = expectThrows( OpenSearchException.class, - () -> restHighLevelClient.performRequest( - mainRequest, - requestConverter, - RequestOptions.DEFAULT, - response -> { throw new IllegalStateException(); }, - Collections.singleton(404) - ) + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> { + throw new IllegalStateException(); + }, Collections.singleton(404)) ); assertEquals(RestStatus.NOT_FOUND, openSearchException.status()); assertSame(responseException, openSearchException.getSuppressed()[0]); @@ -841,7 +846,6 @@ public void testApiNamingConventions() throws Exception { "create", "get_script_context", "get_script_languages", - "indices.exists_type", "indices.get_upgrade", "indices.put_alias", "render_search_template", @@ -872,7 +876,15 @@ public void testApiNamingConventions() throws Exception { "nodes.hot_threads", "nodes.usage", "nodes.reload_secure_settings", - "search_shards", }; + "search_shards", + "remote_store.restore", + "remote_store.stats", + "cluster.put_weighted_routing", + "cluster.get_weighted_routing", + "cluster.delete_weighted_routing", + "cluster.put_decommission_awareness", + "cluster.get_decommission_awareness", + "cluster.delete_decommission_awareness", }; List booleanReturnMethods = Arrays.asList("security.enable_user", "security.disable_user", "security.change_password"); Set deprecatedMethods = new HashSet<>(); deprecatedMethods.add("indices.force_merge"); @@ -985,37 +997,34 @@ private static void assertSyncMethod(Method method, String apiName, List } assertEquals("incorrect number of exceptions for method [" + method + "]", 1, method.getExceptionTypes().length); + final Class[] parameterTypes = method.getParameterTypes(); // a few methods don't accept a request object as argument if (APIS_WITHOUT_REQUEST_OBJECT.contains(apiName)) { - assertEquals("incorrect number of arguments for method [" + method + "]", 1, method.getParameterTypes().length); - assertThat( - "the parameter to method [" + method + "] is the wrong type", - method.getParameterTypes()[0], - equalTo(RequestOptions.class) - ); + assertEquals("incorrect number of arguments for method [" + method + "]", 1, method.getParameterCount()); + assertThat("the parameter to method [" + method + "] is the wrong type", parameterTypes[0], equalTo(RequestOptions.class)); } else { - assertEquals("incorrect number of arguments for method [" + method + "]", 2, method.getParameterTypes().length); + assertEquals("incorrect number of arguments for method [" + method + "]", 2, method.getParameterCount()); // This is no longer true for all methods. Some methods can contain these 2 args backwards because of deprecation - if (method.getParameterTypes()[0].equals(RequestOptions.class)) { + if (parameterTypes[0].equals(RequestOptions.class)) { assertThat( "the first parameter to method [" + method + "] is the wrong type", - method.getParameterTypes()[0], + parameterTypes[0], equalTo(RequestOptions.class) ); assertThat( "the second parameter to method [" + method + "] is the wrong type", - method.getParameterTypes()[1].getSimpleName(), + parameterTypes[1].getSimpleName(), endsWith("Request") ); } else { assertThat( "the first parameter to method [" + method + "] is the wrong type", - method.getParameterTypes()[0].getSimpleName(), + parameterTypes[0].getSimpleName(), endsWith("Request") ); assertThat( "the second parameter to method [" + method + "] is the wrong type", - method.getParameterTypes()[1], + parameterTypes[1], equalTo(RequestOptions.class) ); } @@ -1029,39 +1038,40 @@ private static void assertAsyncMethod(Map> methods, Method m ); assertThat("async method [" + method + "] should return Cancellable", method.getReturnType(), equalTo(Cancellable.class)); assertEquals("async method [" + method + "] should not throw any exceptions", 0, method.getExceptionTypes().length); + final Class[] parameterTypes = method.getParameterTypes(); if (APIS_WITHOUT_REQUEST_OBJECT.contains(apiName.replaceAll("_async$", ""))) { - assertEquals(2, method.getParameterTypes().length); - assertThat(method.getParameterTypes()[0], equalTo(RequestOptions.class)); - assertThat(method.getParameterTypes()[1], equalTo(ActionListener.class)); + assertEquals(2, parameterTypes.length); + assertThat(parameterTypes[0], equalTo(RequestOptions.class)); + assertThat(parameterTypes[1], equalTo(ActionListener.class)); } else { - assertEquals("async method [" + method + "] has the wrong number of arguments", 3, method.getParameterTypes().length); + assertEquals("async method [" + method + "] has the wrong number of arguments", 3, method.getParameterCount()); // This is no longer true for all methods. Some methods can contain these 2 args backwards because of deprecation - if (method.getParameterTypes()[0].equals(RequestOptions.class)) { + if (parameterTypes[0].equals(RequestOptions.class)) { assertThat( "the first parameter to async method [" + method + "] should be a request type", - method.getParameterTypes()[0], + parameterTypes[0], equalTo(RequestOptions.class) ); assertThat( "the second parameter to async method [" + method + "] is the wrong type", - method.getParameterTypes()[1].getSimpleName(), + parameterTypes[1].getSimpleName(), endsWith("Request") ); } else { assertThat( "the first parameter to async method [" + method + "] should be a request type", - method.getParameterTypes()[0].getSimpleName(), + parameterTypes[0].getSimpleName(), endsWith("Request") ); assertThat( "the second parameter to async method [" + method + "] is the wrong type", - method.getParameterTypes()[1], + parameterTypes[1], equalTo(RequestOptions.class) ); } assertThat( "the third parameter to async method [" + method + "] is the wrong type", - method.getParameterTypes()[2], + parameterTypes[2], equalTo(ActionListener.class) ); } @@ -1074,16 +1084,17 @@ private static void assertSubmitTaskMethod( ClientYamlSuiteRestSpec restSpec ) { String methodName = extractMethodName(apiName); + final Class[] parameterTypes = method.getParameterTypes(); assertTrue("submit task method [" + method.getName() + "] doesn't have corresponding sync method", methods.containsKey(methodName)); - assertEquals("submit task method [" + method + "] has the wrong number of arguments", 2, method.getParameterTypes().length); + assertEquals("submit task method [" + method + "] has the wrong number of arguments", 2, method.getParameterCount()); assertThat( "the first parameter to submit task method [" + method + "] is the wrong type", - method.getParameterTypes()[0].getSimpleName(), + parameterTypes[0].getSimpleName(), endsWith("Request") ); assertThat( "the second parameter to submit task method [" + method + "] is the wrong type", - method.getParameterTypes()[1], + parameterTypes[1], equalTo(RequestOptions.class) ); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/SearchIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/SearchIT.java index 19e287fb91be5..bb705aebd2fcf 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/SearchIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/SearchIT.java @@ -43,6 +43,10 @@ import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; import org.opensearch.action.search.ClearScrollRequest; import org.opensearch.action.search.ClearScrollResponse; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.SearchRequest; @@ -50,12 +54,12 @@ import org.opensearch.action.search.SearchScrollRequest; import org.opensearch.client.core.CountRequest; import org.opensearch.client.core.CountResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -63,7 +67,6 @@ import org.opensearch.index.query.TermsQueryBuilder; import org.opensearch.join.aggregations.Children; import org.opensearch.join.aggregations.ChildrenAggregationBuilder; -import org.opensearch.rest.RestStatus; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.script.mustache.MultiSearchTemplateRequest; @@ -79,6 +82,7 @@ import org.opensearch.search.aggregations.bucket.composite.TermsValuesSourceBuilder; import org.opensearch.search.aggregations.bucket.range.Range; import org.opensearch.search.aggregations.bucket.range.RangeAggregationBuilder; +import org.opensearch.search.aggregations.bucket.terms.MultiTermsAggregationBuilder; import org.opensearch.search.aggregations.bucket.terms.RareTerms; import org.opensearch.search.aggregations.bucket.terms.RareTermsAggregationBuilder; import org.opensearch.search.aggregations.bucket.terms.Terms; @@ -87,8 +91,10 @@ import org.opensearch.search.aggregations.matrix.stats.MatrixStatsAggregationBuilder; import org.opensearch.search.aggregations.metrics.WeightedAvg; import org.opensearch.search.aggregations.metrics.WeightedAvgAggregationBuilder; +import org.opensearch.search.aggregations.support.MultiTermsValuesSourceConfig; import org.opensearch.search.aggregations.support.MultiValuesSourceFieldConfig; import org.opensearch.search.aggregations.support.ValueType; +import org.opensearch.search.builder.PointInTimeBuilder; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.fetch.subphase.FetchSourceContext; import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder; @@ -100,11 +106,14 @@ import org.junit.Before; import java.io.IOException; +import java.math.BigInteger; +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.concurrent.TimeUnit; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertToXContentEquivalent; @@ -290,6 +299,67 @@ public void testSearchWithTermsAgg() throws IOException { assertEquals(0, type2.getAggregations().asList().size()); } + public void testSearchWithMultiTermsAgg() throws IOException { + final String indexName = "multi_aggs"; + Request createIndex = new Request(HttpPut.METHOD_NAME, "/" + indexName); + createIndex.setJsonEntity( + "{\n" + + " \"mappings\": {\n" + + " \"properties\" : {\n" + + " \"username\" : {\n" + + " \"type\" : \"keyword\"\n" + + " },\n" + + " \"rating\" : {\n" + + " \"type\" : \"unsigned_long\"\n" + + " }\n" + + " }\n" + + " }" + + "}" + ); + client().performRequest(createIndex); + + { + Request doc1 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/_doc/1"); + doc1.setJsonEntity("{\"username\":\"bob\", \"rating\": 18446744073709551615}"); + client().performRequest(doc1); + Request doc2 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/_doc/2"); + doc2.setJsonEntity("{\"username\":\"tom\", \"rating\": 10223372036854775807}"); + client().performRequest(doc2); + Request doc3 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/_doc/3"); + doc3.setJsonEntity("{\"username\":\"john\"}"); + client().performRequest(doc3); + } + client().performRequest(new Request(HttpPost.METHOD_NAME, "/_refresh")); + + SearchRequest searchRequest = new SearchRequest().indices(indexName); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + searchSourceBuilder.aggregation( + new MultiTermsAggregationBuilder("agg1").terms( + Arrays.asList( + new MultiTermsValuesSourceConfig.Builder().setFieldName("username").build(), + new MultiTermsValuesSourceConfig.Builder().setFieldName("rating").build() + ) + ) + ); + searchSourceBuilder.size(0); + searchRequest.source(searchSourceBuilder); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + assertSearchHeader(searchResponse); + assertNull(searchResponse.getSuggest()); + assertEquals(Collections.emptyMap(), searchResponse.getProfileResults()); + assertEquals(0, searchResponse.getHits().getHits().length); + assertEquals(Float.NaN, searchResponse.getHits().getMaxScore(), 0f); + Terms termsAgg = searchResponse.getAggregations().get("agg1"); + assertEquals("agg1", termsAgg.getName()); + assertEquals(2, termsAgg.getBuckets().size()); + Terms.Bucket bucket1 = termsAgg.getBucketByKey("bob|18446744073709551615"); + assertEquals(1, bucket1.getDocCount()); + assertEquals(0, bucket1.getAggregations().asList().size()); + Terms.Bucket bucket2 = termsAgg.getBucketByKey("tom|10223372036854775807"); + assertEquals(1, bucket2.getDocCount()); + assertEquals(0, bucket2.getAggregations().asList().size()); + } + public void testSearchWithRareTermsAgg() throws IOException { SearchRequest searchRequest = new SearchRequest(); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); @@ -698,7 +768,7 @@ public void testSearchScroll() throws Exception { for (int i = 0; i < 100; i++) { XContentBuilder builder = jsonBuilder().startObject().field("field", i).endObject(); Request doc = new Request(HttpPut.METHOD_NAME, "/test/_doc/" + Integer.toString(i)); - doc.setJsonEntity(Strings.toString(builder)); + doc.setJsonEntity(builder.toString()); client().performRequest(doc); } client().performRequest(new Request(HttpPost.METHOD_NAME, "/test/_refresh")); @@ -762,6 +832,46 @@ public void testSearchScroll() throws Exception { } } + public void testSearchWithPit() throws Exception { + for (int i = 0; i < 100; i++) { + XContentBuilder builder = jsonBuilder().startObject().field("field", i).endObject(); + Request doc = new Request(HttpPut.METHOD_NAME, "/test/_doc/" + Integer.toString(i)); + doc.setJsonEntity(builder.toString()); + client().performRequest(doc); + } + client().performRequest(new Request(HttpPost.METHOD_NAME, "/test/_refresh")); + + CreatePitRequest pitRequest = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, "test"); + CreatePitResponse pitResponse = execute(pitRequest, highLevelClient()::createPit, highLevelClient()::createPitAsync); + + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder().size(35) + .sort("field", SortOrder.ASC) + .pointInTimeBuilder(new PointInTimeBuilder(pitResponse.getId())); + SearchRequest searchRequest = new SearchRequest().source(searchSourceBuilder); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + + try { + long counter = 0; + assertSearchHeader(searchResponse); + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(100L)); + assertThat(searchResponse.getHits().getHits().length, equalTo(35)); + for (SearchHit hit : searchResponse.getHits()) { + assertThat(((Number) hit.getSortValues()[0]).longValue(), equalTo(counter++)); + } + } finally { + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + DeletePitRequest deletePitRequest = new DeletePitRequest(pitIds); + DeletePitResponse deletePitResponse = execute( + deletePitRequest, + highLevelClient()::deletePit, + highLevelClient()::deletePitAsync + ); + assertTrue(deletePitResponse.getDeletePitResults().get(0).isSuccessful()); + assertTrue(deletePitResponse.getDeletePitResults().get(0).getPitId().equals(pitResponse.getId())); + } + } + public void testMultiSearch() throws Exception { MultiSearchRequest multiSearchRequest = new MultiSearchRequest(); SearchRequest searchRequest1 = new SearchRequest("index1"); @@ -800,6 +910,56 @@ public void testMultiSearch() throws Exception { assertThat(multiSearchResponse.getResponses()[2].getResponse().getHits().getAt(1).getId(), Matchers.equalTo("6")); } + public void testSearchWithSort() throws Exception { + final String indexName = "search_sort"; + Request createIndex = new Request(HttpPut.METHOD_NAME, "/" + indexName); + createIndex.setJsonEntity( + "{\n" + + " \"mappings\": {\n" + + " \"properties\" : {\n" + + " \"username\" : {\n" + + " \"type\" : \"keyword\"\n" + + " },\n" + + " \"rating\" : {\n" + + " \"type\" : \"unsigned_long\"\n" + + " }\n" + + " }\n" + + " }" + + "}" + ); + client().performRequest(createIndex); + + { + Request doc1 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/_doc/1"); + doc1.setJsonEntity("{\"username\":\"bob\", \"rating\": 18446744073709551610}"); + client().performRequest(doc1); + Request doc2 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/_doc/2"); + doc2.setJsonEntity("{\"username\":\"tom\", \"rating\": 10223372036854775807}"); + client().performRequest(doc2); + Request doc3 = new Request(HttpPut.METHOD_NAME, "/" + indexName + "/_doc/3"); + doc3.setJsonEntity("{\"username\":\"john\"}"); + client().performRequest(doc3); + } + client().performRequest(new Request(HttpPost.METHOD_NAME, "/search_sort/_refresh")); + + SearchRequest searchRequest = new SearchRequest("search_sort"); + searchRequest.source().sort("rating", SortOrder.ASC); + SearchResponse searchResponse = execute(searchRequest, highLevelClient()::search, highLevelClient()::searchAsync); + + assertThat(searchResponse.getTook().millis(), Matchers.greaterThanOrEqualTo(0L)); + assertThat(searchResponse.getHits().getTotalHits().value, Matchers.equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), Matchers.equalTo("2")); + assertThat(searchResponse.getHits().getAt(1).getId(), Matchers.equalTo("1")); + assertThat(searchResponse.getHits().getAt(2).getId(), Matchers.equalTo("3")); + + assertThat(searchResponse.getHits().getAt(0).getSortValues().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(0).getSortValues()[0], equalTo(new BigInteger("10223372036854775807"))); + assertThat(searchResponse.getHits().getAt(1).getSortValues().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(1).getSortValues()[0], equalTo(new BigInteger("18446744073709551610"))); + assertThat(searchResponse.getHits().getAt(2).getSortValues().length, equalTo(1)); + assertThat(searchResponse.getHits().getAt(2).getSortValues()[0], equalTo(new BigInteger("18446744073709551615"))); + } + public void testMultiSearch_withAgg() throws Exception { MultiSearchRequest multiSearchRequest = new MultiSearchRequest(); SearchRequest searchRequest1 = new SearchRequest("index1"); @@ -1040,7 +1200,7 @@ public void testRenderSearchTemplate() throws IOException { BytesReference actualSource = searchTemplateResponse.getSource(); assertNotNull(actualSource); - assertToXContentEquivalent(expectedSource, actualSource, XContentType.JSON); + assertToXContentEquivalent(expectedSource, actualSource, MediaTypeRegistry.JSON); } public void testMultiSearchTemplate() throws Exception { diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/SearchPipelineClientIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/SearchPipelineClientIT.java new file mode 100644 index 0000000000000..9304be7f21899 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/opensearch/client/SearchPipelineClientIT.java @@ -0,0 +1,115 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client; + +import org.opensearch.action.search.DeleteSearchPipelineRequest; +import org.opensearch.action.search.GetSearchPipelineRequest; +import org.opensearch.action.search.GetSearchPipelineResponse; +import org.opensearch.action.search.PutSearchPipelineRequest; +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; + +public class SearchPipelineClientIT extends OpenSearchRestHighLevelClientTestCase { + + public void testPutPipeline() throws IOException { + String id = "some_pipeline_id"; + XContentBuilder pipelineBuilder = buildSearchPipeline(); + PutSearchPipelineRequest request = new PutSearchPipelineRequest( + id, + BytesReference.bytes(pipelineBuilder), + pipelineBuilder.contentType() + ); + createPipeline(request); + } + + private static void createPipeline(PutSearchPipelineRequest request) throws IOException { + AcknowledgedResponse response = execute( + request, + highLevelClient().searchPipeline()::put, + highLevelClient().searchPipeline()::putAsync + ); + assertTrue(response.isAcknowledged()); + } + + public void testGetPipeline() throws IOException { + String id = "some_pipeline_id"; + XContentBuilder pipelineBuilder = buildSearchPipeline(); + PutSearchPipelineRequest request = new PutSearchPipelineRequest( + id, + BytesReference.bytes(pipelineBuilder), + pipelineBuilder.contentType() + ); + createPipeline(request); + + GetSearchPipelineRequest getRequest = new GetSearchPipelineRequest(id); + GetSearchPipelineResponse response = execute( + getRequest, + highLevelClient().searchPipeline()::get, + highLevelClient().searchPipeline()::getAsync + ); + assertTrue(response.isFound()); + assertEquals(1, response.pipelines().size()); + assertEquals(id, response.pipelines().get(0).getId()); + } + + public void testDeletePipeline() throws IOException { + String id = "some_pipeline_id"; + XContentBuilder pipelineBuilder = buildSearchPipeline(); + PutSearchPipelineRequest request = new PutSearchPipelineRequest( + id, + BytesReference.bytes(pipelineBuilder), + pipelineBuilder.contentType() + ); + createPipeline(request); + + DeleteSearchPipelineRequest deleteRequest = new DeleteSearchPipelineRequest(id); + AcknowledgedResponse response = execute( + deleteRequest, + highLevelClient().searchPipeline()::delete, + highLevelClient().searchPipeline()::deleteAsync + ); + assertTrue(response.isAcknowledged()); + } + + private static XContentBuilder buildSearchPipeline() throws IOException { + XContentType xContentType = randomFrom(XContentType.values()); + XContentBuilder pipelineBuilder = XContentBuilder.builder(xContentType.xContent()); + return buildSearchPipeline(pipelineBuilder); + } + + private static XContentBuilder buildSearchPipeline(XContentBuilder builder) throws IOException { + builder.startObject(); + { + builder.field("description", "a pipeline description"); + builder.startArray("request_processors"); + { + builder.startObject().startObject("filter_query"); + { + builder.startObject("query"); + { + builder.startObject("term"); + { + builder.field("field", "value"); + } + builder.endObject(); + } + builder.endObject(); + } + builder.endObject().endObject(); + } + builder.endArray(); + } + builder.endObject(); + return builder; + } +} diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/SnapshotIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/SnapshotIT.java index 1d93dae5b2c5b..362a8f10d6a77 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/SnapshotIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/SnapshotIT.java @@ -54,9 +54,9 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.repositories.fs.FsRepository; -import org.opensearch.rest.RestStatus; import org.opensearch.snapshots.RestoreInfo; import org.opensearch.snapshots.SnapshotInfo; @@ -76,7 +76,7 @@ public class SnapshotIT extends OpenSearchRestHighLevelClientTestCase { private AcknowledgedResponse createTestRepository(String repository, String type, String settings) throws IOException { PutRepositoryRequest request = new PutRepositoryRequest(repository); - request.settings(settings, XContentType.JSON); + request.settings(settings, MediaTypeRegistry.JSON); request.type(type); return execute(request, highLevelClient().snapshot()::createRepository, highLevelClient().snapshot()::createRepositoryAsync); } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/SnapshotRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/SnapshotRequestConvertersTests.java index f18679127bf2b..93ffd7cade7c3 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/SnapshotRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/SnapshotRequestConvertersTests.java @@ -48,7 +48,7 @@ import org.opensearch.action.support.master.AcknowledgedRequest; import org.opensearch.common.io.PathUtils; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeUnit; import org.opensearch.repositories.fs.FsRepository; import org.opensearch.test.OpenSearchTestCase; @@ -263,7 +263,7 @@ public void testRestoreSnapshot() throws IOException { if (randomBoolean()) { String timeout = randomTimeValue(); - restoreSnapshotRequest.masterNodeTimeout(timeout); + restoreSnapshotRequest.clusterManagerNodeTimeout(timeout); expectedParams.put("master_timeout", timeout); } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/StoredScriptsIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/StoredScriptsIT.java index b3898fe0b9566..4d792e53c6064 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/StoredScriptsIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/StoredScriptsIT.java @@ -37,9 +37,9 @@ import org.opensearch.action.admin.cluster.storedscripts.GetStoredScriptRequest; import org.opensearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; import org.opensearch.action.admin.cluster.storedscripts.PutStoredScriptRequest; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.script.Script; import org.opensearch.script.StoredScriptSource; @@ -58,14 +58,20 @@ public void testGetStoredScript() throws Exception { final StoredScriptSource scriptSource = new StoredScriptSource( "painless", "Math.log(_score * 2) + params.my_modifier", - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()) + Collections.singletonMap(Script.CONTENT_TYPE_OPTION, MediaTypeRegistry.JSON.mediaType()) ); - PutStoredScriptRequest request = new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource); + PutStoredScriptRequest request = new PutStoredScriptRequest( + id, + "score", + new BytesArray("{}"), + MediaTypeRegistry.JSON, + scriptSource + ); assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); GetStoredScriptRequest getRequest = new GetStoredScriptRequest("calculate-score"); - getRequest.masterNodeTimeout("50s"); + getRequest.clusterManagerNodeTimeout("50s"); GetStoredScriptResponse getResponse = execute(getRequest, highLevelClient()::getScript, highLevelClient()::getScriptAsync); @@ -76,14 +82,20 @@ public void testDeleteStoredScript() throws Exception { final StoredScriptSource scriptSource = new StoredScriptSource( "painless", "Math.log(_score * 2) + params.my_modifier", - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()) + Collections.singletonMap(Script.CONTENT_TYPE_OPTION, MediaTypeRegistry.JSON.mediaType()) ); - PutStoredScriptRequest request = new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource); + PutStoredScriptRequest request = new PutStoredScriptRequest( + id, + "score", + new BytesArray("{}"), + MediaTypeRegistry.JSON, + scriptSource + ); assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); DeleteStoredScriptRequest deleteRequest = new DeleteStoredScriptRequest(id); - deleteRequest.masterNodeTimeout("50s"); + deleteRequest.clusterManagerNodeTimeout("50s"); deleteRequest.timeout("50s"); assertAcked(execute(deleteRequest, highLevelClient()::deleteScript, highLevelClient()::deleteScriptAsync)); @@ -100,10 +112,16 @@ public void testPutScript() throws Exception { final StoredScriptSource scriptSource = new StoredScriptSource( "painless", "Math.log(_score * 2) + params.my_modifier", - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()) + Collections.singletonMap(Script.CONTENT_TYPE_OPTION, MediaTypeRegistry.JSON.mediaType()) ); - PutStoredScriptRequest request = new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource); + PutStoredScriptRequest request = new PutStoredScriptRequest( + id, + "score", + new BytesArray("{}"), + MediaTypeRegistry.JSON, + scriptSource + ); assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); Map script = getAsMap("/_scripts/" + id); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/TasksIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/TasksIT.java index d987e786fff76..c5a16ec32e686 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/TasksIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/TasksIT.java @@ -32,7 +32,6 @@ package org.opensearch.client; -import org.opensearch.index.reindex.ReindexRequest; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.admin.cluster.node.tasks.list.TaskGroup; @@ -46,8 +45,9 @@ import org.opensearch.client.tasks.TaskId; import org.opensearch.client.tasks.TaskSubmissionResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.index.reindex.ReindexRequest; import java.io.IOException; import java.util.Collections; @@ -94,9 +94,9 @@ public void testGetValidTask() throws Exception { createIndex(sourceIndex, settings); createIndex(destinationIndex, settings); BulkRequest bulkRequest = new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo2", "bar2"), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo2", "bar2"), MediaTypeRegistry.JSON)) .setRefreshPolicy(RefreshPolicy.IMMEDIATE); assertEquals(RestStatus.OK, highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT).status()); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/TasksRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/TasksRequestConvertersTests.java index 64fec3c8fb810..e67babc2b3ca2 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/TasksRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/TasksRequestConvertersTests.java @@ -36,7 +36,7 @@ import org.apache.http.client.methods.HttpPost; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.opensearch.client.tasks.CancelTasksRequest; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.tasks.TaskId; import org.opensearch.test.OpenSearchTestCase; import java.util.HashMap; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/TimedRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/TimedRequestTests.java index 3026472bb8e53..a630f4f5c1489 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/TimedRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/TimedRequestTests.java @@ -41,17 +41,17 @@ public void testDefaults() { TimedRequest timedRequest = new TimedRequest() { }; assertEquals(timedRequest.timeout(), TimedRequest.DEFAULT_ACK_TIMEOUT); - assertEquals(timedRequest.masterNodeTimeout(), TimedRequest.DEFAULT_MASTER_NODE_TIMEOUT); + assertEquals(timedRequest.clusterManagerNodeTimeout(), TimedRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT); } public void testNonDefaults() { TimedRequest timedRequest = new TimedRequest() { }; TimeValue timeout = TimeValue.timeValueSeconds(randomIntBetween(0, 1000)); - TimeValue masterTimeout = TimeValue.timeValueSeconds(randomIntBetween(0, 1000)); + TimeValue clusterManagerTimeout = TimeValue.timeValueSeconds(randomIntBetween(0, 1000)); timedRequest.setTimeout(timeout); - timedRequest.setMasterTimeout(masterTimeout); + timedRequest.setClusterManagerTimeout(clusterManagerTimeout); assertEquals(timedRequest.timeout(), timeout); - assertEquals(timedRequest.masterNodeTimeout(), masterTimeout); + assertEquals(timedRequest.clusterManagerNodeTimeout(), clusterManagerTimeout); } } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/UpdateByQueryIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/UpdateByQueryIT.java index d73fa2bd0aa80..ef7b7d5a39b6f 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/UpdateByQueryIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/UpdateByQueryIT.java @@ -32,7 +32,6 @@ package org.opensearch.client; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.bulk.BulkItemResponse; import org.opensearch.action.bulk.BulkRequest; @@ -41,15 +40,16 @@ import org.opensearch.action.support.WriteRequest; import org.opensearch.client.tasks.TaskSubmissionResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.query.IdsQueryBuilder; import org.opensearch.index.reindex.BulkByScrollResponse; import org.opensearch.index.reindex.UpdateByQueryAction; import org.opensearch.index.reindex.UpdateByQueryRequest; -import org.opensearch.rest.RestStatus; import org.opensearch.script.Script; import org.opensearch.tasks.RawTaskStatus; -import org.opensearch.tasks.TaskId; import java.io.IOException; import java.util.Collections; @@ -76,9 +76,9 @@ public void testUpdateByQuery() throws Exception { RestStatus.OK, highLevelClient().bulk( new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", 1), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", 1), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", 2), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", 2), MediaTypeRegistry.JSON)) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT ).status() @@ -197,10 +197,10 @@ public void testUpdateByQueryTask() throws Exception { RestStatus.OK, highLevelClient().bulk( new BulkRequest().add( - new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", 1), XContentType.JSON) + new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", 1), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", 2), XContentType.JSON)) - .add(new IndexRequest(sourceIndex).id("3").source(Collections.singletonMap("foo", 3), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", 2), MediaTypeRegistry.JSON)) + .add(new IndexRequest(sourceIndex).id("3").source(Collections.singletonMap("foo", 3), MediaTypeRegistry.JSON)) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE), RequestOptions.DEFAULT ).status() @@ -230,9 +230,9 @@ public void testUpdateByQueryConflict() throws IOException { final Settings settings = Settings.builder().put("number_of_shards", 1).put("number_of_replicas", 0).build(); createIndex(index, settings); final BulkRequest bulkRequest = new BulkRequest().add( - new IndexRequest(index).id("1").source(Collections.singletonMap("foo", "bar"), XContentType.JSON) + new IndexRequest(index).id("1").source(Collections.singletonMap("foo", "bar"), MediaTypeRegistry.JSON) ) - .add(new IndexRequest(index).id("2").source(Collections.singletonMap("foo", "bar"), XContentType.JSON)) + .add(new IndexRequest(index).id("2").source(Collections.singletonMap("foo", "bar"), MediaTypeRegistry.JSON)) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); assertThat(highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT).status(), equalTo(RestStatus.OK)); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/cluster/RemoteInfoResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/cluster/RemoteInfoResponseTests.java index a6ebec8197698..201a8ee1e60ac 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/cluster/RemoteInfoResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/cluster/RemoteInfoResponseTests.java @@ -34,8 +34,8 @@ import org.opensearch.client.AbstractResponseTestCase; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.transport.ProxyConnectionStrategy; import org.opensearch.transport.RemoteConnectionInfo; import org.opensearch.transport.SniffConnectionStrategy; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/AcknowledgedResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/AcknowledgedResponseTests.java index a517e37dc8764..e184df7ad013c 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/AcknowledgedResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/AcknowledgedResponseTests.java @@ -32,8 +32,8 @@ package org.opensearch.client.core; import org.opensearch.client.AbstractResponseTestCase; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/BroadcastResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/BroadcastResponseTests.java index b608e6945a4b3..de2af42cb900f 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/BroadcastResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/BroadcastResponseTests.java @@ -32,10 +32,10 @@ package org.opensearch.client.core; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.client.AbstractResponseTestCase; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.seqno.RetentionLeaseNotFoundException; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/CountRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/CountRequestTests.java index a973f8352a08c..d5e739789829d 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/CountRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/CountRequestTests.java @@ -36,8 +36,8 @@ import org.opensearch.client.AbstractRequestTestCase; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.ArrayUtils; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.index.query.QueryBuilder; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/CountResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/CountResponseTests.java index 3b9b91bd93c1d..946ebc1089580 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/CountResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/CountResponseTests.java @@ -34,11 +34,11 @@ import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.ParsingException; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.Index; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.rest.action.RestActions; import org.opensearch.search.SearchShardTarget; import org.opensearch.test.OpenSearchTestCase; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/GetSourceResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/GetSourceResponseTests.java index 6f26fed2d0458..0c2b269c7e78d 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/GetSourceResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/GetSourceResponseTests.java @@ -33,13 +33,13 @@ package org.opensearch.client.core; import org.opensearch.client.AbstractResponseTestCase; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.InputStream; @@ -61,7 +61,7 @@ static class SourceOnlyResponse implements ToXContentObject { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { // this implementation copied from RestGetSourceAction.RestGetSourceResponseListener::buildResponse try (InputStream stream = source.streamInput()) { - builder.rawValue(stream, XContentHelper.xContentType(source)); + builder.rawValue(stream, MediaTypeRegistry.xContentType(source)); } return builder; } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/MainResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/MainResponseTests.java index cabb125a739b7..99f28ae337dc5 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/MainResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/MainResponseTests.java @@ -37,8 +37,8 @@ import org.opensearch.Version; import org.opensearch.client.AbstractResponseTestCase; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.VersionUtils; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/MultiTermVectorsResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/MultiTermVectorsResponseTests.java index c7f1fa0982afa..20835cacfd356 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/MultiTermVectorsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/MultiTermVectorsResponseTests.java @@ -32,7 +32,7 @@ package org.opensearch.client.core; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/ShardsAcknowledgedResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/ShardsAcknowledgedResponseTests.java index 5e2c275708537..e426e7fbdbf5f 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/ShardsAcknowledgedResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/ShardsAcknowledgedResponseTests.java @@ -31,7 +31,7 @@ package org.opensearch.client.core; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/TermVectorsResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/TermVectorsResponseTests.java index 33b82c10d8873..5b6b96fd9144f 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/TermVectorsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/TermVectorsResponseTests.java @@ -32,14 +32,14 @@ package org.opensearch.client.core; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; -import java.util.ArrayList; -import java.util.List; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.List; import static org.opensearch.test.AbstractXContentTestCase.xContentTester; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/core/tasks/GetTaskResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/core/tasks/GetTaskResponseTests.java index 403e295303784..060caeddd9826 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/core/tasks/GetTaskResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/core/tasks/GetTaskResponseTests.java @@ -34,17 +34,21 @@ import org.opensearch.client.Requests; import org.opensearch.client.tasks.GetTaskResponse; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.tasks.resourcetracker.TaskResourceStats; +import org.opensearch.core.tasks.resourcetracker.TaskResourceUsage; +import org.opensearch.core.tasks.resourcetracker.TaskThreadUsage; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.tasks.RawTaskStatus; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import static org.opensearch.test.AbstractXContentTestCase.xContentTester; @@ -57,7 +61,7 @@ public void testFromXContent() throws IOException { ) .assertEqualsConsumer(this::assertEqualInstances) .assertToXContentEquivalence(true) - .randomFieldsExcludeFilter(field -> field.endsWith("headers") || field.endsWith("status")) + .randomFieldsExcludeFilter(field -> field.endsWith("headers") || field.endsWith("status") || field.contains("resource_stats")) .test(); } @@ -92,6 +96,10 @@ static TaskInfo randomTaskInfo() { boolean cancellable = randomBoolean(); boolean cancelled = cancellable == true ? randomBoolean() : false; TaskId parentTaskId = randomBoolean() ? TaskId.EMPTY_TASK_ID : randomTaskId(); + Long cancellationStartTime = null; + if (cancelled) { + cancellationStartTime = randomNonNegativeLong(); + } Map headers = randomBoolean() ? Collections.emptyMap() : Collections.singletonMap(randomAlphaOfLength(5), randomAlphaOfLength(5)); @@ -106,7 +114,9 @@ static TaskInfo randomTaskInfo() { cancellable, cancelled, parentTaskId, - headers + headers, + randomResourceStats(), + cancellationStartTime ); } @@ -127,4 +137,14 @@ private static RawTaskStatus randomRawTaskStatus() { throw new IllegalStateException(e); } } + + private static TaskResourceStats randomResourceStats() { + return randomBoolean() ? null : new TaskResourceStats(new HashMap<>() { + { + for (int i = 0; i < randomInt(5); i++) { + put(randomAlphaOfLength(5), new TaskResourceUsage(randomNonNegativeLong(), randomNonNegativeLong())); + } + } + }, new TaskThreadUsage(randomInt(10), randomInt(10))); + } } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/CRUDDocumentationIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/CRUDDocumentationIT.java index 959c5a827f143..b2cfe5ff9f55c 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/CRUDDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/CRUDDocumentationIT.java @@ -34,7 +34,6 @@ import org.apache.http.HttpHost; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.LatchedActionListener; @@ -74,16 +73,19 @@ import org.opensearch.client.core.TermVectorsResponse; import org.opensearch.client.indices.CreateIndexRequest; import org.opensearch.client.indices.CreateIndexResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.VersionType; import org.opensearch.index.get.GetResult; import org.opensearch.index.query.MatchAllQueryBuilder; @@ -94,11 +96,9 @@ import org.opensearch.index.reindex.RemoteInfo; import org.opensearch.index.reindex.ScrollableHitSource; import org.opensearch.index.reindex.UpdateByQueryRequest; -import org.opensearch.rest.RestStatus; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.fetch.subphase.FetchSourceContext; -import org.opensearch.tasks.TaskId; import java.util.Collections; import java.util.Date; @@ -173,7 +173,7 @@ public void testIndex() throws Exception { "\"postDate\":\"2013-01-30\"," + "\"message\":\"trying out OpenSearch\"" + "}"; - request.source(jsonString, XContentType.JSON); // <3> + request.source(jsonString, MediaTypeRegistry.JSON); // <3> //end::index-request-string // tag::index-execute @@ -298,15 +298,14 @@ public void testUpdate() throws Exception { Request request = new Request("POST", "/_scripts/increment-field"); request.setJsonEntity( - Strings.toString( - JsonXContent.contentBuilder() - .startObject() - .startObject("script") - .field("lang", "painless") - .field("source", "ctx._source.field += params.count") - .endObject() - .endObject() - ) + JsonXContent.contentBuilder() + .startObject() + .startObject("script") + .field("lang", "painless") + .field("source", "ctx._source.field += params.count") + .endObject() + .endObject() + .toString() ); Response response = client().performRequest(request); assertEquals(RestStatus.OK.getStatus(), response.getStatusLine().getStatusCode()); @@ -381,7 +380,7 @@ public void testUpdate() throws Exception { "\"updated\":\"2017-01-01\"," + "\"reason\":\"daily update\"" + "}"; - request.doc(jsonString, XContentType.JSON); // <1> + request.doc(jsonString, MediaTypeRegistry.JSON); // <1> //end::update-request-with-doc-as-string request.fetchSource(true); // tag::update-execute @@ -525,7 +524,7 @@ public void testUpdate() throws Exception { // end::update-request-detect-noop // tag::update-request-upsert String jsonString = "{\"created\":\"2017-01-01\"}"; - request.upsert(jsonString, XContentType.JSON); // <1> + request.upsert(jsonString, MediaTypeRegistry.JSON); // <1> // end::update-request-upsert // tag::update-request-scripted-upsert request.scriptedUpsert(true); // <1> @@ -699,11 +698,11 @@ public void testBulk() throws Exception { // tag::bulk-request BulkRequest request = new BulkRequest(); // <1> request.add(new IndexRequest("posts").id("1") // <2> - .source(XContentType.JSON,"field", "foo")); + .source(MediaTypeRegistry.JSON,"field", "foo")); request.add(new IndexRequest("posts").id("2") // <3> - .source(XContentType.JSON,"field", "bar")); + .source(MediaTypeRegistry.JSON,"field", "bar")); request.add(new IndexRequest("posts").id("3") // <4> - .source(XContentType.JSON,"field", "baz")); + .source(MediaTypeRegistry.JSON,"field", "baz")); // end::bulk-request // tag::bulk-execute BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT); @@ -716,9 +715,9 @@ public void testBulk() throws Exception { BulkRequest request = new BulkRequest(); request.add(new DeleteRequest("posts", "3")); // <1> request.add(new UpdateRequest("posts", "2") // <2> - .doc(XContentType.JSON,"other", "test")); + .doc(MediaTypeRegistry.JSON,"other", "test")); request.add(new IndexRequest("posts").id("4") // <3> - .source(XContentType.JSON,"field", "baz")); + .source(MediaTypeRegistry.JSON,"field", "baz")); // end::bulk-request-with-mixed-operations BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT); assertSame(RestStatus.OK, bulkResponse.status()); @@ -1581,13 +1580,13 @@ public void afterBulk(long executionId, BulkRequest request, // tag::bulk-processor-add IndexRequest one = new IndexRequest("posts").id("1") - .source(XContentType.JSON, "title", + .source(MediaTypeRegistry.JSON, "title", "In which order are my OpenSearch queries executed?"); IndexRequest two = new IndexRequest("posts").id("2") - .source(XContentType.JSON, "title", + .source(MediaTypeRegistry.JSON, "title", "Current status and upcoming changes in OpenSearch"); IndexRequest three = new IndexRequest("posts").id("3") - .source(XContentType.JSON, "title", + .source(MediaTypeRegistry.JSON, "title", "The Future of Federated Search in OpenSearch"); bulkProcessor.add(one); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/ClusterClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/ClusterClientDocumentationIT.java index 7762813aa53ce..17ea5b273d2a2 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/ClusterClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/ClusterClientDocumentationIT.java @@ -32,7 +32,6 @@ package org.opensearch.client.documentation; -import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -63,11 +62,12 @@ import org.opensearch.common.Priority; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.indices.recovery.RecoverySettings; -import org.opensearch.rest.RestStatus; import java.io.IOException; import java.util.HashMap; @@ -138,7 +138,7 @@ public void testClusterPutSettings() throws IOException { // tag::put-settings-settings-source request.transientSettings( "{\"indices.recovery.max_bytes_per_sec\": \"10b\"}" - , XContentType.JSON); // <1> + , MediaTypeRegistry.JSON); // <1> // end::put-settings-settings-source } @@ -147,8 +147,8 @@ public void testClusterPutSettings() throws IOException { request.timeout("2m"); // <2> // end::put-settings-request-timeout // tag::put-settings-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::put-settings-request-masterTimeout // tag::put-settings-execute @@ -222,8 +222,8 @@ public void testClusterGetSettings() throws IOException { // end::get-settings-request-local // tag::get-settings-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::get-settings-request-masterTimeout // tag::get-settings-execute @@ -299,8 +299,8 @@ public void testClusterHealth() throws IOException { // end::health-request-timeout // tag::health-request-master-timeout - request.masterNodeTimeout(TimeValue.timeValueSeconds(20)); // <1> - request.masterNodeTimeout("20s"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueSeconds(20)); // <1> + request.clusterManagerNodeTimeout("20s"); // <2> // end::health-request-master-timeout // tag::health-request-wait-status @@ -515,8 +515,8 @@ public void testGetComponentTemplates() throws Exception { // end::get-component-templates-request // tag::get-component-templates-request-masterTimeout - request.setMasterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.setMasterNodeTimeout("1m"); // <2> + request.setClusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerNodeTimeout("1m"); // <2> // end::get-component-templates-request-masterTimeout // tag::get-component-templates-execute @@ -603,7 +603,7 @@ public void testPutComponentTemplate() throws Exception { // end::put-component-template-request-create // tag::put-component-template-request-masterTimeout - request.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::put-component-template-request-masterTimeout request.create(false); // make test happy @@ -670,7 +670,7 @@ public void testDeleteComponentTemplate() throws Exception { // end::delete-component-template-request // tag::delete-component-template-request-masterTimeout - deleteRequest.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + deleteRequest.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::delete-component-template-request-masterTimeout // tag::delete-component-template-execute diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/IndicesClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/IndicesClientDocumentationIT.java index 3fbe7f63b09a2..abb2d75aea751 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/IndicesClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/IndicesClientDocumentationIT.java @@ -33,7 +33,6 @@ package org.opensearch.client.documentation; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; @@ -61,11 +60,10 @@ import org.opensearch.action.admin.indices.validate.query.ValidateQueryRequest; import org.opensearch.action.admin.indices.validate.query.ValidateQueryResponse; import org.opensearch.action.support.ActiveShardCount; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.client.OpenSearchRestHighLevelClientTestCase; import org.opensearch.client.GetAliasesResponse; +import org.opensearch.client.OpenSearchRestHighLevelClientTestCase; import org.opensearch.client.RequestOptions; import org.opensearch.client.RestHighLevelClient; import org.opensearch.client.indices.AnalyzeRequest; @@ -77,21 +75,21 @@ import org.opensearch.client.indices.DeleteAliasRequest; import org.opensearch.client.indices.DeleteComposableIndexTemplateRequest; import org.opensearch.client.indices.DetailAnalyzeResponse; +import org.opensearch.client.indices.GetComposableIndexTemplateRequest; +import org.opensearch.client.indices.GetComposableIndexTemplatesResponse; import org.opensearch.client.indices.GetFieldMappingsRequest; import org.opensearch.client.indices.GetFieldMappingsResponse; import org.opensearch.client.indices.GetIndexRequest; import org.opensearch.client.indices.GetIndexResponse; -import org.opensearch.client.indices.GetComposableIndexTemplateRequest; import org.opensearch.client.indices.GetIndexTemplatesRequest; import org.opensearch.client.indices.GetIndexTemplatesResponse; -import org.opensearch.client.indices.GetComposableIndexTemplatesResponse; import org.opensearch.client.indices.GetMappingsRequest; import org.opensearch.client.indices.GetMappingsResponse; import org.opensearch.client.indices.IndexTemplateMetadata; import org.opensearch.client.indices.IndexTemplatesExistRequest; import org.opensearch.client.indices.PutComponentTemplateRequest; -import org.opensearch.client.indices.PutIndexTemplateRequest; import org.opensearch.client.indices.PutComposableIndexTemplateRequest; +import org.opensearch.client.indices.PutIndexTemplateRequest; import org.opensearch.client.indices.PutMappingRequest; import org.opensearch.client.indices.SimulateIndexTemplateRequest; import org.opensearch.client.indices.SimulateIndexTemplateResponse; @@ -104,16 +102,18 @@ import org.opensearch.cluster.metadata.Template; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.rest.RestStatus; import java.io.IOException; import java.util.Arrays; @@ -235,8 +235,8 @@ public void testDeleteIndex() throws IOException { request.timeout("2m"); // <2> // end::delete-index-request-timeout // tag::delete-index-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::delete-index-request-masterTimeout // tag::delete-index-request-indicesOptions request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1> @@ -329,7 +329,7 @@ public void testCreateIndex() throws IOException { " }\n" + " }\n" + "}", // <2> - XContentType.JSON); + MediaTypeRegistry.JSON); // end::create-index-request-mappings CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT); assertTrue(createIndexResponse.isAcknowledged()); @@ -381,7 +381,7 @@ public void testCreateIndex() throws IOException { request.setTimeout(TimeValue.timeValueMinutes(2)); // <1> // end::create-index-request-timeout // tag::create-index-request-masterTimeout - request.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::create-index-request-masterTimeout // tag::create-index-request-waitForActiveShards request.waitForActiveShards(ActiveShardCount.from(2)); // <1> @@ -407,7 +407,7 @@ public void testCreateIndex() throws IOException { " \"aliases\" : {\n" + " \"twitter_alias\" : {}\n" + " }\n" + - "}", XContentType.JSON); // <1> + "}", MediaTypeRegistry.JSON); // <1> // end::create-index-whole-source // tag::create-index-execute @@ -480,7 +480,7 @@ public void testPutMapping() throws IOException { " }\n" + " }\n" + "}", // <1> - XContentType.JSON); + MediaTypeRegistry.JSON); // end::put-mapping-request-source AcknowledgedResponse putMappingResponse = client.indices().putMapping(request, RequestOptions.DEFAULT); assertTrue(putMappingResponse.isAcknowledged()); @@ -526,7 +526,7 @@ public void testPutMapping() throws IOException { // end::put-mapping-request-timeout // tag::put-mapping-request-masterTimeout - request.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::put-mapping-request-masterTimeout // tag::put-mapping-execute @@ -585,7 +585,7 @@ public void testGetMapping() throws IOException { CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter"), RequestOptions.DEFAULT); assertTrue(createIndexResponse.isAcknowledged()); PutMappingRequest request = new PutMappingRequest("twitter"); - request.source("{ \"properties\": { \"message\": { \"type\": \"text\" } } }", XContentType.JSON); + request.source("{ \"properties\": { \"message\": { \"type\": \"text\" } } }", MediaTypeRegistry.JSON); AcknowledgedResponse putMappingResponse = client.indices().putMapping(request, RequestOptions.DEFAULT); assertTrue(putMappingResponse.isAcknowledged()); } @@ -597,7 +597,7 @@ public void testGetMapping() throws IOException { // end::get-mappings-request // tag::get-mappings-request-masterTimeout - request.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::get-mappings-request-masterTimeout // tag::get-mappings-request-indicesOptions @@ -631,7 +631,7 @@ public void testGetMappingAsync() throws Exception { CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter"), RequestOptions.DEFAULT); assertTrue(createIndexResponse.isAcknowledged()); PutMappingRequest request = new PutMappingRequest("twitter"); - request.source("{ \"properties\": { \"message\": { \"type\": \"text\" } } }", XContentType.JSON); + request.source("{ \"properties\": { \"message\": { \"type\": \"text\" } } }", MediaTypeRegistry.JSON); AcknowledgedResponse putMappingResponse = client.indices().putMapping(request, RequestOptions.DEFAULT); assertTrue(putMappingResponse.isAcknowledged()); } @@ -703,7 +703,7 @@ public void testGetFieldMapping() throws IOException, InterruptedException { + " }\n" + " }\n" + "}", // <1> - XContentType.JSON + MediaTypeRegistry.JSON ); AcknowledgedResponse putMappingResponse = client.indices().putMapping(request, RequestOptions.DEFAULT); assertTrue(putMappingResponse.isAcknowledged()); @@ -801,8 +801,8 @@ public void testOpenIndex() throws Exception { request.timeout("2m"); // <2> // end::open-index-request-timeout // tag::open-index-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::open-index-request-masterTimeout // tag::open-index-request-waitForActiveShards request.waitForActiveShards(2); // <1> @@ -1127,7 +1127,8 @@ public void testGetIndex() throws Exception { { Settings settings = Settings.builder().put("number_of_shards", 3).build(); String mappings = "{\"properties\":{\"field-1\":{\"type\":\"integer\"}}}"; - CreateIndexRequest createIndexRequest = new CreateIndexRequest("index").settings(settings).mapping(mappings, XContentType.JSON); + CreateIndexRequest createIndexRequest = new CreateIndexRequest("index").settings(settings) + .mapping(mappings, MediaTypeRegistry.JSON); CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest, RequestOptions.DEFAULT); assertTrue(createIndexResponse.isAcknowledged()); } @@ -1374,7 +1375,7 @@ public void testCloseIndex() throws Exception { request.setTimeout(TimeValue.timeValueMinutes(2)); // <1> // end::close-index-request-timeout // tag::close-index-request-masterTimeout - request.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::close-index-request-masterTimeout // tag::close-index-request-indicesOptions @@ -1530,8 +1531,8 @@ public void testUpdateAliases() throws Exception { request.timeout("2m"); // <2> // end::update-aliases-request-timeout // tag::update-aliases-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::update-aliases-request-masterTimeout // tag::update-aliases-execute @@ -1600,8 +1601,8 @@ public void testShrinkIndex() throws Exception { request.timeout("2m"); // <2> // end::shrink-index-request-timeout // tag::shrink-index-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::shrink-index-request-masterTimeout // tag::shrink-index-request-waitForActiveShards request.setWaitForActiveShards(2); // <1> @@ -1677,8 +1678,8 @@ public void testSplitIndex() throws Exception { request.timeout("2m"); // <2> // end::split-index-request-timeout // tag::split-index-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::split-index-request-masterTimeout // tag::split-index-request-waitForActiveShards request.setWaitForActiveShards(2); // <1> @@ -1746,8 +1747,8 @@ public void testCloneIndex() throws Exception { request.timeout("2m"); // <2> // end::clone-index-request-timeout // tag::clone-index-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::clone-index-request-masterTimeout // tag::clone-index-request-waitForActiveShards request.setWaitForActiveShards(2); // <1> @@ -1815,7 +1816,7 @@ public void testRolloverIndex() throws Exception { request.setTimeout(TimeValue.timeValueMinutes(2)); // <1> // end::rollover-index-request-timeout // tag::rollover-index-request-masterTimeout - request.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::rollover-index-request-masterTimeout // tag::rollover-index-request-dryRun request.dryRun(true); // <1> @@ -1830,7 +1831,7 @@ public void testRolloverIndex() throws Exception { // end::rollover-index-request-settings // tag::rollover-index-request-mapping String mappings = "{\"properties\":{\"field-1\":{\"type\":\"keyword\"}}}"; - request.getCreateIndexRequest().mapping(mappings, XContentType.JSON); // <1> + request.getCreateIndexRequest().mapping(mappings, MediaTypeRegistry.JSON); // <1> // end::rollover-index-request-mapping // tag::rollover-index-request-alias request.getCreateIndexRequest().alias(new Alias("another_alias")); // <1> @@ -2009,7 +2010,7 @@ public void testIndexPutSettings() throws Exception { // tag::indices-put-settings-settings-source request.settings( "{\"index.number_of_replicas\": \"2\"}" - , XContentType.JSON); // <1> + , MediaTypeRegistry.JSON); // <1> // end::indices-put-settings-settings-source } @@ -2021,8 +2022,8 @@ public void testIndexPutSettings() throws Exception { request.timeout("2m"); // <2> // end::indices-put-settings-request-timeout // tag::indices-put-settings-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::indices-put-settings-request-masterTimeout // tag::indices-put-settings-request-indicesOptions request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1> @@ -2090,7 +2091,7 @@ public void testPutTemplate() throws Exception { " }\n" + " }\n" + "}", - XContentType.JSON); + MediaTypeRegistry.JSON); // end::put-template-request-mappings-json assertTrue(client.indices().putTemplate(request, RequestOptions.DEFAULT).isAcknowledged()); } @@ -2165,7 +2166,7 @@ public void testPutTemplate() throws Exception { " \"alias-1\": {},\n" + " \"{index}-alias\": {}\n" + " }\n" + - "}", XContentType.JSON); // <1> + "}", MediaTypeRegistry.JSON); // <1> // end::put-template-whole-source // tag::put-template-request-create @@ -2173,8 +2174,8 @@ public void testPutTemplate() throws Exception { // end::put-template-request-create // tag::put-template-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::put-template-request-masterTimeout request.create(false); // make test happy @@ -2220,7 +2221,7 @@ public void testGetTemplates() throws Exception { PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest("my-template"); putRequest.patterns(Arrays.asList("pattern-1", "log-*")); putRequest.settings(Settings.builder().put("index.number_of_shards", 3).put("index.number_of_replicas", 1)); - putRequest.mapping("{ \"properties\": { \"message\": { \"type\": \"text\" } } }", XContentType.JSON); + putRequest.mapping("{ \"properties\": { \"message\": { \"type\": \"text\" } } }", MediaTypeRegistry.JSON); assertTrue(client.indices().putTemplate(putRequest, RequestOptions.DEFAULT).isAcknowledged()); } @@ -2231,8 +2232,8 @@ public void testGetTemplates() throws Exception { // end::get-templates-request // tag::get-templates-request-masterTimeout - request.setMasterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.setMasterNodeTimeout("1m"); // <2> + request.setClusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerNodeTimeout("1m"); // <2> // end::get-templates-request-masterTimeout // tag::get-templates-execute @@ -2291,8 +2292,8 @@ public void testGetIndexTemplatesV2() throws Exception { // end::get-index-templates-v2-request // tag::get-index-templates-v2-request-masterTimeout - request.setMasterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.setMasterNodeTimeout("1m"); // <2> + request.setClusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerNodeTimeout("1m"); // <2> // end::get-index-templates-v2-request-masterTimeout // tag::get-index-templates-v2-execute @@ -2450,7 +2451,7 @@ public void testPutIndexTemplateV2() throws Exception { // end::put-index-template-v2-request-create // tag::put-index-template-v2-request-masterTimeout - request.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::put-index-template-v2-request-masterTimeout request.create(false); // make test happy @@ -2511,7 +2512,7 @@ public void testDeleteIndexTemplateV2() throws Exception { // end::delete-index-template-v2-request // tag::delete-index-template-v2-request-masterTimeout - deleteRequest.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + deleteRequest.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::delete-index-template-v2-request-masterTimeout // tag::delete-index-template-v2-execute @@ -2644,8 +2645,8 @@ public void testTemplatesExist() throws Exception { // tag::templates-exist-request-optionals request.setLocal(true); // <1> - request.setMasterNodeTimeout(TimeValue.timeValueMinutes(1)); // <2> - request.setMasterNodeTimeout("1m"); // <3> + request.setClusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <2> + request.setClusterManagerNodeTimeout("1m"); // <3> // end::templates-exist-request-optionals // tag::templates-exist-execute @@ -2897,8 +2898,8 @@ public void testDeleteTemplate() throws Exception { // end::delete-template-request // tag::delete-template-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::delete-template-request-masterTimeout // tag::delete-template-execute @@ -2973,7 +2974,7 @@ public void testDeleteAlias() throws Exception { request.setTimeout(TimeValue.timeValueMinutes(2)); // <1> // end::delete-alias-request-timeout // tag::delete-alias-request-masterTimeout - request.setMasterTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.setClusterManagerTimeout(TimeValue.timeValueMinutes(1)); // <1> // end::delete-alias-request-masterTimeout // tag::delete-alias-execute diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/IngestClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/IngestClientDocumentationIT.java index a6157f2903103..d14759065b5eb 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/IngestClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/IngestClientDocumentationIT.java @@ -32,7 +32,6 @@ package org.opensearch.client.documentation; -import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.ingest.DeletePipelineRequest; import org.opensearch.action.ingest.GetPipelineRequest; @@ -48,9 +47,10 @@ import org.opensearch.client.OpenSearchRestHighLevelClientTestCase; import org.opensearch.client.RequestOptions; import org.opensearch.client.RestHighLevelClient; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.ingest.PipelineConfiguration; import java.io.IOException; @@ -91,7 +91,7 @@ public void testPutPipeline() throws IOException { PutPipelineRequest request = new PutPipelineRequest( "my-pipeline-id", // <1> new BytesArray(source.getBytes(StandardCharsets.UTF_8)), // <2> - XContentType.JSON // <3> + MediaTypeRegistry.JSON // <3> ); // end::put-pipeline-request @@ -101,8 +101,8 @@ public void testPutPipeline() throws IOException { // end::put-pipeline-request-timeout // tag::put-pipeline-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::put-pipeline-request-masterTimeout // tag::put-pipeline-execute @@ -125,7 +125,7 @@ public void testPutPipelineAsync() throws Exception { PutPipelineRequest request = new PutPipelineRequest( "my-pipeline-id", new BytesArray(source.getBytes(StandardCharsets.UTF_8)), - XContentType.JSON + MediaTypeRegistry.JSON ); // tag::put-pipeline-execute-listener @@ -169,8 +169,8 @@ public void testGetPipeline() throws IOException { // end::get-pipeline-request // tag::get-pipeline-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::get-pipeline-request-masterTimeout // tag::get-pipeline-execute @@ -244,8 +244,8 @@ public void testDeletePipeline() throws IOException { // end::delete-pipeline-request-timeout // tag::delete-pipeline-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::delete-pipeline-request-masterTimeout // tag::delete-pipeline-execute @@ -314,7 +314,7 @@ public void testSimulatePipeline() throws IOException { "}"; SimulatePipelineRequest request = new SimulatePipelineRequest( new BytesArray(source.getBytes(StandardCharsets.UTF_8)), // <1> - XContentType.JSON // <2> + MediaTypeRegistry.JSON // <2> ); // end::simulate-pipeline-request @@ -370,7 +370,7 @@ public void testSimulatePipelineAsync() throws Exception { + "}"; SimulatePipelineRequest request = new SimulatePipelineRequest( new BytesArray(source.getBytes(StandardCharsets.UTF_8)), - XContentType.JSON + MediaTypeRegistry.JSON ); // tag::simulate-pipeline-execute-listener diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/QueryDSLDocumentationTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/QueryDSLDocumentationTests.java index 9f5c2e51a7960..4304ef04f6dc4 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/QueryDSLDocumentationTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/QueryDSLDocumentationTests.java @@ -39,10 +39,10 @@ import org.opensearch.common.geo.builders.MultiPointBuilder; import org.opensearch.common.unit.DistanceUnit; import org.opensearch.index.query.GeoShapeQueryBuilder; +import org.opensearch.index.query.RankFeatureQueryBuilders; import org.opensearch.index.query.functionscore.FunctionScoreQueryBuilder; import org.opensearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder; import org.opensearch.join.query.JoinQueryBuilders; -import org.opensearch.index.query.RankFeatureQueryBuilders; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.test.OpenSearchTestCase; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/SearchDocumentationIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/SearchDocumentationIT.java index d514f66ff36d9..bf0f70304168e 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/SearchDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/SearchDocumentationIT.java @@ -34,7 +34,6 @@ import org.apache.lucene.search.Explanation; import org.apache.lucene.search.TotalHits; -import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.bulk.BulkRequest; import org.opensearch.action.bulk.BulkResponse; @@ -65,13 +64,15 @@ import org.opensearch.client.core.CountResponse; import org.opensearch.client.indices.CreateIndexRequest; import org.opensearch.client.indices.CreateIndexResponse; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.document.DocumentField; -import org.opensearch.common.text.Text; import org.opensearch.common.unit.Fuzziness; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.text.Text; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.get.GetResult; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.index.query.QueryBuilder; @@ -86,7 +87,6 @@ import org.opensearch.index.rankeval.RatedDocument; import org.opensearch.index.rankeval.RatedRequest; import org.opensearch.index.rankeval.RatedSearchHit; -import org.opensearch.rest.RestStatus; import org.opensearch.script.ScriptType; import org.opensearch.script.mustache.MultiSearchTemplateRequest; import org.opensearch.script.mustache.MultiSearchTemplateResponse; @@ -319,9 +319,9 @@ public void testSearchRequestAggregations() throws IOException { RestHighLevelClient client = highLevelClient(); { BulkRequest request = new BulkRequest(); - request.add(new IndexRequest("posts").id("1").source(XContentType.JSON, "company", "OpenSearch", "age", 20)); - request.add(new IndexRequest("posts").id("2").source(XContentType.JSON, "company", "OpenSearch", "age", 30)); - request.add(new IndexRequest("posts").id("3").source(XContentType.JSON, "company", "OpenSearch", "age", 40)); + request.add(new IndexRequest("posts").id("1").source(MediaTypeRegistry.JSON, "company", "OpenSearch", "age", 20)); + request.add(new IndexRequest("posts").id("2").source(MediaTypeRegistry.JSON, "company", "OpenSearch", "age", 30)); + request.add(new IndexRequest("posts").id("3").source(MediaTypeRegistry.JSON, "company", "OpenSearch", "age", 40)); request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT); assertSame(RestStatus.OK, bulkResponse.status()); @@ -392,10 +392,10 @@ public void testSearchRequestSuggestions() throws IOException { RestHighLevelClient client = highLevelClient(); { BulkRequest request = new BulkRequest(); - request.add(new IndexRequest("posts").id("1").source(XContentType.JSON, "user", "foobar")); - request.add(new IndexRequest("posts").id("2").source(XContentType.JSON, "user", "quxx")); - request.add(new IndexRequest("posts").id("3").source(XContentType.JSON, "user", "quzz")); - request.add(new IndexRequest("posts").id("4").source(XContentType.JSON, "user", "corge")); + request.add(new IndexRequest("posts").id("1").source(MediaTypeRegistry.JSON, "user", "foobar")); + request.add(new IndexRequest("posts").id("2").source(MediaTypeRegistry.JSON, "user", "quxx")); + request.add(new IndexRequest("posts").id("3").source(MediaTypeRegistry.JSON, "user", "quzz")); + request.add(new IndexRequest("posts").id("4").source(MediaTypeRegistry.JSON, "user", "corge")); request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT); assertSame(RestStatus.OK, bulkResponse.status()); @@ -438,7 +438,7 @@ public void testSearchRequestHighlighting() throws IOException { request.add( new IndexRequest("posts").id("1") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "title", "In which order are my OpenSearch queries executed?", "user", @@ -450,7 +450,7 @@ public void testSearchRequestHighlighting() throws IOException { request.add( new IndexRequest("posts").id("2") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "title", "Current status and upcoming changes in OpenSearch", "user", @@ -462,7 +462,7 @@ public void testSearchRequestHighlighting() throws IOException { request.add( new IndexRequest("posts").id("3") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "title", "The Future of Federated Search in OpenSearch", "user", @@ -525,7 +525,7 @@ public void testSearchRequestHighlighting() throws IOException { public void testSearchRequestProfiling() throws IOException { RestHighLevelClient client = highLevelClient(); { - IndexRequest request = new IndexRequest("posts").id("1").source(XContentType.JSON, "tags", "opensearch", "comments", 123); + IndexRequest request = new IndexRequest("posts").id("1").source(MediaTypeRegistry.JSON, "tags", "opensearch", "comments", 123); request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL); IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); assertSame(RestStatus.CREATED, indexResponse.status()); @@ -597,13 +597,15 @@ public void testScroll() throws Exception { { BulkRequest request = new BulkRequest(); request.add( - new IndexRequest("posts").id("1").source(XContentType.JSON, "title", "In which order are my OpenSearch queries executed?") + new IndexRequest("posts").id("1") + .source(MediaTypeRegistry.JSON, "title", "In which order are my OpenSearch queries executed?") ); request.add( - new IndexRequest("posts").id("2").source(XContentType.JSON, "title", "Current status and upcoming changes in OpenSearch") + new IndexRequest("posts").id("2") + .source(MediaTypeRegistry.JSON, "title", "Current status and upcoming changes in OpenSearch") ); request.add( - new IndexRequest("posts").id("3").source(XContentType.JSON, "title", "The Future of Federated Search in OpenSearch") + new IndexRequest("posts").id("3").source(MediaTypeRegistry.JSON, "title", "The Future of Federated Search in OpenSearch") ); request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT); @@ -1320,7 +1322,7 @@ private void indexSearchTestData() throws IOException { bulkRequest.add( new IndexRequest("posts").id("1") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "id", 1, "title", @@ -1334,7 +1336,7 @@ private void indexSearchTestData() throws IOException { bulkRequest.add( new IndexRequest("posts").id("2") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "id", 2, "title", @@ -1348,7 +1350,7 @@ private void indexSearchTestData() throws IOException { bulkRequest.add( new IndexRequest("posts").id("3") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "id", 3, "title", @@ -1360,8 +1362,8 @@ private void indexSearchTestData() throws IOException { ) ); - bulkRequest.add(new IndexRequest("authors").id("1").source(XContentType.JSON, "id", 1, "user", "foobar")); - bulkRequest.add(new IndexRequest("contributors").id("1").source(XContentType.JSON, "id", 1, "user", "quuz")); + bulkRequest.add(new IndexRequest("authors").id("1").source(MediaTypeRegistry.JSON, "id", 1, "user", "foobar")); + bulkRequest.add(new IndexRequest("contributors").id("1").source(MediaTypeRegistry.JSON, "id", 1, "user", "quuz")); bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); BulkResponse bulkResponse = highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT); @@ -1472,7 +1474,7 @@ private static void indexCountTestData() throws IOException { bulkRequest.add( new IndexRequest("blog").id("1") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "title", "Doubling Down on Open?", "user", @@ -1484,7 +1486,7 @@ private static void indexCountTestData() throws IOException { bulkRequest.add( new IndexRequest("blog").id("2") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "title", "XYZ Joins Forces with OpenSearch", "user", @@ -1496,7 +1498,7 @@ private static void indexCountTestData() throws IOException { bulkRequest.add( new IndexRequest("blog").id("3") .source( - XContentType.JSON, + MediaTypeRegistry.JSON, "title", "On Net Neutrality", "user", @@ -1506,7 +1508,7 @@ private static void indexCountTestData() throws IOException { ) ); - bulkRequest.add(new IndexRequest("author").id("1").source(XContentType.JSON, "user", "foobar")); + bulkRequest.add(new IndexRequest("author").id("1").source(MediaTypeRegistry.JSON, "user", "foobar")); bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); BulkResponse bulkResponse = highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/SnapshotClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/SnapshotClientDocumentationIT.java index 0ca3c0cd5a598..50bcf79642eac 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/SnapshotClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/SnapshotClientDocumentationIT.java @@ -32,7 +32,6 @@ package org.opensearch.client.documentation; -import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; import org.opensearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; @@ -64,9 +63,10 @@ import org.opensearch.common.Booleans; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.repositories.fs.FsRepository; -import org.opensearch.rest.RestStatus; import org.opensearch.snapshots.RestoreInfo; import org.opensearch.snapshots.SnapshotId; import org.opensearch.snapshots.SnapshotInfo; @@ -156,7 +156,7 @@ public void testSnapshotCreateRepository() throws IOException { { // tag::create-repository-settings-source request.settings("{\"location\": \".\", \"compress\": \"true\"}", - XContentType.JSON); // <1> + MediaTypeRegistry.JSON); // <1> // end::create-repository-settings-source } @@ -168,8 +168,8 @@ public void testSnapshotCreateRepository() throws IOException { // end::create-repository-request-type // tag::create-repository-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::create-repository-request-masterTimeout // tag::create-repository-request-timeout request.timeout(TimeValue.timeValueMinutes(1)); // <1> @@ -238,8 +238,8 @@ public void testSnapshotGetRepository() throws IOException { request.local(true); // <1> // end::get-repository-request-local // tag::get-repository-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::get-repository-request-masterTimeout // tag::get-repository-execute @@ -298,8 +298,8 @@ public void testRestoreSnapshot() throws IOException { // we need to restore as a different index name // tag::restore-snapshot-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::restore-snapshot-request-masterTimeout // tag::restore-snapshot-request-waitForCompletion @@ -395,8 +395,8 @@ public void testSnapshotDeleteRepository() throws IOException { // end::delete-repository-request // tag::delete-repository-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::delete-repository-request-masterTimeout // tag::delete-repository-request-timeout request.timeout(TimeValue.timeValueMinutes(1)); // <1> @@ -454,8 +454,8 @@ public void testSnapshotVerifyRepository() throws IOException { // end::verify-repository-request // tag::verify-repository-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::verify-repository-request-masterTimeout // tag::verify-repository-request-timeout request.timeout(TimeValue.timeValueMinutes(1)); // <1> @@ -544,8 +544,8 @@ public void testSnapshotCreate() throws IOException { // end::create-snapshot-request-includeGlobalState // tag::create-snapshot-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::create-snapshot-request-masterTimeout // tag::create-snapshot-request-waitForCompletion request.waitForCompletion(true); // <1> @@ -622,8 +622,8 @@ public void testSnapshotGetSnapshots() throws IOException { // end::get-snapshots-request-snapshots // tag::get-snapshots-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::get-snapshots-request-masterTimeout // tag::get-snapshots-request-verbose @@ -704,8 +704,8 @@ public void testSnapshotSnapshotsStatus() throws IOException { request.ignoreUnavailable(true); // <1> // end::snapshots-status-request-ignoreUnavailable // tag::snapshots-status-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::snapshots-status-request-masterTimeout // tag::snapshots-status-execute @@ -769,8 +769,8 @@ public void testSnapshotDeleteSnapshot() throws IOException { // end::delete-snapshot-request // tag::delete-snapshot-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::delete-snapshot-request-masterTimeout // tag::delete-snapshot-execute @@ -818,7 +818,7 @@ public void onFailure(Exception e) { private void createTestRepositories() throws IOException { PutRepositoryRequest request = new PutRepositoryRequest(repositoryName); request.type(FsRepository.TYPE); - request.settings("{\"location\": \".\"}", XContentType.JSON); + request.settings("{\"location\": \".\"}", MediaTypeRegistry.JSON); assertTrue(highLevelClient().snapshot().createRepository(request, RequestOptions.DEFAULT).isAcknowledged()); } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/StoredScriptsDocumentationIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/StoredScriptsDocumentationIT.java index c9c36a6165c45..6916ae11556e2 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/StoredScriptsDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/StoredScriptsDocumentationIT.java @@ -32,7 +32,6 @@ package org.opensearch.client.documentation; -import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.admin.cluster.storedscripts.DeleteStoredScriptRequest; import org.opensearch.action.admin.cluster.storedscripts.GetStoredScriptRequest; @@ -42,12 +41,13 @@ import org.opensearch.client.OpenSearchRestHighLevelClientTestCase; import org.opensearch.client.RequestOptions; import org.opensearch.client.RestHighLevelClient; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.script.Script; import org.opensearch.script.StoredScriptSource; @@ -88,7 +88,7 @@ public void testGetStoredScript() throws Exception { final StoredScriptSource scriptSource = new StoredScriptSource( "painless", "Math.log(_score * 2) + params.my_modifier", - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()) + Collections.singletonMap(Script.CONTENT_TYPE_OPTION, MediaTypeRegistry.JSON.mediaType()) ); putStoredScript("calculate-score", scriptSource); @@ -99,8 +99,8 @@ public void testGetStoredScript() throws Exception { // end::get-stored-script-request // tag::get-stored-script-request-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueSeconds(50)); // <1> - request.masterNodeTimeout("50s"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueSeconds(50)); // <1> + request.clusterManagerNodeTimeout("50s"); // <2> // end::get-stored-script-request-masterTimeout // tag::get-stored-script-execute @@ -152,7 +152,7 @@ public void testDeleteStoredScript() throws Exception { final StoredScriptSource scriptSource = new StoredScriptSource( "painless", "Math.log(_score * 2) + params.my_modifier", - Collections.singletonMap(Script.CONTENT_TYPE_OPTION, XContentType.JSON.mediaType()) + Collections.singletonMap(Script.CONTENT_TYPE_OPTION, MediaTypeRegistry.JSON.mediaType()) ); putStoredScript("calculate-score", scriptSource); @@ -162,8 +162,8 @@ public void testDeleteStoredScript() throws Exception { // end::delete-stored-script-request // tag::delete-stored-script-request-masterTimeout - deleteRequest.masterNodeTimeout(TimeValue.timeValueSeconds(50)); // <1> - deleteRequest.masterNodeTimeout("50s"); // <2> + deleteRequest.clusterManagerNodeTimeout(TimeValue.timeValueSeconds(50)); // <1> + deleteRequest.clusterManagerNodeTimeout("50s"); // <2> // end::delete-stored-script-request-masterTimeout // tag::delete-stored-script-request-timeout @@ -221,7 +221,7 @@ public void testPutScript() throws Exception { "\"source\": \"Math.log(_score * 2) + params.multiplier\"" + "}\n" + "}\n" - ), XContentType.JSON); // <2> + ), MediaTypeRegistry.JSON); // <2> // end::put-stored-script-request // tag::put-stored-script-context @@ -234,8 +234,8 @@ public void testPutScript() throws Exception { // end::put-stored-script-timeout // tag::put-stored-script-masterTimeout - request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> - request.masterNodeTimeout("1m"); // <2> + request.clusterManagerNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.clusterManagerNodeTimeout("1m"); // <2> // end::put-stored-script-masterTimeout } @@ -255,7 +255,7 @@ public void testPutScript() throws Exception { builder.endObject(); } builder.endObject(); - request.content(BytesReference.bytes(builder), XContentType.JSON); // <1> + request.content(BytesReference.bytes(builder), MediaTypeRegistry.JSON); // <1> // end::put-stored-script-content-painless // tag::put-stored-script-execute @@ -310,7 +310,7 @@ public void onFailure(Exception e) { builder.endObject(); } builder.endObject(); - request.content(BytesReference.bytes(builder), XContentType.JSON); // <1> + request.content(BytesReference.bytes(builder), MediaTypeRegistry.JSON); // <1> // end::put-stored-script-content-mustache client.putScript(request, RequestOptions.DEFAULT); @@ -322,7 +322,13 @@ public void onFailure(Exception e) { } private void putStoredScript(String id, StoredScriptSource scriptSource) throws IOException { - PutStoredScriptRequest request = new PutStoredScriptRequest(id, "score", new BytesArray("{}"), XContentType.JSON, scriptSource); + PutStoredScriptRequest request = new PutStoredScriptRequest( + id, + "score", + new BytesArray("{}"), + MediaTypeRegistry.JSON, + scriptSource + ); assertAcked(execute(request, highLevelClient()::putScript, highLevelClient()::putScriptAsync)); } } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/TasksClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/TasksClientDocumentationIT.java index 05479e2e3e81c..03e267aafd1b7 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/documentation/TasksClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/documentation/TasksClientDocumentationIT.java @@ -33,7 +33,6 @@ package org.opensearch.client.documentation; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.TaskOperationFailure; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksRequest; @@ -45,7 +44,8 @@ import org.opensearch.client.tasks.CancelTasksRequest; import org.opensearch.client.tasks.CancelTasksResponse; import org.opensearch.common.unit.TimeValue; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeGlobalRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeGlobalRequestTests.java index d90c7184e877d..13f37d9d2509a 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeGlobalRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeGlobalRequestTests.java @@ -33,7 +33,7 @@ package org.opensearch.client.indices; import org.opensearch.action.admin.indices.analyze.AnalyzeAction; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeIndexRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeIndexRequestTests.java index 400bfcffb44e7..6d85f961cb79e 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeIndexRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeIndexRequestTests.java @@ -33,7 +33,7 @@ package org.opensearch.client.indices; import org.opensearch.action.admin.indices.analyze.AnalyzeAction; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeResponseTests.java index 41b91fcaec340..dadd64f8329cc 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/AnalyzeResponseTests.java @@ -34,8 +34,8 @@ import org.opensearch.action.admin.indices.analyze.AnalyzeAction; import org.opensearch.client.AbstractResponseTestCase; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.RandomObjects; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/CloseIndexRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/CloseIndexRequestTests.java index c96e296891fb9..1b93c478fcb7d 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/CloseIndexRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/CloseIndexRequestTests.java @@ -35,8 +35,8 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.client.TimedRequest; import org.opensearch.client.ValidationException; -import org.opensearch.common.Strings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; import org.opensearch.test.OpenSearchTestCase; import java.util.Optional; @@ -75,15 +75,15 @@ public void testWaitForActiveShards() { public void testTimeout() { final CloseIndexRequest request = new CloseIndexRequest("index"); assertEquals(request.timeout(), TimedRequest.DEFAULT_ACK_TIMEOUT); - assertEquals(request.masterNodeTimeout(), TimedRequest.DEFAULT_MASTER_NODE_TIMEOUT); + assertEquals(request.clusterManagerNodeTimeout(), TimedRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT); final TimeValue timeout = TimeValue.timeValueSeconds(randomIntBetween(0, 1000)); request.setTimeout(timeout); - final TimeValue masterTimeout = TimeValue.timeValueSeconds(randomIntBetween(0, 1000)); - request.setMasterTimeout(masterTimeout); + final TimeValue clusterManagerTimeout = TimeValue.timeValueSeconds(randomIntBetween(0, 1000)); + request.setClusterManagerTimeout(clusterManagerTimeout); assertEquals(request.timeout(), timeout); - assertEquals(request.masterNodeTimeout(), masterTimeout); + assertEquals(request.clusterManagerNodeTimeout(), clusterManagerTimeout); } } diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/CloseIndexResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/CloseIndexResponseTests.java index 3fa35f6fffd22..6aafee142bd22 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/CloseIndexResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/CloseIndexResponseTests.java @@ -35,16 +35,15 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; import org.opensearch.client.AbstractResponseTestCase; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.index.Index; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.IndexNotFoundException; -import org.opensearch.rest.RestStatus; import org.opensearch.transport.ActionNotFoundTransportException; import java.io.IOException; @@ -194,7 +193,7 @@ public final void testBwcFromXContent() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); final BytesReference bytes = toShuffledXContent(expected, xContentType, getParams(), randomBoolean()); - final XContent xContent = XContentFactory.xContent(xContentType); + final XContent xContent = xContentType.xContent(); final XContentParser parser = xContent.createParser( NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, @@ -215,7 +214,7 @@ public final void testBwcFromXContent() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); final BytesReference bytes = toShuffledXContent(expected, xContentType, getParams(), randomBoolean()); - final XContent xContent = XContentFactory.xContent(xContentType); + final XContent xContent = xContentType.xContent(); final XContentParser parser = xContent.createParser( NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/CreateIndexRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/CreateIndexRequestTests.java index 6d9695c376e23..0cfbd8c29fe43 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/CreateIndexRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/CreateIndexRequestTests.java @@ -33,7 +33,7 @@ package org.opensearch.client.indices; import org.opensearch.action.admin.indices.alias.Alias; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.AbstractXContentTestCase; import java.io.IOException; @@ -69,8 +69,8 @@ private void assertMappingsEqual(CreateIndexRequest expected, CreateIndexRequest } else { assertNotNull(actual.mappings()); try ( - XContentParser expectedJson = createParser(expected.mappingsXContentType().xContent(), expected.mappings()); - XContentParser actualJson = createParser(actual.mappingsXContentType().xContent(), actual.mappings()) + XContentParser expectedJson = createParser(expected.mappingsMediaType().xContent(), expected.mappings()); + XContentParser actualJson = createParser(actual.mappingsMediaType().xContent(), actual.mappings()) ) { assertEquals(expectedJson.map(), actualJson.map()); } catch (IOException e) { diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/DataStreamsStatsResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/DataStreamsStatsResponseTests.java index c53aaffcbcaba..9418a82fc6107 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/DataStreamsStatsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/DataStreamsStatsResponseTests.java @@ -34,11 +34,11 @@ import org.opensearch.OpenSearchException; import org.opensearch.action.admin.indices.datastream.DataStreamsStatsAction; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.client.AbstractResponseTestCase; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetComponentTemplatesResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetComponentTemplatesResponseTests.java index 03ca152501c00..fd7d79fc4532d 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetComponentTemplatesResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetComponentTemplatesResponseTests.java @@ -37,7 +37,7 @@ import org.opensearch.cluster.metadata.Template; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetComposableIndexTemplatesResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetComposableIndexTemplatesResponseTests.java index d2fd4ed16299e..f8013c6cc3d5d 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetComposableIndexTemplatesResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetComposableIndexTemplatesResponseTests.java @@ -33,7 +33,7 @@ package org.opensearch.client.indices; import org.opensearch.cluster.metadata.ComposableIndexTemplate; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetDataStreamResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetDataStreamResponseTests.java index 27d0a423b1b35..3f0d56a3d9455 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetDataStreamResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetDataStreamResponseTests.java @@ -38,9 +38,9 @@ import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.metadata.DataStream; import org.opensearch.common.UUIDs; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetFieldMappingsResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetFieldMappingsResponseTests.java index b866242085115..115685a2c5afc 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetFieldMappingsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetFieldMappingsResponseTests.java @@ -33,8 +33,8 @@ package org.opensearch.client.indices; import org.opensearch.client.indices.GetFieldMappingsResponse.FieldMappingMetadata; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetIndexResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetIndexResponseTests.java index 37f4d95d5f4d0..a00f0487116dc 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetIndexResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetIndexResponseTests.java @@ -37,11 +37,10 @@ import org.opensearch.client.GetAliasesResponseTests; import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.RandomCreateIndexGenerator; import org.opensearch.index.mapper.MapperService; @@ -62,11 +61,11 @@ public class GetIndexResponseTests extends AbstractResponseTestCase< @Override protected org.opensearch.action.admin.indices.get.GetIndexResponse createServerTestInstance(XContentType xContentType) { String[] indices = generateRandomStringArray(5, 5, false, false); - ImmutableOpenMap.Builder mappings = ImmutableOpenMap.builder(); - ImmutableOpenMap.Builder> aliases = ImmutableOpenMap.builder(); - ImmutableOpenMap.Builder settings = ImmutableOpenMap.builder(); - ImmutableOpenMap.Builder defaultSettings = ImmutableOpenMap.builder(); - ImmutableOpenMap.Builder dataStreams = ImmutableOpenMap.builder(); + final Map mappings = new HashMap<>(); + final Map> aliases = new HashMap<>(); + final Map settings = new HashMap<>(); + final Map defaultSettings = new HashMap<>(); + final Map dataStreams = new HashMap<>(); IndexScopedSettings indexScopedSettings = IndexScopedSettings.DEFAULT_SCOPED_SETTINGS; boolean includeDefaults = randomBoolean(); for (String index : indices) { @@ -94,11 +93,11 @@ protected org.opensearch.action.admin.indices.get.GetIndexResponse createServerT } return new org.opensearch.action.admin.indices.get.GetIndexResponse( indices, - mappings.build(), - aliases.build(), - settings.build(), - defaultSettings.build(), - dataStreams.build() + mappings, + aliases, + settings, + defaultSettings, + dataStreams ); } @@ -113,10 +112,10 @@ protected void assertInstances( GetIndexResponse clientInstance ) { assertArrayEquals(serverTestInstance.getIndices(), clientInstance.getIndices()); - assertMapEquals(serverTestInstance.getMappings(), clientInstance.getMappings()); - assertMapEquals(serverTestInstance.getSettings(), clientInstance.getSettings()); - assertMapEquals(serverTestInstance.defaultSettings(), clientInstance.getDefaultSettings()); - assertMapEquals(serverTestInstance.getAliases(), clientInstance.getAliases()); + assertEquals(serverTestInstance.getMappings(), clientInstance.getMappings()); + assertEquals(serverTestInstance.getSettings(), clientInstance.getSettings()); + assertEquals(serverTestInstance.defaultSettings(), clientInstance.getDefaultSettings()); + assertEquals(serverTestInstance.getAliases(), clientInstance.getAliases()); } private static MappingMetadata createMappingsForIndex() { diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetIndexTemplatesResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetIndexTemplatesResponseTests.java index ead5fd4087c0b..507dc7802283f 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetIndexTemplatesResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetIndexTemplatesResponseTests.java @@ -34,18 +34,18 @@ import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import org.opensearch.test.OpenSearchTestCase; @@ -137,10 +137,10 @@ public void testParsingFromOpenSearchResponse() throws IOException { assertThat(result.mappings().sourceAsMap(), equalTo(expectedMapping.get("_doc"))); assertThat(result.aliases().size(), equalTo(esIMD.aliases().size())); - List expectedAliases = Arrays.stream(esIMD.aliases().values().toArray(AliasMetadata.class)) + List expectedAliases = Arrays.stream(esIMD.aliases().values().toArray(new AliasMetadata[0])) .sorted(Comparator.comparing(AliasMetadata::alias)) .collect(Collectors.toList()); - List actualAliases = Arrays.stream(result.aliases().values().toArray(AliasMetadata.class)) + List actualAliases = Arrays.stream(result.aliases().values().toArray(new AliasMetadata[0])) .sorted(Comparator.comparing(AliasMetadata::alias)) .collect(Collectors.toList()); for (int j = 0; j < result.aliases().size(); j++) { @@ -164,7 +164,7 @@ private Predicate randomFieldsExcludeFilter() { private static void assertEqualInstances(GetIndexTemplatesResponse expectedInstance, GetIndexTemplatesResponse newInstance) { assertEquals(expectedInstance, newInstance); // Check there's no doc types at the root of the mapping - Map expectedMap = XContentHelper.convertToMap(new BytesArray(mappingString), true, XContentType.JSON).v2(); + Map expectedMap = XContentHelper.convertToMap(new BytesArray(mappingString), true, MediaTypeRegistry.JSON).v2(); for (IndexTemplateMetadata template : newInstance.getIndexTemplates()) { MappingMetadata mappingMD = template.mappings(); if (mappingMD != null) { @@ -194,7 +194,7 @@ static GetIndexTemplatesResponse createTestInstance() { templateBuilder.version(between(0, 100)); } if (randomBoolean()) { - Map map = XContentHelper.convertToMap(new BytesArray(mappingString), true, XContentType.JSON).v2(); + Map map = XContentHelper.convertToMap(new BytesArray(mappingString), true, MediaTypeRegistry.JSON).v2(); MappingMetadata mapping = new MappingMetadata(MapperService.SINGLE_MAPPING_NAME, map); templateBuilder.mapping(mapping); } @@ -216,7 +216,7 @@ static void toXContent(GetIndexTemplatesResponse response, XContentBuilder build serverTemplateBuilder.patterns(clientITMD.patterns()); - Iterator aliases = clientITMD.aliases().valuesIt(); + Iterator aliases = clientITMD.aliases().values().iterator(); aliases.forEachRemaining((a) -> serverTemplateBuilder.putAlias(a)); serverTemplateBuilder.settings(clientITMD.settings()); @@ -262,7 +262,7 @@ private static AliasMetadata randomAliasMetadata(String name) { } static XContentBuilder randomMapping(String type, XContentType xContentType) throws IOException { - XContentBuilder builder = XContentFactory.contentBuilder(xContentType); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(xContentType); builder.startObject().startObject(type); randomMappingFields(builder, true); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetMappingsResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetMappingsResponseTests.java index cb62b116de020..6d80cdd3ad074 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetMappingsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/GetMappingsResponseTests.java @@ -34,9 +34,8 @@ import org.opensearch.client.AbstractResponseTestCase; import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import java.io.IOException; @@ -50,12 +49,12 @@ public class GetMappingsResponseTests extends AbstractResponseTestCase< @Override protected org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse createServerTestInstance(XContentType xContentType) { - ImmutableOpenMap.Builder mappings = ImmutableOpenMap.builder(); + final Map mappings = new HashMap<>(); int numberOfIndexes = randomIntBetween(1, 5); for (int i = 0; i < numberOfIndexes; i++) { mappings.put("index-" + randomAlphaOfLength(5), randomMappingMetadata()); } - return new org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse(mappings.build()); + return new org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse(mappings); } @Override @@ -68,7 +67,7 @@ protected void assertInstances( org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse serverTestInstance, GetMappingsResponse clientInstance ) { - assertMapEquals(serverTestInstance.getMappings(), clientInstance.mappings()); + assertEquals(serverTestInstance.getMappings(), clientInstance.mappings()); } public static MappingMetadata randomMappingMetadata() { diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/PutIndexTemplateRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/PutIndexTemplateRequestTests.java index f3800ac430ed1..e4c8e589b17ee 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/PutIndexTemplateRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/PutIndexTemplateRequestTests.java @@ -35,7 +35,7 @@ import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.AbstractXContentTestCase; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/PutMappingRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/PutMappingRequestTests.java index 6065be82ddb22..fb00bad09f61a 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/PutMappingRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/PutMappingRequestTests.java @@ -32,8 +32,8 @@ package org.opensearch.client.indices; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.AbstractXContentTestCase; import java.io.IOException; @@ -74,8 +74,8 @@ protected boolean supportsUnknownFields() { protected void assertEqualInstances(PutMappingRequest expected, PutMappingRequest actual) { if (actual.source() != null) { try ( - XContentParser expectedJson = createParser(expected.xContentType().xContent(), expected.source()); - XContentParser actualJson = createParser(actual.xContentType().xContent(), actual.source()) + XContentParser expectedJson = createParser(expected.mediaType().xContent(), expected.source()); + XContentParser actualJson = createParser(actual.mediaType().xContent(), actual.source()) ) { assertEquals(expectedJson.mapOrdered(), actualJson.mapOrdered()); } catch (IOException e) { diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/RandomCreateIndexGenerator.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/RandomCreateIndexGenerator.java index b50bb444cd00a..1f747dc139d15 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/RandomCreateIndexGenerator.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/RandomCreateIndexGenerator.java @@ -32,8 +32,8 @@ package org.opensearch.client.indices; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/ResizeRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/ResizeRequestTests.java index d5ff513676e48..1de3010a65247 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/ResizeRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/ResizeRequestTests.java @@ -35,7 +35,7 @@ import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.client.AbstractRequestTestCase; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/ResizeResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/ResizeResponseTests.java index 954efed52d79a..919cc89a9ae44 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/ResizeResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/ResizeResponseTests.java @@ -33,8 +33,8 @@ package org.opensearch.client.indices; import org.opensearch.client.AbstractResponseTestCase; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/rollover/RolloverRequestTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/rollover/RolloverRequestTests.java index 9d51c09ba2e44..b4e0fc8f7f38c 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/rollover/RolloverRequestTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/rollover/RolloverRequestTests.java @@ -36,8 +36,8 @@ import org.opensearch.action.admin.indices.rollover.MaxAgeCondition; import org.opensearch.action.admin.indices.rollover.MaxDocsCondition; import org.opensearch.action.admin.indices.rollover.MaxSizeCondition; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.test.OpenSearchTestCase; import java.util.ArrayList; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/indices/rollover/RolloverResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/indices/rollover/RolloverResponseTests.java index 0c924bc06046c..14477f07f8b42 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/indices/rollover/RolloverResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/indices/rollover/RolloverResponseTests.java @@ -36,9 +36,9 @@ import org.opensearch.action.admin.indices.rollover.MaxAgeCondition; import org.opensearch.action.admin.indices.rollover.MaxDocsCondition; import org.opensearch.action.admin.indices.rollover.MaxSizeCondition; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/tasks/CancelTasksResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/tasks/CancelTasksResponseTests.java index 552a3712eea40..faf5024d0c173 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/tasks/CancelTasksResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/tasks/CancelTasksResponseTests.java @@ -38,14 +38,14 @@ import org.opensearch.client.AbstractResponseTestCase; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import java.io.IOException; @@ -84,6 +84,10 @@ protected CancelTasksResponseTests.ByNodeCancelTasksResponse createServerTestIns for (int i = 0; i < 4; i++) { boolean cancellable = randomBoolean(); boolean cancelled = cancellable == true ? randomBoolean() : false; + Long cancellationStartTime = null; + if (cancelled) { + cancellationStartTime = randomNonNegativeLong(); + } tasks.add( new org.opensearch.tasks.TaskInfo( new TaskId(NODE_ID, (long) i), @@ -96,7 +100,9 @@ protected CancelTasksResponseTests.ByNodeCancelTasksResponse createServerTestIns cancellable, cancelled, new TaskId("node1", randomLong()), - Collections.singletonMap("x-header-of", "some-value") + Collections.singletonMap("x-header-of", "some-value"), + null, + cancellationStartTime ) ); } @@ -134,6 +140,7 @@ protected void assertInstances( assertEquals(ti.isCancelled(), taskInfo.isCancelled()); assertEquals(ti.getParentTaskId().getNodeId(), taskInfo.getParentTaskId().getNodeId()); assertEquals(ti.getParentTaskId().getId(), taskInfo.getParentTaskId().getId()); + assertEquals(ti.getCancellationStartTime(), taskInfo.getCancellationStartTime()); FakeTaskStatus status = (FakeTaskStatus) ti.getStatus(); assertEquals(status.code, taskInfo.getStatus().get("code")); assertEquals(status.status, taskInfo.getStatus().get("status")); diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/tasks/OpenSearchExceptionTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/tasks/OpenSearchExceptionTests.java index 42d6a3f560660..fb8e4f731fb19 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/tasks/OpenSearchExceptionTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/tasks/OpenSearchExceptionTests.java @@ -32,8 +32,8 @@ package org.opensearch.client.tasks; import org.opensearch.client.AbstractResponseTestCase; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collections; diff --git a/client/rest-high-level/src/test/java/org/opensearch/client/tasks/TaskSubmissionResponseTests.java b/client/rest-high-level/src/test/java/org/opensearch/client/tasks/TaskSubmissionResponseTests.java index b2f60ca53841f..ebef17ba2f161 100644 --- a/client/rest-high-level/src/test/java/org/opensearch/client/tasks/TaskSubmissionResponseTests.java +++ b/client/rest-high-level/src/test/java/org/opensearch/client/tasks/TaskSubmissionResponseTests.java @@ -32,7 +32,7 @@ package org.opensearch.client.tasks; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/client/rest/build.gradle b/client/rest/build.gradle index 5c1252061443a..894c15b1dbc49 100644 --- a/client/rest/build.gradle +++ b/client/rest/build.gradle @@ -33,11 +33,15 @@ import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis apply plugin: 'opensearch.build' apply plugin: 'opensearch.publish' -targetCompatibility = JavaVersion.VERSION_11 -sourceCompatibility = JavaVersion.VERSION_11 +java { + targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_11 +} -group = 'org.opensearch.client' -archivesBaseName = 'opensearch-rest-client' +base { + group = 'org.opensearch.client' + archivesBaseName = 'opensearch-rest-client' +} dependencies { api "org.apache.httpcomponents:httpclient:${versions.httpclient}" @@ -54,6 +58,10 @@ dependencies { testImplementation "org.mockito:mockito-core:${versions.mockito}" testImplementation "org.objenesis:objenesis:${versions.objenesis}" testImplementation "net.bytebuddy:byte-buddy:${versions.bytebuddy}" + testImplementation "net.bytebuddy:byte-buddy-agent:${versions.bytebuddy}" + testImplementation "org.apache.logging.log4j:log4j-api:${versions.log4j}" + testImplementation "org.apache.logging.log4j:log4j-core:${versions.log4j}" + testImplementation "org.apache.logging.log4j:log4j-jul:${versions.log4j}" } tasks.withType(CheckForbiddenApis).configureEach { @@ -89,7 +97,6 @@ thirdPartyAudit.ignoreMissingClasses( 'org.apache.avalon.framework.logger.Logger', 'org.apache.log.Hierarchy', 'org.apache.log.Logger', - 'org.apache.log4j.Category', 'org.apache.log4j.Level', 'org.apache.log4j.Logger', 'org.apache.log4j.Priority', diff --git a/client/rest/licenses/commons-codec-1.13.jar.sha1 b/client/rest/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/client/rest/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/client/rest/licenses/commons-codec-1.15.jar.sha1 b/client/rest/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/client/rest/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/client/rest/licenses/commons-logging-1.1.3.jar.sha1 b/client/rest/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index 5b8f029e58293..0000000000000 --- a/client/rest/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f \ No newline at end of file diff --git a/client/rest/licenses/commons-logging-1.2.jar.sha1 b/client/rest/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/client/rest/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/client/rest/licenses/httpasyncclient-4.1.4.jar.sha1 b/client/rest/licenses/httpasyncclient-4.1.4.jar.sha1 deleted file mode 100644 index 8360ab45c7ab3..0000000000000 --- a/client/rest/licenses/httpasyncclient-4.1.4.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f3a3240681faae3fa46b573a4c7e50cec9db0d86 \ No newline at end of file diff --git a/client/rest/licenses/httpasyncclient-4.1.5.jar.sha1 b/client/rest/licenses/httpasyncclient-4.1.5.jar.sha1 new file mode 100644 index 0000000000000..366a9e31069a6 --- /dev/null +++ b/client/rest/licenses/httpasyncclient-4.1.5.jar.sha1 @@ -0,0 +1 @@ +cd18227f1eb8e9a263286c1d7362ceb24f6f9b32 \ No newline at end of file diff --git a/client/rest/licenses/httpclient-4.5.13.jar.sha1 b/client/rest/licenses/httpclient-4.5.13.jar.sha1 deleted file mode 100644 index 3281e21595b39..0000000000000 --- a/client/rest/licenses/httpclient-4.5.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada \ No newline at end of file diff --git a/client/rest/licenses/httpclient-4.5.14.jar.sha1 b/client/rest/licenses/httpclient-4.5.14.jar.sha1 new file mode 100644 index 0000000000000..66e05851c2e3c --- /dev/null +++ b/client/rest/licenses/httpclient-4.5.14.jar.sha1 @@ -0,0 +1 @@ +1194890e6f56ec29177673f2f12d0b8e627dec98 \ No newline at end of file diff --git a/client/rest/licenses/httpcore-4.4.12.jar.sha1 b/client/rest/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/client/rest/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/client/rest/licenses/httpcore-4.4.16.jar.sha1 b/client/rest/licenses/httpcore-4.4.16.jar.sha1 new file mode 100644 index 0000000000000..172110694b5bd --- /dev/null +++ b/client/rest/licenses/httpcore-4.4.16.jar.sha1 @@ -0,0 +1 @@ +51cf043c87253c9f58b539c9f7e44c8894223850 \ No newline at end of file diff --git a/client/rest/licenses/httpcore-nio-4.4.12.jar.sha1 b/client/rest/licenses/httpcore-nio-4.4.12.jar.sha1 deleted file mode 100644 index 4de932dc5aca0..0000000000000 --- a/client/rest/licenses/httpcore-nio-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -84cd29eca842f31db02987cfedea245af020198b \ No newline at end of file diff --git a/client/rest/licenses/httpcore-nio-4.4.16.jar.sha1 b/client/rest/licenses/httpcore-nio-4.4.16.jar.sha1 new file mode 100644 index 0000000000000..f4749c11e4b27 --- /dev/null +++ b/client/rest/licenses/httpcore-nio-4.4.16.jar.sha1 @@ -0,0 +1 @@ +cd21c80a9956be48c4c1cfd2f594ba02857d0927 \ No newline at end of file diff --git a/client/rest/src/main/java/org/opensearch/client/Node.java b/client/rest/src/main/java/org/opensearch/client/Node.java index c982ae8eb931f..2fa6605d57ad2 100644 --- a/client/rest/src/main/java/org/opensearch/client/Node.java +++ b/client/rest/src/main/java/org/opensearch/client/Node.java @@ -210,12 +210,21 @@ public Roles(final Set roles) { } /** - * Returns whether or not the node could be elected master. + * Returns whether or not the node could be elected cluster-manager. */ - public boolean isMasterEligible() { + public boolean isClusterManagerEligible() { return roles.contains("master") || roles.contains("cluster_manager"); } + /** + * Returns whether or not the node could be elected cluster-manager. + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #isClusterManagerEligible()} + */ + @Deprecated + public boolean isMasterEligible() { + return isClusterManagerEligible(); + } + /** * Returns whether or not the node stores data. */ @@ -230,6 +239,13 @@ public boolean isIngest() { return roles.contains("ingest"); } + /** + * Returns whether the node is dedicated to provide search capability. + */ + public boolean isSearch() { + return roles.contains("search"); + } + @Override public String toString() { return String.join(",", roles); diff --git a/client/rest/src/main/java/org/opensearch/client/NodeSelector.java b/client/rest/src/main/java/org/opensearch/client/NodeSelector.java index 09d5a2c1fe576..a54c6b1514717 100644 --- a/client/rest/src/main/java/org/opensearch/client/NodeSelector.java +++ b/client/rest/src/main/java/org/opensearch/client/NodeSelector.java @@ -36,7 +36,7 @@ /** * Selects nodes that can receive requests. Used to keep requests away - * from master nodes or to send them to nodes with a particular attribute. + * from cluster-manager nodes or to send them to nodes with a particular attribute. * Use with {@link RestClientBuilder#setNodeSelector(NodeSelector)}. */ public interface NodeSelector { @@ -80,16 +80,18 @@ public String toString() { /** * Selector that matches any node that has metadata and doesn't - * have the {@code master} role OR it has the data {@code data} + * have the {@code cluster_manager} role OR it has the data {@code data} * role. */ - NodeSelector SKIP_DEDICATED_MASTERS = new NodeSelector() { + NodeSelector SKIP_DEDICATED_CLUSTER_MANAGERS = new NodeSelector() { @Override public void select(Iterable nodes) { for (Iterator itr = nodes.iterator(); itr.hasNext();) { Node node = itr.next(); if (node.getRoles() == null) continue; - if (node.getRoles().isMasterEligible() && false == node.getRoles().isData() && false == node.getRoles().isIngest()) { + if (node.getRoles().isClusterManagerEligible() + && false == node.getRoles().isData() + && false == node.getRoles().isIngest()) { itr.remove(); } } @@ -97,7 +99,7 @@ public void select(Iterable nodes) { @Override public String toString() { - return "SKIP_DEDICATED_MASTERS"; + return "SKIP_DEDICATED_CLUSTER_MANAGERS"; } }; } diff --git a/client/rest/src/main/java/org/opensearch/client/RestClient.java b/client/rest/src/main/java/org/opensearch/client/RestClient.java index 4f899fd709112..946b06e46d6fc 100644 --- a/client/rest/src/main/java/org/opensearch/client/RestClient.java +++ b/client/rest/src/main/java/org/opensearch/client/RestClient.java @@ -41,9 +41,9 @@ import org.apache.http.HttpResponse; import org.apache.http.client.AuthCache; import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.GzipCompressingEntity; import org.apache.http.client.entity.GzipDecompressingEntity; -import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpOptions; @@ -56,6 +56,7 @@ import org.apache.http.client.utils.URIBuilder; import org.apache.http.concurrent.FutureCallback; import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.entity.HttpEntityWrapper; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; @@ -64,6 +65,7 @@ import org.apache.http.nio.protocol.HttpAsyncResponseConsumer; import javax.net.ssl.SSLHandshakeException; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -131,6 +133,29 @@ public class RestClient implements Closeable { private volatile NodeTuple> nodeTuple; private final WarningsHandler warningsHandler; private final boolean compressionEnabled; + private final Optional chunkedEnabled; + + RestClient( + CloseableHttpAsyncClient client, + Header[] defaultHeaders, + List nodes, + String pathPrefix, + FailureListener failureListener, + NodeSelector nodeSelector, + boolean strictDeprecationMode, + boolean compressionEnabled, + boolean chunkedEnabled + ) { + this.client = client; + this.defaultHeaders = Collections.unmodifiableList(Arrays.asList(defaultHeaders)); + this.failureListener = failureListener; + this.pathPrefix = pathPrefix; + this.nodeSelector = nodeSelector; + this.warningsHandler = strictDeprecationMode ? WarningsHandler.STRICT : WarningsHandler.PERMISSIVE; + this.compressionEnabled = compressionEnabled; + this.chunkedEnabled = Optional.of(chunkedEnabled); + setNodes(nodes); + } RestClient( CloseableHttpAsyncClient client, @@ -149,6 +174,7 @@ public class RestClient implements Closeable { this.nodeSelector = nodeSelector; this.warningsHandler = strictDeprecationMode ? WarningsHandler.STRICT : WarningsHandler.PERMISSIVE; this.compressionEnabled = compressionEnabled; + this.chunkedEnabled = Optional.empty(); setNodes(nodes); } @@ -583,36 +609,42 @@ private static void addSuppressedException(Exception suppressedException, Except } } - private static HttpRequestBase createHttpRequest(String method, URI uri, HttpEntity entity, boolean compressionEnabled) { + private HttpRequestBase createHttpRequest(String method, URI uri, HttpEntity entity) { switch (method.toUpperCase(Locale.ROOT)) { case HttpDeleteWithEntity.METHOD_NAME: - return addRequestBody(new HttpDeleteWithEntity(uri), entity, compressionEnabled); + return addRequestBody(new HttpDeleteWithEntity(uri), entity); case HttpGetWithEntity.METHOD_NAME: - return addRequestBody(new HttpGetWithEntity(uri), entity, compressionEnabled); + return addRequestBody(new HttpGetWithEntity(uri), entity); case HttpHead.METHOD_NAME: - return addRequestBody(new HttpHead(uri), entity, compressionEnabled); + return addRequestBody(new HttpHead(uri), entity); case HttpOptions.METHOD_NAME: - return addRequestBody(new HttpOptions(uri), entity, compressionEnabled); + return addRequestBody(new HttpOptions(uri), entity); case HttpPatch.METHOD_NAME: - return addRequestBody(new HttpPatch(uri), entity, compressionEnabled); + return addRequestBody(new HttpPatch(uri), entity); case HttpPost.METHOD_NAME: HttpPost httpPost = new HttpPost(uri); - addRequestBody(httpPost, entity, compressionEnabled); + addRequestBody(httpPost, entity); return httpPost; case HttpPut.METHOD_NAME: - return addRequestBody(new HttpPut(uri), entity, compressionEnabled); + return addRequestBody(new HttpPut(uri), entity); case HttpTrace.METHOD_NAME: - return addRequestBody(new HttpTrace(uri), entity, compressionEnabled); + return addRequestBody(new HttpTrace(uri), entity); default: throw new UnsupportedOperationException("http method not supported: " + method); } } - private static HttpRequestBase addRequestBody(HttpRequestBase httpRequest, HttpEntity entity, boolean compressionEnabled) { + private HttpRequestBase addRequestBody(HttpRequestBase httpRequest, HttpEntity entity) { if (entity != null) { if (httpRequest instanceof HttpEntityEnclosingRequestBase) { if (compressionEnabled) { - entity = new ContentCompressingEntity(entity); + if (chunkedEnabled.isPresent()) { + entity = new ContentCompressingEntity(entity, chunkedEnabled.get()); + } else { + entity = new ContentCompressingEntity(entity); + } + } else if (chunkedEnabled.isPresent()) { + entity = new ContentHttpEntity(entity, chunkedEnabled.get()); } ((HttpEntityEnclosingRequestBase) httpRequest).setEntity(entity); } else { @@ -782,7 +814,7 @@ private class InternalRequest { String ignoreString = params.remove("ignore"); this.ignoreErrorCodes = getIgnoreErrorCodes(ignoreString, request.getMethod()); URI uri = buildUri(pathPrefix, request.getEndpoint(), params); - this.httpRequest = createHttpRequest(request.getMethod(), uri, request.getEntity(), compressionEnabled); + this.httpRequest = createHttpRequest(request.getMethod(), uri, request.getEntity()); this.cancellable = Cancellable.fromRequest(httpRequest); setHeaders(httpRequest, request.getOptions().getHeaders()); setRequestConfig(httpRequest, request.getOptions().getRequestConfig()); @@ -936,6 +968,7 @@ private static Exception extractAndWrapCause(Exception exception) { * A gzip compressing entity that also implements {@code getContent()}. */ public static class ContentCompressingEntity extends GzipCompressingEntity { + private Optional chunkedEnabled; /** * Creates a {@link ContentCompressingEntity} instance with the provided HTTP entity. @@ -944,6 +977,18 @@ public static class ContentCompressingEntity extends GzipCompressingEntity { */ public ContentCompressingEntity(HttpEntity entity) { super(entity); + this.chunkedEnabled = Optional.empty(); + } + + /** + * Creates a {@link ContentCompressingEntity} instance with the provided HTTP entity. + * + * @param entity the HTTP entity. + * @param chunkedEnabled force enable/disable chunked transfer-encoding. + */ + public ContentCompressingEntity(HttpEntity entity, boolean chunkedEnabled) { + super(entity); + this.chunkedEnabled = Optional.of(chunkedEnabled); } @Override @@ -954,6 +999,80 @@ public InputStream getContent() throws IOException { } return out.asInput(); } + + /** + * A gzip compressing entity doesn't work with chunked encoding with sigv4 + * + * @return false + */ + @Override + public boolean isChunked() { + return chunkedEnabled.orElseGet(super::isChunked); + } + + /** + * A gzip entity requires content length in http headers. + * + * @return content length of gzip entity + */ + @Override + public long getContentLength() { + if (chunkedEnabled.isPresent()) { + if (chunkedEnabled.get()) { + return -1L; + } else { + long size; + try (InputStream is = getContent()) { + size = is.readAllBytes().length; + } catch (IOException ex) { + size = -1L; + } + + return size; + } + } else { + return super.getContentLength(); + } + } + } + + /** + * An entity that lets the caller specify the return value of {@code isChunked()}. + */ + public static class ContentHttpEntity extends HttpEntityWrapper { + private Optional chunkedEnabled; + + /** + * Creates a {@link ContentHttpEntity} instance with the provided HTTP entity. + * + * @param entity the HTTP entity. + */ + public ContentHttpEntity(HttpEntity entity) { + super(entity); + this.chunkedEnabled = Optional.empty(); + } + + /** + * Creates a {@link ContentHttpEntity} instance with the provided HTTP entity. + * + * @param entity the HTTP entity. + * @param chunkedEnabled force enable/disable chunked transfer-encoding. + */ + public ContentHttpEntity(HttpEntity entity, boolean chunkedEnabled) { + super(entity); + this.chunkedEnabled = Optional.of(chunkedEnabled); + } + + /** + * A chunked entity requires transfer-encoding:chunked in http headers + * which requires isChunked to be true + * + * @return true + */ + @Override + public boolean isChunked() { + return chunkedEnabled.orElseGet(super::isChunked); + } } /** diff --git a/client/rest/src/main/java/org/opensearch/client/RestClientBuilder.java b/client/rest/src/main/java/org/opensearch/client/RestClientBuilder.java index 0b259c7983ca5..4682c734a2172 100644 --- a/client/rest/src/main/java/org/opensearch/client/RestClientBuilder.java +++ b/client/rest/src/main/java/org/opensearch/client/RestClientBuilder.java @@ -41,11 +41,13 @@ import org.apache.http.nio.conn.SchemeIOSessionStrategy; import javax.net.ssl.SSLContext; + import java.security.AccessController; import java.security.NoSuchAlgorithmException; import java.security.PrivilegedAction; import java.util.List; import java.util.Objects; +import java.util.Optional; /** * Helps creating a new {@link RestClient}. Allows to set the most common http client configuration options when internally @@ -84,6 +86,7 @@ public final class RestClientBuilder { private NodeSelector nodeSelector = NodeSelector.ANY; private boolean strictDeprecationMode = false; private boolean compressionEnabled = false; + private Optional chunkedEnabled; /** * Creates a new builder instance and sets the hosts that the client will send requests to. @@ -100,6 +103,7 @@ public final class RestClientBuilder { } } this.nodes = nodes; + this.chunkedEnabled = Optional.empty(); } /** @@ -238,6 +242,16 @@ public RestClientBuilder setCompressionEnabled(boolean compressionEnabled) { return this; } + /** + * Whether the REST client should use Transfer-Encoding: chunked for requests or not" + * + * @param chunkedEnabled force enable/disable chunked transfer-encoding. + */ + public RestClientBuilder setChunkedEnabled(boolean chunkedEnabled) { + this.chunkedEnabled = Optional.of(chunkedEnabled); + return this; + } + /** * Creates a new {@link RestClient} based on the provided configuration. */ @@ -248,16 +262,34 @@ public RestClient build() { CloseableHttpAsyncClient httpClient = AccessController.doPrivileged( (PrivilegedAction) this::createHttpClient ); - RestClient restClient = new RestClient( - httpClient, - defaultHeaders, - nodes, - pathPrefix, - failureListener, - nodeSelector, - strictDeprecationMode, - compressionEnabled - ); + + RestClient restClient = null; + + if (chunkedEnabled.isPresent()) { + restClient = new RestClient( + httpClient, + defaultHeaders, + nodes, + pathPrefix, + failureListener, + nodeSelector, + strictDeprecationMode, + compressionEnabled, + chunkedEnabled.get() + ); + } else { + restClient = new RestClient( + httpClient, + defaultHeaders, + nodes, + pathPrefix, + failureListener, + nodeSelector, + strictDeprecationMode, + compressionEnabled + ); + } + httpClient.start(); return restClient; } diff --git a/client/rest/src/test/java/org/opensearch/client/NodeSelectorTests.java b/client/rest/src/test/java/org/opensearch/client/NodeSelectorTests.java index f7cb0733bb8c5..65a831e59bfb0 100644 --- a/client/rest/src/test/java/org/opensearch/client/NodeSelectorTests.java +++ b/client/rest/src/test/java/org/opensearch/client/NodeSelectorTests.java @@ -55,33 +55,33 @@ public void testAny() { assertEquals(expected, nodes); } - public void testNotMasterOnly() { - Node masterOnly = dummyNode(true, false, false); + public void testNotClusterManagerOnly() { + Node clusterManagerOnly = dummyNode(true, false, false); Node all = dummyNode(true, true, true); - Node masterAndData = dummyNode(true, true, false); - Node masterAndIngest = dummyNode(true, false, true); + Node clusterManagerAndData = dummyNode(true, true, false); + Node clusterManagerAndIngest = dummyNode(true, false, true); Node coordinatingOnly = dummyNode(false, false, false); Node ingestOnly = dummyNode(false, false, true); Node data = dummyNode(false, true, randomBoolean()); List nodes = new ArrayList<>(); - nodes.add(masterOnly); + nodes.add(clusterManagerOnly); nodes.add(all); - nodes.add(masterAndData); - nodes.add(masterAndIngest); + nodes.add(clusterManagerAndData); + nodes.add(clusterManagerAndIngest); nodes.add(coordinatingOnly); nodes.add(ingestOnly); nodes.add(data); Collections.shuffle(nodes, getRandom()); List expected = new ArrayList<>(nodes); - expected.remove(masterOnly); - NodeSelector.SKIP_DEDICATED_MASTERS.select(nodes); + expected.remove(clusterManagerOnly); + NodeSelector.SKIP_DEDICATED_CLUSTER_MANAGERS.select(nodes); assertEquals(expected, nodes); } - private static Node dummyNode(boolean master, boolean data, boolean ingest) { + private static Node dummyNode(boolean clusterManager, boolean data, boolean ingest) { final Set roles = new TreeSet<>(); - if (master) { - roles.add("master"); + if (clusterManager) { + roles.add("cluster_manager"); } if (data) { roles.add("data"); @@ -98,4 +98,33 @@ private static Node dummyNode(boolean master, boolean data, boolean ingest) { Collections.>emptyMap() ); } + + /* + * Validate SKIP_DEDICATED_CLUSTER_MANAGERS can filter both the deprecated "master" role and the new "cluster_manager" role. + * The test is a modified copy of the above testNotClusterManagerOnly(). + */ + public void testDeprecatedNotMasterOnly() { + Node clusterManagerOnly = dummyNode(true, false, false); + Node all = dummyNode(true, true, true); + Node data = dummyNode(false, true, randomBoolean()); + Node deprecatedMasterOnly = new Node( + new HttpHost("dummy"), + Collections.emptySet(), + randomAsciiAlphanumOfLength(5), + randomAsciiAlphanumOfLength(5), + new Roles(Collections.singleton("master")), + Collections.emptyMap() + ); + List nodes = new ArrayList<>(); + nodes.add(clusterManagerOnly); + nodes.add(all); + nodes.add(data); + nodes.add(deprecatedMasterOnly); + Collections.shuffle(nodes, getRandom()); + List expected = new ArrayList<>(nodes); + expected.remove(clusterManagerOnly); + expected.remove(deprecatedMasterOnly); + NodeSelector.SKIP_DEDICATED_CLUSTER_MANAGERS.select(nodes); + assertEquals(expected, nodes); + } } diff --git a/client/rest/src/test/java/org/opensearch/client/NodeTests.java b/client/rest/src/test/java/org/opensearch/client/NodeTests.java index 352296fa3024a..8a5c881d3b1a0 100644 --- a/client/rest/src/test/java/org/opensearch/client/NodeTests.java +++ b/client/rest/src/test/java/org/opensearch/client/NodeTests.java @@ -46,8 +46,10 @@ import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; +import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; public class NodeTests extends RestClientTestCase { @@ -161,4 +163,9 @@ public void testEqualsAndHashCode() { ) ); } + + public void testIsSearchNode() { + Roles searchRole = new Roles(Collections.singleton("search")); + assertThat(searchRole.isSearch(), equalTo(true)); + } } diff --git a/client/rest/src/test/java/org/opensearch/client/RestClientBuilderIntegTests.java b/client/rest/src/test/java/org/opensearch/client/RestClientBuilderIntegTests.java index 10bf9568c8798..089138e4c65b9 100644 --- a/client/rest/src/test/java/org/opensearch/client/RestClientBuilderIntegTests.java +++ b/client/rest/src/test/java/org/opensearch/client/RestClientBuilderIntegTests.java @@ -36,6 +36,7 @@ import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsServer; + import org.apache.http.HttpHost; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -44,6 +45,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.TrustManagerFactory; + import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; diff --git a/client/rest/src/test/java/org/opensearch/client/RestClientCompressionTests.java b/client/rest/src/test/java/org/opensearch/client/RestClientCompressionTests.java new file mode 100644 index 0000000000000..0cf00fb4ed5fd --- /dev/null +++ b/client/rest/src/test/java/org/opensearch/client/RestClientCompressionTests.java @@ -0,0 +1,166 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CompletableFuture; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public class RestClientCompressionTests extends RestClientTestCase { + + private static HttpServer httpServer; + + @BeforeClass + public static void startHttpServer() throws Exception { + httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); + httpServer.createContext("/", new GzipResponseHandler()); + httpServer.start(); + } + + @AfterClass + public static void stopHttpServers() throws IOException { + httpServer.stop(0); + httpServer = null; + } + + /** + * A response handler that accepts gzip-encoded data and replies request and response encoding values + * followed by the request body. The response is compressed if "Accept-Encoding" is "gzip". + */ + private static class GzipResponseHandler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) throws IOException { + + // Decode body (if any) + String contentEncoding = exchange.getRequestHeaders().getFirst("Content-Encoding"); + String contentLength = exchange.getRequestHeaders().getFirst("Content-Length"); + InputStream body = exchange.getRequestBody(); + boolean compressedRequest = false; + if ("gzip".equals(contentEncoding)) { + body = new GZIPInputStream(body); + compressedRequest = true; + } + byte[] bytes = readAll(body); + boolean compress = "gzip".equals(exchange.getRequestHeaders().getFirst("Accept-Encoding")); + if (compress) { + exchange.getResponseHeaders().add("Content-Encoding", "gzip"); + } + + exchange.sendResponseHeaders(200, 0); + + // Encode response if needed + OutputStream out = exchange.getResponseBody(); + if (compress) { + out = new GZIPOutputStream(out); + } + + // Outputs ## + out.write(String.valueOf(contentEncoding).getBytes(StandardCharsets.UTF_8)); + out.write('#'); + out.write((compress ? "gzip" : "null").getBytes(StandardCharsets.UTF_8)); + out.write('#'); + out.write((compressedRequest ? contentLength : "null").getBytes(StandardCharsets.UTF_8)); + out.write('#'); + out.write(bytes); + out.close(); + + exchange.close(); + } + } + + /** + * Read all bytes of an input stream and close it. + */ + private static byte[] readAll(InputStream in) throws IOException { + byte[] buffer = new byte[1024]; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int len = 0; + while ((len = in.read(buffer)) > 0) { + bos.write(buffer, 0, len); + } + in.close(); + return bos.toByteArray(); + } + + private RestClient createClient(boolean enableCompression, boolean chunkedEnabled) { + InetSocketAddress address = httpServer.getAddress(); + return RestClient.builder(new HttpHost(address.getHostString(), address.getPort(), "http")) + .setCompressionEnabled(enableCompression) + .setChunkedEnabled(chunkedEnabled) + .build(); + } + + public void testCompressingClientWithContentLengthSync() throws Exception { + RestClient restClient = createClient(true, false); + + Request request = new Request("POST", "/"); + request.setEntity(new StringEntity("compressing client", ContentType.TEXT_PLAIN)); + + Response response = restClient.performRequest(request); + + HttpEntity entity = response.getEntity(); + String content = new String(readAll(entity.getContent()), StandardCharsets.UTF_8); + // Content-Encoding#Accept-Encoding#Content-Length#Content + Assert.assertEquals("gzip#gzip#38#compressing client", content); + + restClient.close(); + } + + public void testCompressingClientContentLengthAsync() throws Exception { + InetSocketAddress address = httpServer.getAddress(); + RestClient restClient = createClient(true, false); + + Request request = new Request("POST", "/"); + request.setEntity(new StringEntity("compressing client", ContentType.TEXT_PLAIN)); + + FutureResponse futureResponse = new FutureResponse(); + restClient.performRequestAsync(request, futureResponse); + Response response = futureResponse.get(); + + // Server should report it had a compressed request and sent back a compressed response + HttpEntity entity = response.getEntity(); + String content = new String(readAll(entity.getContent()), StandardCharsets.UTF_8); + + // Content-Encoding#Accept-Encoding#Content-Length#Content + Assert.assertEquals("gzip#gzip#38#compressing client", content); + + restClient.close(); + } + + public static class FutureResponse extends CompletableFuture implements ResponseListener { + @Override + public void onSuccess(Response response) { + this.complete(response); + } + + @Override + public void onFailure(Exception exception) { + this.completeExceptionally(exception); + } + } +} diff --git a/client/rest/src/test/java/org/opensearch/client/RestClientGzipCompressionTests.java b/client/rest/src/test/java/org/opensearch/client/RestClientGzipCompressionTests.java index 8c4d993517fee..570cd851e5842 100644 --- a/client/rest/src/test/java/org/opensearch/client/RestClientGzipCompressionTests.java +++ b/client/rest/src/test/java/org/opensearch/client/RestClientGzipCompressionTests.java @@ -35,6 +35,7 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; + import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.entity.ContentType; diff --git a/client/rest/src/test/java/org/opensearch/client/RestClientMultipleHostsIntegTests.java b/client/rest/src/test/java/org/opensearch/client/RestClientMultipleHostsIntegTests.java index 277446191a36e..ea39490979a76 100644 --- a/client/rest/src/test/java/org/opensearch/client/RestClientMultipleHostsIntegTests.java +++ b/client/rest/src/test/java/org/opensearch/client/RestClientMultipleHostsIntegTests.java @@ -35,6 +35,7 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; + import org.apache.http.HttpHost; import org.junit.AfterClass; import org.junit.Before; diff --git a/client/rest/src/test/java/org/opensearch/client/RestClientMultipleHostsTests.java b/client/rest/src/test/java/org/opensearch/client/RestClientMultipleHostsTests.java index 0b7d2881ccb54..a373e11a8e759 100644 --- a/client/rest/src/test/java/org/opensearch/client/RestClientMultipleHostsTests.java +++ b/client/rest/src/test/java/org/opensearch/client/RestClientMultipleHostsTests.java @@ -33,6 +33,7 @@ package org.opensearch.client; import com.carrotsearch.randomizedtesting.generators.RandomNumbers; + import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; @@ -297,7 +298,7 @@ public void testNodeSelector() throws Exception { } public void testSetNodes() throws Exception { - RestClient restClient = createRestClient(NodeSelector.SKIP_DEDICATED_MASTERS); + RestClient restClient = createRestClient(NodeSelector.SKIP_DEDICATED_CLUSTER_MANAGERS); List newNodes = new ArrayList<>(nodes.size()); for (int i = 0; i < nodes.size(); i++) { Node.Roles roles = i == 0 diff --git a/client/rest/src/test/java/org/opensearch/client/RestClientSingleHostIntegTests.java b/client/rest/src/test/java/org/opensearch/client/RestClientSingleHostIntegTests.java index 0500d282a506d..ad82cb7aab85c 100644 --- a/client/rest/src/test/java/org/opensearch/client/RestClientSingleHostIntegTests.java +++ b/client/rest/src/test/java/org/opensearch/client/RestClientSingleHostIntegTests.java @@ -36,6 +36,7 @@ import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; + import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpHost; diff --git a/client/rest/src/test/java/org/opensearch/client/RestClientSingleHostTests.java b/client/rest/src/test/java/org/opensearch/client/RestClientSingleHostTests.java index a2aef065c9ae8..c1df0a1c881a5 100644 --- a/client/rest/src/test/java/org/opensearch/client/RestClientSingleHostTests.java +++ b/client/rest/src/test/java/org/opensearch/client/RestClientSingleHostTests.java @@ -64,10 +64,9 @@ import org.apache.http.util.EntityUtils; import org.junit.After; import org.junit.Before; -import org.mockito.ArgumentCaptor; -import org.mockito.stubbing.Answer; import javax.net.ssl.SSLHandshakeException; + import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; @@ -86,6 +85,9 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; +import org.mockito.ArgumentCaptor; +import org.mockito.stubbing.Answer; + import static java.util.Collections.singletonList; import static org.opensearch.client.RestClientTestUtil.getAllErrorStatusCodes; import static org.opensearch.client.RestClientTestUtil.getHttpMethods; @@ -101,8 +103,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.nullable; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.nullable; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -138,6 +140,7 @@ public void createRestClient() { failureListener, NodeSelector.ANY, strictDeprecationMode, + false, false ); } diff --git a/client/rest/src/test/java/org/opensearch/client/documentation/RestClientDocumentation.java b/client/rest/src/test/java/org/opensearch/client/documentation/RestClientDocumentation.java index 82c4fc2896213..ae4b8f4f20a4d 100644 --- a/client/rest/src/test/java/org/opensearch/client/documentation/RestClientDocumentation.java +++ b/client/rest/src/test/java/org/opensearch/client/documentation/RestClientDocumentation.java @@ -62,6 +62,7 @@ import org.opensearch.client.RestClientBuilder.HttpClientConfigCallback; import javax.net.ssl.SSLContext; + import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -133,7 +134,7 @@ public void usage() throws IOException, InterruptedException { //tag::rest-client-init-node-selector RestClientBuilder builder = RestClient.builder( new HttpHost("localhost", 9200, "http")); - builder.setNodeSelector(NodeSelector.SKIP_DEDICATED_MASTERS); // <1> + builder.setNodeSelector(NodeSelector.SKIP_DEDICATED_CLUSTER_MANAGERS); // <1> //end::rest-client-init-node-selector } { diff --git a/client/sniffer/build.gradle b/client/sniffer/build.gradle index bc4be1dd153e8..ab095ffdcefa8 100644 --- a/client/sniffer/build.gradle +++ b/client/sniffer/build.gradle @@ -30,11 +30,15 @@ apply plugin: 'opensearch.build' apply plugin: 'opensearch.publish' -targetCompatibility = JavaVersion.VERSION_11 -sourceCompatibility = JavaVersion.VERSION_11 +java { + targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_11 +} -group = 'org.opensearch.client' -archivesBaseName = 'opensearch-rest-client-sniffer' +base { + group = 'org.opensearch.client' + archivesBaseName = 'opensearch-rest-client-sniffer' +} dependencies { api project(":client:rest") @@ -50,6 +54,7 @@ dependencies { testImplementation "org.mockito:mockito-core:${versions.mockito}" testImplementation "org.objenesis:objenesis:${versions.objenesis}" testImplementation "net.bytebuddy:byte-buddy:${versions.bytebuddy}" + testImplementation "net.bytebuddy:byte-buddy-agent:${versions.bytebuddy}" } tasks.named('forbiddenApisMain').configure { @@ -88,7 +93,6 @@ thirdPartyAudit.ignoreMissingClasses( 'org.apache.avalon.framework.logger.Logger', 'org.apache.log.Hierarchy', 'org.apache.log.Logger', - 'org.apache.log4j.Category', 'org.apache.log4j.Level', 'org.apache.log4j.Logger', 'org.apache.log4j.Priority', diff --git a/client/sniffer/licenses/commons-codec-1.13.jar.sha1 b/client/sniffer/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/client/sniffer/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/client/sniffer/licenses/commons-codec-1.15.jar.sha1 b/client/sniffer/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/client/sniffer/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/client/sniffer/licenses/commons-logging-1.1.3.jar.sha1 b/client/sniffer/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index 5b8f029e58293..0000000000000 --- a/client/sniffer/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f \ No newline at end of file diff --git a/client/sniffer/licenses/commons-logging-1.2.jar.sha1 b/client/sniffer/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/client/sniffer/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/client/sniffer/licenses/httpclient-4.5.13.jar.sha1 b/client/sniffer/licenses/httpclient-4.5.13.jar.sha1 deleted file mode 100644 index 3281e21595b39..0000000000000 --- a/client/sniffer/licenses/httpclient-4.5.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada \ No newline at end of file diff --git a/client/sniffer/licenses/httpclient-4.5.14.jar.sha1 b/client/sniffer/licenses/httpclient-4.5.14.jar.sha1 new file mode 100644 index 0000000000000..66e05851c2e3c --- /dev/null +++ b/client/sniffer/licenses/httpclient-4.5.14.jar.sha1 @@ -0,0 +1 @@ +1194890e6f56ec29177673f2f12d0b8e627dec98 \ No newline at end of file diff --git a/client/sniffer/licenses/httpcore-4.4.12.jar.sha1 b/client/sniffer/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/client/sniffer/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/client/sniffer/licenses/httpcore-4.4.16.jar.sha1 b/client/sniffer/licenses/httpcore-4.4.16.jar.sha1 new file mode 100644 index 0000000000000..172110694b5bd --- /dev/null +++ b/client/sniffer/licenses/httpcore-4.4.16.jar.sha1 @@ -0,0 +1 @@ +51cf043c87253c9f58b539c9f7e44c8894223850 \ No newline at end of file diff --git a/client/sniffer/licenses/jackson-core-2.13.2.jar.sha1 b/client/sniffer/licenses/jackson-core-2.13.2.jar.sha1 deleted file mode 100644 index eb8a8bc45f041..0000000000000 --- a/client/sniffer/licenses/jackson-core-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0a6a0e0620d51833feffc67bccb51937b2345763 \ No newline at end of file diff --git a/client/sniffer/licenses/jackson-core-2.15.2.jar.sha1 b/client/sniffer/licenses/jackson-core-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..ec6781b968eed --- /dev/null +++ b/client/sniffer/licenses/jackson-core-2.15.2.jar.sha1 @@ -0,0 +1 @@ +a6fe1836469a69b3ff66037c324d75fc66ef137c \ No newline at end of file diff --git a/client/sniffer/src/main/java/org/opensearch/client/sniff/OpenSearchNodesSniffer.java b/client/sniffer/src/main/java/org/opensearch/client/sniff/OpenSearchNodesSniffer.java index 2829439627dbc..63fd6c336cfc8 100644 --- a/client/sniffer/src/main/java/org/opensearch/client/sniff/OpenSearchNodesSniffer.java +++ b/client/sniffer/src/main/java/org/opensearch/client/sniff/OpenSearchNodesSniffer.java @@ -35,6 +35,7 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpEntity; @@ -49,6 +50,7 @@ import java.io.InputStream; import java.net.URI; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -241,74 +243,23 @@ private static Node readNode(String nodeId, JsonParser parser, Scheme scheme) th } Map> realAttributes = new HashMap<>(protoAttributes.size()); - List keys = new ArrayList<>(protoAttributes.keySet()); - for (String key : keys) { - if (key.endsWith(".0")) { - String realKey = key.substring(0, key.length() - 2); - List values = new ArrayList<>(); - int i = 0; - while (true) { - String value = protoAttributes.remove(realKey + "." + i); - if (value == null) { - break; - } - values.add(value); - i++; - } - realAttributes.put(realKey, unmodifiableList(values)); - } - } for (Map.Entry entry : protoAttributes.entrySet()) { - realAttributes.put(entry.getKey(), singletonList(entry.getValue())); - } - - if (version.startsWith("2.")) { - /* - * 2.x doesn't send roles, instead we try to read them from - * attributes. - */ - boolean clientAttribute = v2RoleAttributeValue(realAttributes, "client", false); - Boolean masterAttribute = v2RoleAttributeValue(realAttributes, "master", null); - Boolean dataAttribute = v2RoleAttributeValue(realAttributes, "data", null); - if ((masterAttribute == null && false == clientAttribute) || masterAttribute) { - roles.add("master"); - } - if ((dataAttribute == null && false == clientAttribute) || dataAttribute) { - roles.add("data"); + if (entry.getValue().startsWith("[")) { + // Convert string array to list + String value = entry.getValue(); + String[] values = value.substring(1, value.length() - 1).split(", "); + realAttributes.put(entry.getKey(), unmodifiableList(Arrays.asList(values))); + } else { + realAttributes.put(entry.getKey(), singletonList(entry.getValue())); } - } else { - assert sawRoles : "didn't see roles for [" + nodeId + "]"; } + + assert sawRoles : "didn't see roles for [" + nodeId + "]"; assert boundHosts.contains(publishedHost) : "[" + nodeId + "] doesn't make sense! publishedHost should be in boundHosts"; logger.trace("adding node [" + nodeId + "]"); return new Node(publishedHost, boundHosts, name, version, new Roles(roles), unmodifiableMap(realAttributes)); } - /** - * Returns {@code defaultValue} if the attribute didn't come back, - * {@code true} or {@code false} if it did come back as - * either of those, or throws an IOException if the attribute - * came back in a strange way. - */ - private static Boolean v2RoleAttributeValue(Map> attributes, String name, Boolean defaultValue) - throws IOException { - List valueList = attributes.remove(name); - if (valueList == null) { - return defaultValue; - } - if (valueList.size() != 1) { - throw new IOException("expected only a single attribute value for [" + name + "] but got " + valueList); - } - switch (valueList.get(0)) { - case "true": - return true; - case "false": - return false; - default: - throw new IOException("expected [" + name + "] to be either [true] or [false] but was [" + valueList.get(0) + "]"); - } - } - /** * The supported host schemes. */ diff --git a/client/sniffer/src/test/java/org/opensearch/client/sniff/OpenSearchNodesSnifferParseTests.java b/client/sniffer/src/test/java/org/opensearch/client/sniff/OpenSearchNodesSnifferParseTests.java index a9ff47eab5366..615e63fdaca37 100644 --- a/client/sniffer/src/test/java/org/opensearch/client/sniff/OpenSearchNodesSnifferParseTests.java +++ b/client/sniffer/src/test/java/org/opensearch/client/sniff/OpenSearchNodesSnifferParseTests.java @@ -33,6 +33,7 @@ package org.opensearch.client.sniff; import com.fasterxml.jackson.core.JsonFactory; + import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.entity.ContentType; @@ -45,8 +46,8 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; -import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -85,59 +86,31 @@ private void checkFile(String file, Node... expected) throws IOException { } } - public void test2x() throws IOException { - checkFile( - "2.0.0_nodes_http.json", - node(9200, "m1", "2.0.0", true, false, false), - node(9201, "m2", "2.0.0", true, true, false), - node(9202, "m3", "2.0.0", true, false, false), - node(9203, "d1", "2.0.0", false, true, false), - node(9204, "d2", "2.0.0", false, true, false), - node(9205, "d3", "2.0.0", false, true, false), - node(9206, "c1", "2.0.0", false, false, false), - node(9207, "c2", "2.0.0", false, false, false) - ); - } - - public void test5x() throws IOException { - checkFile( - "5.0.0_nodes_http.json", - node(9200, "m1", "5.0.0", true, false, true), - node(9201, "m2", "5.0.0", true, true, true), - node(9202, "m3", "5.0.0", true, false, true), - node(9203, "d1", "5.0.0", false, true, true), - node(9204, "d2", "5.0.0", false, true, true), - node(9205, "d3", "5.0.0", false, true, true), - node(9206, "c1", "5.0.0", false, false, true), - node(9207, "c2", "5.0.0", false, false, true) - ); - } - - public void test6x() throws IOException { + public void test1x() throws IOException { checkFile( - "6.0.0_nodes_http.json", - node(9200, "m1", "6.0.0", true, false, true), - node(9201, "m2", "6.0.0", true, true, true), - node(9202, "m3", "6.0.0", true, false, true), - node(9203, "d1", "6.0.0", false, true, true), - node(9204, "d2", "6.0.0", false, true, true), - node(9205, "d3", "6.0.0", false, true, true), - node(9206, "c1", "6.0.0", false, false, true), - node(9207, "c2", "6.0.0", false, false, true) + "1.0.0_nodes_http.json", + node(9200, "m1", "1.0.0", "master", "ingest"), + node(9201, "m2", "1.0.0", "master", "data", "ingest"), + node(9202, "m3", "1.0.0", "master", "ingest"), + node(9203, "d1", "1.0.0", "data", "ingest"), + node(9204, "d2", "1.0.0", "data", "ingest"), + node(9205, "d3", "1.0.0", "data", "ingest"), + node(9206, "c1", "1.0.0", "ingest"), + node(9207, "c2", "1.0.0", "ingest") ); } - public void test7x() throws IOException { + public void test2x() throws IOException { checkFile( - "7.3.0_nodes_http.json", - node(9200, "m1", "7.3.0", "master", "ingest"), - node(9201, "m2", "7.3.0", "master", "data", "ingest"), - node(9202, "m3", "7.3.0", "master", "ingest"), - node(9203, "d1", "7.3.0", "data", "ingest", "ml"), - node(9204, "d2", "7.3.0", "data", "ingest"), - node(9205, "d3", "7.3.0", "data", "ingest"), - node(9206, "c1", "7.3.0", "ingest"), - node(9207, "c2", "7.3.0", "ingest") + "2.0.0_nodes_http.json", + node(9200, "m1", "2.0.0", "cluster_manager", "ingest"), + node(9201, "m2", "2.0.0", "cluster_manager", "data", "ingest"), + node(9202, "m3", "2.0.0", "cluster_manager", "ingest"), + node(9203, "d1", "2.0.0", "data", "ingest"), + node(9204, "d2", "2.0.0", "data", "ingest"), + node(9205, "d3", "2.0.0", "data", "ingest"), + node(9206, "c1", "2.0.0", "ingest"), + node(9207, "c2", "2.0.0", "ingest") ); } @@ -163,20 +136,6 @@ public void testParsingPublishAddressWithES7Format() throws IOException { assertEquals("http", nodes.get(0).getHost().getSchemeName()); } - private Node node(int port, String name, String version, boolean master, boolean data, boolean ingest) { - final Set roles = new TreeSet<>(); - if (master) { - roles.add("master"); - } - if (data) { - roles.add("data"); - } - if (ingest) { - roles.add("ingest"); - } - return node(port, name, version, roles); - } - private Node node(int port, String name, String version, String... roles) { return node(port, name, version, new TreeSet<>(Arrays.asList(roles))); } @@ -184,11 +143,15 @@ private Node node(int port, String name, String version, String... roles) { private Node node(int port, String name, String version, Set roles) { HttpHost host = new HttpHost("127.0.0.1", port); Set boundHosts = new HashSet<>(2); - boundHosts.add(host); boundHosts.add(new HttpHost("[::1]", port)); - Map> attributes = new HashMap<>(); + boundHosts.add(host); + Map> attributes = new LinkedHashMap<>(); // LinkedHashMap to preserve insertion order attributes.put("dummy", singletonList("everyone_has_me")); attributes.put("number", singletonList(name.substring(1))); + if (!version.startsWith("1.0") && !version.startsWith("1.1")) { + // Shard Indexing Pressure feature is added in version 1.2.0 + attributes.put("shard_indexing_pressure_enabled", singletonList(Boolean.TRUE.toString())); + } attributes.put("array", Arrays.asList(name.substring(0, 1), name.substring(1))); return new Node(host, boundHosts, name, version, new Roles(new TreeSet<>(roles)), attributes); } diff --git a/client/sniffer/src/test/java/org/opensearch/client/sniff/OpenSearchNodesSnifferTests.java b/client/sniffer/src/test/java/org/opensearch/client/sniff/OpenSearchNodesSnifferTests.java index 993844524c2d1..f9e658c3005ea 100644 --- a/client/sniffer/src/test/java/org/opensearch/client/sniff/OpenSearchNodesSnifferTests.java +++ b/client/sniffer/src/test/java/org/opensearch/client/sniff/OpenSearchNodesSnifferTests.java @@ -35,11 +35,14 @@ import com.carrotsearch.randomizedtesting.generators.RandomNumbers; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import com.carrotsearch.randomizedtesting.generators.RandomStrings; + import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; + import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; + import org.apache.http.Consts; import org.apache.http.HttpHost; import org.apache.http.client.methods.HttpGet; @@ -234,7 +237,7 @@ private static SniffResponse buildSniffResponse(OpenSearchNodesSniffer.Scheme sc final Set nodeRoles = new TreeSet<>(); if (randomBoolean()) { - nodeRoles.add("master"); + nodeRoles.add("cluster_manager"); } if (randomBoolean()) { nodeRoles.add("data"); @@ -283,12 +286,12 @@ private static SniffResponse buildSniffResponse(OpenSearchNodesSniffer.Scheme sc generator.writeEndObject(); } - List roles = Arrays.asList(new String[] { "master", "data", "ingest" }); + List roles = Arrays.asList(new String[] { "cluster_manager", "data", "ingest" }); Collections.shuffle(roles, getRandom()); generator.writeArrayFieldStart("roles"); for (String role : roles) { - if ("master".equals(role) && node.getRoles().isMasterEligible()) { - generator.writeString("master"); + if ("cluster_manager".equals(role) && node.getRoles().isClusterManagerEligible()) { + generator.writeString("cluster_manager"); } if ("data".equals(role) && node.getRoles().isData()) { generator.writeString("data"); @@ -307,13 +310,7 @@ private static SniffResponse buildSniffResponse(OpenSearchNodesSniffer.Scheme sc if (numAttributes > 0) { generator.writeObjectFieldStart("attributes"); for (Map.Entry> entry : attributes.entrySet()) { - if (entry.getValue().size() == 1) { - generator.writeStringField(entry.getKey(), entry.getValue().get(0)); - } else { - for (int v = 0; v < entry.getValue().size(); v++) { - generator.writeStringField(entry.getKey() + "." + v, entry.getValue().get(v)); - } - } + generator.writeStringField(entry.getKey(), entry.getValue().toString()); } generator.writeEndObject(); } diff --git a/client/sniffer/src/test/java/org/opensearch/client/sniff/SnifferBuilderTests.java b/client/sniffer/src/test/java/org/opensearch/client/sniff/SnifferBuilderTests.java index 25a3162e238ed..78650bad01643 100644 --- a/client/sniffer/src/test/java/org/opensearch/client/sniff/SnifferBuilderTests.java +++ b/client/sniffer/src/test/java/org/opensearch/client/sniff/SnifferBuilderTests.java @@ -33,6 +33,7 @@ package org.opensearch.client.sniff; import com.carrotsearch.randomizedtesting.generators.RandomNumbers; + import org.apache.http.HttpHost; import org.opensearch.client.RestClient; import org.opensearch.client.RestClientTestCase; diff --git a/client/sniffer/src/test/java/org/opensearch/client/sniff/SnifferTests.java b/client/sniffer/src/test/java/org/opensearch/client/sniff/SnifferTests.java index 304243e73c078..b73e4401ce60c 100644 --- a/client/sniffer/src/test/java/org/opensearch/client/sniff/SnifferTests.java +++ b/client/sniffer/src/test/java/org/opensearch/client/sniff/SnifferTests.java @@ -38,8 +38,6 @@ import org.opensearch.client.RestClientTestCase; import org.opensearch.client.sniff.Sniffer.DefaultScheduler; import org.opensearch.client.sniff.Sniffer.Scheduler; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import java.io.IOException; import java.util.ArrayList; @@ -61,6 +59,9 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.Matchers.greaterThan; diff --git a/client/sniffer/src/test/java/org/opensearch/client/sniff/documentation/SnifferDocumentation.java b/client/sniffer/src/test/java/org/opensearch/client/sniff/documentation/SnifferDocumentation.java index 3b612aab80851..e337e8930ad2a 100644 --- a/client/sniffer/src/test/java/org/opensearch/client/sniff/documentation/SnifferDocumentation.java +++ b/client/sniffer/src/test/java/org/opensearch/client/sniff/documentation/SnifferDocumentation.java @@ -35,8 +35,8 @@ import org.apache.http.HttpHost; import org.opensearch.client.Node; import org.opensearch.client.RestClient; -import org.opensearch.client.sniff.OpenSearchNodesSniffer; import org.opensearch.client.sniff.NodesSniffer; +import org.opensearch.client.sniff.OpenSearchNodesSniffer; import org.opensearch.client.sniff.SniffOnFailureListener; import org.opensearch.client.sniff.Sniffer; diff --git a/client/sniffer/src/test/resources/1.0.0_nodes_http.json b/client/sniffer/src/test/resources/1.0.0_nodes_http.json new file mode 100644 index 0000000000000..5557f0c7955c2 --- /dev/null +++ b/client/sniffer/src/test/resources/1.0.0_nodes_http.json @@ -0,0 +1,217 @@ +{ + "_nodes": { + "total": 8, + "successful": 8, + "failed": 0 + }, + "cluster_name": "opensearch", + "nodes": { + "ikXK_skVTfWkhONhldnbkw": { + "name": "m1", + "transport_address": "127.0.0.1:9300", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "1.0.0", + "build_type": "tar", + "build_hash": "34550c5b17124ddc59458ef774f6b43a086522e3", + "roles": [ + "ingest", + "master" + ], + "attributes": { + "dummy": "everyone_has_me", + "number": "1", + "array": "[m, 1]" + }, + "http": { + "bound_address": [ + "[::1]:9200", + "127.0.0.1:9200" + ], + "publish_address": "127.0.0.1:9200", + "max_content_length_in_bytes": 104857600 + } + }, + "TMHa34w4RqeuYoHCfJGXZg": { + "name": "m2", + "transport_address": "127.0.0.1:9301", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "1.0.0", + "build_type": "tar", + "build_hash": "34550c5b17124ddc59458ef774f6b43a086522e3", + "roles": [ + "data", + "ingest", + "master" + ], + "attributes": { + "dummy": "everyone_has_me", + "number": "2", + "array": "[m, 2]" + }, + "http": { + "bound_address": [ + "[::1]:9201", + "127.0.0.1:9201" + ], + "publish_address": "127.0.0.1:9201", + "max_content_length_in_bytes": 104857600 + } + }, + "lzaMRJTVT166sgVZdQ5thA": { + "name": "m3", + "transport_address": "127.0.0.1:9302", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "1.0.0", + "build_type": "tar", + "build_hash": "34550c5b17124ddc59458ef774f6b43a086522e3", + "roles": [ + "ingest", + "master" + ], + "attributes": { + "dummy": "everyone_has_me", + "number": "3", + "array": "[m, 3]" + }, + "http": { + "bound_address": [ + "[::1]:9202", + "127.0.0.1:9202" + ], + "publish_address": "127.0.0.1:9202", + "max_content_length_in_bytes": 104857600 + } + }, + "tGP5sUecSd6BLTWk1NWF8Q": { + "name": "d1", + "transport_address": "127.0.0.1:9303", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "1.0.0", + "build_type": "tar", + "build_hash": "34550c5b17124ddc59458ef774f6b43a086522e3", + "roles": [ + "data", + "ingest" + ], + "attributes": { + "dummy": "everyone_has_me", + "number": "1", + "array": "[d, 1]" + }, + "http": { + "bound_address": [ + "[::1]:9203", + "127.0.0.1:9203" + ], + "publish_address": "127.0.0.1:9203", + "max_content_length_in_bytes": 104857600 + } + }, + "c1UgW5ROTkSa2YnM_T56tw": { + "name": "d2", + "transport_address": "127.0.0.1:9304", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "1.0.0", + "build_type": "tar", + "build_hash": "34550c5b17124ddc59458ef774f6b43a086522e3", + "roles": [ + "data", + "ingest" + ], + "attributes": { + "dummy": "everyone_has_me", + "number": "2", + "array": "[d, 2]" + }, + "http": { + "bound_address": [ + "[::1]:9204", + "127.0.0.1:9204" + ], + "publish_address": "127.0.0.1:9204", + "max_content_length_in_bytes": 104857600 + } + }, + "QM9yjqjmS72MstpNYV_trg": { + "name": "d3", + "transport_address": "127.0.0.1:9305", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "1.0.0", + "build_type": "tar", + "build_hash": "34550c5b17124ddc59458ef774f6b43a086522e3", + "roles": [ + "data", + "ingest" + ], + "attributes": { + "dummy": "everyone_has_me", + "number": "3", + "array": "[d, 3]" + }, + "http": { + "bound_address": [ + "[::1]:9205", + "127.0.0.1:9205" + ], + "publish_address": "127.0.0.1:9205", + "max_content_length_in_bytes": 104857600 + } + }, + "wLtzAssoQYeX_4TstgCj0Q": { + "name": "c1", + "transport_address": "127.0.0.1:9306", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "1.0.0", + "build_type": "tar", + "build_hash": "34550c5b17124ddc59458ef774f6b43a086522e3", + "roles": [ + "ingest" + ], + "attributes": { + "dummy": "everyone_has_me", + "number": "1", + "array": "[c, 1]" + }, + "http": { + "bound_address": [ + "[::1]:9206", + "127.0.0.1:9206" + ], + "publish_address": "127.0.0.1:9206", + "max_content_length_in_bytes": 104857600 + } + }, + "ONOzpst8TH-ZebG7fxGwaA": { + "name": "c2", + "transport_address": "127.0.0.1:9307", + "host": "127.0.0.1", + "ip": "127.0.0.1", + "version": "1.0.0", + "build_type": "tar", + "build_hash": "34550c5b17124ddc59458ef774f6b43a086522e3", + "roles": [ + "ingest" + ], + "attributes": { + "dummy": "everyone_has_me", + "number": "2", + "array": "[c, 2]" + }, + "http": { + "bound_address": [ + "[::1]:9207", + "127.0.0.1:9207" + ], + "publish_address": "127.0.0.1:9207", + "max_content_length_in_bytes": 104857600 + } + } + } +} diff --git a/client/sniffer/src/test/resources/2.0.0_nodes_http.json b/client/sniffer/src/test/resources/2.0.0_nodes_http.json index 4e8dbbcba58c4..e1b75d460d7d9 100644 --- a/client/sniffer/src/test/resources/2.0.0_nodes_http.json +++ b/client/sniffer/src/test/resources/2.0.0_nodes_http.json @@ -1,4 +1,9 @@ { + "_nodes": { + "total": 8, + "successful": 8, + "failed": 0 + }, "cluster_name": "opensearch", "nodes": { "qr-SOrELSaGW8SlU8nflBw": { @@ -7,20 +12,22 @@ "host": "127.0.0.1", "ip": "127.0.0.1", "version": "2.0.0", - "build": "de54438", - "http_address": "127.0.0.1:9200", + "build_type": "tar", + "build_hash": "bae3b4e4178c20ac24fece8e82099abe3b2630d0", + "roles": [ + "cluster_manager", + "ingest" + ], "attributes": { "dummy": "everyone_has_me", "number": "1", - "array.0": "m", - "data": "false", - "array.1": "1", - "master": "true" + "array": "[m, 1]", + "shard_indexing_pressure_enabled": "true" }, "http": { "bound_address": [ - "127.0.0.1:9200", - "[::1]:9200" + "[::1]:9200", + "127.0.0.1:9200" ], "publish_address": "127.0.0.1:9200", "max_content_length_in_bytes": 104857600 @@ -32,19 +39,23 @@ "host": "127.0.0.1", "ip": "127.0.0.1", "version": "2.0.0", - "build": "de54438", - "http_address": "127.0.0.1:9201", + "build_type": "tar", + "build_hash": "bae3b4e4178c20ac24fece8e82099abe3b2630d0", + "roles": [ + "cluster_manager", + "data", + "ingest" + ], "attributes": { "dummy": "everyone_has_me", "number": "2", - "array.0": "m", - "array.1": "2", - "master": "true" + "shard_indexing_pressure_enabled": "true", + "array": "[m, 2]" }, "http": { "bound_address": [ - "127.0.0.1:9201", - "[::1]:9201" + "[::1]:9201", + "127.0.0.1:9201" ], "publish_address": "127.0.0.1:9201", "max_content_length_in_bytes": 104857600 @@ -56,20 +67,22 @@ "host": "127.0.0.1", "ip": "127.0.0.1", "version": "2.0.0", - "build": "de54438", - "http_address": "127.0.0.1:9202", + "build_type": "tar", + "build_hash": "bae3b4e4178c20ac24fece8e82099abe3b2630d0", + "roles": [ + "cluster_manager", + "ingest" + ], "attributes": { "dummy": "everyone_has_me", "number": "3", - "array.0": "m", - "data": "false", - "array.1": "3", - "master": "true" + "shard_indexing_pressure_enabled": "true", + "array": "[m, 3]" }, "http": { "bound_address": [ - "127.0.0.1:9202", - "[::1]:9202" + "[::1]:9202", + "127.0.0.1:9202" ], "publish_address": "127.0.0.1:9202", "max_content_length_in_bytes": 104857600 @@ -81,19 +94,22 @@ "host": "127.0.0.1", "ip": "127.0.0.1", "version": "2.0.0", - "build": "de54438", - "http_address": "127.0.0.1:9203", + "build_type": "tar", + "build_hash": "bae3b4e4178c20ac24fece8e82099abe3b2630d0", + "roles": [ + "data", + "ingest" + ], "attributes": { "dummy": "everyone_has_me", "number": "1", - "array.0": "d", - "array.1": "1", - "master": "false" + "shard_indexing_pressure_enabled": "true", + "array": "[d, 1]" }, "http": { "bound_address": [ - "127.0.0.1:9203", - "[::1]:9203" + "[::1]:9203", + "127.0.0.1:9203" ], "publish_address": "127.0.0.1:9203", "max_content_length_in_bytes": 104857600 @@ -105,19 +121,22 @@ "host": "127.0.0.1", "ip": "127.0.0.1", "version": "2.0.0", - "build": "de54438", - "http_address": "127.0.0.1:9204", + "build_type": "tar", + "build_hash": "bae3b4e4178c20ac24fece8e82099abe3b2630d0", + "roles": [ + "data", + "ingest" + ], "attributes": { "dummy": "everyone_has_me", "number": "2", - "array.0": "d", - "array.1": "2", - "master": "false" + "shard_indexing_pressure_enabled": "true", + "array": "[d, 2]" }, "http": { "bound_address": [ - "127.0.0.1:9204", - "[::1]:9204" + "[::1]:9204", + "127.0.0.1:9204" ], "publish_address": "127.0.0.1:9204", "max_content_length_in_bytes": 104857600 @@ -129,19 +148,22 @@ "host": "127.0.0.1", "ip": "127.0.0.1", "version": "2.0.0", - "build": "de54438", - "http_address": "127.0.0.1:9205", + "build_type": "tar", + "build_hash": "bae3b4e4178c20ac24fece8e82099abe3b2630d0", + "roles": [ + "data", + "ingest" + ], "attributes": { "dummy": "everyone_has_me", "number": "3", - "array.0": "d", - "array.1": "3", - "master": "false" + "shard_indexing_pressure_enabled": "true", + "array": "[d, 3]" }, "http": { "bound_address": [ - "127.0.0.1:9205", - "[::1]:9205" + "[::1]:9205", + "127.0.0.1:9205" ], "publish_address": "127.0.0.1:9205", "max_content_length_in_bytes": 104857600 @@ -153,20 +175,21 @@ "host": "127.0.0.1", "ip": "127.0.0.1", "version": "2.0.0", - "build": "de54438", - "http_address": "127.0.0.1:9206", + "build_type": "tar", + "build_hash": "bae3b4e4178c20ac24fece8e82099abe3b2630d0", + "roles": [ + "ingest" + ], "attributes": { "dummy": "everyone_has_me", "number": "1", - "array.0": "c", - "data": "false", - "array.1": "1", - "master": "false" + "shard_indexing_pressure_enabled": "true", + "array": "[c, 1]" }, "http": { "bound_address": [ - "127.0.0.1:9206", - "[::1]:9206" + "[::1]:9206", + "127.0.0.1:9206" ], "publish_address": "127.0.0.1:9206", "max_content_length_in_bytes": 104857600 @@ -178,20 +201,21 @@ "host": "127.0.0.1", "ip": "127.0.0.1", "version": "2.0.0", - "build": "de54438", - "http_address": "127.0.0.1:9207", + "build_type": "tar", + "build_hash": "bae3b4e4178c20ac24fece8e82099abe3b2630d0", + "roles": [ + "ingest" + ], "attributes": { "dummy": "everyone_has_me", "number": "2", - "array.0": "c", - "data": "false", - "array.1": "2", - "master": "false" + "shard_indexing_pressure_enabled": "true", + "array": "[c, 2]" }, "http": { "bound_address": [ - "127.0.0.1:9207", - "[::1]:9207" + "[::1]:9207", + "127.0.0.1:9207" ], "publish_address": "127.0.0.1:9207", "max_content_length_in_bytes": 104857600 diff --git a/client/sniffer/src/test/resources/5.0.0_nodes_http.json b/client/sniffer/src/test/resources/5.0.0_nodes_http.json deleted file mode 100644 index 4eb0443bc09d8..0000000000000 --- a/client/sniffer/src/test/resources/5.0.0_nodes_http.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "_nodes": { - "total": 8, - "successful": 8, - "failed": 0 - }, - "cluster_name": "opensearch", - "nodes": { - "0S4r3NurTYSFSb8R9SxwWA": { - "name": "m1", - "transport_address": "127.0.0.1:9300", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "5.0.0", - "build_hash": "253032b", - "roles": [ - "master", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "m", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9200", - "127.0.0.1:9200" - ], - "publish_address": "127.0.0.1:9200", - "max_content_length_in_bytes": 104857600 - } - }, - "k_CBrMXARkS57Qb5-3Mw5g": { - "name": "m2", - "transport_address": "127.0.0.1:9301", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "5.0.0", - "build_hash": "253032b", - "roles": [ - "master", - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "m", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9201", - "127.0.0.1:9201" - ], - "publish_address": "127.0.0.1:9201", - "max_content_length_in_bytes": 104857600 - } - }, - "6eynRPQ1RleJTeGDuTR9mw": { - "name": "m3", - "transport_address": "127.0.0.1:9302", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "5.0.0", - "build_hash": "253032b", - "roles": [ - "master", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "3", - "array.0": "m", - "array.1": "3" - }, - "http": { - "bound_address": [ - "[::1]:9202", - "127.0.0.1:9202" - ], - "publish_address": "127.0.0.1:9202", - "max_content_length_in_bytes": 104857600 - } - }, - "cbGC-ay1QNWaESvEh5513w": { - "name": "d1", - "transport_address": "127.0.0.1:9303", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "5.0.0", - "build_hash": "253032b", - "roles": [ - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "d", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9203", - "127.0.0.1:9203" - ], - "publish_address": "127.0.0.1:9203", - "max_content_length_in_bytes": 104857600 - } - }, - "LexndPpXR2ytYsU5fTElnQ": { - "name": "d2", - "transport_address": "127.0.0.1:9304", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "5.0.0", - "build_hash": "253032b", - "roles": [ - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "d", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9204", - "127.0.0.1:9204" - ], - "publish_address": "127.0.0.1:9204", - "max_content_length_in_bytes": 104857600 - } - }, - "SbNG1DKYSBu20zfOz2gDZQ": { - "name": "d3", - "transport_address": "127.0.0.1:9305", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "5.0.0", - "build_hash": "253032b", - "roles": [ - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "3", - "array.0": "d", - "array.1": "3" - }, - "http": { - "bound_address": [ - "[::1]:9205", - "127.0.0.1:9205" - ], - "publish_address": "127.0.0.1:9205", - "max_content_length_in_bytes": 104857600 - } - }, - "fM4H-m2WTDWmsGsL7jIJew": { - "name": "c1", - "transport_address": "127.0.0.1:9306", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "5.0.0", - "build_hash": "253032b", - "roles": [ - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "c", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9206", - "127.0.0.1:9206" - ], - "publish_address": "127.0.0.1:9206", - "max_content_length_in_bytes": 104857600 - } - }, - "pFoh7d0BTbqqI3HKd9na5A": { - "name": "c2", - "transport_address": "127.0.0.1:9307", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "5.0.0", - "build_hash": "253032b", - "roles": [ - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "c", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9207", - "127.0.0.1:9207" - ], - "publish_address": "127.0.0.1:9207", - "max_content_length_in_bytes": 104857600 - } - } - } -} diff --git a/client/sniffer/src/test/resources/6.0.0_nodes_http.json b/client/sniffer/src/test/resources/6.0.0_nodes_http.json deleted file mode 100644 index adc8f535d6aad..0000000000000 --- a/client/sniffer/src/test/resources/6.0.0_nodes_http.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "_nodes": { - "total": 8, - "successful": 8, - "failed": 0 - }, - "cluster_name": "opensearch", - "nodes": { - "ikXK_skVTfWkhONhldnbkw": { - "name": "m1", - "transport_address": "127.0.0.1:9300", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "6.0.0", - "build_hash": "8f0685b", - "roles": [ - "master", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "m", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9200", - "127.0.0.1:9200" - ], - "publish_address": "127.0.0.1:9200", - "max_content_length_in_bytes": 104857600 - } - }, - "TMHa34w4RqeuYoHCfJGXZg": { - "name": "m2", - "transport_address": "127.0.0.1:9301", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "6.0.0", - "build_hash": "8f0685b", - "roles": [ - "master", - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "m", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9201", - "127.0.0.1:9201" - ], - "publish_address": "127.0.0.1:9201", - "max_content_length_in_bytes": 104857600 - } - }, - "lzaMRJTVT166sgVZdQ5thA": { - "name": "m3", - "transport_address": "127.0.0.1:9302", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "6.0.0", - "build_hash": "8f0685b", - "roles": [ - "master", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "3", - "array.0": "m", - "array.1": "3" - }, - "http": { - "bound_address": [ - "[::1]:9202", - "127.0.0.1:9202" - ], - "publish_address": "127.0.0.1:9202", - "max_content_length_in_bytes": 104857600 - } - }, - "tGP5sUecSd6BLTWk1NWF8Q": { - "name": "d1", - "transport_address": "127.0.0.1:9303", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "6.0.0", - "build_hash": "8f0685b", - "roles": [ - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "d", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9203", - "127.0.0.1:9203" - ], - "publish_address": "127.0.0.1:9203", - "max_content_length_in_bytes": 104857600 - } - }, - "c1UgW5ROTkSa2YnM_T56tw": { - "name": "d2", - "transport_address": "127.0.0.1:9304", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "6.0.0", - "build_hash": "8f0685b", - "roles": [ - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "d", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9204", - "127.0.0.1:9204" - ], - "publish_address": "127.0.0.1:9204", - "max_content_length_in_bytes": 104857600 - } - }, - "QM9yjqjmS72MstpNYV_trg": { - "name": "d3", - "transport_address": "127.0.0.1:9305", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "6.0.0", - "build_hash": "8f0685b", - "roles": [ - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "3", - "array.0": "d", - "array.1": "3" - }, - "http": { - "bound_address": [ - "[::1]:9205", - "127.0.0.1:9205" - ], - "publish_address": "127.0.0.1:9205", - "max_content_length_in_bytes": 104857600 - } - }, - "wLtzAssoQYeX_4TstgCj0Q": { - "name": "c1", - "transport_address": "127.0.0.1:9306", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "6.0.0", - "build_hash": "8f0685b", - "roles": [ - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "c", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9206", - "127.0.0.1:9206" - ], - "publish_address": "127.0.0.1:9206", - "max_content_length_in_bytes": 104857600 - } - }, - "ONOzpst8TH-ZebG7fxGwaA": { - "name": "c2", - "transport_address": "127.0.0.1:9307", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "6.0.0", - "build_hash": "8f0685b", - "roles": [ - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "c", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9207", - "127.0.0.1:9207" - ], - "publish_address": "127.0.0.1:9207", - "max_content_length_in_bytes": 104857600 - } - } - } -} diff --git a/client/sniffer/src/test/resources/7.3.0_nodes_http.json b/client/sniffer/src/test/resources/7.3.0_nodes_http.json deleted file mode 100644 index 9e85511fadb62..0000000000000 --- a/client/sniffer/src/test/resources/7.3.0_nodes_http.json +++ /dev/null @@ -1,218 +0,0 @@ -{ - "_nodes": { - "total": 8, - "successful": 8, - "failed": 0 - }, - "cluster_name": "opensearch", - "nodes": { - "ikXK_skVTfWkhONhldnbkw": { - "name": "m1", - "transport_address": "127.0.0.1:9300", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "7.3.0", - "build_hash": "8f0685b", - "roles": [ - "master", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "m", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9200", - "127.0.0.1:9200" - ], - "publish_address": "127.0.0.1:9200", - "max_content_length_in_bytes": 104857600 - } - }, - "TMHa34w4RqeuYoHCfJGXZg": { - "name": "m2", - "transport_address": "127.0.0.1:9301", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "7.3.0", - "build_hash": "8f0685b", - "roles": [ - "master", - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "m", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9201", - "127.0.0.1:9201" - ], - "publish_address": "127.0.0.1:9201", - "max_content_length_in_bytes": 104857600 - } - }, - "lzaMRJTVT166sgVZdQ5thA": { - "name": "m3", - "transport_address": "127.0.0.1:9302", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "7.3.0", - "build_hash": "8f0685b", - "roles": [ - "master", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "3", - "array.0": "m", - "array.1": "3" - }, - "http": { - "bound_address": [ - "[::1]:9202", - "127.0.0.1:9202" - ], - "publish_address": "127.0.0.1:9202", - "max_content_length_in_bytes": 104857600 - } - }, - "tGP5sUecSd6BLTWk1NWF8Q": { - "name": "d1", - "transport_address": "127.0.0.1:9303", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "7.3.0", - "build_hash": "8f0685b", - "roles": [ - "data", - "ingest", - "ml" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "d", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9203", - "127.0.0.1:9203" - ], - "publish_address": "127.0.0.1:9203", - "max_content_length_in_bytes": 104857600 - } - }, - "c1UgW5ROTkSa2YnM_T56tw": { - "name": "d2", - "transport_address": "127.0.0.1:9304", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "7.3.0", - "build_hash": "8f0685b", - "roles": [ - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "d", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9204", - "127.0.0.1:9204" - ], - "publish_address": "127.0.0.1:9204", - "max_content_length_in_bytes": 104857600 - } - }, - "QM9yjqjmS72MstpNYV_trg": { - "name": "d3", - "transport_address": "127.0.0.1:9305", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "7.3.0", - "build_hash": "8f0685b", - "roles": [ - "data", - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "3", - "array.0": "d", - "array.1": "3" - }, - "http": { - "bound_address": [ - "[::1]:9205", - "127.0.0.1:9205" - ], - "publish_address": "127.0.0.1:9205", - "max_content_length_in_bytes": 104857600 - } - }, - "wLtzAssoQYeX_4TstgCj0Q": { - "name": "c1", - "transport_address": "127.0.0.1:9306", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "7.3.0", - "build_hash": "8f0685b", - "roles": [ - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "1", - "array.0": "c", - "array.1": "1" - }, - "http": { - "bound_address": [ - "[::1]:9206", - "127.0.0.1:9206" - ], - "publish_address": "127.0.0.1:9206", - "max_content_length_in_bytes": 104857600 - } - }, - "ONOzpst8TH-ZebG7fxGwaA": { - "name": "c2", - "transport_address": "127.0.0.1:9307", - "host": "127.0.0.1", - "ip": "127.0.0.1", - "version": "7.3.0", - "build_hash": "8f0685b", - "roles": [ - "ingest" - ], - "attributes": { - "dummy": "everyone_has_me", - "number": "2", - "array.0": "c", - "array.1": "2" - }, - "http": { - "bound_address": [ - "[::1]:9207", - "127.0.0.1:9207" - ], - "publish_address": "127.0.0.1:9207", - "max_content_length_in_bytes": 104857600 - } - } - } -} diff --git a/client/sniffer/src/test/resources/create_test_nodes_info.bash b/client/sniffer/src/test/resources/create_test_nodes_info.bash index e5925b195057d..78e67562d815b 100644 --- a/client/sniffer/src/test/resources/create_test_nodes_info.bash +++ b/client/sniffer/src/test/resources/create_test_nodes_info.bash @@ -14,22 +14,18 @@ # when we do rebuild the files they don't jump around too much. That # way the diffs are smaller. -set -e +set -e -o pipefail script_path="$( cd "$(dirname "$0")" ; pwd -P )" work=$(mktemp -d) pushd ${work} >> /dev/null echo Working in ${work} -wget https://download.elasticsearch.org/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.0.0/elasticsearch-2.0.0.tar.gz -wget https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-5.0.0.tar.gz -wget https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-6.0.0.tar.gz -sha1sum -c - << __SHAs -e369d8579bd3a2e8b5344278d5043f19f14cac88 elasticsearch-2.0.0.tar.gz -d25f6547bccec9f0b5ea7583815f96a6f50849e0 elasticsearch-5.0.0.tar.gz -__SHAs +wget https://artifacts.opensearch.org/releases/core/opensearch/1.0.0/opensearch-min-1.0.0-linux-x64.tar.gz +wget https://artifacts.opensearch.org/releases/core/opensearch/2.0.0/opensearch-min-2.0.0-linux-x64.tar.gz sha512sum -c - << __SHAs -25bb622d2fc557d8b8eded634a9b333766f7b58e701359e1bcfafee390776eb323cb7ea7a5e02e8803e25d8b1d3aabec0ec1b0cf492d0bab5689686fe440181c elasticsearch-6.0.0.tar.gz +96595cd3b173188d8a3f0f18d7bfa2457782839d06b519f01a99b4dc0280f81b08ba1d01bd1aef454feaa574cbbd04d3ad9a1f6a829182627e914f3e58f2899f opensearch-min-1.0.0-linux-x64.tar.gz +5b91456a2eb517bc48f13bec0a3f9c220494bd5fe979946dce6cfc3fa7ca00b003927157194d62f2a1c36c850eda74c70b93fbffa91bb082b2e1a17985d50976 opensearch-min-2.0.0-linux-x64.tar.gz __SHAs @@ -40,37 +36,38 @@ function do_version() { mkdir -p ${version} pushd ${version} >> /dev/null - tar xf ../opensearch-${version}.tar.gz + tar xf ../opensearch-min-${version}-linux-x64.tar.gz local http_port=9200 for node in ${nodes}; do mkdir ${node} cp -r opensearch-${version}/* ${node} - local master=$([[ "$node" =~ ^m.* ]] && echo true || echo false) - local data=$([[ "$node" =~ ^d.* ]] && echo true || echo false) - # m2 is always master and data for these test just so we have a node like that - data=$([[ "$node" == 'm2' ]] && echo true || echo ${data}) - local attr=$([ ${version} == '2.0.0' ] && echo '' || echo '.attr') + local cluster_manager=$([[ "$node" =~ ^m.* ]] && echo 'cluster_manager,' || echo '') + # 'cluster_manager' role is add in version 2.x and above, use 'master' role in 1.x + cluster_manager=$([[ ! "$cluster_manager" == '' && ${version} =~ ^1\. ]] && echo 'master,' || echo ${cluster_manager}) + local data=$([[ "$node" =~ ^d.* ]] && echo 'data,' || echo '') + # m2 is always cluster_manager and data for these test just so we have a node like that + data=$([[ "$node" == 'm2' ]] && echo 'data,' || echo ${data}) + # setting name 'cluster.initial_cluster_manager_nodes' is add in version 2.x and above + local initial_cluster_manager_nodes=$([[ ${version} =~ ^1\. ]] && echo 'initial_master_nodes' || echo 'initial_cluster_manager_nodes') local transport_port=$((http_port+100)) - cat >> ${node}/config/opensearch.yml << __ES_YML + cat >> ${node}/config/opensearch.yml << __OPENSEARCH_YML node.name: ${node} -node.master: ${master} -node.data: ${data} -node${attr}.dummy: everyone_has_me -node${attr}.number: ${node:1} -node${attr}.array: [${node:0:1}, ${node:1}] +node.roles: [${cluster_manager} ${data} ingest] +node.attr.dummy: everyone_has_me +node.attr.number: ${node:1} +node.attr.array: [${node:0:1}, ${node:1}] http.port: ${http_port} transport.tcp.port: ${transport_port} -discovery.zen.minimum_master_nodes: 3 -discovery.zen.ping.unicast.hosts: ['localhost:9300','localhost:9301','localhost:9302'] -__ES_YML +cluster.${initial_cluster_manager_nodes}: [m1, m2, m3] +discovery.seed_hosts: ['localhost:9300','localhost:9301','localhost:9302'] +__OPENSEARCH_YML - if [ ${version} != '2.0.0' ]; then - perl -pi -e 's/-Xm([sx]).+/-Xm${1}512m/g' ${node}/config/jvm.options - fi + # configure the JVM heap size + perl -pi -e 's/-Xm([sx]).+/-Xm${1}512m/g' ${node}/config/jvm.options echo "starting ${version}/${node}..." - ${node}/bin/opensearch -d -p ${node}/pidfile + ${node}/bin/opensearch -d -p pidfile ((http_port++)) done @@ -99,9 +96,8 @@ __ES_YML popd >> /dev/null } -JAVA_HOME=$JAVA8_HOME do_version 2.0.0 -JAVA_HOME=$JAVA8_HOME do_version 5.0.0 -JAVA_HOME=$JAVA8_HOME do_version 6.0.0 +JAVA_HOME=$JAVA11_HOME do_version 1.0.0 +JAVA_HOME=$JAVA11_HOME do_version 2.0.0 popd >> /dev/null rm -rf ${work} diff --git a/client/test/build.gradle b/client/test/build.gradle index 07d874cf01ea7..811ac585409dc 100644 --- a/client/test/build.gradle +++ b/client/test/build.gradle @@ -29,10 +29,14 @@ */ apply plugin: 'opensearch.build' -targetCompatibility = JavaVersion.VERSION_11 -sourceCompatibility = JavaVersion.VERSION_11 +java { + targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_11 +} -group = "${group}.client.test" +base { + group = "${group}.client.test" +} dependencies { api "org.apache.httpcomponents:httpcore:${versions.httpcore}" diff --git a/client/test/src/main/java/org/opensearch/client/RestClientTestCase.java b/client/test/src/main/java/org/opensearch/client/RestClientTestCase.java index 2b3e867929e27..28b74c14d929b 100644 --- a/client/test/src/main/java/org/opensearch/client/RestClientTestCase.java +++ b/client/test/src/main/java/org/opensearch/client/RestClientTestCase.java @@ -43,6 +43,7 @@ import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakZombies; import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite; + import org.apache.http.Header; import java.util.ArrayList; diff --git a/client/test/src/main/java/org/opensearch/client/RestClientTestUtil.java b/client/test/src/main/java/org/opensearch/client/RestClientTestUtil.java index aeba9bde4bff4..786302d9142cf 100644 --- a/client/test/src/main/java/org/opensearch/client/RestClientTestUtil.java +++ b/client/test/src/main/java/org/opensearch/client/RestClientTestUtil.java @@ -35,6 +35,7 @@ import com.carrotsearch.randomizedtesting.generators.RandomNumbers; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import com.carrotsearch.randomizedtesting.generators.RandomStrings; + import org.apache.http.Header; import org.apache.http.message.BasicHeader; diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000000000..dac8f30956846 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,16 @@ +codecov: + require_ci_to_pass: yes + +ignore: + - "test" + - "benchmarks" + +coverage: + precision: 2 + round: down + range: "70...100" + status: + project: + default: + target: 70% # the required coverage value + threshold: 1% # the leniency in hitting the target diff --git a/dev-tools/atomic_push.sh b/dev-tools/atomic_push.sh index d1b735f15374e..533fc5f6ed0b4 100755 --- a/dev-tools/atomic_push.sh +++ b/dev-tools/atomic_push.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -e +set -e -o pipefail if [ "$#" -eq 0 ]; then printf 'Usage: %s ...\n' "$(basename "$0")" diff --git a/dev-tools/prepare_release_update_documentation.py b/dev-tools/prepare_release_update_documentation.py index c7eae4eeb3245..d4edbb110f278 100644 --- a/dev-tools/prepare_release_update_documentation.py +++ b/dev-tools/prepare_release_update_documentation.py @@ -136,4 +136,3 @@ def callback(line): print('WARNING: no documentation references updates for release %s' % (release_version)) print('*** Done.') - diff --git a/dev-tools/signoff-check.sh b/dev-tools/signoff-check.sh index 56cb49455165e..539301a1cd79d 100755 --- a/dev-tools/signoff-check.sh +++ b/dev-tools/signoff-check.sh @@ -1,4 +1,6 @@ -#!/bin/sh +#!/usr/bin/env bash + +set -e -o pipefail ### Script to check for signoff presents on commits @@ -28,4 +30,3 @@ done # Return non-zero error code if any commits were missing signoff exit $missingSignoff - diff --git a/distribution/README.md b/distribution/README.md new file mode 100644 index 0000000000000..b9e948b625659 --- /dev/null +++ b/distribution/README.md @@ -0,0 +1,12 @@ +# Distributions +This subproject contains the necessary tooling to build the various distributions. +Note that some of this can only be run on the specific architecture and does not support cross-compile. + +The following distributions are being built: +* Archives (`*.zip`, `*.tar`): these form the basis for all other OpenSearch distributions +* Packages (`*.deb`, `*.rpm`): specific package formats for some Linux distributions +* Docker images +* Backwards compatibility tests: used internally for version compatibility testing, not for public consumption + +## With or Without JDK? +For each supported platform there should be both a target bundled with a JDK and a target without a bundled JDK. diff --git a/distribution/archives/build.gradle b/distribution/archives/build.gradle index ac70ee04444c7..161b8008525b4 100644 --- a/distribution/archives/build.gradle +++ b/distribution/archives/build.gradle @@ -28,9 +28,11 @@ * under the License. */ +import org.opensearch.gradle.JavaPackageType + apply plugin: 'opensearch.internal-distribution-archive-setup' -CopySpec archiveFiles(CopySpec modulesFiles, String distributionType, String platform, String architecture, boolean jdk) { +CopySpec archiveFiles(CopySpec modulesFiles, String distributionType, String platform, String architecture, JavaPackageType java) { return copySpec { into("opensearch-${version}") { into('lib') { @@ -39,19 +41,23 @@ CopySpec archiveFiles(CopySpec modulesFiles, String distributionType, String pla into('config') { dirMode 0750 fileMode 0660 - with configFiles(distributionType, jdk) + with configFiles(distributionType, java) from { dirMode 0750 jvmOptionsDir.getParent() } } into('bin') { - with binFiles(distributionType, jdk) + with binFiles(distributionType, java) } - if (jdk) { + if (java == JavaPackageType.JDK) { into("darwin".equals(platform) ? 'jdk.app' : 'jdk') { with jdkFiles(project, platform, architecture) } + } else if (java == JavaPackageType.JRE) { + into("darwin".equals(platform) ? 'jre.app' : 'jre') { + with jreFiles(project, platform, architecture) + } } into('') { from { @@ -73,7 +79,7 @@ CopySpec archiveFiles(CopySpec modulesFiles, String distributionType, String pla rename { 'LICENSE.txt' } } - with noticeFile(jdk) + with noticeFile(java) into('modules') { with modulesFiles } @@ -84,84 +90,122 @@ CopySpec archiveFiles(CopySpec modulesFiles, String distributionType, String pla distribution_archives { integTestZip { content { - archiveFiles(transportModulesFiles, 'zip', null, 'x64', false) + archiveFiles(transportModulesFiles, 'zip', null, 'x64', JavaPackageType.NONE) } } darwinTar { archiveClassifier = 'darwin-x64' content { - archiveFiles(modulesFiles('darwin-x64'), 'tar', 'darwin', 'x64', true) + archiveFiles(modulesFiles('darwin-x64'), 'tar', 'darwin', 'x64', JavaPackageType.JDK) } } darwinArm64Tar { archiveClassifier = 'darwin-arm64' content { - archiveFiles(modulesFiles('darwin-arm64'), 'tar', 'darwin', 'arm64', true) + archiveFiles(modulesFiles('darwin-arm64'), 'tar', 'darwin', 'arm64', JavaPackageType.JDK) } } noJdkDarwinTar { archiveClassifier = 'no-jdk-darwin-x64' content { - archiveFiles(modulesFiles('darwin-x64'), 'tar', 'darwin', 'x64', false) + archiveFiles(modulesFiles('darwin-x64'), 'tar', 'darwin', 'x64', JavaPackageType.NONE) } } noJdkDarwinArm64Tar { archiveClassifier = 'no-jdk-darwin-arm64' content { - archiveFiles(modulesFiles('darwin-arm64'), 'tar', 'darwin', 'arm64', false) + archiveFiles(modulesFiles('darwin-arm64'), 'tar', 'darwin', 'arm64', JavaPackageType.NONE) } } freebsdTar { archiveClassifier = 'freebsd-x64' content { - archiveFiles(modulesFiles('freebsd-x64'), 'tar', 'freebsd', 'x64', false) + archiveFiles(modulesFiles('freebsd-x64'), 'tar', 'freebsd', 'x64', JavaPackageType.NONE) } } noJdkFreebsdTar { archiveClassifier = 'no-jdk-freebsd-x64' content { - archiveFiles(modulesFiles('freebsd-x64'), 'tar', 'freebsd', 'x64', false) + archiveFiles(modulesFiles('freebsd-x64'), 'tar', 'freebsd', 'x64', JavaPackageType.NONE) } } linuxArm64Tar { archiveClassifier = 'linux-arm64' content { - archiveFiles(modulesFiles('linux-arm64'), 'tar', 'linux', 'arm64', true) + archiveFiles(modulesFiles('linux-arm64'), 'tar', 'linux', 'arm64', JavaPackageType.JDK) + } + } + + noJdkLinuxArm64Tar { + archiveClassifier = 'no-jdk-linux-arm64' + content { + archiveFiles(modulesFiles('linux-arm64'), 'tar', 'linux', 'arm64', JavaPackageType.NONE) } } linuxTar { archiveClassifier = 'linux-x64' content { - archiveFiles(modulesFiles('linux-x64'), 'tar', 'linux', 'x64', true) + archiveFiles(modulesFiles('linux-x64'), 'tar', 'linux', 'x64', JavaPackageType.JDK) } } noJdkLinuxTar { archiveClassifier = 'no-jdk-linux-x64' content { - archiveFiles(modulesFiles('linux-x64'), 'tar', 'linux', 'x64', false) + archiveFiles(modulesFiles('linux-x64'), 'tar', 'linux', 'x64', JavaPackageType.NONE) + } + } + + jreLinuxTar { + archiveClassifier = 'jre-linux-x64' + content { + archiveFiles(modulesFiles('linux-x64'), 'tar', 'linux', 'x64', JavaPackageType.JRE) + } + } + + // Should really be `no-jdk-linux-s390x` as it ships without a JDK, however it seems that the build can't handle + // the absence of the `linux-s390x` target. + linuxS390xTar { + archiveClassifier = 'linux-s390x' + content { + archiveFiles(modulesFiles('linux-s390x'), 'tar', 'linux', 's390x', JavaPackageType.NONE) + } + } + + + linuxPpc64leTar { + archiveClassifier = 'linux-ppc64le' + content { + archiveFiles(modulesFiles('linux-ppc64le'), 'tar', 'linux', 'ppc64le', JavaPackageType.JDK) + } + } + + noJdkLinuxPpc64leTar { + archiveClassifier = 'no-jdk-linux-ppc64le' + content { + archiveFiles(modulesFiles('linux-ppc64le'), 'tar', 'linux', 'ppc64le', JavaPackageType.NONE) } } windowsZip { archiveClassifier = 'windows-x64' content { - archiveFiles(modulesFiles('windows-x64'), 'zip', 'windows', 'x64', true) + archiveFiles(modulesFiles('windows-x64'), 'zip', 'windows', 'x64', JavaPackageType.JDK) } } noJdkWindowsZip { archiveClassifier = 'no-jdk-windows-x64' content { - archiveFiles(modulesFiles('windows-x64'), 'zip', 'windows', 'x64', false) + archiveFiles(modulesFiles('windows-x64'), 'zip', 'windows', 'x64', JavaPackageType.NONE) } } } diff --git a/distribution/archives/darwin-arm64-tar/build.gradle b/distribution/archives/darwin-arm64-tar/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/darwin-arm64-tar/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/archives/darwin-tar/build.gradle b/distribution/archives/darwin-tar/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/darwin-tar/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/archives/integ-test-zip/build.gradle b/distribution/archives/integ-test-zip/build.gradle index 35c5a9b60b568..9418223b0a44d 100644 --- a/distribution/archives/integ-test-zip/build.gradle +++ b/distribution/archives/integ-test-zip/build.gradle @@ -34,8 +34,12 @@ apply plugin: 'opensearch.rest-test' // The integ-test-distribution is published to maven apply plugin: 'opensearch.publish' +apply plugin: 'com.netflix.nebula.maven-publish' -group = "org.opensearch.distribution.integ-test-zip" +base { + group = "org.opensearch.distribution.integ-test-zip" + archivesBaseName = "opensearch" +} integTest { dependsOn assemble @@ -47,7 +51,6 @@ processTestResources { } // make the pom file name use opensearch instead of the project name -archivesBaseName = "opensearch" ext.buildDist = parent.tasks.named("buildIntegTestZip") publishing { diff --git a/distribution/archives/integ-test-zip/src/test/java/org/opensearch/test/rest/NodeRestUsageIT.java b/distribution/archives/integ-test-zip/src/test/java/org/opensearch/test/rest/NodeRestUsageIT.java index cb2318351086c..59df4122713d5 100644 --- a/distribution/archives/integ-test-zip/src/test/java/org/opensearch/test/rest/NodeRestUsageIT.java +++ b/distribution/archives/integ-test-zip/src/test/java/org/opensearch/test/rest/NodeRestUsageIT.java @@ -35,7 +35,8 @@ import org.opensearch.client.Request; import org.opensearch.client.Response; import org.opensearch.client.ResponseException; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.search.aggregations.AggregationBuilders; import org.opensearch.search.builder.SearchSourceBuilder; @@ -171,8 +172,8 @@ public void testAggregationUsage() throws IOException { .aggregation(AggregationBuilders.terms("str_terms").field("str.keyword")) .aggregation(AggregationBuilders.terms("num_terms").field("num")) .aggregation(AggregationBuilders.avg("num_avg").field("num")); - searchRequest.setJsonEntity(Strings.toString(searchSource)); - searchRequest.setJsonEntity(Strings.toString(searchSource)); + searchRequest.setJsonEntity(Strings.toString(MediaTypeRegistry.JSON, searchSource)); + searchRequest.setJsonEntity(Strings.toString(MediaTypeRegistry.JSON, searchSource)); client().performRequest(searchRequest); searchRequest = new Request("GET", "/test/_search"); @@ -181,8 +182,8 @@ public void testAggregationUsage() throws IOException { .aggregation(AggregationBuilders.avg("num1").field("num")) .aggregation(AggregationBuilders.avg("num2").field("num")) .aggregation(AggregationBuilders.terms("foo").field("foo.keyword")); - String r = Strings.toString(searchSource); - searchRequest.setJsonEntity(Strings.toString(searchSource)); + String r = Strings.toString(MediaTypeRegistry.JSON, searchSource); + searchRequest.setJsonEntity(Strings.toString(MediaTypeRegistry.JSON, searchSource)); client().performRequest(searchRequest); Response response = client().performRequest(new Request("GET", "_nodes/usage")); diff --git a/distribution/archives/integ-test-zip/src/test/java/org/opensearch/test/rest/WaitForRefreshAndCloseIT.java b/distribution/archives/integ-test-zip/src/test/java/org/opensearch/test/rest/WaitForRefreshAndCloseIT.java index 37ffe32d19509..e4fc5b65e0f17 100644 --- a/distribution/archives/integ-test-zip/src/test/java/org/opensearch/test/rest/WaitForRefreshAndCloseIT.java +++ b/distribution/archives/integ-test-zip/src/test/java/org/opensearch/test/rest/WaitForRefreshAndCloseIT.java @@ -32,8 +32,8 @@ package org.opensearch.test.rest; +import org.opensearch.common.action.ActionFuture; import org.apache.http.util.EntityUtils; -import org.opensearch.action.ActionFuture; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.client.Request; import org.opensearch.client.Response; diff --git a/distribution/archives/linux-arm64-tar/build.gradle b/distribution/archives/linux-arm64-tar/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/linux-arm64-tar/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/archives/linux-tar/build.gradle b/distribution/archives/linux-tar/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/linux-tar/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/archives/no-jdk-darwin-arm64-tar/build.gradle b/distribution/archives/no-jdk-darwin-arm64-tar/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/no-jdk-darwin-arm64-tar/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/archives/no-jdk-darwin-tar/build.gradle b/distribution/archives/no-jdk-darwin-tar/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/no-jdk-darwin-tar/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/archives/no-jdk-linux-tar/build.gradle b/distribution/archives/no-jdk-linux-tar/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/no-jdk-linux-tar/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/archives/no-jdk-windows-zip/build.gradle b/distribution/archives/no-jdk-windows-zip/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/no-jdk-windows-zip/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/archives/windows-zip/build.gradle b/distribution/archives/windows-zip/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/archives/windows-zip/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/build.gradle b/distribution/build.gradle index 356aaa269e106..bbbef40f51c57 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -36,6 +36,7 @@ import org.opensearch.gradle.MavenFilteringHack import org.opensearch.gradle.NoticeTask import org.opensearch.gradle.VersionProperties import org.opensearch.gradle.info.BuildParams +import org.opensearch.gradle.JavaPackageType import java.nio.file.Files import java.nio.file.Path @@ -217,7 +218,7 @@ ext.restTestExpansions = [ // loop over modules to also setup cross task dependencies and increment our modules counter project.rootProject.subprojects.findAll { it.parent.path == ':modules' }.each { Project module -> if (module.name == 'systemd') { - // the systemd module is only included in the package distributions + // the systemd module is only included in the package distributions or in linux and freebsd archives return } File licenses = new File(module.projectDir, 'licenses') @@ -275,15 +276,16 @@ project(':test:external-modules').subprojects.each { Project testModule -> configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { apply plugin: 'opensearch.jdk-download' + apply plugin: 'opensearch.jre-download' apply plugin: 'opensearch.repositories' // Setup all required JDKs project.jdks { ['darwin', 'linux', 'windows'].each { platform -> - (platform == 'linux' || platform == 'darwin' ? ['x64', 'aarch64'] : ['x64']).each { architecture -> - "bundled_${platform}_${architecture}" { + (platform == 'linux' || platform == 'darwin' ? ['x64', 'aarch64', 's390x', 'ppc64le'] : ['x64']).each { architecture -> + "bundled_jdk_${platform}_${architecture}" { it.platform = platform - it.version = VersionProperties.getBundledJdk(platform) + it.version = VersionProperties.getBundledJdk(platform, architecture) it.vendor = VersionProperties.bundledJdkVendor it.architecture = architecture } @@ -291,6 +293,20 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { } } + // Setup all required JREs + project.jres { + ['darwin', 'linux', 'windows'].each { platform -> + (platform == 'linux' || platform == 'darwin' ? ['x64', 'aarch64', 's390x', 'ppc64le'] : ['x64']).each { architecture -> + "bundled_jre_${platform}_${architecture}" { + it.platform = platform + it.version = VersionProperties.getBundledJre(platform, architecture) + it.vendor = VersionProperties.bundledJdkVendor + it.architecture = architecture + } + } + } + } + // TODO: the map needs to be an input of the tasks, so that when it changes, the task will re-run... /***************************************************************************** * Properties to expand when copying packaging files * @@ -353,7 +369,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { } } def buildModules = buildModulesTaskProvider - List excludePlatforms = ['darwin-x64', 'freebsd-x64', 'linux-x64', 'linux-arm64', 'windows-x64', 'darwin-arm64'] + List excludePlatforms = ['darwin-x64', 'freebsd-x64', 'linux-x64', 'linux-arm64', 'linux-s390x', 'linux-ppc64le', 'windows-x64', 'darwin-arm64'] if (platform != null) { excludePlatforms.remove(excludePlatforms.indexOf(platform)) } else { @@ -367,7 +383,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { if (BuildParams.isSnapshotBuild()) { from(buildExternalTestModulesTaskProvider) } - if (project.path.startsWith(':distribution:packages')) { + if (project.path.startsWith(':distribution:packages') || ['freebsd-x64','linux-x64', 'linux-arm64'].contains(platform)) { from(buildSystemdModuleTaskProvider) } } @@ -377,20 +393,20 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { from buildTransportModulesTaskProvider } - configFiles = { distributionType, jdk -> + configFiles = { distributionType, java -> copySpec { with copySpec { // main config files, processed with distribution specific substitutions from '../src/config' exclude 'log4j2.properties' // this is handled separately below - MavenFilteringHack.filter(it, expansionsForDistribution(distributionType, jdk)) + MavenFilteringHack.filter(it, expansionsForDistribution(distributionType, java)) } from project(':distribution').buildLog4jConfig from project(':distribution').buildConfig } } - binFiles = { distributionType, jdk -> + binFiles = { distributionType, java -> copySpec { // non-windows files, for all distributions with copySpec { @@ -398,7 +414,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { exclude '*.exe' exclude '*.bat' eachFile { it.setMode(0755) } - MavenFilteringHack.filter(it, expansionsForDistribution(distributionType, jdk)) + MavenFilteringHack.filter(it, expansionsForDistribution(distributionType, java)) } // windows files, only for zip if (distributionType == 'zip') { @@ -406,7 +422,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { from '../src/bin' include '*.bat' filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) - MavenFilteringHack.filter(it, expansionsForDistribution(distributionType, jdk)) + MavenFilteringHack.filter(it, expansionsForDistribution(distributionType, java)) } with copySpec { from '../src/bin' @@ -424,12 +440,12 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { } } - noticeFile = { jdk -> + noticeFile = { java -> copySpec { if (project.name == 'integ-test-zip') { from buildServerNoticeTaskProvider } else { - if (jdk) { + if (java != JavaPackageType.NONE) { from buildNoticeTaskProvider } else { from buildNoJdkNoticeTaskProvider @@ -446,7 +462,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { if ("arm64".equals(architecture)) { architecture = "aarch64" } - from project.jdks."bundled_${platform}_${architecture}" + from project.jdks."bundled_jdk_${platform}_${architecture}" exclude "demo/**" /* * The Contents/MacOS directory interferes with notarization, and is unused by our distribution, so we exclude @@ -465,6 +481,31 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { } } } + + jreFiles = { Project project, String platform, String architecture -> + return copySpec { + /* + * Jdk uses aarch64 from ARM. Translating from arm64 to aarch64 which Jdk understands. + */ + if ("arm64".equals(architecture)) { + architecture = "aarch64" + } + from project.jres."bundled_jre_${platform}_${architecture}" + exclude "demo/**" + /* + * The Contents/MacOS directory interferes with notarization, and is unused by our distribution, so we exclude + * it from the build. + */ + if ("darwin".equals(platform)) { + exclude "Contents/MacOS" + } + eachFile { FileCopyDetails details -> + if (details.relativePath.segments[-2] == 'bin' || details.relativePath.segments[-1] == 'jspawnhelper') { + details.mode = 0755 + } + } + } + } } } @@ -583,7 +624,7 @@ subprojects { ], 'opensearch.bundled_jdk': [ - 'def': jdk ? 'true' : 'false' + 'def': jdk != JavaPackageType.NONE ? true : false ], 'license.name': [ diff --git a/distribution/docker/build.gradle b/distribution/docker/build.gradle index f5d8048a06276..ad8678c608b54 100644 --- a/distribution/docker/build.gradle +++ b/distribution/docker/build.gradle @@ -27,11 +27,15 @@ testFixtures.useFixture() configurations { arm64DockerSource + s390xDockerSource + ppc64leDockerSource dockerSource } dependencies { arm64DockerSource project(path: ":distribution:archives:linux-arm64-tar", configuration:"default") + s390xDockerSource project(path: ":distribution:archives:linux-s390x-tar", configuration:"default") + ppc64leDockerSource project(path: ":distribution:archives:linux-ppc64le-tar", configuration:"default") dockerSource project(path: ":distribution:archives:linux-tar", configuration:"default") } @@ -42,6 +46,10 @@ ext.expansions = { Architecture architecture, DockerBase base, boolean local -> classifier = "linux-arm64" } else if (architecture == Architecture.X64) { classifier = "linux-x64" + } else if (architecture == Architecture.S390X) { + classifier = "linux-s390x" + } else if (architecture == Architecture.PPC64LE) { + classifier = "linux-ppc64le" } else { throw new IllegalArgumentException("Unsupported architecture [" + architecture + "]") } @@ -85,12 +93,16 @@ RUN curl --retry 8 -S -L \\ private static String buildPath(Architecture architecture, DockerBase base) { return 'build/' + (architecture == Architecture.ARM64 ? 'arm64-' : '') + + (architecture == Architecture.S390X ? 's390x-' : '') + + (architecture == Architecture.PPC64LE ? 'ppc64le-' : '') + 'docker' } private static String taskName(String prefix, Architecture architecture, DockerBase base, String suffix) { return prefix + (architecture == Architecture.ARM64 ? 'Arm64' : '') + + (architecture == Architecture.S390X ? 'S390x' : '') + + (architecture == Architecture.PPC64LE ? 'Ppc64le' : '') + suffix } @@ -127,6 +139,10 @@ void addCopyDockerContextTask(Architecture architecture, DockerBase base) { if (architecture == Architecture.ARM64) { from configurations.arm64DockerSource + } else if (architecture == Architecture.S390X) { + from configurations.s390xDockerSource + } else if (architecture == Architecture.PPC64LE) { + from configurations.ppc64leDockerSource } else { from configurations.dockerSource } diff --git a/distribution/docker/docker-ppc64le-export/build.gradle b/distribution/docker/docker-ppc64le-export/build.gradle new file mode 100644 index 0000000000000..820a0cdf69dfc --- /dev/null +++ b/distribution/docker/docker-ppc64le-export/build.gradle @@ -0,0 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * +*/ + +// This file is intentionally blank. All configuration of the +// export is done in the parent project. diff --git a/distribution/docker/docker-s390x-export/build.gradle b/distribution/docker/docker-s390x-export/build.gradle new file mode 100644 index 0000000000000..3506c4e39c234 --- /dev/null +++ b/distribution/docker/docker-s390x-export/build.gradle @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +// This file is intentionally blank. All configuration of the +// export is done in the parent project. diff --git a/distribution/docker/docker-test-entrypoint.sh b/distribution/docker/docker-test-entrypoint.sh index 85a90df003ab4..1cfc62f6b02b0 100755 --- a/distribution/docker/docker-test-entrypoint.sh +++ b/distribution/docker/docker-test-entrypoint.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +set -e -o pipefail + cd /usr/share/opensearch/bin/ /usr/local/bin/docker-entrypoint.sh | tee > /usr/share/opensearch/logs/console.log diff --git a/distribution/docker/src/docker/bin/docker-entrypoint.sh b/distribution/docker/src/docker/bin/docker-entrypoint.sh index 0d6ca588d875d..33c68afce0bfc 100644 --- a/distribution/docker/src/docker/bin/docker-entrypoint.sh +++ b/distribution/docker/src/docker/bin/docker-entrypoint.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -e +set -e -o pipefail # Files created by Elasticsearch should always be group writable too umask 0002 diff --git a/distribution/docker/src/docker/config/log4j2.properties b/distribution/docker/src/docker/config/log4j2.properties index a8c54137c7fd2..761478a9fdc6e 100644 --- a/distribution/docker/src/docker/config/log4j2.properties +++ b/distribution/docker/src/docker/config/log4j2.properties @@ -53,3 +53,13 @@ logger.index_indexing_slowlog.name = index.indexing.slowlog.index logger.index_indexing_slowlog.level = trace logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling logger.index_indexing_slowlog.additivity = false + +appender.task_detailslog_rolling.type = Console +appender.task_detailslog_rolling.name = task_detailslog_rolling +appender.task_detailslog_rolling.layout.type = OpenSearchJsonLayout +appender.task_detailslog_rolling.layout.type_name = task_detailslog + +logger.task_detailslog_rolling.name = task.detailslog +logger.task_detailslog_rolling.level = trace +logger.task_detailslog_rolling.appenderRef.task_detailslog_rolling.ref = task_detailslog_rolling +logger.task_detailslog_rolling.additivity = false diff --git a/distribution/docker/src/test/resources/rest-api-spec/test/10_info.yml b/distribution/docker/src/test/resources/rest-api-spec/test/10_info.yml index 2b0f6683a24cf..97b3b7b5d0f4d 100644 --- a/distribution/docker/src/test/resources/rest-api-spec/test/10_info.yml +++ b/distribution/docker/src/test/resources/rest-api-spec/test/10_info.yml @@ -7,4 +7,3 @@ - is_true: version - is_true: version.number - match: { version.build_type: "docker" } - diff --git a/distribution/docker/src/test/resources/rest-api-spec/test/11_nodes.yml b/distribution/docker/src/test/resources/rest-api-spec/test/11_nodes.yml index 95ea022696942..1c10166e96eeb 100644 --- a/distribution/docker/src/test/resources/rest-api-spec/test/11_nodes.yml +++ b/distribution/docker/src/test/resources/rest-api-spec/test/11_nodes.yml @@ -24,8 +24,8 @@ - match: $body: | - / #ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role cluster_manager name - ^ ((\d{1,3}\.){3}\d{1,3} \s+ \d+ \s+ \d* \s+ (-)?\d* \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)?\s+ ((-)?\d*(\.\d+)?)? \s+ (-|[cdhilmrstvw]{1,11}) \s+ [-*x] \s+ (\S+\s?)+ \n)+ $/ + / #ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role node.roles cluster_manager name + ^ ((\d{1,3}\.){3}\d{1,3} \s+ \d+ \s+ \d* \s+ (-)?\d* \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)?\s+ ((-)?\d*(\.\d+)?)? \s+ (-|[cdhilmrstvw]{1,11}) (\s+ (-|\w+(,\w+)*+))? \s+ [-*x] \s+ (\S+\s?)+ \n)+ $/ - do: cat.nodes: @@ -33,8 +33,8 @@ - match: $body: | - /^ ip \s+ heap\.percent \s+ ram\.percent \s+ cpu \s+ load_1m \s+ load_5m \s+ load_15m \s+ node\.role \s+ cluster_manager \s+ name \n - ((\d{1,3}\.){3}\d{1,3} \s+ \d+ \s+ \d* \s+ (-)?\d* \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)? \s+ (-|[cdhilmrstvw]{1,11}) \s+ [-*x] \s+ (\S+\s?)+ \n)+ $/ + /^ ip \s+ heap\.percent \s+ ram\.percent \s+ cpu \s+ load_1m \s+ load_5m \s+ load_15m \s+ node\.role \s+ node\.roles \s+ cluster_manager \s+ name \n + ((\d{1,3}\.){3}\d{1,3} \s+ \d+ \s+ \d* \s+ (-)?\d* \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)? \s+ (-|[cdhilmrstvw]{1,11}) (\s+ (-|\w+(,\w+)*+ ))? \s+ [-*x] \s+ (\S+\s?)+ \n)+ $/ - do: cat.nodes: @@ -123,4 +123,3 @@ - match: $body: | /^(\S{5,}\n)+$/ - diff --git a/distribution/packages/arm64-deb/build.gradle b/distribution/packages/arm64-deb/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/packages/arm64-deb/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/packages/arm64-rpm/build.gradle b/distribution/packages/arm64-rpm/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/packages/arm64-rpm/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/packages/build.gradle b/distribution/packages/build.gradle index 93a82ff324835..394d47639fb0a 100644 --- a/distribution/packages/build.gradle +++ b/distribution/packages/build.gradle @@ -63,7 +63,7 @@ import java.util.regex.Pattern */ plugins { - id "nebula.ospackage-base" version "9.1.1" + id "com.netflix.nebula.ospackage-base" version "11.4.0" } void addProcessFilesTask(String type, boolean jdk) { @@ -213,7 +213,7 @@ Closure commonPackageConfig(String type, boolean jdk, String architecture) { configurationFile '/etc/opensearch/jvm.options' configurationFile '/etc/opensearch/log4j2.properties' from("${packagingFiles}") { - dirMode 02750 + dirMode 0750 into('/etc') permissionGroup 'opensearch' includeEmptyDirs true @@ -223,7 +223,7 @@ Closure commonPackageConfig(String type, boolean jdk, String architecture) { } from("${packagingFiles}/etc/opensearch") { into('/etc/opensearch') - dirMode 02750 + dirMode 0750 fileMode 0660 permissionGroup 'opensearch' includeEmptyDirs true @@ -281,8 +281,8 @@ Closure commonPackageConfig(String type, boolean jdk, String architecture) { dirMode mode } } - copyEmptyDir('/var/log/opensearch', 'opensearch', 'opensearch', 02750) - copyEmptyDir('/var/lib/opensearch', 'opensearch', 'opensearch', 02750) + copyEmptyDir('/var/log/opensearch', 'opensearch', 'opensearch', 0750) + copyEmptyDir('/var/lib/opensearch', 'opensearch', 'opensearch', 0750) copyEmptyDir('/usr/share/opensearch/plugins', 'root', 'root', 0755) into '/usr/share/opensearch' @@ -290,7 +290,7 @@ Closure commonPackageConfig(String type, boolean jdk, String architecture) { } } -apply plugin: 'nebula.ospackage-base' +apply plugin: 'com.netflix.nebula.ospackage-base' // this is package indepdendent configuration ospackage { @@ -350,6 +350,10 @@ tasks.register('buildArm64Deb', Deb) { configure(commonDebConfig(true, 'arm64')) } +tasks.register('buildNoJdkArm64Deb', Deb) { + configure(commonDebConfig(false, 'arm64')) +} + tasks.register('buildDeb', Deb) { configure(commonDebConfig(true, 'x64')) } @@ -387,12 +391,16 @@ tasks.register('buildArm64Rpm', Rpm) { configure(commonRpmConfig(true, 'arm64')) } +tasks.register('buildNoJdkArm64Rpm', Rpm) { + configure(commonRpmConfig(false, 'arm64')) +} + tasks.register('buildRpm', Rpm) { configure(commonRpmConfig(true, 'x64')) } tasks.register('buildNoJdkRpm', Rpm) { - configure(commonRpmConfig(true, 'x64')) + configure(commonRpmConfig(false, 'x64')) } Closure dpkgExists = { it -> new File('/bin/dpkg-deb').exists() || new File('/usr/bin/dpkg-deb').exists() || new File('/usr/local/bin/dpkg-deb').exists() } diff --git a/distribution/packages/deb/build.gradle b/distribution/packages/deb/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/packages/deb/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/packages/no-jdk-deb/build.gradle b/distribution/packages/no-jdk-deb/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/packages/no-jdk-deb/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/packages/no-jdk-rpm/build.gradle b/distribution/packages/no-jdk-rpm/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/packages/no-jdk-rpm/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/packages/rpm/build.gradle b/distribution/packages/rpm/build.gradle deleted file mode 100644 index bb3e3a302c8d6..0000000000000 --- a/distribution/packages/rpm/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -// This file is intentionally blank. All configuration of the -// distribution is done in the parent project. diff --git a/distribution/packages/src/common/scripts/preinst b/distribution/packages/src/common/scripts/preinst index c13f079666467..31e5b803b1604 100644 --- a/distribution/packages/src/common/scripts/preinst +++ b/distribution/packages/src/common/scripts/preinst @@ -10,6 +10,8 @@ # $1=1 : indicates an new install # $1=2 : indicates an upgrade +set -e -o pipefail + err_exit() { echo "$@" >&2 exit 1 diff --git a/distribution/packages/src/common/systemd/systemd-entrypoint b/distribution/packages/src/common/systemd/systemd-entrypoint index 0773bc6f1dc04..de59b4573f79a 100644 --- a/distribution/packages/src/common/systemd/systemd-entrypoint +++ b/distribution/packages/src/common/systemd/systemd-entrypoint @@ -1,8 +1,10 @@ -#!/bin/sh +#!/usr/bin/env bash # This wrapper script allows SystemD to feed a file containing a passphrase into # the main OpenSearch startup script +set -e -o pipefail + if [ -n "$OPENSEARCH_KEYSTORE_PASSPHRASE_FILE" ] ; then exec /usr/share/opensearch/bin/opensearch "$@" < "$OPENSEARCH_KEYSTORE_PASSPHRASE_FILE" else diff --git a/distribution/packages/src/deb/init.d/opensearch b/distribution/packages/src/deb/init.d/opensearch index e5195d2d54dba..681d87df1d356 100755 --- a/distribution/packages/src/deb/init.d/opensearch +++ b/distribution/packages/src/deb/init.d/opensearch @@ -12,6 +12,8 @@ # Description: Starts opensearch using start-stop-daemon ### END INIT INFO +set -e -o pipefail + PATH=/bin:/usr/bin:/sbin:/usr/sbin NAME=opensearch DESC="OpenSearch Server" diff --git a/distribution/packages/src/deb/lintian/opensearch b/distribution/packages/src/deb/lintian/opensearch index 854b23131ecbc..e6db8e8c6b322 100644 --- a/distribution/packages/src/deb/lintian/opensearch +++ b/distribution/packages/src/deb/lintian/opensearch @@ -15,11 +15,11 @@ missing-dep-on-jarwrapper # we prefer to not make our config and log files world readable non-standard-file-perm etc/default/opensearch 0660 != 0644 -non-standard-dir-perm etc/opensearch/ 2750 != 0755 -non-standard-dir-perm etc/opensearch/jvm.options.d/ 2750 != 0755 +non-standard-dir-perm etc/opensearch/ 0750 != 0755 +non-standard-dir-perm etc/opensearch/jvm.options.d/ 0750 != 0755 non-standard-file-perm etc/opensearch/* -non-standard-dir-perm var/lib/opensearch/ 2750 != 0755 -non-standard-dir-perm var/log/opensearch/ 2750 != 0755 +non-standard-dir-perm var/lib/opensearch/ 0750 != 0755 +non-standard-dir-perm var/log/opensearch/ 0750 != 0755 executable-is-not-world-readable etc/init.d/opensearch 0750 non-standard-file-permissions-for-etc-init.d-script etc/init.d/opensearch 0750 != 0755 diff --git a/distribution/packages/src/rpm/init.d/opensearch b/distribution/packages/src/rpm/init.d/opensearch index 12a1470e75acb..0cb9bf65796ad 100644 --- a/distribution/packages/src/rpm/init.d/opensearch +++ b/distribution/packages/src/rpm/init.d/opensearch @@ -16,6 +16,8 @@ # Description: OpenSearch is a very scalable, schema-free and high-performance search solution supporting multi-tenancy and near realtime search. ### END INIT INFO +set -e -o pipefail + # # init.d / servicectl compatibility (openSUSE) # diff --git a/distribution/src/bin/opensearch b/distribution/src/bin/opensearch index 52087ceb8bca9..947d1167f79f2 100755 --- a/distribution/src/bin/opensearch +++ b/distribution/src/bin/opensearch @@ -13,6 +13,8 @@ # # OPENSEARCH_JAVA_OPTS="-Xms8g -Xmx8g" ./bin/opensearch +set -e -o pipefail + source "`dirname "$0"`"/opensearch-env CHECK_KEYSTORE=true diff --git a/distribution/src/bin/opensearch-cli.bat b/distribution/src/bin/opensearch-cli.bat index 734669e1f9349..f080346a4478a 100644 --- a/distribution/src/bin/opensearch-cli.bat +++ b/distribution/src/bin/opensearch-cli.bat @@ -16,7 +16,7 @@ rem use a small heap size for the CLI tools, and thus the serial collector to rem avoid stealing many CPU cycles; a user can override by setting OPENSEARCH_JAVA_OPTS set OPENSEARCH_JAVA_OPTS=-Xms4m -Xmx64m -XX:+UseSerialGC %OPENSEARCH_JAVA_OPTS% -%JAVA% ^ +"%JAVA%" ^ %OPENSEARCH_JAVA_OPTS% ^ -Dopensearch.path.home="%OPENSEARCH_HOME%" ^ -Dopensearch.path.conf="%OPENSEARCH_PATH_CONF%" ^ diff --git a/distribution/src/bin/opensearch-env b/distribution/src/bin/opensearch-env index 6fe703a73b2de..cd7a0b2b4520b 100644 --- a/distribution/src/bin/opensearch-env +++ b/distribution/src/bin/opensearch-env @@ -28,23 +28,25 @@ while [ -h "$SCRIPT" ] ; do fi done -# determine OpenSearch home; to do this, we strip from the path until we find -# bin, and then strip bin (there is an assumption here that there is no nested -# directory under bin also named bin) -OPENSEARCH_HOME=`dirname "$SCRIPT"` - -# now make OPENSEARCH_HOME absolute -OPENSEARCH_HOME=`cd "$OPENSEARCH_HOME"; pwd` - -while [ "`basename "$OPENSEARCH_HOME"`" != "bin" ]; do +if [[ -z "$OPENSEARCH_HOME" ]]; then + # determine OpenSearch home; to do this, we strip from the path until we find + # bin, and then strip bin (there is an assumption here that there is no nested + # directory under bin also named bin) + OPENSEARCH_HOME=`dirname "$SCRIPT"` + + # now make OPENSEARCH_HOME absolute + OPENSEARCH_HOME=`cd "$OPENSEARCH_HOME"; pwd` + + while [ "`basename "$OPENSEARCH_HOME"`" != "bin" ]; do + OPENSEARCH_HOME=`dirname "$OPENSEARCH_HOME"` + done OPENSEARCH_HOME=`dirname "$OPENSEARCH_HOME"` -done -OPENSEARCH_HOME=`dirname "$OPENSEARCH_HOME"` +fi # now set the classpath OPENSEARCH_CLASSPATH="$OPENSEARCH_HOME/lib/*" -# now set the path to java: OPENSEARCH_JAVA_HOME -> JAVA_HOME -> bundled JDK +# now set the path to java: OPENSEARCH_JAVA_HOME -> JAVA_HOME -> bundled JRE -> bundled JDK if [ ! -z "$OPENSEARCH_JAVA_HOME" ]; then JAVA="$OPENSEARCH_JAVA_HOME/bin/java" JAVA_TYPE="OPENSEARCH_JAVA_HOME" @@ -55,13 +57,18 @@ else if [ $OS = "darwin" ]; then # macOS bundled Java JAVA="$OPENSEARCH_HOME/jdk.app/Contents/Home/bin/java" + JAVA_TYPE="bundled jdk" elif [ $OS = "freebsd" ]; then # using FreeBSD default java from ports if JAVA_HOME is not set JAVA="/usr/local/bin/java" + JAVA_TYPE="bundled jdk" + elif [ -d "$OPENSEARCH_HOME/jre" ]; then + JAVA="$OPENSEARCH_HOME/jre/bin/java" + JAVA_TYPE="bundled jre" else JAVA="$OPENSEARCH_HOME/jdk/bin/java" + JAVA_TYPE="bundled jdk" fi - JAVA_TYPE="bundled jdk" fi if [ ! -x "$JAVA" ]; then diff --git a/distribution/src/bin/opensearch-env-from-file b/distribution/src/bin/opensearch-env-from-file index 73cd11123bfff..be5b428c268c8 100644 --- a/distribution/src/bin/opensearch-env-from-file +++ b/distribution/src/bin/opensearch-env-from-file @@ -47,4 +47,3 @@ for VAR_NAME_FILE in OPENSEARCH_PASSWORD_FILE KEYSTORE_PASSWORD_FILE ; do unset "$VAR_NAME_FILE" fi done - diff --git a/distribution/src/bin/opensearch-env.bat b/distribution/src/bin/opensearch-env.bat index bc8a6ce53a5f5..be9f0411e39f8 100644 --- a/distribution/src/bin/opensearch-env.bat +++ b/distribution/src/bin/opensearch-env.bat @@ -3,6 +3,10 @@ set SCRIPT=%0 rem determine OpenSearch home; to do this, we strip from the path until we rem find bin, and then strip bin (there is an assumption here that there is no rem nested directory under bin also named bin) +if not defined OPENSEARCH_HOME goto opensearch_home_start_setup +goto opensearch_home_done_setup + +:opensearch_home_start_setup for %%I in (%SCRIPT%) do set OPENSEARCH_HOME=%%~dpI :opensearch_home_loop @@ -13,6 +17,7 @@ if not "%DIRNAME%" == "bin" ( ) for %%I in ("%OPENSEARCH_HOME%..") do set OPENSEARCH_HOME=%%~dpfI +:opensearch_home_done_setup rem now set the classpath set OPENSEARCH_CLASSPATH=!OPENSEARCH_HOME!\lib\* @@ -43,14 +48,14 @@ rem comparing to empty string makes this equivalent to bash -v check on env var rem and allows to effectively force use of the bundled jdk when launching OpenSearch rem by setting OPENSEARCH_JAVA_HOME= and JAVA_HOME= if not "%OPENSEARCH_JAVA_HOME%" == "" ( - set JAVA="%OPENSEARCH_JAVA_HOME%\bin\java.exe" + set "JAVA=%OPENSEARCH_JAVA_HOME%\bin\java.exe" set JAVA_TYPE=OPENSEARCH_JAVA_HOME ) else if not "%JAVA_HOME%" == "" ( - set JAVA="%JAVA_HOME%\bin\java.exe" + set "JAVA=%JAVA_HOME%\bin\java.exe" set JAVA_TYPE=JAVA_HOME ) else ( - set JAVA="%OPENSEARCH_HOME%\jdk\bin\java.exe" - set JAVA_HOME="%OPENSEARCH_HOME%\jdk" + set "JAVA=%OPENSEARCH_HOME%\jdk\bin\java.exe" + set "JAVA_HOME=%OPENSEARCH_HOME%\jdk" set JAVA_TYPE=bundled jdk ) @@ -73,5 +78,4 @@ if defined JAVA_OPTS ( ) rem check the Java version -%JAVA% -cp "%OPENSEARCH_CLASSPATH%" "org.opensearch.tools.java_version_checker.JavaVersionChecker" || exit /b 1 - +"%JAVA%" -cp "%OPENSEARCH_CLASSPATH%" "org.opensearch.tools.java_version_checker.JavaVersionChecker" || exit /b 1 diff --git a/distribution/src/bin/opensearch-keystore b/distribution/src/bin/opensearch-keystore index 794d3eb5eba41..9f6cb65feeeeb 100755 --- a/distribution/src/bin/opensearch-keystore +++ b/distribution/src/bin/opensearch-keystore @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -e -o pipefail + OPENSEARCH_MAIN_CLASS=org.opensearch.common.settings.KeyStoreCli \ OPENSEARCH_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/keystore-cli \ "`dirname "$0"`"/opensearch-cli \ diff --git a/distribution/src/bin/opensearch-node b/distribution/src/bin/opensearch-node index 210037e933b22..b8b231cc43d4a 100755 --- a/distribution/src/bin/opensearch-node +++ b/distribution/src/bin/opensearch-node @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -e -o pipefail OPENSEARCH_MAIN_CLASS=org.opensearch.cluster.coordination.NodeToolCli \ "`dirname "$0"`"/opensearch-cli \ diff --git a/distribution/src/bin/opensearch-plugin b/distribution/src/bin/opensearch-plugin index 64d14e611fd04..b58a2695d2ecf 100755 --- a/distribution/src/bin/opensearch-plugin +++ b/distribution/src/bin/opensearch-plugin @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -e -o pipefail + OPENSEARCH_MAIN_CLASS=org.opensearch.plugins.PluginCli \ OPENSEARCH_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/plugin-cli \ "`dirname "$0"`"/opensearch-cli \ diff --git a/distribution/src/bin/opensearch-service.bat b/distribution/src/bin/opensearch-service.bat index 4dd8356340d10..c1f3f264ec4a0 100644 --- a/distribution/src/bin/opensearch-service.bat +++ b/distribution/src/bin/opensearch-service.bat @@ -8,6 +8,10 @@ if /i "%1" == "install" set NOJAVA= call "%~dp0opensearch-env.bat" %NOJAVA% || exit /b 1 +rem opensearch-service-x64.exe is based off of the Apache Commons Daemon procrun service application. +rem Run "opensearch-service-x64.exe version" for version information. +rem Run "opensearch-service-x64.exe help" for command options. +rem See https://commons.apache.org/proper/commons-daemon/procrun.html for more information. set EXECUTABLE=%OPENSEARCH_HOME%\bin\opensearch-service-x64.exe if "%SERVICE_ID%" == "" set SERVICE_ID=opensearch-service-x64 set ARCH=64-bit @@ -20,6 +24,10 @@ exit /B 1 set OPENSEARCH_VERSION=${project.version} if "%SERVICE_LOG_DIR%" == "" set SERVICE_LOG_DIR=%OPENSEARCH_HOME%\logs +rem The logs directory must exist for the service to start. +if not exist "%SERVICE_LOG_DIR%" ( + mkdir "%SERVICE_LOG_DIR%" +) if "x%1x" == "xx" goto displayUsage set SERVICE_CMD=%1 @@ -45,7 +53,8 @@ echo Usage: opensearch-service.bat install^|remove^|start^|stop^|manager [SERVIC goto:eof :doStart -"%EXECUTABLE%" //OPENSEARCH//%SERVICE_ID% %LOG_OPTS% +rem //ES == Execute Service +"%EXECUTABLE%" //ES//%SERVICE_ID% %LOG_OPTS% if not errorlevel 1 goto started echo Failed starting '%SERVICE_ID%' service exit /B 1 @@ -55,6 +64,7 @@ echo The service '%SERVICE_ID%' has been started goto:eof :doStop +rem //SS == Stop Service "%EXECUTABLE%" //SS//%SERVICE_ID% %LOG_OPTS% if not errorlevel 1 goto stopped echo Failed stopping '%SERVICE_ID%' service @@ -65,8 +75,11 @@ echo The service '%SERVICE_ID%' has been stopped goto:eof :doManagment +rem opensearch-service-mgr.exe is based off of the Apache Commons Daemon procrun monitor application. +rem See https://commons.apache.org/proper/commons-daemon/procrun.html for more information. set EXECUTABLE_MGR=%OPENSEARCH_HOME%\bin\opensearch-service-mgr -"%EXECUTABLE_MGR%" //OPENSEARCH//%SERVICE_ID% +rem //ES == Edit Service +"%EXECUTABLE_MGR%" //ES//%SERVICE_ID% if not errorlevel 1 goto managed echo Failed starting service manager for '%SERVICE_ID%' exit /B 1 @@ -77,6 +90,7 @@ goto:eof :doRemove rem Remove the service +rem //DS == Delete Service "%EXECUTABLE%" //DS//%SERVICE_ID% %LOG_OPTS% if not errorlevel 1 goto removed echo Failed removing '%SERVICE_ID%' service @@ -107,7 +121,7 @@ if exist "%JAVA_HOME%\bin\server\jvm.dll" ( :foundJVM if not defined OPENSEARCH_TMPDIR ( - for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.TempDirectory"`) do set OPENSEARCH_TMPDIR=%%a + for /f "tokens=* usebackq" %%a in (`CALL "%JAVA%" -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.TempDirectory"`) do set OPENSEARCH_TMPDIR=%%a ) rem The JVM options parser produces the final JVM options to start @@ -121,7 +135,7 @@ rem - third, JVM options from OPENSEARCH_JAVA_OPTS are applied rem - fourth, ergonomic JVM options are applied @setlocal -for /F "usebackq delims=" %%a in (`CALL %JAVA% -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.JvmOptionsParser" "!OPENSEARCH_PATH_CONF!" ^|^| echo jvm_options_parser_failed`) do set OPENSEARCH_JAVA_OPTS=%%a +for /F "usebackq delims=" %%a in (`CALL "%JAVA%" -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.JvmOptionsParser" "!OPENSEARCH_PATH_CONF!" ^|^| echo jvm_options_parser_failed`) do set OPENSEARCH_JAVA_OPTS=%%a @endlocal & set "MAYBE_JVM_OPTIONS_PARSER_FAILED=%OPENSEARCH_JAVA_OPTS%" & set OPENSEARCH_JAVA_OPTS=%OPENSEARCH_JAVA_OPTS% if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" ( @@ -207,6 +221,7 @@ if not "%SERVICE_USERNAME%" == "" ( set SERVICE_PARAMS=%SERVICE_PARAMS% --ServiceUser "%SERVICE_USERNAME%" --ServicePassword "%SERVICE_PASSWORD%" ) ) +rem //IS == Install Service "%EXECUTABLE%" //IS//%SERVICE_ID% --Startup %OPENSEARCH_START_TYPE% --StopTimeout %OPENSEARCH_STOP_TIMEOUT% --StartClass org.opensearch.bootstrap.OpenSearch --StartMethod main ++StartParams --quiet --StopClass org.opensearch.bootstrap.OpenSearch --StopMethod close --Classpath "%OPENSEARCH_CLASSPATH%" --JvmMs %JVM_MS% --JvmMx %JVM_MX% --JvmSs %JVM_SS% --JvmOptions %OTHER_JAVA_OPTS% ++JvmOptions %OPENSEARCH_PARAMS% %LOG_OPTS% --PidFile "%SERVICE_ID%.pid" --DisplayName "%SERVICE_DISPLAY_NAME%" --Description "%SERVICE_DESCRIPTION%" --Jvm "%JAVA_HOME%%JVM_DLL%" --StartMode jvm --StopMode jvm --StartPath "%OPENSEARCH_HOME%" %SERVICE_PARAMS% ++Environment HOSTNAME="%%COMPUTERNAME%%" if not errorlevel 1 goto installed diff --git a/distribution/src/bin/opensearch-shard b/distribution/src/bin/opensearch-shard index 59511ee68647d..953c792af16e8 100755 --- a/distribution/src/bin/opensearch-shard +++ b/distribution/src/bin/opensearch-shard @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -e -o pipefail + OPENSEARCH_MAIN_CLASS=org.opensearch.index.shard.ShardToolCli \ "`dirname "$0"`"/opensearch-cli \ "$@" diff --git a/distribution/src/bin/opensearch-upgrade b/distribution/src/bin/opensearch-upgrade index 4305fae462726..5aae184341b6b 100755 --- a/distribution/src/bin/opensearch-upgrade +++ b/distribution/src/bin/opensearch-upgrade @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -e -o pipefail OPENSEARCH_MAIN_CLASS=org.opensearch.upgrade.UpgradeCli \ OPENSEARCH_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/upgrade-cli \ diff --git a/distribution/src/bin/opensearch.bat b/distribution/src/bin/opensearch.bat index 49a12aa5c968d..cce21504c55b7 100644 --- a/distribution/src/bin/opensearch.bat +++ b/distribution/src/bin/opensearch.bat @@ -56,6 +56,12 @@ IF ERRORLEVEL 1 ( EXIT /B %ERRORLEVEL% ) +if "%SERVICE_LOG_DIR%" == "" set SERVICE_LOG_DIR=%OPENSEARCH_HOME%\logs +rem The logs directory must exist for the service to start. +if not exist "%SERVICE_LOG_DIR%" ( + mkdir "%SERVICE_LOG_DIR%" +) + SET KEYSTORE_PASSWORD= IF "%checkpassword%"=="Y" ( CALL "%~dp0opensearch-keystore.bat" has-passwd --silent @@ -69,7 +75,7 @@ IF "%checkpassword%"=="Y" ( ) if not defined OPENSEARCH_TMPDIR ( - for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.TempDirectory"`) do set OPENSEARCH_TMPDIR=%%a + for /f "tokens=* usebackq" %%a in (`CALL "%JAVA%" -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.TempDirectory"`) do set OPENSEARCH_TMPDIR=%%a ) rem The JVM options parser produces the final JVM options to start @@ -82,7 +88,7 @@ rem jvm.options.d/*.options rem - third, JVM options from OPENSEARCH_JAVA_OPTS are applied rem - fourth, ergonomic JVM options are applied @setlocal -for /F "usebackq delims=" %%a in (`CALL %JAVA% -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.JvmOptionsParser" "!OPENSEARCH_PATH_CONF!" ^|^| echo jvm_options_parser_failed`) do set OPENSEARCH_JAVA_OPTS=%%a +for /F "usebackq delims=" %%a in (`CALL "%JAVA%" -cp "!OPENSEARCH_CLASSPATH!" "org.opensearch.tools.launchers.JvmOptionsParser" "!OPENSEARCH_PATH_CONF!" ^|^| echo jvm_options_parser_failed`) do set OPENSEARCH_JAVA_OPTS=%%a @endlocal & set "MAYBE_JVM_OPTIONS_PARSER_FAILED=%OPENSEARCH_JAVA_OPTS%" & set OPENSEARCH_JAVA_OPTS=%OPENSEARCH_JAVA_OPTS% if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" ( @@ -97,7 +103,7 @@ SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^<=^^^=^^^>! SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^\=^^^\! -ECHO.!KEYSTORE_PASSWORD!| %JAVA% %OPENSEARCH_JAVA_OPTS% -Dopensearch ^ +ECHO.!KEYSTORE_PASSWORD!| "%JAVA%" %OPENSEARCH_JAVA_OPTS% -Dopensearch ^ -Dopensearch.path.home="%OPENSEARCH_HOME%" -Dopensearch.path.conf="%OPENSEARCH_PATH_CONF%" ^ -Dopensearch.distribution.type="%OPENSEARCH_DISTRIBUTION_TYPE%" ^ -Dopensearch.bundled_jdk="%OPENSEARCH_BUNDLED_JDK%" ^ diff --git a/distribution/src/config/jvm.options b/distribution/src/config/jvm.options index a20baf1be0906..952110c6c0289 100644 --- a/distribution/src/config/jvm.options +++ b/distribution/src/config/jvm.options @@ -33,19 +33,19 @@ ################################################################ ## GC configuration -8-13:-XX:+UseConcMarkSweepGC -8-13:-XX:CMSInitiatingOccupancyFraction=75 -8-13:-XX:+UseCMSInitiatingOccupancyOnly +8-10:-XX:+UseConcMarkSweepGC +8-10:-XX:CMSInitiatingOccupancyFraction=75 +8-10:-XX:+UseCMSInitiatingOccupancyOnly ## G1GC Configuration # NOTE: G1 GC is only supported on JDK version 10 or later # to use G1GC, uncomment the next two lines and update the version on the # following three lines to your version of the JDK -# 10-13:-XX:-UseConcMarkSweepGC -# 10-13:-XX:-UseCMSInitiatingOccupancyOnly -14-:-XX:+UseG1GC -14-:-XX:G1ReservePercent=25 -14-:-XX:InitiatingHeapOccupancyPercent=30 +# 10:-XX:-UseConcMarkSweepGC +# 10:-XX:-UseCMSInitiatingOccupancyOnly +11-:-XX:+UseG1GC +11-:-XX:G1ReservePercent=25 +11-:-XX:InitiatingHeapOccupancyPercent=30 ## JVM temporary directory -Djava.io.tmpdir=${OPENSEARCH_TMPDIR} @@ -78,3 +78,10 @@ ${error.file} # Explicitly allow security manager (https://bugs.openjdk.java.net/browse/JDK-8270380) 18-:-Djava.security.manager=allow + +# JDK 20+ Incubating Vector Module for SIMD optimizations; +# disabling may reduce performance on vector optimized lucene +20:--add-modules=jdk.incubator.vector + +# HDFS ForkJoinPool.common() support by SecurityManager +-Djava.util.concurrent.ForkJoinPool.common.threadFactory=org.opensearch.secure_sm.SecuredForkJoinWorkerThreadFactory diff --git a/distribution/src/config/log4j2.properties b/distribution/src/config/log4j2.properties index 4820396c79eb7..bb27aaf2e22e6 100644 --- a/distribution/src/config/log4j2.properties +++ b/distribution/src/config/log4j2.properties @@ -195,3 +195,40 @@ logger.index_indexing_slowlog.level = trace logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling_old.ref = index_indexing_slowlog_rolling_old logger.index_indexing_slowlog.additivity = false + +######## Task details log JSON #################### +appender.task_detailslog_rolling.type = RollingFile +appender.task_detailslog_rolling.name = task_detailslog_rolling +appender.task_detailslog_rolling.fileName = ${sys:opensearch.logs.base_path}${sys:file.separator}${sys:opensearch.logs.cluster_name}_task_detailslog.json +appender.task_detailslog_rolling.filePermissions = rw-r----- +appender.task_detailslog_rolling.layout.type = OpenSearchJsonLayout +appender.task_detailslog_rolling.layout.type_name = task_detailslog +appender.task_detailslog_rolling.layout.opensearchmessagefields=taskId,type,action,description,start_time_millis,resource_stats,metadata + +appender.task_detailslog_rolling.filePattern = ${sys:opensearch.logs.base_path}${sys:file.separator}${sys:opensearch.logs.cluster_name}_task_detailslog-%i.json.gz +appender.task_detailslog_rolling.policies.type = Policies +appender.task_detailslog_rolling.policies.size.type = SizeBasedTriggeringPolicy +appender.task_detailslog_rolling.policies.size.size = 1GB +appender.task_detailslog_rolling.strategy.type = DefaultRolloverStrategy +appender.task_detailslog_rolling.strategy.max = 4 +################################################# +######## Task details log - old style pattern #### +appender.task_detailslog_rolling_old.type = RollingFile +appender.task_detailslog_rolling_old.name = task_detailslog_rolling_old +appender.task_detailslog_rolling_old.fileName = ${sys:opensearch.logs.base_path}${sys:file.separator}${sys:opensearch.logs.cluster_name}_task_detailslog.log +appender.task_detailslog_rolling_old.filePermissions = rw-r----- +appender.task_detailslog_rolling_old.layout.type = PatternLayout +appender.task_detailslog_rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n + +appender.task_detailslog_rolling_old.filePattern = ${sys:opensearch.logs.base_path}${sys:file.separator}${sys:opensearch.logs.cluster_name}_task_detailslog-%i.log.gz +appender.task_detailslog_rolling_old.policies.type = Policies +appender.task_detailslog_rolling_old.policies.size.type = SizeBasedTriggeringPolicy +appender.task_detailslog_rolling_old.policies.size.size = 1GB +appender.task_detailslog_rolling_old.strategy.type = DefaultRolloverStrategy +appender.task_detailslog_rolling_old.strategy.max = 4 +################################################# +logger.task_detailslog_rolling.name = task.detailslog +logger.task_detailslog_rolling.level = trace +logger.task_detailslog_rolling.appenderRef.task_detailslog_rolling.ref = task_detailslog_rolling +logger.task_detailslog_rolling.appenderRef.task_detailslog_rolling_old.ref = task_detailslog_rolling_old +logger.task_detailslog_rolling.additivity = false diff --git a/distribution/src/config/opensearch.yml b/distribution/src/config/opensearch.yml index 2188fbe600cbf..1d2cfe7eccae6 100644 --- a/distribution/src/config/opensearch.yml +++ b/distribution/src/config/opensearch.yml @@ -86,3 +86,38 @@ ${path.logs} # Require explicit names when deleting indices: # #action.destructive_requires_name: true +# +# ---------------------------------- Remote Store ----------------------------------- +# Controls whether cluster imposes index creation only with remote store enabled +# cluster.remote_store.enabled: true +# +# Repository to use for segment upload while enforcing remote store for an index +# node.attr.remote_store.segment.repository: my-repo-1 +# +# Repository to use for translog upload while enforcing remote store for an index +# node.attr.remote_store.translog.repository: my-repo-1 +# +# ---------------------------------- Experimental Features ----------------------------------- +# Gates the visibility of the experimental segment replication features until they are production ready. +# +#opensearch.experimental.feature.segment_replication_experimental.enabled: false +# +# Gates the functionality of a new parameter to the snapshot restore API +# that allows for creation of a new index type that searches a snapshot +# directly in a remote repository without restoring all index data to disk +# ahead of time. +# +#opensearch.experimental.feature.searchable_snapshot.enabled: false +# +# +# Gates the functionality of enabling extensions to work with OpenSearch. +# This feature enables applications to extend features of OpenSearch outside of +# the core. +# +#opensearch.experimental.feature.extensions.enabled: false +# +# +# Gates the concurrent segment search feature. This feature enables concurrent segment search in a separate +# index searcher threadpool. +# +#opensearch.experimental.feature.concurrent_segment_search.enabled: false diff --git a/distribution/tools/java-version-checker/build.gradle b/distribution/tools/java-version-checker/build.gradle index 9480a86ce6fb7..7fd16b910b293 100644 --- a/distribution/tools/java-version-checker/build.gradle +++ b/distribution/tools/java-version-checker/build.gradle @@ -11,8 +11,10 @@ apply plugin: 'opensearch.build' -sourceCompatibility = JavaVersion.VERSION_11 -targetCompatibility = JavaVersion.VERSION_11 +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} // targetting very old java versions enables a warning by default on newer JDK: disable it. compileJava.options.compilerArgs += '-Xlint:-options' diff --git a/distribution/tools/java-version-checker/src/main/java/org/opensearch/tools/java_version_checker/JavaVersion.java b/distribution/tools/java-version-checker/src/main/java/org/opensearch/tools/java_version_checker/JavaVersion.java deleted file mode 100644 index 7873f29fdff69..0000000000000 --- a/distribution/tools/java-version-checker/src/main/java/org/opensearch/tools/java_version_checker/JavaVersion.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.tools.java_version_checker; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -public class JavaVersion { - - public static final List CURRENT = parse(System.getProperty("java.specification.version")); - public static final List JAVA_8 = parse("1.8"); - public static final List JAVA_11 = parse("11"); - - static List parse(final String value) { - if (!value.matches("^0*[0-9]+(\\.[0-9]+)*$")) { - throw new IllegalArgumentException(value); - } - - final List version = new ArrayList(); - final String[] components = value.split("\\."); - for (final String component : components) { - version.add(Integer.valueOf(component)); - } - return version; - } - - public static int majorVersion(final List javaVersion) { - Objects.requireNonNull(javaVersion); - if (javaVersion.get(0) > 1) { - return javaVersion.get(0); - } else { - return javaVersion.get(1); - } - } - - static int compare(final List left, final List right) { - // lexicographically compare two lists, treating missing entries as zeros - final int len = Math.max(left.size(), right.size()); - for (int i = 0; i < len; i++) { - final int l = (i < left.size()) ? left.get(i) : 0; - final int r = (i < right.size()) ? right.get(i) : 0; - if (l < r) { - return -1; - } - if (r < l) { - return 1; - } - } - return 0; - } - -} diff --git a/distribution/tools/java-version-checker/src/main/java/org/opensearch/tools/java_version_checker/JavaVersionChecker.java b/distribution/tools/java-version-checker/src/main/java/org/opensearch/tools/java_version_checker/JavaVersionChecker.java index e8ff5f3e6f3f2..eb5c7cf1592e7 100644 --- a/distribution/tools/java-version-checker/src/main/java/org/opensearch/tools/java_version_checker/JavaVersionChecker.java +++ b/distribution/tools/java-version-checker/src/main/java/org/opensearch/tools/java_version_checker/JavaVersionChecker.java @@ -32,18 +32,19 @@ package org.opensearch.tools.java_version_checker; +import java.lang.Runtime.Version; import java.util.Arrays; import java.util.Locale; /** - * Simple program that checks if the runtime Java version is at least 1.8. + * Simple program that checks if the runtime Java version is at least 11 */ final class JavaVersionChecker { private JavaVersionChecker() {} /** - * The main entry point. The exit code is 0 if the Java version is at least 1.8, otherwise the exit code is 1. + * The main entry point. The exit code is 0 if the Java version is at least 11, otherwise the exit code is 1. * * @param args the args to the program which are rejected if not empty */ @@ -52,23 +53,15 @@ public static void main(final String[] args) { if (args.length != 0) { throw new IllegalArgumentException("expected zero arguments but was " + Arrays.toString(args)); } - if (JavaVersion.compare(JavaVersion.CURRENT, JavaVersion.JAVA_8) < 0) { + if (Runtime.version().compareTo(Version.parse("11")) < 0) { final String message = String.format( Locale.ROOT, - "the minimum required Java version is 8; your Java version from [%s] does not meet this requirement", + "OpenSearch requires Java 11; your Java version from [%s] does not meet this requirement", System.getProperty("java.home") ); errPrintln(message); exit(1); } - if (JavaVersion.compare(JavaVersion.CURRENT, JavaVersion.JAVA_11) < 0) { - final String message = String.format( - Locale.ROOT, - "future versions of OpenSearch will require Java 11; your Java version from [%s] does not meet this requirement", - System.getProperty("java.home") - ); - errPrintln(message); - } exit(0); } diff --git a/distribution/tools/keystore-cli/build.gradle b/distribution/tools/keystore-cli/build.gradle index 1e7473f787ca0..5dcddf3ef127e 100644 --- a/distribution/tools/keystore-cli/build.gradle +++ b/distribution/tools/keystore-cli/build.gradle @@ -34,6 +34,8 @@ dependencies { compileOnly project(":server") compileOnly project(":libs:opensearch-cli") testImplementation project(":test:framework") - testImplementation 'com.google.jimfs:jimfs:1.2' - testRuntimeOnly 'com.google.guava:guava:31.1-jre' + testImplementation 'com.google.jimfs:jimfs:1.3.0' + testRuntimeOnly("com.google.guava:guava:${versions.guava}") { + transitive = false + } } diff --git a/distribution/tools/keystore-cli/src/main/java/org/opensearch/common/settings/CreateKeyStoreCommand.java b/distribution/tools/keystore-cli/src/main/java/org/opensearch/common/settings/CreateKeyStoreCommand.java index b96dd46236b87..a62473ebea0f7 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/opensearch/common/settings/CreateKeyStoreCommand.java +++ b/distribution/tools/keystore-cli/src/main/java/org/opensearch/common/settings/CreateKeyStoreCommand.java @@ -32,18 +32,19 @@ package org.opensearch.common.settings; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; - import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.opensearch.cli.ExitCodes; import org.opensearch.cli.KeyStoreAwareCommand; import org.opensearch.cli.Terminal; import org.opensearch.cli.UserException; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; + /** * A sub-command for the keystore cli to create a new keystore. */ diff --git a/distribution/tools/keystore-cli/src/main/java/org/opensearch/common/settings/ListKeyStoreCommand.java b/distribution/tools/keystore-cli/src/main/java/org/opensearch/common/settings/ListKeyStoreCommand.java index 32f03ec3822de..379b61efc5d32 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/opensearch/common/settings/ListKeyStoreCommand.java +++ b/distribution/tools/keystore-cli/src/main/java/org/opensearch/common/settings/ListKeyStoreCommand.java @@ -32,14 +32,14 @@ package org.opensearch.common.settings; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import joptsimple.OptionSet; import org.opensearch.cli.Terminal; import org.opensearch.env.Environment; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * A subcommand for the keystore cli to list all settings in the keystore. */ diff --git a/distribution/tools/keystore-cli/src/test/java/org/opensearch/bootstrap/BootstrapTests.java b/distribution/tools/keystore-cli/src/test/java/org/opensearch/bootstrap/BootstrapTests.java index 5b280f210af03..70dc4c58b3643 100644 --- a/distribution/tools/keystore-cli/src/test/java/org/opensearch/bootstrap/BootstrapTests.java +++ b/distribution/tools/keystore-cli/src/test/java/org/opensearch/bootstrap/BootstrapTests.java @@ -31,12 +31,12 @@ package org.opensearch.bootstrap; -import org.opensearch.core.internal.io.IOUtils; import org.opensearch.common.settings.KeyStoreCommandTestCase; import org.opensearch.common.settings.KeyStoreWrapper; import org.opensearch.common.settings.SecureSettings; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; import org.opensearch.test.OpenSearchTestCase; import org.junit.After; diff --git a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/CreateKeyStoreCommandTests.java b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/CreateKeyStoreCommandTests.java index 7d07208de766e..966d7e48cef29 100644 --- a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/CreateKeyStoreCommandTests.java +++ b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/CreateKeyStoreCommandTests.java @@ -32,16 +32,16 @@ package org.opensearch.common.settings; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; - import org.opensearch.cli.Command; import org.opensearch.cli.ExitCodes; import org.opensearch.cli.UserException; import org.opensearch.env.Environment; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + import static org.hamcrest.Matchers.containsString; public class CreateKeyStoreCommandTests extends KeyStoreCommandTestCase { diff --git a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreCommandTestCase.java b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreCommandTestCase.java index aa31e07368fc2..cd982cd53cfab 100644 --- a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreCommandTestCase.java +++ b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreCommandTestCase.java @@ -32,25 +32,25 @@ package org.opensearch.common.settings; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; -import org.opensearch.core.internal.io.IOUtils; import org.apache.lucene.tests.util.LuceneTestCase; import org.opensearch.cli.CommandTestCase; import org.opensearch.common.io.PathUtilsForTesting; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.junit.After; import org.junit.Before; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + /** * Base test case for manipulating the OpenSearch keystore. */ diff --git a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreWrapperTests.java b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreWrapperTests.java index 2688e7637c9ba..ef8a9e54078c4 100644 --- a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreWrapperTests.java +++ b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/KeyStoreWrapperTests.java @@ -39,7 +39,8 @@ import org.apache.lucene.store.IndexOutput; import org.apache.lucene.store.NIOFSDirectory; import org.opensearch.common.Randomness; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.Matchers; diff --git a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/ListKeyStoreCommandTests.java b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/ListKeyStoreCommandTests.java index 62d27c4010849..42452d5c12beb 100644 --- a/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/ListKeyStoreCommandTests.java +++ b/distribution/tools/keystore-cli/src/test/java/org/opensearch/common/settings/ListKeyStoreCommandTests.java @@ -32,13 +32,13 @@ package org.opensearch.common.settings; -import java.util.Map; - import org.opensearch.cli.Command; import org.opensearch.cli.ExitCodes; import org.opensearch.cli.UserException; import org.opensearch.env.Environment; +import java.util.Map; + import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; diff --git a/distribution/tools/launchers/build.gradle b/distribution/tools/launchers/build.gradle index 52100296ac7e6..7028757cdc402 100644 --- a/distribution/tools/launchers/build.gradle +++ b/distribution/tools/launchers/build.gradle @@ -38,7 +38,9 @@ dependencies { testImplementation "org.hamcrest:hamcrest:${versions.hamcrest}" } -archivesBaseName = 'opensearch-launchers' +base { + archivesBaseName = 'opensearch-launchers' +} tasks.withType(CheckForbiddenApis).configureEach { replaceSignatureFiles 'jdk-signatures' diff --git a/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/JvmErgonomics.java b/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/JvmErgonomics.java index 053d892d0ec2f..cd4bea689f776 100644 --- a/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/JvmErgonomics.java +++ b/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/JvmErgonomics.java @@ -32,8 +32,6 @@ package org.opensearch.tools.launchers; -import org.opensearch.tools.java_version_checker.JavaVersion; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -73,19 +71,7 @@ static List choose(final List userDefinedJvmOptions) throws Inte final long heapSize = extractHeapSize(finalJvmOptions); final long maxDirectMemorySize = extractMaxDirectMemorySize(finalJvmOptions); - if (System.getProperty("os.name").startsWith("Windows") && JavaVersion.majorVersion(JavaVersion.CURRENT) == 8) { - Launchers.errPrintln("Warning: with JDK 8 on Windows, OpenSearch may be unable to derive correct"); - Launchers.errPrintln(" ergonomic settings due to a JDK issue (JDK-8074459). Please use a newer"); - Launchers.errPrintln(" version of Java."); - } - if (maxDirectMemorySize == 0 && userDefinedJvmOptions.stream().noneMatch(s -> s.startsWith("-XX:MaxDirectMemorySize"))) { - - if (System.getProperty("os.name").startsWith("Windows") && JavaVersion.majorVersion(JavaVersion.CURRENT) == 8) { - Launchers.errPrintln("Warning: MaxDirectMemorySize may have been miscalculated due to JDK-8074459."); - Launchers.errPrintln(" Please use a newer version of Java or set MaxDirectMemorySize explicitly."); - } - ergonomicChoices.add("-XX:MaxDirectMemorySize=" + heapSize / 2); } return ergonomicChoices; diff --git a/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/JvmOptionsParser.java b/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/JvmOptionsParser.java index 7703efdc56986..533d1f7e782ba 100644 --- a/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/JvmOptionsParser.java +++ b/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/JvmOptionsParser.java @@ -32,8 +32,6 @@ package org.opensearch.tools.launchers; -import org.opensearch.tools.java_version_checker.JavaVersion; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -183,7 +181,7 @@ List readJvmOptionsFiles(final Path config) throws IOException, JvmOptio Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(reader) ) { - parse(JavaVersion.majorVersion(JavaVersion.CURRENT), br, jvmOptions::add, invalidLines::put); + parse(Runtime.version().feature(), br, jvmOptions::add, invalidLines::put); } if (invalidLines.isEmpty() == false) { throw new JvmOptionsFileParserException(jvmOptionsFile, invalidLines); diff --git a/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java b/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java index 738d57951c4ef..fc613ccdaae68 100644 --- a/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java +++ b/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java @@ -32,8 +32,6 @@ package org.opensearch.tools.launchers; -import org.opensearch.tools.java_version_checker.JavaVersion; - import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -86,7 +84,7 @@ static List systemJvmOptions() { } private static String maybeShowCodeDetailsInExceptionMessages() { - if (JavaVersion.majorVersion(JavaVersion.CURRENT) >= 14) { + if (Runtime.version().feature() >= 14) { return "-XX:+ShowCodeDetailsInExceptionMessages"; } else { return ""; @@ -101,14 +99,10 @@ private static String javaLocaleProviders() { * * Due to internationalization enhancements in JDK 9 OpenSearch need to set the provider to COMPAT otherwise time/date * parsing will break in an incompatible way for some date patterns and locales. - * //TODO COMPAT will be deprecated in jdk14 https://bugs.openjdk.java.net/browse/JDK-8232906 + * //TODO COMPAT will be deprecated in at some point, see please https://bugs.openjdk.java.net/browse/JDK-8232906 * See also: documentation in server/org.opensearch.common.time.IsoCalendarDataProvider */ - if (JavaVersion.majorVersion(JavaVersion.CURRENT) == 8) { - return "-Djava.locale.providers=SPI,JRE"; - } else { - return "-Djava.locale.providers=SPI,COMPAT"; - } + return "-Djava.locale.providers=SPI,COMPAT"; } } diff --git a/distribution/tools/launchers/src/test/java/org/opensearch/tools/launchers/JvmErgonomicsTests.java b/distribution/tools/launchers/src/test/java/org/opensearch/tools/launchers/JvmErgonomicsTests.java index ffdf2c2898032..5a8c9841aa0fe 100644 --- a/distribution/tools/launchers/src/test/java/org/opensearch/tools/launchers/JvmErgonomicsTests.java +++ b/distribution/tools/launchers/src/test/java/org/opensearch/tools/launchers/JvmErgonomicsTests.java @@ -32,8 +32,6 @@ package org.opensearch.tools.launchers; -import org.opensearch.tools.java_version_checker.JavaVersion; - import java.io.IOException; import java.util.Arrays; import java.util.Collections; @@ -69,8 +67,6 @@ public void testExtractValidHeapSizeUsingMaxHeapSize() throws InterruptedExcepti } public void testExtractValidHeapSizeNoOptionPresent() throws InterruptedException, IOException { - // Muted for jdk8/Windows, see: https://github.com/elastic/elasticsearch/issues/47384 - assumeFalse(System.getProperty("os.name").startsWith("Windows") && JavaVersion.majorVersion(JavaVersion.CURRENT) == 8); assertThat(JvmErgonomics.extractHeapSize(JvmErgonomics.finalJvmOptions(Collections.emptyList())), greaterThan(0L)); } @@ -141,8 +137,6 @@ public void testExtractNoSystemProperties() { } public void testMaxDirectMemorySizeChoice() throws InterruptedException, IOException { - // Muted for jdk8/Windows, see: https://github.com/elastic/elasticsearch/issues/47384 - assumeFalse(System.getProperty("os.name").startsWith("Windows") && JavaVersion.majorVersion(JavaVersion.CURRENT) == 8); final Map heapMaxDirectMemorySize = new HashMap<>(); heapMaxDirectMemorySize.put("64M", Long.toString((64L << 20) / 2)); heapMaxDirectMemorySize.put("512M", Long.toString((512L << 20) / 2)); diff --git a/distribution/tools/plugin-cli/build.gradle b/distribution/tools/plugin-cli/build.gradle index b2e81491da6bd..2db3fef55d02e 100644 --- a/distribution/tools/plugin-cli/build.gradle +++ b/distribution/tools/plugin-cli/build.gradle @@ -30,16 +30,22 @@ apply plugin: 'opensearch.build' -archivesBaseName = 'opensearch-plugin-cli' +base { + archivesBaseName = 'opensearch-plugin-cli' +} dependencies { compileOnly project(":server") compileOnly project(":libs:opensearch-cli") - api "org.bouncycastle:bcpg-fips:1.0.5.1" + api "org.bouncycastle:bcpg-fips:1.0.7.1" api "org.bouncycastle:bc-fips:1.0.2.3" testImplementation project(":test:framework") - testImplementation 'com.google.jimfs:jimfs:1.2' - testRuntimeOnly 'com.google.guava:guava:31.1-jre' + testImplementation 'com.google.jimfs:jimfs:1.3.0' + testRuntimeOnly("com.google.guava:guava:${versions.guava}") { + transitive = false + } + + implementation "org.apache.commons:commons-compress:${versions.commonscompress}" } tasks.named("dependencyLicenses").configure { @@ -78,3 +84,25 @@ thirdPartyAudit.ignoreViolations( 'org.bouncycastle.jcajce.provider.ProvSunTLSKDF$TLSExtendedMasterSecretGenerator', 'org.bouncycastle.jcajce.provider.ProvSunTLSKDF$TLSExtendedMasterSecretGenerator$2' ) + +thirdPartyAudit.ignoreMissingClasses( + 'org.brotli.dec.BrotliInputStream', + 'org.objectweb.asm.AnnotationVisitor', + 'org.objectweb.asm.Attribute', + 'org.objectweb.asm.ClassReader', + 'org.objectweb.asm.ClassVisitor', + 'org.objectweb.asm.FieldVisitor', + 'org.objectweb.asm.Label', + 'org.objectweb.asm.MethodVisitor', + 'org.objectweb.asm.Type', + 'org.tukaani.xz.DeltaOptions', + 'org.tukaani.xz.FilterOptions', + 'org.tukaani.xz.LZMA2InputStream', + 'org.tukaani.xz.LZMA2Options', + 'org.tukaani.xz.LZMAInputStream', + 'org.tukaani.xz.LZMAOutputStream', + 'org.tukaani.xz.MemoryLimitException', + 'org.tukaani.xz.UnsupportedOptionsException', + 'org.tukaani.xz.XZ', + 'org.tukaani.xz.XZOutputStream' +) diff --git a/distribution/tools/plugin-cli/licenses/bcpg-fips-1.0.5.1.jar.sha1 b/distribution/tools/plugin-cli/licenses/bcpg-fips-1.0.5.1.jar.sha1 deleted file mode 100644 index 30c30bb4af8e0..0000000000000 --- a/distribution/tools/plugin-cli/licenses/bcpg-fips-1.0.5.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -63a454936d930fadb1c7a3206b8e758378dd0a26 \ No newline at end of file diff --git a/distribution/tools/plugin-cli/licenses/bcpg-fips-1.0.7.1.jar.sha1 b/distribution/tools/plugin-cli/licenses/bcpg-fips-1.0.7.1.jar.sha1 new file mode 100644 index 0000000000000..44cebc7c92d87 --- /dev/null +++ b/distribution/tools/plugin-cli/licenses/bcpg-fips-1.0.7.1.jar.sha1 @@ -0,0 +1 @@ +5e1952428655ea822066f86df2e3ecda8fa0ba2b \ No newline at end of file diff --git a/distribution/tools/plugin-cli/licenses/commons-compress-1.24.0.jar.sha1 b/distribution/tools/plugin-cli/licenses/commons-compress-1.24.0.jar.sha1 new file mode 100644 index 0000000000000..23999d1bfbde4 --- /dev/null +++ b/distribution/tools/plugin-cli/licenses/commons-compress-1.24.0.jar.sha1 @@ -0,0 +1 @@ +b4b1b5a3d9573b2970fddab236102c0a4d27d35e \ No newline at end of file diff --git a/distribution/tools/plugin-cli/licenses/commons-compress-LICENSE.txt b/distribution/tools/plugin-cli/licenses/commons-compress-LICENSE.txt new file mode 100644 index 0000000000000..261eeb9e9f8b2 --- /dev/null +++ b/distribution/tools/plugin-cli/licenses/commons-compress-LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/distribution/tools/plugin-cli/licenses/commons-compress-NOTICE.txt b/distribution/tools/plugin-cli/licenses/commons-compress-NOTICE.txt new file mode 100644 index 0000000000000..3dcbcdd6d36ee --- /dev/null +++ b/distribution/tools/plugin-cli/licenses/commons-compress-NOTICE.txt @@ -0,0 +1,55 @@ +Apache Commons Compress +Copyright 2002-2023 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/). + +--- + +The files in the package org.apache.commons.compress.archivers.sevenz +were derived from the LZMA SDK, version 9.20 (C/ and CPP/7zip/), +which has been placed in the public domain: + +"LZMA SDK is placed in the public domain." (http://www.7-zip.org/sdk.html) + +--- + +The test file lbzip2_32767.bz2 has been copied from libbzip2's source +repository: + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2019 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, jseward@acm.org diff --git a/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/InstallPluginCommand.java index 8acf137043a92..c222b82dfa480 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/InstallPluginCommand.java @@ -34,6 +34,8 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; import org.apache.lucene.search.spell.LevenshteinDistance; import org.apache.lucene.util.CollectionUtil; import org.apache.lucene.util.Constants; @@ -58,7 +60,7 @@ import org.opensearch.common.SuppressForbidden; import org.opensearch.common.collect.Tuple; import org.opensearch.common.hash.MessageDigests; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.env.Environment; import java.io.BufferedReader; @@ -89,6 +91,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -99,8 +102,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; import static org.opensearch.cli.Terminal.Verbosity.VERBOSE; @@ -374,7 +375,12 @@ private String getOpenSearchUrl( stagingHash ); } else { - baseUrl = String.format(Locale.ROOT, "https://artifacts.opensearch.org/releases/plugins/%s/%s", pluginId, version); + baseUrl = String.format( + Locale.ROOT, + "https://artifacts.opensearch.org/releases/plugins/%s/%s", + pluginId, + Build.CURRENT.getQualifiedVersion() + ); } final String platformUrl = String.format( Locale.ROOT, @@ -711,10 +717,12 @@ private Path unzip(Path zip, Path pluginsDir) throws IOException, UserException final Path target = stagingDirectory(pluginsDir); pathsToDeleteOnShutdown.add(target); - try (ZipInputStream zipInput = new ZipInputStream(Files.newInputStream(zip))) { - ZipEntry entry; + try (ZipFile zipFile = new ZipFile(zip, "UTF8", true, false)) { + final Enumeration entries = zipFile.getEntries(); + ZipArchiveEntry entry; byte[] buffer = new byte[8192]; - while ((entry = zipInput.getNextEntry()) != null) { + while (entries.hasMoreElements()) { + entry = entries.nextElement(); if (entry.getName().startsWith("opensearch/")) { throw new UserException( PLUGIN_MALFORMED, @@ -742,14 +750,11 @@ private Path unzip(Path zip, Path pluginsDir) throws IOException, UserException Files.createDirectories(targetFile.getParent()); } if (entry.isDirectory() == false) { - try (OutputStream out = Files.newOutputStream(targetFile)) { - int len; - while ((len = zipInput.read(buffer)) >= 0) { - out.write(buffer, 0, len); - } + // streams will be auto-closed with try-with-resources + try (OutputStream out = Files.newOutputStream(targetFile); InputStream input = zipFile.getInputStream(entry)) { + input.transferTo(out); } } - zipInput.closeEntry(); } } catch (UserException e) { IOUtils.rm(target); diff --git a/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/PluginCli.java b/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/PluginCli.java index fc93068ce416b..38647170cc610 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/PluginCli.java +++ b/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/PluginCli.java @@ -32,10 +32,10 @@ package org.opensearch.plugins; -import org.opensearch.core.internal.io.IOUtils; import org.opensearch.cli.Command; import org.opensearch.cli.LoggingAwareMultiCommand; import org.opensearch.cli.Terminal; +import org.opensearch.common.util.io.IOUtils; import java.io.IOException; import java.util.Collection; diff --git a/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/RemovePluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/RemovePluginCommand.java index fb567e6609ba9..5c3bfbd0d81a8 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/RemovePluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/opensearch/plugins/RemovePluginCommand.java @@ -38,7 +38,7 @@ import org.opensearch.cli.ExitCodes; import org.opensearch.cli.Terminal; import org.opensearch.cli.UserException; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.env.Environment; import java.io.IOException; diff --git a/distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/InstallPluginCommandTests.java index c1b4568759f4d..4cd3a84aac136 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/InstallPluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/InstallPluginCommandTests.java @@ -33,6 +33,7 @@ package org.opensearch.plugins; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import org.apache.lucene.tests.util.LuceneTestCase; @@ -63,10 +64,10 @@ import org.opensearch.common.SuppressForbidden; import org.opensearch.common.collect.Tuple; import org.opensearch.common.hash.MessageDigests; -import org.opensearch.common.io.FileSystemUtils; import org.opensearch.common.io.PathUtils; import org.opensearch.common.io.PathUtilsForTesting; import org.opensearch.common.settings.Settings; +import org.opensearch.core.util.FileSystemUtils; import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.test.OpenSearchTestCase; @@ -1049,7 +1050,7 @@ public void assertInstallPluginFromUrl( public void testOfficialPlugin() throws Exception { String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/analysis-icu-" + Build.CURRENT.getQualifiedVersion() + ".zip"; @@ -1093,7 +1094,7 @@ public void testOfficialPluginStaging() throws Exception { public void testOfficialPlatformPlugin() throws Exception { String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/analysis-icu-" + Platforms.PLATFORM_NAME + "-" @@ -1159,7 +1160,7 @@ public void testMavenChecksumWithoutFilename() throws Exception { public void testOfficialChecksumWithoutFilename() throws Exception { String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/analysis-icu-" + Build.CURRENT.getQualifiedVersion() + ".zip"; @@ -1184,7 +1185,7 @@ public void testOfficialChecksumWithoutFilename() throws Exception { public void testOfficialShaMissing() throws Exception { String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/analysis-icu-" + Build.CURRENT.getQualifiedVersion() + ".zip"; @@ -1229,7 +1230,7 @@ public void testMavenShaMissing() throws Exception { public void testInvalidShaFileMissingFilename() throws Exception { String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/analysis-icu-" + Build.CURRENT.getQualifiedVersion() + ".zip"; @@ -1254,7 +1255,7 @@ public void testInvalidShaFileMissingFilename() throws Exception { public void testInvalidShaFileMismatchFilename() throws Exception { String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/analysis-icu-" + Build.CURRENT.getQualifiedVersion() + ".zip"; @@ -1279,7 +1280,7 @@ public void testInvalidShaFileMismatchFilename() throws Exception { public void testInvalidShaFileContainingExtraLine() throws Exception { String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/analysis-icu-" + Build.CURRENT.getQualifiedVersion() + ".zip"; @@ -1304,7 +1305,7 @@ public void testInvalidShaFileContainingExtraLine() throws Exception { public void testSha512Mismatch() throws Exception { String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/analysis-icu-" + Build.CURRENT.getQualifiedVersion() + ".zip"; @@ -1349,7 +1350,7 @@ public void testSha1Mismatch() throws Exception { public void testPublicKeyIdMismatchToExpectedPublicKeyId() throws Exception { final String icu = "analysis-icu"; final String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/" + icu + "-" @@ -1386,7 +1387,7 @@ public void testPublicKeyIdMismatchToExpectedPublicKeyId() throws Exception { public void testFailedSignatureVerification() throws Exception { final String icu = "analysis-icu"; final String url = "https://artifacts.opensearch.org/releases/plugins/analysis-icu/" - + Version.CURRENT + + Build.CURRENT.getQualifiedVersion() + "/" + icu + "-" diff --git a/distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/ListPluginsCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/ListPluginsCommandTests.java index 376e470159731..9162e32937364 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/ListPluginsCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/opensearch/plugins/ListPluginsCommandTests.java @@ -32,14 +32,6 @@ package org.opensearch.plugins; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Collectors; - import org.apache.lucene.tests.util.LuceneTestCase; import org.opensearch.LegacyESVersion; import org.opensearch.Version; @@ -52,6 +44,14 @@ import org.opensearch.test.OpenSearchTestCase; import org.junit.Before; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + @LuceneTestCase.SuppressFileSystems("*") public class ListPluginsCommandTests extends OpenSearchTestCase { diff --git a/distribution/tools/upgrade-cli/build.gradle b/distribution/tools/upgrade-cli/build.gradle index 0e1996f3d68fa..c7bf0ff1d2810 100644 --- a/distribution/tools/upgrade-cli/build.gradle +++ b/distribution/tools/upgrade-cli/build.gradle @@ -9,17 +9,21 @@ apply plugin: 'opensearch.build' -archivesBaseName = 'opensearch-upgrade-cli' +base { + archivesBaseName = 'opensearch-upgrade-cli' +} dependencies { compileOnly project(":server") compileOnly project(":libs:opensearch-cli") implementation "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" - implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + implementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}" implementation "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" testImplementation project(":test:framework") testImplementation 'com.google.jimfs:jimfs:1.2' - testRuntimeOnly 'com.google.guava:guava:31.1-jre' + testRuntimeOnly("com.google.guava:guava:${versions.guava}") { + transitive = false + } } tasks.named("dependencyLicenses").configure { diff --git a/distribution/tools/upgrade-cli/licenses/jackson-annotations-2.13.2.jar.sha1 b/distribution/tools/upgrade-cli/licenses/jackson-annotations-2.13.2.jar.sha1 deleted file mode 100644 index ecd3fb49d5b12..0000000000000 --- a/distribution/tools/upgrade-cli/licenses/jackson-annotations-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -ec18851f1976d5b810ae1a5fcc32520d2d38f77a \ No newline at end of file diff --git a/distribution/tools/upgrade-cli/licenses/jackson-annotations-2.15.2.jar.sha1 b/distribution/tools/upgrade-cli/licenses/jackson-annotations-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f63416ddb8ceb --- /dev/null +++ b/distribution/tools/upgrade-cli/licenses/jackson-annotations-2.15.2.jar.sha1 @@ -0,0 +1 @@ +4724a65ac8e8d156a24898d50fd5dbd3642870b8 \ No newline at end of file diff --git a/distribution/tools/upgrade-cli/licenses/jackson-databind-2.13.2.jar.sha1 b/distribution/tools/upgrade-cli/licenses/jackson-databind-2.13.2.jar.sha1 deleted file mode 100644 index 5d356f3fd045f..0000000000000 --- a/distribution/tools/upgrade-cli/licenses/jackson-databind-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -926e48c451166a291f1ce6c6276d9abbefa7c00f \ No newline at end of file diff --git a/distribution/tools/upgrade-cli/licenses/jackson-databind-2.15.2.jar.sha1 b/distribution/tools/upgrade-cli/licenses/jackson-databind-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f16d80af8dce6 --- /dev/null +++ b/distribution/tools/upgrade-cli/licenses/jackson-databind-2.15.2.jar.sha1 @@ -0,0 +1 @@ +9353b021f10c307c00328f52090de2bdb4b6ff9c \ No newline at end of file diff --git a/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/DetectEsInstallationTask.java b/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/DetectEsInstallationTask.java index 24f4b79d12528..90067ffd221bf 100644 --- a/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/DetectEsInstallationTask.java +++ b/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/DetectEsInstallationTask.java @@ -9,6 +9,7 @@ package org.opensearch.upgrade; import com.fasterxml.jackson.databind.ObjectMapper; + import org.opensearch.Version; import org.opensearch.cli.Terminal; import org.opensearch.common.SuppressForbidden; diff --git a/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/ImportKeystoreTask.java b/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/ImportKeystoreTask.java index c5d578dfad4df..5d35a7fcc0463 100644 --- a/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/ImportKeystoreTask.java +++ b/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/ImportKeystoreTask.java @@ -12,7 +12,7 @@ import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.KeyStoreWrapper; import org.opensearch.common.settings.KeystoreWrapperUtil; -import org.opensearch.common.settings.SecureString; +import org.opensearch.core.common.settings.SecureString; import java.io.InputStream; diff --git a/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/ImportYmlConfigTask.java b/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/ImportYmlConfigTask.java index 80b8db12b6ec5..6e29bd2d04239 100644 --- a/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/ImportYmlConfigTask.java +++ b/distribution/tools/upgrade-cli/src/main/java/org/opensearch/upgrade/ImportYmlConfigTask.java @@ -12,9 +12,9 @@ import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.yaml.YamlXContent; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.io.OutputStream; diff --git a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/DetectEsInstallationTaskTests.java b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/DetectEsInstallationTaskTests.java index 1038e6d4a484f..a1391ba70a8e8 100644 --- a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/DetectEsInstallationTaskTests.java +++ b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/DetectEsInstallationTaskTests.java @@ -8,7 +8,6 @@ package org.opensearch.upgrade; -import org.junit.Before; import org.opensearch.cli.MockTerminal; import org.opensearch.cli.Terminal; import org.opensearch.common.SuppressForbidden; @@ -17,6 +16,7 @@ import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; import java.io.File; import java.nio.file.Path; diff --git a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ImportLog4jPropertiesTaskTests.java b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ImportLog4jPropertiesTaskTests.java index 7f67e08c66b9e..529253c9ce824 100644 --- a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ImportLog4jPropertiesTaskTests.java +++ b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ImportLog4jPropertiesTaskTests.java @@ -10,7 +10,6 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; -import org.junit.Before; import org.opensearch.cli.MockTerminal; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.collect.Tuple; @@ -19,6 +18,7 @@ import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; import java.io.File; import java.io.IOException; @@ -67,7 +67,7 @@ public void testImportLog4jPropertiesTask() throws IOException { Properties properties = new Properties(); properties.load(Files.newInputStream(taskInput.getOpenSearchConfig().resolve(ImportLog4jPropertiesTask.LOG4J_PROPERTIES))); assertThat(properties, is(notNullValue())); - assertThat(properties.entrySet(), hasSize(137)); + assertThat(properties.entrySet(), hasSize(165)); assertThat(properties.get("appender.rolling.layout.type"), equalTo("OpenSearchJsonLayout")); assertThat( properties.get("appender.deprecation_rolling.fileName"), diff --git a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ImportYmlConfigTaskTests.java b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ImportYmlConfigTaskTests.java index f328cc21685ad..be03470b201a1 100644 --- a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ImportYmlConfigTaskTests.java +++ b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ImportYmlConfigTaskTests.java @@ -10,7 +10,6 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; -import org.junit.Before; import org.opensearch.cli.MockTerminal; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.collect.Tuple; @@ -19,6 +18,7 @@ import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; import java.io.File; import java.io.IOException; diff --git a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/InstallPluginsTaskTests.java b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/InstallPluginsTaskTests.java index 6cb6f0b7cf116..46e189a4765d0 100644 --- a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/InstallPluginsTaskTests.java +++ b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/InstallPluginsTaskTests.java @@ -8,18 +8,19 @@ package org.opensearch.upgrade; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; -import org.mockito.Mockito; import org.opensearch.cli.MockTerminal; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.mockito.Mockito; import static org.hamcrest.Matchers.containsString; import static org.mockito.Mockito.spy; diff --git a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/UpgradeCliTests.java b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/UpgradeCliTests.java index 3db782925a660..837ce6c1e3010 100644 --- a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/UpgradeCliTests.java +++ b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/UpgradeCliTests.java @@ -10,17 +10,17 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; -import org.junit.After; -import org.junit.Before; import org.opensearch.cli.Command; import org.opensearch.cli.CommandTestCase; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.io.PathUtilsForTesting; import org.opensearch.common.settings.KeyStoreWrapper; import org.opensearch.common.settings.Settings; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; +import org.junit.After; +import org.junit.Before; import java.io.File; import java.io.IOException; diff --git a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ValidateInputTaskTests.java b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ValidateInputTaskTests.java index f72e49d5961bf..fde6e5a995761 100644 --- a/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ValidateInputTaskTests.java +++ b/distribution/tools/upgrade-cli/src/test/java/org/opensearch/upgrade/ValidateInputTaskTests.java @@ -8,7 +8,6 @@ package org.opensearch.upgrade; -import org.junit.Before; import org.opensearch.LegacyESVersion; import org.opensearch.cli.MockTerminal; import org.opensearch.common.collect.Tuple; @@ -17,6 +16,7 @@ import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; import java.util.Arrays; import java.util.Map; diff --git a/distribution/tools/upgrade-cli/src/test/resources/config/log4j2.properties b/distribution/tools/upgrade-cli/src/test/resources/config/log4j2.properties index b9ad71121165a..4b92d3fc62376 100644 --- a/distribution/tools/upgrade-cli/src/test/resources/config/log4j2.properties +++ b/distribution/tools/upgrade-cli/src/test/resources/config/log4j2.properties @@ -176,3 +176,38 @@ logger.index_indexing_slowlog.level = trace logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling_old.ref = index_indexing_slowlog_rolling_old logger.index_indexing_slowlog.additivity = false + +######## Task details log JSON #################### +appender.task_detailslog_rolling.type = RollingFile +appender.task_detailslog_rolling.name = task_detailslog_rolling +appender.task_detailslog_rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_task_detailslog.json +appender.task_detailslog_rolling.layout.type = ESJsonLayout +appender.task_detailslog_rolling.layout.type_name = task_detailslog +appender.task_detailslog_rolling.layout.esmessagefields=taskId,type,action,description,start_time_millis,resource_stats,metadata + +appender.task_detailslog_rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_task_detailslog-%i.json.gz +appender.task_detailslog_rolling.policies.type = Policies +appender.task_detailslog_rolling.policies.size.type = SizeBasedTriggeringPolicy +appender.task_detailslog_rolling.policies.size.size = 1GB +appender.task_detailslog_rolling.strategy.type = DefaultRolloverStrategy +appender.task_detailslog_rolling.strategy.max = 4 +################################################# +######## Task details log - old style pattern #### +appender.task_detailslog_rolling_old.type = RollingFile +appender.task_detailslog_rolling_old.name = task_detailslog_rolling_old +appender.task_detailslog_rolling_old.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_task_detailslog.log +appender.task_detailslog_rolling_old.layout.type = PatternLayout +appender.task_detailslog_rolling_old.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n + +appender.task_detailslog_rolling_old.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}_task_detailslog-%i.log.gz +appender.task_detailslog_rolling_old.policies.type = Policies +appender.task_detailslog_rolling_old.policies.size.type = SizeBasedTriggeringPolicy +appender.task_detailslog_rolling_old.policies.size.size = 1GB +appender.task_detailslog_rolling_old.strategy.type = DefaultRolloverStrategy +appender.task_detailslog_rolling_old.strategy.max = 4 +################################################# +logger.task_detailslog_rolling.name = task.detailslog +logger.task_detailslog_rolling.level = trace +logger.task_detailslog_rolling.appenderRef.task_detailslog_rolling.ref = task_detailslog_rolling +logger.task_detailslog_rolling.appenderRef.task_detailslog_rolling_old.ref = task_detailslog_rolling_old +logger.task_detailslog_rolling.additivity = false diff --git a/doc-tools/build.gradle b/doc-tools/build.gradle index 98b2149cb59a9..e6ace21420dda 100644 --- a/doc-tools/build.gradle +++ b/doc-tools/build.gradle @@ -2,10 +2,11 @@ plugins { id 'java' } -group 'org.opensearch' -version '1.0.0-SNAPSHOT' +base { + group 'org.opensearch' + version '1.0.0-SNAPSHOT' +} repositories { mavenCentral() } - diff --git a/doc-tools/missing-doclet/build.gradle b/doc-tools/missing-doclet/build.gradle index e16900afce876..114ccc948951a 100644 --- a/doc-tools/missing-doclet/build.gradle +++ b/doc-tools/missing-doclet/build.gradle @@ -5,10 +5,6 @@ plugins { group 'org.opensearch' version '1.0.0-SNAPSHOT' -repositories { - mavenCentral() -} - tasks.withType(JavaCompile) { options.compilerArgs += ["--release", targetCompatibility.toString()] options.encoding = "UTF-8" diff --git a/doc-tools/missing-doclet/src/main/java/org/opensearch/missingdoclet/MissingDoclet.java b/doc-tools/missing-doclet/src/main/java/org/opensearch/missingdoclet/MissingDoclet.java index 021fc04a42e86..e6122e7baf91a 100644 --- a/doc-tools/missing-doclet/src/main/java/org/opensearch/missingdoclet/MissingDoclet.java +++ b/doc-tools/missing-doclet/src/main/java/org/opensearch/missingdoclet/MissingDoclet.java @@ -13,6 +13,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -291,6 +292,22 @@ private void checkComment(Element element) { if (ignored.contains(element.toString())) { return; } + // Ignore classes annotated with @Generated and all enclosed elements in them. + if (isGenerated(element)) { + return; + } + Element enclosing = element.getEnclosingElement(); + if (enclosing != null && isGenerated(enclosing)) { + return; + } + // If a package contains only generated classes, ignore the package as well. + if (element.getKind() == ElementKind.PACKAGE) { + List enclosedElements = element.getEnclosedElements(); + Optional elm = enclosedElements.stream().findFirst().filter(e -> ((e.getKind() != ElementKind.CLASS) || !isGenerated(e))); + if (elm.isEmpty()) { + return; + } + } var tree = docTrees.getDocCommentTree(element); if (tree == null || tree.getFirstSentence().isEmpty()) { // Check for methods that override other stuff and perhaps inherit their Javadocs. @@ -313,6 +330,17 @@ private void checkComment(Element element) { } } + // Ignore classes annotated with @Generated and all enclosed elements in them. + private boolean isGenerated(Element element) { + return element + .getAnnotationMirrors() + .stream() + .anyMatch(m -> m + .getAnnotationType() + .toString() /* ClassSymbol.toString() returns class name */ + .equalsIgnoreCase("javax.annotation.Generated")); + } + private boolean hasInheritedJavadocs(Element element) { boolean hasOverrides = element.getAnnotationMirrors().stream() .anyMatch(ann -> ann.getAnnotationType().toString().equals(Override.class.getName())); diff --git a/gradle.properties b/gradle.properties index 53b593923ce26..7c359ed2b652c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # GitHub history for details. # +# Enable build caching +org.gradle.caching=true org.gradle.warning.mode=none org.gradle.parallel=true org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError -Xss2m \ @@ -17,7 +19,10 @@ org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError -Xss2m \ --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED -options.forkOptions.memoryMaximumSize=2g +options.forkOptions.memoryMaximumSize=3g + +# Disable Gradle Enterprise Gradle plugin's test retry +systemProp.gradle.enterprise.testretry.enabled=false # Disable duplicate project id detection # See https://docs.gradle.org/current/userguide/upgrading_version_6.html#duplicate_project_names_may_cause_publication_to_fail diff --git a/gradle/code-coverage.gradle b/gradle/code-coverage.gradle index de041eae7b72d..dfb4ddba24113 100644 --- a/gradle/code-coverage.gradle +++ b/gradle/code-coverage.gradle @@ -10,92 +10,41 @@ apply plugin: 'jacoco' repositories { mavenCentral() + gradlePluginPortal() + // TODO: Find the way to use the repositories from RepositoriesSetupPlugin + maven { + url = "https://d1nvenhzbhpy0q.cloudfront.net/snapshots/lucene/" + } } allprojects { plugins.withId('jacoco') { - // The default JaCoCo version in Gradle 6.6.1 is 0.8.5, but at least version 0.8.6 officially supports Java 14 - jacoco.toolVersion = '0.8.7' + jacoco.toolVersion = '0.8.10' } } -def codeCoverageReportTask = tasks.register("codeCoverageReport", JacocoReport) { - description = 'Generates aggregate report from all subprojects.' - executionData.setFrom fileTree(dir: '.', include: '**/build/jacoco/*.exec') - dependsOn subprojects.findAll(s -> s.tasks.findByName('check') != null).check -} - -tasks.register("codeCoverageReportForUnitTest", JacocoReport) { - description = 'Generates aggregate report from all subprojects for unit test.' - executionData.setFrom fileTree(dir: '.', include: '**/build/jacoco/test.exec') -} - -tasks.register("codeCoverageReportForIntegrationTest", JacocoReport) { - description = 'Generates aggregate report from all subprojects for integration test.' - // These kinds of tests are integration test, and the tests can be ran by Gradle tasks with the same name - def integrationTestExecPathList = ['**/build/jacoco/integTest.exec', - '**/build/jacoco/internalClusterTest.exec', - '**/build/jacoco/javaRestTest.exec', - '**/build/jacoco/yamlRestTest.exec' ] - executionData.setFrom fileTree(dir: '.', include: integrationTestExecPathList) -} - tasks.withType(JacocoReport).configureEach { group = JavaBasePlugin.VERIFICATION_GROUP - // Select projects with corresponding tests in order to run proper tests and select proper classes to generate the report - def projectsWithJavaPlugin = subprojects.findAll { it.pluginManager.hasPlugin('java') } - def projectsWithUnitTest = projectsWithJavaPlugin.findAll { it.tasks.findByName('test').enabled } - def projectsWithIntegTest = projectsWithJavaPlugin.findAll {it.tasks.findByName('integTest')} - def projectsWithAsyncIntegTest = projectsWithJavaPlugin.findAll {it.tasks.findByName('asyncIntegTest')} - def projectsWithInternalClusterTest = projectsWithJavaPlugin.findAll {it.tasks.findByName('internalClusterTest')} - def projectsWithPooledInternalClusterTest = projectsWithJavaPlugin.findAll {it.tasks.findByName('pooledInternalClusterTest')} - def projectsWithJavaRestTest = projectsWithJavaPlugin.findAll {it.tasks.findByName('javaRestTest')} - def projectsWithYamlRestTest = projectsWithJavaPlugin.findAll {it.tasks.findByName('yamlRestTest')} - def projectsWithIntegrationTest = projectsWithIntegTest + projectsWithAsyncIntegTest + projectsWithInternalClusterTest + projectsWithPooledInternalClusterTest + projectsWithJavaRestTest + projectsWithYamlRestTest - def projectsWithTest = projectsWithUnitTest + projectsWithIntegrationTest - - def selectedProjects - switch (name) { - case "codeCoverageReportForUnitTest": - dependsOn projectsWithUnitTest.test - selectedProjects = projectsWithUnitTest - break - case "codeCoverageReportForIntegrationTest": - dependsOn projectsWithIntegTest.integTest - dependsOn projectsWithAsyncIntegTest.asyncIntegTest - dependsOn projectsWithInternalClusterTest.internalClusterTest - dependsOn projectsWithPooledInternalClusterTest.pooledInternalClusterTest - dependsOn projectsWithJavaRestTest.javaRestTest - dependsOn projectsWithYamlRestTest.yamlRestTest - selectedProjects = projectsWithIntegrationTest - break - default: - dependsOn projectsWithUnitTest.test - dependsOn projectsWithIntegTest.integTest - dependsOn projectsWithAsyncIntegTest.asyncIntegTest - dependsOn projectsWithInternalClusterTest.internalClusterTest - dependsOn projectsWithPooledInternalClusterTest.pooledInternalClusterTest - dependsOn projectsWithJavaRestTest.javaRestTest - dependsOn projectsWithYamlRestTest.yamlRestTest - selectedProjects = projectsWithJavaPlugin - break - } - - sourceDirectories.setFrom files(selectedProjects.sourceSets.main.allSource.srcDirs) - classDirectories.setFrom files(selectedProjects.sourceSets.main.output) - reports { // Code coverage report in HTML and CSV formats are on demand, in case they take extra disk space. - xml.getRequired().set(System.getProperty('tests.coverage.report.xml', 'true').toBoolean()) - html.getRequired().set(System.getProperty('tests.coverage.report.html', 'false').toBoolean()) - csv.getRequired().set(System.getProperty('tests.coverage.report.csv', 'false').toBoolean()) + xml.required = System.getProperty('tests.coverage.report.xml', 'true').toBoolean() + html.required = System.getProperty('tests.coverage.report.html', 'false').toBoolean() + csv.required = System.getProperty('tests.coverage.report.csv', 'false').toBoolean() } } if (System.getProperty("tests.coverage")) { + reporting { + reports { + testCodeCoverageReport(JacocoCoverageReport) { + testType = TestSuiteType.UNIT_TEST + } + } + } + // Attach code coverage report task to Gradle check task project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME).configure { - dependsOn codeCoverageReportTask + dependsOn tasks.named('testCodeCoverageReport', JacocoReport) } } diff --git a/gradle/formatting.gradle b/gradle/formatting.gradle index 19adea33b23b3..e0c5fd9d4974e 100644 --- a/gradle/formatting.gradle +++ b/gradle/formatting.gradle @@ -66,14 +66,44 @@ allprojects { target '**/*.java' removeUnusedImports() + importOrder( + 'de.thetaphi', + 'com.carrotsearch', + 'com.fasterxml', + 'com.avast', + 'com.sun', + 'com.maxmind|com.github|com.networknt|groovy|nebula', + 'org.antlr', + 'software.amazon', + 'com.azure|com.microsoft|com.ibm|com.google|joptsimple|org.apache|org.bouncycastle|org.codehaus|org.opensearch|org.objectweb|org.joda|org.hamcrest|org.openjdk|org.gradle|org.junit', + 'javax', + 'java', + '', + '\\#java|\\#org.opensearch|\\#org.hamcrest|\\#' + ) + eclipse().configFile rootProject.file('buildSrc/formatterConfig.xml') trimTrailingWhitespace() + endWithNewline() + custom 'Refuse wildcard imports', { + // Wildcard imports can't be resolved; fail the build + if (it =~ /\s+import .*\*;/) { + throw new AssertionError("Do not use wildcard imports. 'spotlessApply' cannot resolve this issue.") + } + } + // See DEVELOPER_GUIDE.md for details of when to enable this. if (System.getProperty('spotless.paddedcell') != null) { paddedCell() } } + format 'misc', { + target '*.md', '*.gradle', '**/*.yaml', '**/*.yml', '**/*.svg' + + trimTrailingWhitespace() + endWithNewline() + } } precommit.dependsOn 'spotlessJavaCheck' diff --git a/gradle/missing-javadoc.gradle b/gradle/missing-javadoc.gradle index df47a3796c825..a9e64a1a93da5 100644 --- a/gradle/missing-javadoc.gradle +++ b/gradle/missing-javadoc.gradle @@ -6,7 +6,10 @@ * compatible open source license. */ + import javax.annotation.Nullable +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.internal.jvm.Jvm /** @@ -68,7 +71,7 @@ allprojects { classpath = sourceSets.main.compileClasspath srcDirSet = sourceSets.main.java - outputDir = project.javadoc.destinationDir + outputDir = file("${project.buildDir}/tmp/missingJavadoc/") } } } @@ -106,7 +109,6 @@ configure([ project(":example-plugins:rest-handler"), project(":example-plugins:script-expert-scoring"), project(":libs:opensearch-cli"), - project(":libs:opensearch-core"), project(":libs:opensearch-dissect"), project(":libs:opensearch-geo"), project(":libs:opensearch-grok"), @@ -146,6 +148,7 @@ configure([ project(":plugins:discovery-ec2:qa:amazon-ec2"), project(":plugins:discovery-gce"), project(":plugins:discovery-gce:qa:gce"), + project(":plugins:identity-shiro"), project(":plugins:ingest-attachment"), project(":plugins:mapper-annotated-text"), project(":plugins:mapper-murmur3"), @@ -156,11 +159,11 @@ configure([ project(":plugins:repository-s3"), project(":plugins:store-smb"), project(":plugins:transport-nio"), + project(":plugins:crypto-kms"), project(":qa:die-with-dignity"), project(":qa:os"), project(":qa:wildfly"), project(":rest-api-spec"), - project(":server"), project(":test:external-modules:test-delayed-aggs"), project(":test:fixtures:azure-fixture"), project(":test:fixtures:gcs-fixture"), @@ -175,9 +178,24 @@ configure([ } } +configure([ + project(":libs:opensearch-common"), + project(":libs:opensearch-core"), + project(":libs:opensearch-compress"), + project(":server") +]) { + project.tasks.withType(MissingJavadocTask) { + // TODO: bump to variable missing level after increasing javadoc coverage + javadocMissingLevel = "class" + } +} + +@CacheableTask class MissingJavadocTask extends DefaultTask { @InputFiles @SkipWhenEmpty + @IgnoreEmptyDirectories + @PathSensitive(PathSensitivity.RELATIVE) SourceDirectorySet srcDirSet; @OutputDirectory @@ -217,7 +235,8 @@ class MissingJavadocTask extends DefaultTask { @Input def executable - @Input + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) def taskResources /** Utility method to recursively collect all tasks with same name like this one that we depend on */ @@ -270,6 +289,10 @@ class MissingJavadocTask extends DefaultTask { if (!classpath.isEmpty()) { opts << [ '-classpath', classpath.asPath ] } + + opts << [ '-tag', 'opensearch.experimental:a:WARNING: This API is experimental and might change in incompatible ways in the next release.' ] + opts << [ '-tag', 'opensearch.internal:a:NOTE: This API is for internal purposes only and might change in incompatible ways in the next release.' ] + opts << [ '-doclet', "org.opensearch.missingdoclet.MissingDoclet" ] opts << [ '-docletpath', docletpath.asPath ] opts << [ '--missing-level', javadocMissingLevel ] diff --git a/gradle/runtime-jdk-provision.gradle b/gradle/runtime-jdk-provision.gradle index 2f0c2f74d6803..6bff164633c25 100644 --- a/gradle/runtime-jdk-provision.gradle +++ b/gradle/runtime-jdk-provision.gradle @@ -20,7 +20,11 @@ if (BuildParams.getIsRuntimeJavaHomeSet()) { configure(allprojects - project(':build-tools')) { project.tasks.withType(Test).configureEach { Test test -> if (BuildParams.getIsRuntimeJavaHomeSet()) { - test.executable = "${BuildParams.runtimeJavaHome}/bin/java" + if (OS.current() == OS.WINDOWS) { + test.executable = "${BuildParams.runtimeJavaHome}/bin/java.exe" + } else { + test.executable = "${BuildParams.runtimeJavaHome}/bin/java" + } } } } @@ -28,7 +32,7 @@ if (BuildParams.getIsRuntimeJavaHomeSet()) { jdks { provisioned_runtime { vendor = VersionProperties.bundledJdkVendor - version = VersionProperties.getBundledJdk(OS.current().name().toLowerCase()) + version = VersionProperties.getBundledJdk(OS.current().name().toLowerCase(), Architecture.current().name().toLowerCase()) platform = OS.current().name().toLowerCase() architecture = Architecture.current().name().toLowerCase() } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae88..7f93135c49b76 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index daf75f8e132cb..f01f0a84a786a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -11,7 +11,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=c9490e938b221daf0094982288e4038deed954a3f12fb54cbf270ddf4e37d879 +distributionSha256Sum=bb09982fdf52718e4c7b25023d10df6d35a5fff969860bdf5a5bd27a3ab27a9e diff --git a/gradlew b/gradlew index 4f906e0c811fc..0adc8e1a53214 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,99 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +119,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index ac1b06f93825d..6689b85beecde 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/libs/build.gradle b/libs/build.gradle index 92cb2bdb2efc7..39d2737966b6d 100644 --- a/libs/build.gradle +++ b/libs/build.gradle @@ -44,11 +44,12 @@ subprojects { dependencies.matching { it instanceof ProjectDependency }.all { ProjectDependency dep -> Project depProject = dep.dependencyProject if (depProject != null - && false == depProject.path.equals(':libs:opensearch-core') + && (false == depProject.path.equals(':libs:opensearch-core') && + false == depProject.path.equals(':libs:opensearch-common')) && depProject.path.startsWith(':libs')) { throw new InvalidUserDataException("projects in :libs " + "may not depend on other projects libs except " - + ":libs:opensearch-core but " + + ":libs:opensearch-core or :libs:opensearch-common but " + "${project.path} depends on ${depProject.path}") } } diff --git a/libs/cli/build.gradle b/libs/cli/build.gradle index 7f1e9cb8d04b3..a58c4dafac874 100644 --- a/libs/cli/build.gradle +++ b/libs/cli/build.gradle @@ -28,12 +28,11 @@ * under the License. */ apply plugin: 'opensearch.build' -apply plugin: 'nebula.optional-base' apply plugin: 'opensearch.publish' dependencies { api 'net.sf.jopt-simple:jopt-simple:5.0.4' - api project(':libs:opensearch-core') + api project(':libs:opensearch-common') } test.enabled = false diff --git a/libs/cli/src/main/java/org/opensearch/cli/ExitCodes.java b/libs/cli/src/main/java/org/opensearch/cli/ExitCodes.java index c705177b0d7b6..90efc89a08caf 100644 --- a/libs/cli/src/main/java/org/opensearch/cli/ExitCodes.java +++ b/libs/cli/src/main/java/org/opensearch/cli/ExitCodes.java @@ -36,20 +36,34 @@ * POSIX exit codes. */ public class ExitCodes { + /** No error */ public static final int OK = 0; - public static final int USAGE = 64; /* command line usage error */ - public static final int DATA_ERROR = 65; /* data format error */ - public static final int NO_INPUT = 66; /* cannot open input */ - public static final int NO_USER = 67; /* addressee unknown */ - public static final int NO_HOST = 68; /* host name unknown */ - public static final int UNAVAILABLE = 69; /* service unavailable */ - public static final int CODE_ERROR = 70; /* internal software error */ - public static final int CANT_CREATE = 73; /* can't create (user) output file */ - public static final int IO_ERROR = 74; /* input/output error */ - public static final int TEMP_FAILURE = 75; /* temp failure; user is invited to retry */ - public static final int PROTOCOL = 76; /* remote error in protocol */ - public static final int NOPERM = 77; /* permission denied */ - public static final int CONFIG = 78; /* configuration error */ + /** command line usage error */ + public static final int USAGE = 64; + /** data format error */ + public static final int DATA_ERROR = 65; + /** cannot open input */ + public static final int NO_INPUT = 66; + /** addressee unknown */ + public static final int NO_USER = 67; + /** host name unknown */ + public static final int NO_HOST = 68; + /** service unavailable */ + public static final int UNAVAILABLE = 69; + /** internal software error */ + public static final int CODE_ERROR = 70; + /** can't create (user) output file */ + public static final int CANT_CREATE = 73; + /** input/output error */ + public static final int IO_ERROR = 74; + /** temp failure; user is invited to retry */ + public static final int TEMP_FAILURE = 75; + /** remote error in protocol */ + public static final int PROTOCOL = 76; + /** permission denied */ + public static final int NOPERM = 77; + /** configuration error */ + public static final int CONFIG = 78; private ExitCodes() { /* no instance, just constants */ } } diff --git a/libs/cli/src/main/java/org/opensearch/cli/MultiCommand.java b/libs/cli/src/main/java/org/opensearch/cli/MultiCommand.java index 1bdcd6189c757..837a93ea95f83 100644 --- a/libs/cli/src/main/java/org/opensearch/cli/MultiCommand.java +++ b/libs/cli/src/main/java/org/opensearch/cli/MultiCommand.java @@ -36,7 +36,7 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; import joptsimple.util.KeyValuePair; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import java.io.IOException; import java.util.ArrayList; diff --git a/libs/cli/src/main/java/org/opensearch/cli/Terminal.java b/libs/cli/src/main/java/org/opensearch/cli/Terminal.java index 657b95fa052ab..be030c18507ad 100644 --- a/libs/cli/src/main/java/org/opensearch/cli/Terminal.java +++ b/libs/cli/src/main/java/org/opensearch/cli/Terminal.java @@ -51,6 +51,8 @@ * which allows {@link #println(Verbosity,String)} calls which act like a logger, * only actually printing if the verbosity level of the terminal is above * the verbosity of the message. + * @see ConsoleTerminal + * @see SystemTerminal */ public abstract class Terminal { @@ -65,35 +67,57 @@ private static PrintWriter newErrorWriter() { return new PrintWriter(System.err); } - /** Defines the available verbosity levels of messages to be printed. */ + /** Defines the available verbosity levels of messages to be printed.*/ public enum Verbosity { - SILENT, /* always printed */ - NORMAL, /* printed when no options are given to cli */ - VERBOSE /* printed only when cli is passed verbose option */ + /** always printed */ + SILENT, + /** printed when no options are given to cli */ + NORMAL, + /** printed only when cli is passed verbose option */ + VERBOSE } /** The current verbosity for the terminal, defaulting to {@link Verbosity#NORMAL}. */ private Verbosity verbosity = Verbosity.NORMAL; - /** The newline used when calling println. */ + /** The newline separator used when calling println. */ private final String lineSeparator; + /** Constructs a new terminal with the given line separator. + * @param lineSeparator the line separator to use when calling println + * */ protected Terminal(String lineSeparator) { this.lineSeparator = lineSeparator; } - /** Sets the verbosity of the terminal. */ + /** Sets the {@link Terminal#verbosity} of the terminal. (Default is {@link Verbosity#NORMAL}) + * @param verbosity the {@link Verbosity} level that will be used for printing + * */ public void setVerbosity(Verbosity verbosity) { this.verbosity = verbosity; } - /** Reads clear text from the terminal input. See {@link Console#readLine()}. */ + /** Reads clear text from the terminal input. + * @see Console#readLine() + * @param prompt message to display to the user + * @return the text entered by the user + * */ public abstract String readText(String prompt); - /** Reads password text from the terminal input. See {@link Console#readPassword()}}. */ + /** Reads secret text from the terminal input with echoing disabled. + * @see Console#readPassword() + * @param prompt message to display to the user + * @return the secret as a character array + * */ public abstract char[] readSecret(String prompt); - /** Read password text form terminal input up to a maximum length. */ + /** Read secret text from terminal input with echoing disabled, up to a maximum length. + * @see Console#readPassword() + * @param prompt message to display to the user + * @param maxLength the maximum length of the secret + * @return the secret as a character array + * @throws IllegalStateException if the secret exceeds the maximum length + * */ public char[] readSecret(String prompt, int maxLength) { char[] result = readSecret(prompt); if (result.length > maxLength) { @@ -103,30 +127,48 @@ public char[] readSecret(String prompt, int maxLength) { return result; } - /** Returns a Writer which can be used to write to the terminal directly using standard output. */ + /** Returns a Writer which can be used to write to the terminal directly using standard output. + * @return a writer to {@link Terminal#DEFAULT} output + * @see Terminal.ConsoleTerminal + * @see Terminal.SystemTerminal + * */ public abstract PrintWriter getWriter(); - /** Returns a Writer which can be used to write to the terminal directly using standard error. */ + /** Returns a Writer which can be used to write to the terminal directly using standard error. + * @return a writer to stderr + * */ public PrintWriter getErrorWriter() { return ERROR_WRITER; } - /** Prints a line to the terminal at {@link Verbosity#NORMAL} verbosity level. */ + /** Prints a line to the terminal at {@link Verbosity#NORMAL} verbosity level, with a {@link Terminal#lineSeparator} + * @param msg the message to print + * */ public final void println(String msg) { println(Verbosity.NORMAL, msg); } - /** Prints a line to the terminal at {@code verbosity} level. */ + /** Prints message to the terminal's standard output at {@link Verbosity} level, with a {@link Terminal#lineSeparator}. + * @param verbosity the {@link Verbosity} level at which to print + * @param msg the message to print + * */ public final void println(Verbosity verbosity, String msg) { print(verbosity, msg + lineSeparator); } - /** Prints message to the terminal's standard output at {@code verbosity} level, without a newline. */ + /** Prints message to the terminal's standard output at {@link Verbosity} level, without adding a {@link Terminal#lineSeparator}. + * @param verbosity the {@link Verbosity} level at which to print + * @param msg the message to print + * */ public final void print(Verbosity verbosity, String msg) { print(verbosity, msg, false); } - /** Prints message to the terminal at {@code verbosity} level, without a newline. */ + /** Prints message to either standard or error output at {@link Verbosity} level, without adding a {@link Terminal#lineSeparator}. + * @param verbosity the {@link Verbosity} level at which to print. + * @param msg the message to print + * @param isError if true, prints to standard error instead of standard output + * */ private void print(Verbosity verbosity, String msg, boolean isError) { if (isPrintable(verbosity)) { PrintWriter writer = isError ? getErrorWriter() : getWriter(); @@ -135,29 +177,44 @@ private void print(Verbosity verbosity, String msg, boolean isError) { } } - /** Prints a line to the terminal's standard error at {@link Verbosity#NORMAL} verbosity level, without a newline. */ + /** Prints a line to the terminal's standard error at {@link Verbosity} level, without adding a {@link Terminal#lineSeparator}. + * @param verbosity the {@link Verbosity} level at which to print. + * @param msg the message to print + * */ public final void errorPrint(Verbosity verbosity, String msg) { print(verbosity, msg, true); } - /** Prints a line to the terminal's standard error at {@link Verbosity#NORMAL} verbosity level. */ + /** Prints a line to the terminal's standard error at {@link Verbosity#NORMAL} verbosity level, with a {@link Terminal#lineSeparator} + * @param msg the message to print + * */ public final void errorPrintln(String msg) { errorPrintln(Verbosity.NORMAL, msg); } - /** Prints a line to the terminal's standard error at {@code verbosity} level. */ + /** Prints a line to the terminal's standard error at {@link Verbosity} level, with a {@link Terminal#lineSeparator}. + * @param verbosity the {@link Verbosity} level at which to print. + * @param msg the message to print + * */ public final void errorPrintln(Verbosity verbosity, String msg) { errorPrint(verbosity, msg + lineSeparator); } - /** Checks if is enough {@code verbosity} level to be printed */ + /** Checks if given {@link Verbosity} level is high enough to be printed at the level defined by {@link Terminal#verbosity} + * @param verbosity the {@link Verbosity} level to check + * @return true if the {@link Verbosity} level is high enough to be printed + * @see Terminal#setVerbosity(Verbosity) + * */ public final boolean isPrintable(Verbosity verbosity) { return this.verbosity.ordinal() >= verbosity.ordinal(); } /** - * Prompt for a yes or no answer from the user. This method will loop until 'y' or 'n' + * Prompt for a yes or no answer from the user. This method will loop until 'y', 'n' * (or the default empty value) is entered. + * @param prompt the prompt to display to the user + * @param defaultYes if true, the default answer is 'y', otherwise it is 'n' + * @return true if the user answered 'y', false if the user answered 'n' or the defaultYes value if the user entered nothing */ public final boolean promptYesNo(String prompt, boolean defaultYes) { String answerPrompt = defaultYes ? " [Y/n]" : " [y/N]"; @@ -181,6 +238,11 @@ public final boolean promptYesNo(String prompt, boolean defaultYes) { * character is immediately preceded by a carriage return, we have * a Windows-style newline, so we discard the carriage return as well * as the newline. + * @param reader the reader to read from + * @param maxLength the maximum length of the line to read + * @return the line read from the reader + * @throws RuntimeException if the line read exceeds the maximum length + * @throws RuntimeException if an IOException occurs while reading */ public static char[] readLineToCharArray(Reader reader, int maxLength) { char[] buf = new char[maxLength + 2]; @@ -215,6 +277,7 @@ public static char[] readLineToCharArray(Reader reader, int maxLength) { } } + /** Flushes the terminal's standard output and standard error. */ public void flush() { this.getWriter().flush(); this.getErrorWriter().flush(); diff --git a/libs/common/build.gradle b/libs/common/build.gradle new file mode 100644 index 0000000000000..973fe30d09842 --- /dev/null +++ b/libs/common/build.gradle @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import org.opensearch.gradle.info.BuildParams + +apply plugin: 'opensearch.publish' + +base { + archivesBaseName = 'opensearch-common' +} + +dependencies { + // This dependency is used only by :libs:core for null-checking interop with other tools + compileOnly "com.google.code.findbugs:jsr305:3.0.2" + + /******* + * !!!! NO THIRD PARTY DEPENDENCIES !!!! + *******/ + + testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" + testImplementation "junit:junit:${versions.junit}" + testImplementation "org.hamcrest:hamcrest:${versions.hamcrest}" + + testImplementation(project(":test:framework")) { + exclude group: 'org.opensearch', module: 'opensearch-common' + } +} + +tasks.withType(JavaCompile).configureEach { + options.compilerArgs -= '-Xlint:unchecked' +} + +tasks.named('forbiddenApisMain').configure { + // :libs:opensearch-common does not depend on server + // TODO: Need to decide how we want to handle for forbidden signatures with the changes to server + replaceSignatureFiles 'jdk-signatures' +} diff --git a/libs/core/src/main/java/org/opensearch/bootstrap/JarHell.java b/libs/common/src/main/java/org/opensearch/bootstrap/JarHell.java similarity index 96% rename from libs/core/src/main/java/org/opensearch/bootstrap/JarHell.java rename to libs/common/src/main/java/org/opensearch/bootstrap/JarHell.java index 843a6b982d7ff..c4ba778e7db86 100644 --- a/libs/core/src/main/java/org/opensearch/bootstrap/JarHell.java +++ b/libs/common/src/main/java/org/opensearch/bootstrap/JarHell.java @@ -36,6 +36,7 @@ import org.opensearch.common.io.PathUtils; import java.io.IOException; +import java.lang.Runtime.Version; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; @@ -70,6 +71,8 @@ *
  • Checks any {@code X-Compile-OpenSearch-Version} value in * the jar manifest is compatible with the current ES
  • * + * + * @opensearch.internal */ public class JarHell { @@ -250,7 +253,9 @@ private static void checkManifest(Manifest manifest, Path jar) { } public static void checkVersionFormat(String targetVersion) { - if (!JavaVersion.isValid(targetVersion)) { + try { + Version.parse(targetVersion); + } catch (final IllegalArgumentException ex) { throw new IllegalStateException( String.format( Locale.ROOT, @@ -267,16 +272,10 @@ public static void checkVersionFormat(String targetVersion) { * required by {@code resource} is compatible with the current installation. */ public static void checkJavaVersion(String resource, String targetVersion) { - JavaVersion version = JavaVersion.parse(targetVersion); - if (JavaVersion.current().compareTo(version) < 0) { + Version version = Version.parse(targetVersion); + if (Runtime.version().compareTo(version) < 0) { throw new IllegalStateException( - String.format( - Locale.ROOT, - "%s requires Java %s:, your system: %s", - resource, - targetVersion, - JavaVersion.current().toString() - ) + String.format(Locale.ROOT, "%s requires Java %s:, your system: %s", resource, targetVersion, Runtime.version().toString()) ); } } diff --git a/libs/core/src/main/java/org/opensearch/bootstrap/JdkJarHellCheck.java b/libs/common/src/main/java/org/opensearch/bootstrap/JdkJarHellCheck.java similarity index 97% rename from libs/core/src/main/java/org/opensearch/bootstrap/JdkJarHellCheck.java rename to libs/common/src/main/java/org/opensearch/bootstrap/JdkJarHellCheck.java index 8cb81639c504f..97b323975db0a 100644 --- a/libs/core/src/main/java/org/opensearch/bootstrap/JdkJarHellCheck.java +++ b/libs/common/src/main/java/org/opensearch/bootstrap/JdkJarHellCheck.java @@ -44,6 +44,11 @@ import java.util.HashSet; import java.util.Set; +/** + * CLI tool for checking jar hell + * + * @opensearch.internal + */ public class JdkJarHellCheck { private Set detected = new HashSet<>(); diff --git a/libs/common/src/main/java/org/opensearch/bootstrap/package-info.java b/libs/common/src/main/java/org/opensearch/bootstrap/package-info.java new file mode 100644 index 0000000000000..f522b1bb91444 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/bootstrap/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Contains JarHell Classes */ +package org.opensearch.bootstrap; diff --git a/libs/core/src/main/java/org/opensearch/common/Booleans.java b/libs/common/src/main/java/org/opensearch/common/Booleans.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/Booleans.java rename to libs/common/src/main/java/org/opensearch/common/Booleans.java index 70536a8f4cc46..2ca061820b2eb 100644 --- a/libs/core/src/main/java/org/opensearch/common/Booleans.java +++ b/libs/common/src/main/java/org/opensearch/common/Booleans.java @@ -32,6 +32,11 @@ package org.opensearch.common; +/** + * Extended Boolean functionality + * + * @opensearch.internal + */ public final class Booleans { private Booleans() { throw new AssertionError("No instances intended"); diff --git a/libs/core/src/main/java/org/opensearch/common/CharArrays.java b/libs/common/src/main/java/org/opensearch/common/CharArrays.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/CharArrays.java rename to libs/common/src/main/java/org/opensearch/common/CharArrays.java index b579581b7044d..5489c067fc887 100644 --- a/libs/core/src/main/java/org/opensearch/common/CharArrays.java +++ b/libs/common/src/main/java/org/opensearch/common/CharArrays.java @@ -40,6 +40,8 @@ /** * Helper class similar to Arrays to handle conversions for Char arrays + * + * @opensearch.api */ public final class CharArrays { diff --git a/server/src/main/java/org/opensearch/common/CheckedBiConsumer.java b/libs/common/src/main/java/org/opensearch/common/CheckedBiConsumer.java similarity index 98% rename from server/src/main/java/org/opensearch/common/CheckedBiConsumer.java rename to libs/common/src/main/java/org/opensearch/common/CheckedBiConsumer.java index 1dc133584829e..50c15bb7a95a8 100644 --- a/server/src/main/java/org/opensearch/common/CheckedBiConsumer.java +++ b/libs/common/src/main/java/org/opensearch/common/CheckedBiConsumer.java @@ -36,6 +36,8 @@ /** * A {@link BiConsumer}-like interface which allows throwing checked exceptions. + * + * @opensearch.internal */ @FunctionalInterface public interface CheckedBiConsumer { diff --git a/server/src/main/java/org/opensearch/common/CheckedBiFunction.java b/libs/common/src/main/java/org/opensearch/common/CheckedBiFunction.java similarity index 98% rename from server/src/main/java/org/opensearch/common/CheckedBiFunction.java rename to libs/common/src/main/java/org/opensearch/common/CheckedBiFunction.java index 057c189e2ce0a..100588eb6d966 100644 --- a/server/src/main/java/org/opensearch/common/CheckedBiFunction.java +++ b/libs/common/src/main/java/org/opensearch/common/CheckedBiFunction.java @@ -34,6 +34,8 @@ /** * A {@link java.util.function.BiFunction}-like interface which allows throwing checked exceptions. + * + * @opensearch.internal */ @FunctionalInterface public interface CheckedBiFunction { diff --git a/libs/core/src/main/java/org/opensearch/common/CheckedConsumer.java b/libs/common/src/main/java/org/opensearch/common/CheckedConsumer.java similarity index 93% rename from libs/core/src/main/java/org/opensearch/common/CheckedConsumer.java rename to libs/common/src/main/java/org/opensearch/common/CheckedConsumer.java index d52c69ee517f1..07b4973c3a340 100644 --- a/libs/core/src/main/java/org/opensearch/common/CheckedConsumer.java +++ b/libs/common/src/main/java/org/opensearch/common/CheckedConsumer.java @@ -32,11 +32,16 @@ package org.opensearch.common; +import org.opensearch.common.annotation.PublicApi; + import java.util.function.Consumer; /** * A {@link Consumer}-like interface which allows throwing checked exceptions. + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") @FunctionalInterface public interface CheckedConsumer { void accept(T t) throws E; diff --git a/libs/core/src/main/java/org/opensearch/common/CheckedFunction.java b/libs/common/src/main/java/org/opensearch/common/CheckedFunction.java similarity index 98% rename from libs/core/src/main/java/org/opensearch/common/CheckedFunction.java rename to libs/common/src/main/java/org/opensearch/common/CheckedFunction.java index 651f68b17a8d8..9c17ad4b4ee3f 100644 --- a/libs/core/src/main/java/org/opensearch/common/CheckedFunction.java +++ b/libs/common/src/main/java/org/opensearch/common/CheckedFunction.java @@ -36,6 +36,8 @@ /** * A {@link Function}-like interface which allows throwing checked exceptions. + * + * @opensearch.api */ @FunctionalInterface public interface CheckedFunction { diff --git a/libs/core/src/main/java/org/opensearch/common/CheckedRunnable.java b/libs/common/src/main/java/org/opensearch/common/CheckedRunnable.java similarity index 98% rename from libs/core/src/main/java/org/opensearch/common/CheckedRunnable.java rename to libs/common/src/main/java/org/opensearch/common/CheckedRunnable.java index ac1b2b82ade8e..cb773ab789180 100644 --- a/libs/core/src/main/java/org/opensearch/common/CheckedRunnable.java +++ b/libs/common/src/main/java/org/opensearch/common/CheckedRunnable.java @@ -34,6 +34,8 @@ /** * A {@link Runnable}-like interface which allows throwing checked exceptions. + * + * @opensearch.api */ @FunctionalInterface public interface CheckedRunnable { diff --git a/server/src/main/java/org/opensearch/common/CheckedSupplier.java b/libs/common/src/main/java/org/opensearch/common/CheckedSupplier.java similarity index 98% rename from server/src/main/java/org/opensearch/common/CheckedSupplier.java rename to libs/common/src/main/java/org/opensearch/common/CheckedSupplier.java index 8ef1b026c7991..3099146901001 100644 --- a/server/src/main/java/org/opensearch/common/CheckedSupplier.java +++ b/libs/common/src/main/java/org/opensearch/common/CheckedSupplier.java @@ -36,6 +36,8 @@ /** * A {@link Supplier}-like interface which allows throwing checked exceptions. + * + * @opensearch.internal */ @FunctionalInterface public interface CheckedSupplier { diff --git a/libs/common/src/main/java/org/opensearch/common/CheckedTriFunction.java b/libs/common/src/main/java/org/opensearch/common/CheckedTriFunction.java new file mode 100644 index 0000000000000..7898226b751f7 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/CheckedTriFunction.java @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common; + +/** + * A {@link TriFunction}-like interface which allows throwing checked exceptions. + * + * @opensearch.internal + */ +@FunctionalInterface +public interface CheckedTriFunction { + R apply(S s, T t, U u) throws E; +} diff --git a/server/src/main/java/org/opensearch/common/Classes.java b/libs/common/src/main/java/org/opensearch/common/Classes.java similarity index 97% rename from server/src/main/java/org/opensearch/common/Classes.java rename to libs/common/src/main/java/org/opensearch/common/Classes.java index 5a59e6f9862a0..1b297639aff6a 100644 --- a/server/src/main/java/org/opensearch/common/Classes.java +++ b/libs/common/src/main/java/org/opensearch/common/Classes.java @@ -34,6 +34,11 @@ import java.lang.reflect.Modifier; +/** + * Base class utilities + * + * @opensearch.internal + */ public class Classes { /** diff --git a/server/src/main/java/org/opensearch/common/Explicit.java b/libs/common/src/main/java/org/opensearch/common/Explicit.java similarity index 99% rename from server/src/main/java/org/opensearch/common/Explicit.java rename to libs/common/src/main/java/org/opensearch/common/Explicit.java index 8607ba49415f5..66e079c461e75 100644 --- a/server/src/main/java/org/opensearch/common/Explicit.java +++ b/libs/common/src/main/java/org/opensearch/common/Explicit.java @@ -43,6 +43,7 @@ * field mapping settings it is preferable to preserve an explicit * choice rather than a choice made only made implicitly by defaults. * + * @opensearch.internal */ public class Explicit { diff --git a/server/src/main/java/org/opensearch/common/ExponentiallyWeightedMovingAverage.java b/libs/common/src/main/java/org/opensearch/common/ExponentiallyWeightedMovingAverage.java similarity index 98% rename from server/src/main/java/org/opensearch/common/ExponentiallyWeightedMovingAverage.java rename to libs/common/src/main/java/org/opensearch/common/ExponentiallyWeightedMovingAverage.java index 4c38483329dec..a6c236fe4fa0a 100644 --- a/server/src/main/java/org/opensearch/common/ExponentiallyWeightedMovingAverage.java +++ b/libs/common/src/main/java/org/opensearch/common/ExponentiallyWeightedMovingAverage.java @@ -37,6 +37,8 @@ /** * Implements exponentially weighted moving averages (commonly abbreviated EWMA) for a single value. * This class is safe to share between threads. + * + * @opensearch.internal */ public class ExponentiallyWeightedMovingAverage { diff --git a/libs/core/src/main/java/org/opensearch/common/Glob.java b/libs/common/src/main/java/org/opensearch/common/Glob.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/Glob.java rename to libs/common/src/main/java/org/opensearch/common/Glob.java index 650a38b83fbfc..daf045dd49e3a 100644 --- a/libs/core/src/main/java/org/opensearch/common/Glob.java +++ b/libs/common/src/main/java/org/opensearch/common/Glob.java @@ -34,6 +34,8 @@ /** * Utility class for glob-like matching + * + * @opensearch.api */ public class Glob { diff --git a/server/src/main/java/org/opensearch/common/LocalTimeOffset.java b/libs/common/src/main/java/org/opensearch/common/LocalTimeOffset.java similarity index 97% rename from server/src/main/java/org/opensearch/common/LocalTimeOffset.java rename to libs/common/src/main/java/org/opensearch/common/LocalTimeOffset.java index 94347c47e56e0..7e89641927ed5 100644 --- a/server/src/main/java/org/opensearch/common/LocalTimeOffset.java +++ b/libs/common/src/main/java/org/opensearch/common/LocalTimeOffset.java @@ -61,6 +61,8 @@ *

    * So there are two methods to convert from local time back to utc, * {@link #localToUtc(long, Strategy)} and {@link #localToUtcInThisOffset(long)}. + * + * @opensearch.internal */ public abstract class LocalTimeOffset { /** @@ -146,6 +148,11 @@ public final long localToUtcInThisOffset(long localMillis) { */ public abstract long localToUtc(long localMillis, Strategy strat); + /** + * Strategy for a local time + * + * @opensearch.internal + */ public interface Strategy { /** * Handle a local time that never actually happened because a "gap" @@ -206,6 +213,8 @@ public String toString() { /** * How to get instances of {@link LocalTimeOffset}. + * + * @opensearch.internal */ public abstract static class Lookup { /** @@ -234,6 +243,11 @@ public abstract static class Lookup { abstract int size(); } + /** + * No previous local time offset + * + * @opensearch.internal + */ private static class NoPrevious extends LocalTimeOffset { NoPrevious(long millis) { super(millis); @@ -269,6 +283,11 @@ protected String toString(long millis) { } } + /** + * Transition for a local time offset + * + * @opensearch.internal + */ public abstract static class Transition extends LocalTimeOffset { private final LocalTimeOffset previous; private final long startUtcMillis; @@ -307,6 +326,11 @@ public long startUtcMillis() { } } + /** + * Gap for a local time offset + * + * @opensearch.internal + */ public static class Gap extends Transition { private final long firstMissingLocalTime; private final long firstLocalTimeAfterGap; @@ -347,6 +371,11 @@ protected String toString(long millis) { } } + /** + * Overlap for a local time offset + * + * @opensearch.internal + */ public static class Overlap extends Transition { private final long firstOverlappingLocalTime; private final long firstNonOverlappingLocalTime; @@ -403,6 +432,11 @@ protected String toString(long millis) { } } + /** + * Fixed lookup the local time offset + * + * @opensearch.internal + */ private static class FixedLookup extends Lookup { private final ZoneId zone; private final LocalTimeOffset fixed; @@ -441,6 +475,8 @@ public boolean anyMoveBackToPreviousDay() { /** * Looks up transitions by checking whether the date is after the start * of each transition. Simple so fast for small numbers of transitions. + * + * @opensearch.internal */ private static class LinkedListLookup extends AbstractManyTransitionsLookup { private final LocalTimeOffset lastOffset; @@ -477,6 +513,8 @@ public boolean anyMoveBackToPreviousDay() { /** * Builds an array that can be {@link Arrays#binarySearch(long[], long)}ed * for the daylight savings time transitions. + * + * @openearch.internal */ private static class TransitionArrayLookup extends AbstractManyTransitionsLookup { private final LocalTimeOffset[] offsets; @@ -536,6 +574,11 @@ public String toString() { } } + /** + * Base class for many transitions lookup + * + * @opensearch.internal + */ private abstract static class AbstractManyTransitionsLookup extends Lookup { protected final ZoneId zone; protected final long minUtcMillis; diff --git a/server/src/main/java/org/opensearch/common/MacAddressProvider.java b/libs/common/src/main/java/org/opensearch/common/MacAddressProvider.java similarity index 97% rename from server/src/main/java/org/opensearch/common/MacAddressProvider.java rename to libs/common/src/main/java/org/opensearch/common/MacAddressProvider.java index 244d3032d2315..db51cc64c5d66 100644 --- a/server/src/main/java/org/opensearch/common/MacAddressProvider.java +++ b/libs/common/src/main/java/org/opensearch/common/MacAddressProvider.java @@ -36,6 +36,11 @@ import java.net.SocketException; import java.util.Enumeration; +/** + * Provider of MAC addressing + * + * @opensearch.internal + */ public class MacAddressProvider { private static byte[] getMacAddress() throws SocketException { diff --git a/libs/core/src/main/java/org/opensearch/common/MemoizedSupplier.java b/libs/common/src/main/java/org/opensearch/common/MemoizedSupplier.java similarity index 94% rename from libs/core/src/main/java/org/opensearch/common/MemoizedSupplier.java rename to libs/common/src/main/java/org/opensearch/common/MemoizedSupplier.java index 590dae2323abd..0bfdcf6b7a144 100644 --- a/libs/core/src/main/java/org/opensearch/common/MemoizedSupplier.java +++ b/libs/common/src/main/java/org/opensearch/common/MemoizedSupplier.java @@ -34,6 +34,11 @@ import java.util.function.Supplier; +/** + * A base supplier using memoization optimization technique + * + * @opensearch.api + */ public class MemoizedSupplier implements Supplier { private Supplier supplier; private T value; diff --git a/server/src/main/java/org/opensearch/common/NamedRegistry.java b/libs/common/src/main/java/org/opensearch/common/NamedRegistry.java similarity index 98% rename from server/src/main/java/org/opensearch/common/NamedRegistry.java rename to libs/common/src/main/java/org/opensearch/common/NamedRegistry.java index c48cbfaedd7de..a0e98d9126628 100644 --- a/server/src/main/java/org/opensearch/common/NamedRegistry.java +++ b/libs/common/src/main/java/org/opensearch/common/NamedRegistry.java @@ -41,6 +41,8 @@ /** * A registry from String to some class implementation. Used to ensure implementations are registered only once. + * + * @opensearch.internal */ public class NamedRegistry { private final Map registry = new HashMap<>(); diff --git a/libs/core/src/main/java/org/opensearch/common/Nullable.java b/libs/common/src/main/java/org/opensearch/common/Nullable.java similarity index 98% rename from libs/core/src/main/java/org/opensearch/common/Nullable.java rename to libs/common/src/main/java/org/opensearch/common/Nullable.java index 8660f2dbb5995..c663ef863ed48 100644 --- a/libs/core/src/main/java/org/opensearch/common/Nullable.java +++ b/libs/common/src/main/java/org/opensearch/common/Nullable.java @@ -34,6 +34,7 @@ import javax.annotation.CheckForNull; import javax.annotation.meta.TypeQualifierNickname; + import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -45,7 +46,7 @@ * {@code null} is an acceptable value for that parameter. It should not be * used for parameters of primitive types. * - * + * @opensearch.api */ @Documented @TypeQualifierNickname diff --git a/libs/common/src/main/java/org/opensearch/common/Numbers.java b/libs/common/src/main/java/org/opensearch/common/Numbers.java new file mode 100644 index 0000000000000..d5a364a4a934e --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/Numbers.java @@ -0,0 +1,271 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * A set of utilities for numbers. + * + * @opensearch.internal + */ +public final class Numbers { + public static final BigInteger MAX_UNSIGNED_LONG_VALUE = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE); + public static final BigInteger MIN_UNSIGNED_LONG_VALUE = BigInteger.ZERO; + + public static final long MIN_UNSIGNED_LONG_VALUE_AS_LONG = MIN_UNSIGNED_LONG_VALUE.longValue(); + public static final long MAX_UNSIGNED_LONG_VALUE_AS_LONG = MAX_UNSIGNED_LONG_VALUE.longValue(); + + private static final BigInteger MAX_LONG_VALUE = BigInteger.valueOf(Long.MAX_VALUE); + private static final BigInteger MIN_LONG_VALUE = BigInteger.valueOf(Long.MIN_VALUE); + + private Numbers() {} + + /** + * Converts a long to a byte array. + * + * @param val The long to convert to a byte array + * @return The byte array converted + */ + public static byte[] longToBytes(long val) { + byte[] arr = new byte[8]; + arr[0] = (byte) (val >>> 56); + arr[1] = (byte) (val >>> 48); + arr[2] = (byte) (val >>> 40); + arr[3] = (byte) (val >>> 32); + arr[4] = (byte) (val >>> 24); + arr[5] = (byte) (val >>> 16); + arr[6] = (byte) (val >>> 8); + arr[7] = (byte) (val); + return arr; + } + + /** Returns true if value is neither NaN nor infinite. */ + public static boolean isValidDouble(double value) { + if (Double.isNaN(value) || Double.isInfinite(value)) { + return false; + } + return true; + } + + /** Return the long that {@code n} stores, or throws an exception if the + * stored value cannot be converted to a long that stores the exact same + * value. */ + public static long toLongExact(Number n) { + if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof Long) { + return n.longValue(); + } else if (n instanceof Float || n instanceof Double) { + double d = n.doubleValue(); + if (d != Math.round(d)) { + throw new IllegalArgumentException(n + " is not an integer value"); + } + return n.longValue(); + } else if (n instanceof BigDecimal) { + return ((BigDecimal) n).toBigIntegerExact().longValueExact(); + } else if (n instanceof BigInteger) { + return ((BigInteger) n).longValueExact(); + } else { + throw new IllegalArgumentException( + "Cannot check whether [" + n + "] of class [" + n.getClass().getName() + "] is actually a long" + ); + } + } + + /** Return the {@link BigInteger} that {@code n} stores, or throws an exception if the + * stored value cannot be converted to a {@link BigInteger} that stores the exact same + * value. */ + public static BigInteger toBigIntegerExact(Number n) { + if (n instanceof Byte || n instanceof Short || n instanceof Integer || n instanceof Long) { + return BigInteger.valueOf(n.longValue()); + } else if (n instanceof Float || n instanceof Double) { + double d = n.doubleValue(); + if (d != Math.round(d)) { + throw new IllegalArgumentException(n + " is not an integer value"); + } + return BigInteger.valueOf(n.longValue()); + } else if (n instanceof BigDecimal) { + return ((BigDecimal) n).toBigIntegerExact(); + } else if (n instanceof BigInteger) { + return ((BigInteger) n); + } else { + throw new IllegalArgumentException("Cannot convert [" + n + "] of class [" + n.getClass().getName() + "] to a BigInteger"); + } + } + + /** Return the unsigned long (as {@link BigInteger}) that {@code n} stores, or throws an exception if the + * stored value cannot be converted to an unsigned long that stores the exact same + * value. */ + public static BigInteger toUnsignedLongExact(Number value) { + final BigInteger v = Numbers.toBigIntegerExact(value); + + if (v.compareTo(MAX_UNSIGNED_LONG_VALUE) > 0 || v.compareTo(MIN_UNSIGNED_LONG_VALUE) < 0) { + throw new IllegalArgumentException("Value [" + value + "] is out of range for an unsigned long"); + } + + return v; + } + + // weak bounds on the BigDecimal representation to allow for coercion + private static BigDecimal BIGDECIMAL_GREATER_THAN_LONG_MAX_VALUE = BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.ONE); + private static BigDecimal BIGDECIMAL_LESS_THAN_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE).subtract(BigDecimal.ONE); + private static BigDecimal BIGDECIMAL_GREATER_THAN_USIGNED_LONG_MAX_VALUE = new BigDecimal(MAX_UNSIGNED_LONG_VALUE).add(BigDecimal.ONE); + private static BigDecimal BIGDECIMAL_LESS_THAN_USIGNED_LONG_MIN_VALUE = new BigDecimal(MIN_UNSIGNED_LONG_VALUE).subtract( + BigDecimal.ONE + ); + + /** Return the long that {@code stringValue} stores or throws an exception if the + * stored value cannot be converted to a long that stores the exact same + * value and {@code coerce} is false. */ + public static long toLong(String stringValue, boolean coerce) { + try { + return Long.parseLong(stringValue); + } catch (NumberFormatException e) { + // we will try again with BigDecimal + } + + final BigInteger bigIntegerValue; + try { + BigDecimal bigDecimalValue = new BigDecimal(stringValue); + if (bigDecimalValue.compareTo(BIGDECIMAL_GREATER_THAN_LONG_MAX_VALUE) >= 0 + || bigDecimalValue.compareTo(BIGDECIMAL_LESS_THAN_LONG_MIN_VALUE) <= 0) { + throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for a long"); + } + bigIntegerValue = coerce ? bigDecimalValue.toBigInteger() : bigDecimalValue.toBigIntegerExact(); + } catch (ArithmeticException e) { + throw new IllegalArgumentException("Value [" + stringValue + "] has a decimal part"); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("For input string: \"" + stringValue + "\""); + } + + if (bigIntegerValue.compareTo(MAX_LONG_VALUE) > 0 || bigIntegerValue.compareTo(MIN_LONG_VALUE) < 0) { + throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for a long"); + } + + return bigIntegerValue.longValue(); + } + + /** Return the long that {@code stringValue} stores or throws an exception if the + * stored value cannot be converted to a long that stores the exact same + * value and {@code coerce} is false. */ + public static BigInteger toUnsignedLong(String stringValue, boolean coerce) { + final BigInteger bigIntegerValue; + try { + BigDecimal bigDecimalValue = new BigDecimal(stringValue); + if (bigDecimalValue.compareTo(BIGDECIMAL_GREATER_THAN_USIGNED_LONG_MAX_VALUE) >= 0 + || bigDecimalValue.compareTo(BIGDECIMAL_LESS_THAN_USIGNED_LONG_MIN_VALUE) <= 0) { + throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for an unsigned long"); + } + bigIntegerValue = coerce ? bigDecimalValue.toBigInteger() : bigDecimalValue.toBigIntegerExact(); + } catch (ArithmeticException e) { + throw new IllegalArgumentException("Value [" + stringValue + "] has a decimal part"); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("For input string: \"" + stringValue + "\""); + } + + if (bigIntegerValue.compareTo(MAX_UNSIGNED_LONG_VALUE) > 0 || bigIntegerValue.compareTo(MIN_UNSIGNED_LONG_VALUE) < 0) { + throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for an unsigned long"); + } + + return bigIntegerValue; + } + + /** Return the int that {@code n} stores, or throws an exception if the + * stored value cannot be converted to an int that stores the exact same + * value. */ + public static int toIntExact(Number n) { + return Math.toIntExact(toLongExact(n)); + } + + /** Return the short that {@code n} stores, or throws an exception if the + * stored value cannot be converted to a short that stores the exact same + * value. */ + public static short toShortExact(Number n) { + long l = toLongExact(n); + if (l != (short) l) { + throw new ArithmeticException("short overflow: " + l); + } + return (short) l; + } + + /** Return the byte that {@code n} stores, or throws an exception if the + * stored value cannot be converted to a byte that stores the exact same + * value. */ + public static byte toByteExact(Number n) { + long l = toLongExact(n); + if (l != (byte) l) { + throw new ArithmeticException("byte overflow: " + l); + } + return (byte) l; + } + + /** + * Return a BigInteger equal to the unsigned value of the + * argument. + */ + public static BigInteger toUnsignedBigInteger(long i) { + if (i >= 0L) return BigInteger.valueOf(i); + else { + int upper = (int) (i >>> 32); + int lower = (int) i; + + // return (upper << 32) + lower + return (BigInteger.valueOf(Integer.toUnsignedLong(upper))).shiftLeft(32).add(BigInteger.valueOf(Integer.toUnsignedLong(lower))); + } + } + + /** + * Convert unsigned long to double value (see please Guava's com.google.common.primitives.UnsignedLong), + * this is faster then going through {@link #toUnsignedBigInteger(long)} conversion. + */ + public static double unsignedLongToDouble(long value) { + if (value >= 0) { + return (double) value; + } + + // The top bit is set, which means that the double value is going to come from the top 53 bits. + // So we can ignore the bottom 11, except for rounding. We can unsigned-shift right 1, aka + // unsigned-divide by 2, and convert that. Then we'll get exactly half of the desired double + // value. But in the specific case where the bottom two bits of the original number are 01, we + // want to replace that with 1 in the shifted value for correct rounding. + return (double) ((value >>> 1) | (value & 1)) * 2.0; + } + + /** + * Return the strictly greater next power of two for the given value. + * For zero and negative numbers, it returns 1. + */ + public static long nextPowerOfTwo(long value) { + return 1L << (Long.SIZE - Long.numberOfLeadingZeros(value)); + } +} diff --git a/server/src/main/java/org/opensearch/common/RandomBasedUUIDGenerator.java b/libs/common/src/main/java/org/opensearch/common/RandomBasedUUIDGenerator.java similarity index 76% rename from server/src/main/java/org/opensearch/common/RandomBasedUUIDGenerator.java rename to libs/common/src/main/java/org/opensearch/common/RandomBasedUUIDGenerator.java index e8b79f33f2313..f83ef930688f8 100644 --- a/server/src/main/java/org/opensearch/common/RandomBasedUUIDGenerator.java +++ b/libs/common/src/main/java/org/opensearch/common/RandomBasedUUIDGenerator.java @@ -32,12 +32,14 @@ package org.opensearch.common; -import org.opensearch.common.settings.SecureString; - -import java.util.Arrays; import java.util.Base64; import java.util.Random; +/** + * Random UUID generator. + * + * @opensearch.internal + */ class RandomBasedUUIDGenerator implements UUIDGenerator { /** @@ -49,27 +51,6 @@ public String getBase64UUID() { return getBase64UUID(SecureRandomHolder.INSTANCE); } - /** - * Returns a Base64 encoded {@link SecureString} of a Version 4.0 compatible UUID - * as defined here: http://www.ietf.org/rfc/rfc4122.txt - */ - public SecureString getBase64UUIDSecureString() { - byte[] uuidBytes = null; - byte[] encodedBytes = null; - try { - uuidBytes = getUUIDBytes(SecureRandomHolder.INSTANCE); - encodedBytes = Base64.getUrlEncoder().withoutPadding().encode(uuidBytes); - return new SecureString(CharArrays.utf8BytesToChars(encodedBytes)); - } finally { - if (uuidBytes != null) { - Arrays.fill(uuidBytes, (byte) 0); - } - if (encodedBytes != null) { - Arrays.fill(encodedBytes, (byte) 0); - } - } - } - /** * Returns a Base64 encoded version of a Version 4.0 compatible UUID * randomly initialized by the given {@link java.util.Random} instance diff --git a/server/src/main/java/org/opensearch/common/SecureRandomHolder.java b/libs/common/src/main/java/org/opensearch/common/SecureRandomHolder.java similarity index 95% rename from server/src/main/java/org/opensearch/common/SecureRandomHolder.java rename to libs/common/src/main/java/org/opensearch/common/SecureRandomHolder.java index 37076f8f4e6e4..14844293b3274 100644 --- a/server/src/main/java/org/opensearch/common/SecureRandomHolder.java +++ b/libs/common/src/main/java/org/opensearch/common/SecureRandomHolder.java @@ -34,6 +34,11 @@ import java.security.SecureRandom; +/** + * Random holder that is secure. + * + * @opensearch.internal + */ class SecureRandomHolder { // class loading is atomic - this is a lazy & safe singleton to be used by this package public static final SecureRandom INSTANCE = new SecureRandom(); diff --git a/libs/common/src/main/java/org/opensearch/common/SetOnce.java b/libs/common/src/main/java/org/opensearch/common/SetOnce.java new file mode 100644 index 0000000000000..a596b5fcdb61d --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/SetOnce.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ +package org.opensearch.common; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * A convenient class which offers a semi-immutable object wrapper implementation which allows one + * to set the value of an object exactly once, and retrieve it many times. If {@link #set(Object)} + * is called more than once, {@link AlreadySetException} is thrown and the operation will fail. + * + * This is borrowed from lucene's experimental API. It is not reused to eliminate the dependency + * on lucene core for such a simple (standalone) utility class that may change beyond OpenSearch needs. + * + * @opensearch.api + */ +public final class SetOnce implements Cloneable { + + /** Thrown when {@link SetOnce#set(Object)} is called more than once. */ + public static final class AlreadySetException extends IllegalStateException { + public AlreadySetException() { + super("The object cannot be set twice!"); + } + } + + /** Holding object and marking that it was already set */ + private static final class Wrapper { + private T object; + + private Wrapper(T object) { + this.object = object; + } + } + + private final AtomicReference> set; + + /** + * A default constructor which does not set the internal object, and allows setting it by calling + * {@link #set(Object)}. + */ + public SetOnce() { + set = new AtomicReference<>(); + } + + /** + * Creates a new instance with the internal object set to the given object. Note that any calls to + * {@link #set(Object)} afterwards will result in {@link AlreadySetException} + * + * @throws AlreadySetException if called more than once + * @see #set(Object) + */ + public SetOnce(T obj) { + set = new AtomicReference<>(new Wrapper<>(obj)); + } + + /** Sets the given object. If the object has already been set, an exception is thrown. */ + public final void set(T obj) { + if (!trySet(obj)) { + throw new AlreadySetException(); + } + } + + /** + * Sets the given object if none was set before. + * + * @return true if object was set successfully, false otherwise + */ + public final boolean trySet(T obj) { + return set.compareAndSet(null, new Wrapper<>(obj)); + } + + /** Returns the object set by {@link #set(Object)}. */ + public final T get() { + Wrapper wrapper = set.get(); + return wrapper == null ? null : wrapper.object; + } +} diff --git a/server/src/main/java/org/opensearch/common/StopWatch.java b/libs/common/src/main/java/org/opensearch/common/StopWatch.java similarity index 96% rename from server/src/main/java/org/opensearch/common/StopWatch.java rename to libs/common/src/main/java/org/opensearch/common/StopWatch.java index 973277fae65b7..dea1b2b695ace 100644 --- a/server/src/main/java/org/opensearch/common/StopWatch.java +++ b/libs/common/src/main/java/org/opensearch/common/StopWatch.java @@ -32,7 +32,6 @@ package org.opensearch.common; -import org.opensearch.common.lease.Releasable; import org.opensearch.common.unit.TimeValue; import java.text.NumberFormat; @@ -54,7 +53,7 @@ * This class is normally used to verify performance during proof-of-concepts * and in development, rather than as part of production applications. * - * + * @opensearch.internal */ public class StopWatch { @@ -155,7 +154,7 @@ public StopWatch stop() throws IllegalStateException { return this; } - public Releasable timing(String taskName) { + public TimingHandle timing(String taskName) { start(taskName); return this::stop; } @@ -239,6 +238,8 @@ public String toString() { /** * Inner class to hold data about one task executed within the stop watch. + * + * @opensearch.internal */ public static class TaskInfo { @@ -266,4 +267,14 @@ public TimeValue getTime() { } } + /** + * Stops the watch and auto calls close in try-with-resources usage + * + * @opensearch.internal + */ + public interface TimingHandle extends AutoCloseable { + @Override + void close(); + } + } diff --git a/libs/core/src/main/java/org/opensearch/common/SuppressForbidden.java b/libs/common/src/main/java/org/opensearch/common/SuppressForbidden.java similarity index 98% rename from libs/core/src/main/java/org/opensearch/common/SuppressForbidden.java rename to libs/common/src/main/java/org/opensearch/common/SuppressForbidden.java index ed4045dd1ef3c..1f1b28bcf6759 100644 --- a/libs/core/src/main/java/org/opensearch/common/SuppressForbidden.java +++ b/libs/common/src/main/java/org/opensearch/common/SuppressForbidden.java @@ -38,6 +38,8 @@ /** * Annotation to suppress forbidden-apis errors inside a whole class, a method, or a field. + * + * @opensearch.api */ @Retention(RetentionPolicy.CLASS) @Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE }) diff --git a/server/src/main/java/org/opensearch/common/SuppressLoggerChecks.java b/libs/common/src/main/java/org/opensearch/common/SuppressLoggerChecks.java similarity index 98% rename from server/src/main/java/org/opensearch/common/SuppressLoggerChecks.java rename to libs/common/src/main/java/org/opensearch/common/SuppressLoggerChecks.java index d93743e03f9db..75d3b63a1841d 100644 --- a/server/src/main/java/org/opensearch/common/SuppressLoggerChecks.java +++ b/libs/common/src/main/java/org/opensearch/common/SuppressLoggerChecks.java @@ -39,6 +39,8 @@ /** * Annotation to suppress logging usage checks errors inside a whole class or a method. + * + * @opensearch.internal */ @Retention(RetentionPolicy.CLASS) @Target({ ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE }) diff --git a/server/src/main/java/org/opensearch/common/TimeBasedUUIDGenerator.java b/libs/common/src/main/java/org/opensearch/common/TimeBasedUUIDGenerator.java similarity index 99% rename from server/src/main/java/org/opensearch/common/TimeBasedUUIDGenerator.java rename to libs/common/src/main/java/org/opensearch/common/TimeBasedUUIDGenerator.java index f76f739787f63..b00a4383b388e 100644 --- a/server/src/main/java/org/opensearch/common/TimeBasedUUIDGenerator.java +++ b/libs/common/src/main/java/org/opensearch/common/TimeBasedUUIDGenerator.java @@ -42,6 +42,8 @@ * structured. * For more information about flake ids, check out * https://archive.fo/2015.07.08-082503/http://www.boundary.com/blog/2012/01/flake-a-decentralized-k-ordered-unique-id-generator-in-erlang/ + * + * @opensearch.internal */ class TimeBasedUUIDGenerator implements UUIDGenerator { diff --git a/server/src/main/java/org/opensearch/common/TriConsumer.java b/libs/common/src/main/java/org/opensearch/common/TriConsumer.java similarity index 98% rename from server/src/main/java/org/opensearch/common/TriConsumer.java rename to libs/common/src/main/java/org/opensearch/common/TriConsumer.java index 2a41b73ad5388..f98276b6d007d 100644 --- a/server/src/main/java/org/opensearch/common/TriConsumer.java +++ b/libs/common/src/main/java/org/opensearch/common/TriConsumer.java @@ -38,6 +38,8 @@ * @param the type of the first argument * @param the type of the second argument * @param the type of the third argument + * + * @opensearch.internal */ @FunctionalInterface public interface TriConsumer { diff --git a/server/src/main/java/org/opensearch/common/TriFunction.java b/libs/common/src/main/java/org/opensearch/common/TriFunction.java similarity index 98% rename from server/src/main/java/org/opensearch/common/TriFunction.java rename to libs/common/src/main/java/org/opensearch/common/TriFunction.java index 2ce82eb49aa37..7b1bbece68680 100644 --- a/server/src/main/java/org/opensearch/common/TriFunction.java +++ b/libs/common/src/main/java/org/opensearch/common/TriFunction.java @@ -39,6 +39,8 @@ * @param the type of the second argument * @param the type of the third argument * @param the return type + * + * @opensearch.internal */ @FunctionalInterface public interface TriFunction { diff --git a/server/src/main/java/org/opensearch/common/UUIDGenerator.java b/libs/common/src/main/java/org/opensearch/common/UUIDGenerator.java similarity index 97% rename from server/src/main/java/org/opensearch/common/UUIDGenerator.java rename to libs/common/src/main/java/org/opensearch/common/UUIDGenerator.java index bf94d8346e842..8705f415216d8 100644 --- a/server/src/main/java/org/opensearch/common/UUIDGenerator.java +++ b/libs/common/src/main/java/org/opensearch/common/UUIDGenerator.java @@ -34,6 +34,8 @@ /** * Generates opaque unique strings. + * + * @opensearch.internal */ interface UUIDGenerator { String getBase64UUID(); diff --git a/server/src/main/java/org/opensearch/common/UUIDs.java b/libs/common/src/main/java/org/opensearch/common/UUIDs.java similarity index 77% rename from server/src/main/java/org/opensearch/common/UUIDs.java rename to libs/common/src/main/java/org/opensearch/common/UUIDs.java index 5ddb628c237fa..c2e1357a67029 100644 --- a/server/src/main/java/org/opensearch/common/UUIDs.java +++ b/libs/common/src/main/java/org/opensearch/common/UUIDs.java @@ -32,14 +32,17 @@ package org.opensearch.common; -import org.opensearch.common.settings.SecureString; - import java.util.Random; +/** + * UUID utility class. + * + * @opensearch.internal + */ public class UUIDs { private static final RandomBasedUUIDGenerator RANDOM_UUID_GENERATOR = new RandomBasedUUIDGenerator(); - private static final UUIDGenerator LEGACY_TIME_UUID_GENERATOR = new LegacyTimeBasedUUIDGenerator(); + private static final UUIDGenerator TIME_UUID_GENERATOR = new TimeBasedUUIDGenerator(); /** Generates a time-based UUID (similar to Flake IDs), which is preferred when generating an ID to be indexed into a Lucene index as @@ -48,11 +51,6 @@ public static String base64UUID() { return TIME_UUID_GENERATOR.getBase64UUID(); } - /** Legacy implementation of {@link #base64UUID()}, for pre 6.0 indices. */ - public static String legacyBase64UUID() { - return LEGACY_TIME_UUID_GENERATOR.getBase64UUID(); - } - /** Returns a Base64 encoded version of a Version 4.0 compatible UUID as defined here: http://www.ietf.org/rfc/rfc4122.txt, using the * provided {@code Random} instance */ public static String randomBase64UUID(Random random) { @@ -64,10 +62,4 @@ public static String randomBase64UUID(Random random) { public static String randomBase64UUID() { return RANDOM_UUID_GENERATOR.getBase64UUID(); } - - /** Returns a Base64 encoded {@link SecureString} of a Version 4.0 compatible UUID as defined here: http://www.ietf.org/rfc/rfc4122.txt, - * using a private {@code SecureRandom} instance */ - public static SecureString randomBase64UUIDSecureString() { - return RANDOM_UUID_GENERATOR.getBase64UUIDSecureString(); - } } diff --git a/server/src/main/java/org/opensearch/common/ValidationException.java b/libs/common/src/main/java/org/opensearch/common/ValidationException.java similarity index 98% rename from server/src/main/java/org/opensearch/common/ValidationException.java rename to libs/common/src/main/java/org/opensearch/common/ValidationException.java index 979d2be5ae148..09e4344df1b80 100644 --- a/server/src/main/java/org/opensearch/common/ValidationException.java +++ b/libs/common/src/main/java/org/opensearch/common/ValidationException.java @@ -37,6 +37,8 @@ /** * Encapsulates an accumulation of validation errors + * + * @opensearch.internal */ public class ValidationException extends IllegalArgumentException { private final List validationErrors = new ArrayList<>(); diff --git a/server/src/main/java/org/opensearch/action/ActionFuture.java b/libs/common/src/main/java/org/opensearch/common/action/ActionFuture.java similarity index 95% rename from server/src/main/java/org/opensearch/action/ActionFuture.java rename to libs/common/src/main/java/org/opensearch/common/action/ActionFuture.java index b1795ecf30b11..827b7dfd51705 100644 --- a/server/src/main/java/org/opensearch/action/ActionFuture.java +++ b/libs/common/src/main/java/org/opensearch/common/action/ActionFuture.java @@ -30,8 +30,9 @@ * GitHub history for details. */ -package org.opensearch.action; +package org.opensearch.common.action; +import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.unit.TimeValue; import java.util.concurrent.Future; @@ -40,8 +41,9 @@ /** * An extension to {@link Future} allowing for simplified "get" operations. * - * + * @opensearch.internal */ +@PublicApi(since = "1.0.0") public interface ActionFuture extends Future { /** diff --git a/libs/common/src/main/java/org/opensearch/common/action/package-info.java b/libs/common/src/main/java/org/opensearch/common/action/package-info.java new file mode 100644 index 0000000000000..4ed2687c80cc9 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/action/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** foundation action classes used across the code base */ +package org.opensearch.common.action; diff --git a/libs/common/src/main/java/org/opensearch/common/annotation/DeprecatedApi.java b/libs/common/src/main/java/org/opensearch/common/annotation/DeprecatedApi.java new file mode 100644 index 0000000000000..964380e1a26ce --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/annotation/DeprecatedApi.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Marks the public APIs as deprecated and scheduled for removal in one of the upcoming + * major releases. The types marked with this annotations could only be other {@link PublicApi}s. + * + * @opensearch.api + */ +@Documented +@Target({ + ElementType.TYPE, + ElementType.PACKAGE, + ElementType.METHOD, + ElementType.CONSTRUCTOR, + ElementType.PARAMETER, + ElementType.FIELD, + ElementType.ANNOTATION_TYPE, + ElementType.MODULE }) +@PublicApi(since = "2.10.0") +public @interface DeprecatedApi { + /** + * Version since this API is deprecated + */ + String since(); + + /** + * Next major version when this API is scheduled for removal + */ + String forRemoval() default ""; +} diff --git a/libs/common/src/main/java/org/opensearch/common/annotation/ExperimentalApi.java b/libs/common/src/main/java/org/opensearch/common/annotation/ExperimentalApi.java new file mode 100644 index 0000000000000..001ffd6eb720a --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/annotation/ExperimentalApi.java @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Experimental APIs that may not retain source and binary compatibility within major, + * minor or patch releases. The types marked with this annotations could only expose + * other {@link PublicApi} or {@link ExperimentalApi} types as public members. + * + * @opensearch.api + */ +@Documented +@Target({ + ElementType.TYPE, + ElementType.PACKAGE, + ElementType.METHOD, + ElementType.CONSTRUCTOR, + ElementType.PARAMETER, + ElementType.FIELD, + ElementType.ANNOTATION_TYPE, + ElementType.MODULE }) +@PublicApi(since = "2.10.0") +public @interface ExperimentalApi { + +} diff --git a/libs/common/src/main/java/org/opensearch/common/annotation/InternalApi.java b/libs/common/src/main/java/org/opensearch/common/annotation/InternalApi.java new file mode 100644 index 0000000000000..ae58c49e58c5e --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/annotation/InternalApi.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Internal APIs that have no compatibility guarantees and should be not used outside + * of OpenSearch core components. + * + * @opensearch.api + */ +@Documented +@Target({ + ElementType.TYPE, + ElementType.PACKAGE, + ElementType.METHOD, + ElementType.CONSTRUCTOR, + ElementType.PARAMETER, + ElementType.FIELD, + ElementType.ANNOTATION_TYPE, + ElementType.MODULE }) +@PublicApi(since = "2.10.0") +public @interface InternalApi { + +} diff --git a/libs/common/src/main/java/org/opensearch/common/annotation/PublicApi.java b/libs/common/src/main/java/org/opensearch/common/annotation/PublicApi.java new file mode 100644 index 0000000000000..33862446d6442 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/annotation/PublicApi.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Stable public APIs that retain source and binary compatibility within a major release. + * These interfaces can change from one major release to another major release + * (e.g. from 1.0 to 2.0). The types marked with this annotations could only expose + * other {@link PublicApi} or {@link ExperimentalApi} types as public members. + * + * @opensearch.api + */ +@Documented +@Target({ + ElementType.TYPE, + ElementType.PACKAGE, + ElementType.METHOD, + ElementType.CONSTRUCTOR, + ElementType.PARAMETER, + ElementType.FIELD, + ElementType.ANNOTATION_TYPE, + ElementType.MODULE }) +@PublicApi(since = "2.10.0") +public @interface PublicApi { + /** + * Version when this API was released + */ + String since(); +} diff --git a/libs/common/src/main/java/org/opensearch/common/annotation/package-info.java b/libs/common/src/main/java/org/opensearch/common/annotation/package-info.java new file mode 100644 index 0000000000000..7bb79d7579747 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/annotation/package-info.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * The OpenSearch API related annotations + * + * @opensearch.api + */ +@PublicApi(since = "2.10.0") +package org.opensearch.common.annotation; diff --git a/libs/common/src/main/java/org/opensearch/common/collect/Iterators.java b/libs/common/src/main/java/org/opensearch/common/collect/Iterators.java new file mode 100644 index 0000000000000..9b64932356c10 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/collect/Iterators.java @@ -0,0 +1,111 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common.collect; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Iterators utility class. + * + * @opensearch.internal + */ +public class Iterators { + + /** + * Concat iterators + * + * @param iterators the iterators to concat + * @param the type of iterator + * @return a new {@link ConcatenatedIterator} + * @throws NullPointerException if iterators is null + */ + public static Iterator concat(Iterator... iterators) { + if (iterators == null) { + throw new NullPointerException("iterators"); + } + + // explicit generic type argument needed for type inference + return new ConcatenatedIterator(iterators); + } + + /** + * Concat iterators + * + * @opensearch.internal + */ + static class ConcatenatedIterator implements Iterator { + private final Iterator[] iterators; + private int index = 0; + + ConcatenatedIterator(Iterator... iterators) { + if (iterators == null) { + throw new NullPointerException("iterators"); + } + for (int i = 0; i < iterators.length; i++) { + if (iterators[i] == null) { + throw new NullPointerException("iterators[" + i + "]"); + } + } + this.iterators = iterators; + } + + /** + * Returns {@code true} if the iteration has more elements. (In other words, returns {@code true} if {@link #next} would return an + * element rather than throwing an exception.) + * @return {@code true} if the iteration has more elements + */ + @Override + public boolean hasNext() { + boolean hasNext = false; + while (index < iterators.length && !(hasNext = iterators[index].hasNext())) { + index++; + } + + return hasNext; + } + + /** + * Returns the next element in the iteration. + * @return the next element in the iteration + * @throws NoSuchElementException if the iteration has no more elements + */ + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return iterators[index].next(); + } + } +} diff --git a/libs/common/src/main/java/org/opensearch/common/collect/List.java b/libs/common/src/main/java/org/opensearch/common/collect/List.java new file mode 100644 index 0000000000000..07cfc2c019856 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/collect/List.java @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common.collect; + +import java.util.Collection; + +/** + * Java 9 List + * + * + * @opensearch.internal + */ +@Deprecated(forRemoval = true) +public class List { + + /** + * Delegates to the Java9 {@code List.of()} method. + * + * @param the {@code List}'s element type + * @return an empty {@code List} + */ + public static java.util.List of() { + return java.util.List.of(); + } + + /** + * Delegates to the Java9 {@code List.of()} method. + * + * @param the {@code List}'s element type + * @param e1 the single element + * @return a {@code List} containing the specified element + */ + public static java.util.List of(T e1) { + return java.util.List.of(e1); + } + + /** + * Delegates to the Java9 {@code List.of()} method. + * + * @param the {@code List}'s element type + * @param e1 the single element + * @return a {@code List} containing the specified element + */ + public static java.util.List of(T e1, T e2) { + return java.util.List.of(e1, e2); + } + + /** + * Delegates to the Java9 {@code List.of()} method. + * + * @param entries the elements to be contained in the list + * @param the {@code List}'s element type + * @return an unmodifiable list containing the specified elements. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public static java.util.List of(T... entries) { + return java.util.List.of(entries); + } + + /** + * Delegates to the Java9 {@code List.copyOf()} method. + * + * @param the {@code List}'s element type + * @param coll a {@code Collection} from which elements are drawn, must be non-null + * @return a {@code List} containing the elements of the given {@code Collection} + */ + public static java.util.List copyOf(Collection coll) { + return java.util.List.copyOf(coll); + } +} diff --git a/libs/common/src/main/java/org/opensearch/common/collect/Map.java b/libs/common/src/main/java/org/opensearch/common/collect/Map.java new file mode 100644 index 0000000000000..3913c0fd942a4 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/collect/Map.java @@ -0,0 +1,201 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common.collect; + +/** + * Java 9 Map + * + * + * @opensearch.internal + */ +@Deprecated(forRemoval = true) +public class Map { + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of() { + return java.util.Map.of(); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of(K k1, V v1) { + return java.util.Map.of(k1, v1); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of(K k1, V v1, K k2, V v2) { + return java.util.Map.of(k1, v1, k2, v2); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3) { + return java.util.Map.of(k1, v1, k2, v2, k3, v3); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { + return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { + return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { + return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8 + ) { + return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9 + ) { + return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9); + } + + /** + * Delegates to the Java9 {@code Map.of()} method. + */ + public static java.util.Map of( + K k1, + V v1, + K k2, + V v2, + K k3, + V v3, + K k4, + V v4, + K k5, + V v5, + K k6, + V v6, + K k7, + V v7, + K k8, + V v8, + K k9, + V v9, + K k10, + V v10 + ) { + return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10); + } + + /** + * Delegates to the Java9 {@code Map.ofEntries()} method. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public static java.util.Map ofEntries(java.util.Map.Entry... entries) { + return java.util.Map.ofEntries(entries); + } + + /** + * Delegates to the Java9 {@code Map.entry()} method. + */ + public static java.util.Map.Entry entry(K k, V v) { + return java.util.Map.entry(k, v); + } + + /** + * Delegates to the Java10 {@code Map.copyOf()} method. + */ + public static java.util.Map copyOf(java.util.Map map) { + return java.util.Map.copyOf(map); + } + +} diff --git a/server/src/main/java/org/opensearch/common/collect/MapBuilder.java b/libs/common/src/main/java/org/opensearch/common/collect/MapBuilder.java similarity index 97% rename from server/src/main/java/org/opensearch/common/collect/MapBuilder.java rename to libs/common/src/main/java/org/opensearch/common/collect/MapBuilder.java index 39e95d46e3a4a..7a28f02ab4b43 100644 --- a/server/src/main/java/org/opensearch/common/collect/MapBuilder.java +++ b/libs/common/src/main/java/org/opensearch/common/collect/MapBuilder.java @@ -37,6 +37,11 @@ import static java.util.Collections.unmodifiableMap; +/** + * Builder for a map. + * + * @opensearch.internal + */ public class MapBuilder { public static MapBuilder newMapBuilder() { diff --git a/libs/common/src/main/java/org/opensearch/common/collect/Set.java b/libs/common/src/main/java/org/opensearch/common/collect/Set.java new file mode 100644 index 0000000000000..0c3899b2aaacd --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/collect/Set.java @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common.collect; + +import java.util.Collection; + +/** + * Java 9 Set + * + * + * @opensearch.internal + */ +@Deprecated(forRemoval = true) +public class Set { + + /** + * Delegates to the Java9 {@code Set.of()} method. + * + * @param the {@code Set}'s element type + * @return an empty {@code Set} + */ + public static java.util.Set of() { + return java.util.Set.of(); + } + + /** + * Delegates to the Java9 {@code Set.of()} method. + * + * @param the {@code Set}'s element type + * @param e1 the single element + * @return a {@code Set} containing the specified element + */ + public static java.util.Set of(T e1) { + return java.util.Set.of(e1); + } + + /** + * Delegates to the Java9 {@code Set.of()} method. + * + * @param the {@code Set}'s element type + * @param e1 the first element + * @param e2 the second element + * @return a {@code Set} containing the specified element + */ + public static java.util.Set of(T e1, T e2) { + return java.util.Set.of(e1, e2); + } + + /** + * Delegates to the Java9 {@code Set.of()} method. + * + * @param entries the elements to be contained in the set + * @param the {@code Set}'s element type + * @return an unmodifiable set containing the specified elements. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public static java.util.Set of(T... entries) { + return java.util.Set.of(entries); + } + + /** + * Delegates to the Java10 {@code Set.copyOf} method. + * + * @param the {@code Set}'s element type + * @param coll a {@code Collection} from which elements are drawn, must be non-null + * @return a {@code Set} containing the elements of the given {@code Collection} + */ + public static java.util.Set copyOf(Collection coll) { + return java.util.Set.copyOf(coll); + } +} diff --git a/libs/common/src/main/java/org/opensearch/common/collect/Tuple.java b/libs/common/src/main/java/org/opensearch/common/collect/Tuple.java new file mode 100644 index 0000000000000..d0b94536b0729 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/collect/Tuple.java @@ -0,0 +1,110 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common.collect; + +/** + * Java 9 Tuple + * + * todo: deprecate and remove w/ min jdk upgrade to 11? + * + * @opensearch.internal + */ +public class Tuple { + + public static Tuple tuple(V1 v1, V2 v2) { + return new Tuple<>(v1, v2); + } + + private final V1 v1; + private final V2 v2; + + public Tuple(V1 v1, V2 v2) { + this.v1 = v1; + this.v2 = v2; + } + + public V1 v1() { + return v1; + } + + public V2 v2() { + return v2; + } + + /** + * Returns {@code true} if the given object is also a tuple and the two tuples + * have equal {@link #v1()} and {@link #v2()} values. + *

    + * Returns {@code false} otherwise, including for {@code null} values or + * objects of different types. + *

    + * Note: {@code Tuple} instances are equal if the underlying values are + * equal, even if the types are different. + * + * @param o the object to compare to + * @return {@code true} if the given object is also a tuple and the two tuples + * have equal {@link #v1()} and {@link #v2()} values. + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Tuple tuple = (Tuple) o; + + if (v1 != null ? !v1.equals(tuple.v1) : tuple.v1 != null) return false; + if (v2 != null ? !v2.equals(tuple.v2) : tuple.v2 != null) return false; + + return true; + } + + /** + * Returns the hash code value for this Tuple. + * @return the hash code value for this Tuple. + */ + @Override + public int hashCode() { + int result = v1 != null ? v1.hashCode() : 0; + result = 31 * result + (v2 != null ? v2.hashCode() : 0); + return result; + } + + /** + * Returns a string representation of a Tuple + * @return {@code "Tuple [v1=value1, v2=value2]"} + */ + @Override + public String toString() { + return "Tuple [v1=" + v1 + ", v2=" + v2 + "]"; + } +} diff --git a/libs/common/src/main/java/org/opensearch/common/collect/package-info.java b/libs/common/src/main/java/org/opensearch/common/collect/package-info.java new file mode 100644 index 0000000000000..d08cfad1d178b --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/collect/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common collections classes used across opensearch. */ +package org.opensearch.common.collect; diff --git a/libs/core/src/main/java/org/opensearch/common/concurrent/CompletableContext.java b/libs/common/src/main/java/org/opensearch/common/concurrent/CompletableContext.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/concurrent/CompletableContext.java rename to libs/common/src/main/java/org/opensearch/common/concurrent/CompletableContext.java index 8565461cc74bd..f7910af51b599 100644 --- a/libs/core/src/main/java/org/opensearch/common/concurrent/CompletableContext.java +++ b/libs/common/src/main/java/org/opensearch/common/concurrent/CompletableContext.java @@ -41,6 +41,8 @@ * an exceptional result. This allows attaching listeners that only handle {@link Exception}. * * @param the result type + * + * @opensearch.api */ public class CompletableContext { diff --git a/libs/common/src/main/java/org/opensearch/common/concurrent/package-info.java b/libs/common/src/main/java/org/opensearch/common/concurrent/package-info.java new file mode 100644 index 0000000000000..d412fdfa4f5dc --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/concurrent/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common concurrency utilities used across opensearch. */ +package org.opensearch.common.concurrent; diff --git a/libs/common/src/main/java/org/opensearch/common/crypto/CryptoHandler.java b/libs/common/src/main/java/org/opensearch/common/crypto/CryptoHandler.java new file mode 100644 index 0000000000000..9572b5b9054b2 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/crypto/CryptoHandler.java @@ -0,0 +1,118 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.crypto; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.common.io.InputStreamContainer; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +/** + * Crypto provider abstractions for encryption and decryption of data. Allows registering multiple providers + * for defining different ways of encrypting or decrypting data. + * + * @param Encryption Metadata / CryptoContext + * @param Parsed Encryption Metadata / CryptoContext + */ +@ExperimentalApi +public interface CryptoHandler extends Closeable { + + /** + * To initialise or create a new crypto metadata to be used in encryption. This is needed to set the context before + * beginning encryption. + * + * @return crypto metadata instance + */ + T initEncryptionMetadata(); + + /** + * To load crypto metadata to be used in encryption from content header. + * Note that underlying information in the loaded metadata object is same as present in the object created during + * encryption but object type may differ. + * + * @param encryptedHeaderContentSupplier supplier for encrypted header content. + * @return crypto metadata instance used in decryption. + */ + U loadEncryptionMetadata(EncryptedHeaderContentSupplier encryptedHeaderContentSupplier) throws IOException; + + /** + * Few encryption algorithms have certain conditions on the unit of content to be encrypted. This requires the + * content size to be re adjusted in order to fulfil these conditions for partial writes. If write requests for + * encryption of a part of content do not fulfil these conditions then encryption fails or can result in corrupted + * content depending on the algorithm used. This method exposes a means to re-adjust sizes of such writes. + * + * @param cryptoContext crypto metadata instance + * @param contentSize Size of the raw content + * @return Adjusted size of the content. + */ + long adjustContentSizeForPartialEncryption(T cryptoContext, long contentSize); + + /** + * Estimate length of the encrypted content. It should only be used to determine length of entire content after + * encryption. + * + * @param cryptoContext crypto metadata instance consisting of encryption metadata used in encryption. + * @param contentLength Size of the raw content + * @return Calculated size of the encrypted content. + */ + long estimateEncryptedLengthOfEntireContent(T cryptoContext, long contentLength); + + /** + * For given encrypted content length, estimate the length of the decrypted content. + * @param cryptoContext crypto metadata instance consisting of encryption metadata used in encryption. + * @param contentLength Size of the encrypted content + * @return Calculated size of the decrypted content. + */ + long estimateDecryptedLength(U cryptoContext, long contentLength); + + /** + * Wraps a raw InputStream with encrypting stream + * + * @param encryptionMetadata created earlier to set the crypto metadata. + * @param stream Raw InputStream to encrypt + * @return encrypting stream wrapped around raw InputStream. + */ + InputStreamContainer createEncryptingStream(T encryptionMetadata, InputStreamContainer stream); + + /** + * Provides encrypted stream for a raw stream emitted for a part of content. + * + * @param cryptoContext crypto metadata instance. + * @param stream raw stream for which encrypted stream has to be created. + * @param totalStreams Number of streams being used for the entire content. + * @param streamIdx Index of the current stream. + * @return Encrypted stream for the provided raw stream. + */ + InputStreamContainer createEncryptingStreamOfPart(T cryptoContext, InputStreamContainer stream, int totalStreams, int streamIdx); + + /** + * This method accepts an encrypted stream and provides a decrypting wrapper. + * @param encryptingStream to be decrypted. + * @return Decrypting wrapper stream + */ + InputStream createDecryptingStream(InputStream encryptingStream); + + /** + * This method creates a {@link DecryptedRangedStreamProvider} which provides a wrapped stream to decrypt the + * underlying stream. This also provides adjusted range against the actual range which should be used for fetching + * and supplying the encrypted content for decryption. Extra content outside the range is trimmed down and returned + * by the decrypted stream. + * For partial reads of encrypted content, few algorithms require the range of content to be adjusted for + * successful decryption. Adjusted range may or may not be same as the provided range. If range is adjusted then + * starting offset of resultant range can be lesser than the starting offset of provided range and end + * offset can be greater than the ending offset of the provided range. + * + * @param cryptoContext crypto metadata instance. + * @param startPosOfRawContent starting position in the raw/decrypted content + * @param endPosOfRawContent ending position in the raw/decrypted content + */ + DecryptedRangedStreamProvider createDecryptingStreamOfRange(U cryptoContext, long startPosOfRawContent, long endPosOfRawContent); +} diff --git a/libs/common/src/main/java/org/opensearch/common/crypto/DataKeyPair.java b/libs/common/src/main/java/org/opensearch/common/crypto/DataKeyPair.java new file mode 100644 index 0000000000000..711c0d314ecef --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/crypto/DataKeyPair.java @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.common.crypto; + +/** + * Key pair generated by {@link MasterKeyProvider} + */ +public class DataKeyPair { + + /** Unencrypted data key used for encryption and decryption */ + private final byte[] rawKey; + /** Encrypted version of rawKey */ + private final byte[] encryptedKey; + + /** + * Constructor to initialize key-pair values + * @param rawKey Unencrypted data key used for encryption and decryption + * @param encryptedKey Encrypted version of rawKey + */ + public DataKeyPair(byte[] rawKey, byte[] encryptedKey) { + this.rawKey = rawKey; + this.encryptedKey = encryptedKey; + } + + /** + * Returns Unencrypted data key + * @return raw/decrypted key + */ + public byte[] getRawKey() { + return rawKey; + } + + /** + * Returns encrypted key + * @return encrypted key + */ + public byte[] getEncryptedKey() { + return encryptedKey; + } +} diff --git a/libs/common/src/main/java/org/opensearch/common/crypto/DecryptedRangedStreamProvider.java b/libs/common/src/main/java/org/opensearch/common/crypto/DecryptedRangedStreamProvider.java new file mode 100644 index 0000000000000..2cda3c1f8bdb4 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/crypto/DecryptedRangedStreamProvider.java @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.crypto; + +import java.io.InputStream; +import java.util.function.UnaryOperator; + +/** + * Contains adjusted range of partial encrypted content which needs to be used for decryption. + */ +public class DecryptedRangedStreamProvider { + + /** Adjusted range of partial encrypted content which needs to be used for decryption. */ + private final long[] adjustedRange; + /** Stream provider for decryption and range re-adjustment. */ + private final UnaryOperator decryptedStreamProvider; + + /** + * To construct adjusted encrypted range. + * @param adjustedRange range of partial encrypted content which needs to be used for decryption. + * @param decryptedStreamProvider stream provider for decryption and range re-adjustment. + */ + public DecryptedRangedStreamProvider(long[] adjustedRange, UnaryOperator decryptedStreamProvider) { + this.adjustedRange = adjustedRange; + this.decryptedStreamProvider = decryptedStreamProvider; + } + + /** + * Adjusted range of partial encrypted content which needs to be used for decryption. + * @return adjusted range + */ + public long[] getAdjustedRange() { + return adjustedRange; + } + + /** + * A utility stream provider which supplies the stream responsible for decrypting the content and reading the + * desired range of decrypted content by skipping extra content which got decrypted as a result of range adjustment. + * @return stream provider for decryption and supplying the desired range of content. + */ + public UnaryOperator getDecryptedStreamProvider() { + return decryptedStreamProvider; + } + +} diff --git a/libs/common/src/main/java/org/opensearch/common/crypto/EncryptedHeaderContentSupplier.java b/libs/common/src/main/java/org/opensearch/common/crypto/EncryptedHeaderContentSupplier.java new file mode 100644 index 0000000000000..49a037f05f185 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/crypto/EncryptedHeaderContentSupplier.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.common.crypto; + +import java.io.IOException; + +/** + * This is used in partial decryption. Header information is required for decryption of actual encrypted content. + * Implementation of this supplier only requires first few bytes of encrypted content to be supplied. + */ +public interface EncryptedHeaderContentSupplier { + + /** + * @param start Start position of the encrypted content (Generally supplied as 0 during usage) + * @param end End position of the header. + * @return Encrypted header content (May contain additional content which is later discarded) + * @throws IOException In case content fetch fails. + */ + byte[] supply(long start, long end) throws IOException; +} diff --git a/libs/common/src/main/java/org/opensearch/common/crypto/MasterKeyProvider.java b/libs/common/src/main/java/org/opensearch/common/crypto/MasterKeyProvider.java new file mode 100644 index 0000000000000..8afa48eb92c0f --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/crypto/MasterKeyProvider.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.common.crypto; + +import java.io.Closeable; +import java.util.Map; + +/** + * Master key provider responsible for management of master keys. + */ +public interface MasterKeyProvider extends Closeable { + + /** + * Returns data key pair + * @return data key pair generated by master key. + */ + DataKeyPair generateDataPair(); + + /** + * Returns decrypted key against the encrypted key. + * @param encryptedKey Key to decrypt + * @return Decrypted version of key. + */ + byte[] decryptKey(byte[] encryptedKey); + + /** + * Returns key id. + * @return key id + */ + String getKeyId(); + + /** + * Returns encryption context associated with this master key. + * @return encryption context associated with this master key. + */ + Map getEncryptionContext(); +} diff --git a/libs/common/src/main/java/org/opensearch/common/crypto/package-info.java b/libs/common/src/main/java/org/opensearch/common/crypto/package-info.java new file mode 100644 index 0000000000000..c744689ebf532 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/crypto/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common crypto utilities used across opensearch. */ +package org.opensearch.common.crypto; diff --git a/libs/common/src/main/java/org/opensearch/common/hash/T1ha1.java b/libs/common/src/main/java/org/opensearch/common/hash/T1ha1.java new file mode 100644 index 0000000000000..07b2306eda4e5 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/hash/T1ha1.java @@ -0,0 +1,277 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.hash; + +import org.opensearch.common.annotation.InternalApi; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; + +import static java.lang.Long.rotateRight; + +/** + * t1ha: Fast Positive Hash + * + *

    + * Implements t1ha1; + * a fast portable hash function with reasonable quality for checksums, hash tables, and thin fingerprinting. + * + *

    + * To overcome language and performance limitations, this implementation differs slightly from the + * reference implementation in C++, + * so the returned values may vary before JDK 18. + * + *

    + * Intended for little-endian systems but returns the same result on big-endian, albeit marginally slower. + * + * @opensearch.internal + */ +@InternalApi +public final class T1ha1 { + private static final long SEED = System.nanoTime(); + private static final Mux64 MUX_64_IMPL = fastestMux64Impl(); + + private static final VarHandle LONG_HANDLE = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle INT_HANDLE = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle SHORT_HANDLE = MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN); + + // "Magic" primes: + private static final long p0 = 0xEC99BF0D8372CAABL; + private static final long p1 = 0x82434FE90EDCEF39L; + private static final long p2 = 0xD4F06DB99D67BE4BL; + private static final long p3 = 0xBD9CACC22C6E9571L; + private static final long p4 = 0x9C06FAF4D023E3ABL; + private static final long p5 = 0xC060724A8424F345L; + private static final long p6 = 0xCB5AF53AE3AAAC31L; + + // Rotations: + private static final int s0 = 41; + private static final int s1 = 17; + private static final int s2 = 31; + + /** + * No public constructor. + */ + private T1ha1() {} + + /** + * Returns the hash code for the specified range of the given {@code byte} array. + * @param input the input byte array + * @param offset the starting offset + * @param length the length of the range + * @return hash code + */ + public static long hash(byte[] input, int offset, int length) { + return hash(input, offset, length, SEED); + } + + /** + * Returns the hash code for the specified range of the given {@code byte} array. + * @param input the input byte array + * @param offset the starting offset + * @param length the length of the range + * @param seed customized seed + * @return hash code + */ + public static long hash(byte[] input, int offset, int length, long seed) { + long a = seed; + long b = length; + + if (length > 32) { + long c = rotateRight(length, s1) + seed; + long d = length ^ rotateRight(seed, s1); + + do { + long w0 = fetch64(input, offset); + long w1 = fetch64(input, offset + 8); + long w2 = fetch64(input, offset + 16); + long w3 = fetch64(input, offset + 24); + + long d02 = w0 ^ rotateRight(w2 + d, s1); + long c13 = w1 ^ rotateRight(w3 + c, s1); + c += a ^ rotateRight(w0, s0); + d -= b ^ rotateRight(w1, s2); + a ^= p1 * (d02 + w3); + b ^= p0 * (c13 + w2); + + offset += 32; + length -= 32; + } while (length >= 32); + + a ^= p6 * (rotateRight(c, s1) + d); + b ^= p5 * (rotateRight(d, s1) + c); + } + + return h32(input, offset, length, a, b); + } + + /** + * Computes the hash of up to 32 bytes. + * Constants in the switch expression are dense; JVM will use them as indices into a table of + * instruction pointers (tableswitch instruction), making lookups really fast. + */ + @SuppressWarnings("fallthrough") + private static long h32(byte[] input, int offset, int length, long a, long b) { + switch (length) { + default: + b += mux64(fetch64(input, offset), p4); + offset += 8; + length -= 8; + case 24: + case 23: + case 22: + case 21: + case 20: + case 19: + case 18: + case 17: + a += mux64(fetch64(input, offset), p3); + offset += 8; + length -= 8; + case 16: + case 15: + case 14: + case 13: + case 12: + case 11: + case 10: + case 9: + b += mux64(fetch64(input, offset), p2); + offset += 8; + length -= 8; + case 8: + case 7: + case 6: + case 5: + case 4: + case 3: + case 2: + case 1: + a += mux64(tail64(input, offset, length), p1); + case 0: + // Final weak avalanche + return mux64(rotateRight(a + b, s1), p4) + mix64(a ^ b, p0); + } + } + + /** + * XOR the high and low parts of the full 128-bit product. + */ + private static long mux64(long a, long b) { + return MUX_64_IMPL.mux64(a, b); + } + + /** + * XOR-MUL-XOR bit-mixer. + */ + private static long mix64(long a, long b) { + a *= b; + return a ^ rotateRight(a, s0); + } + + /** + * Reads "length" bytes starting at "offset" in little-endian order; returned as long. + * It is assumed that the length is between 1 and 8 (inclusive); but no defensive checks are made as such. + */ + private static long tail64(byte[] input, int offset, int length) { + switch (length) { + case 1: + return fetch8(input, offset); + case 2: + return fetch16(input, offset); + case 3: + return fetch16(input, offset) | (fetch8(input, offset + 2) << 16); + case 4: + return fetch32(input, offset); + case 5: + return fetch32(input, offset) | (fetch8(input, offset + 4) << 32); + case 6: + return fetch32(input, offset) | (fetch16(input, offset + 4) << 32); + case 7: + // This is equivalent to: + // return fetch32(input, offset) | (fetch16(input, offset + 4) << 32) | (fetch8(input, offset + 6) << 48); + // But reading two ints overlapping by one byte is faster due to lesser instructions. + return fetch32(input, offset) | (fetch32(input, offset + 3) << 24); + default: + return fetch64(input, offset); + } + } + + /** + * Reads a 64-bit long. + */ + private static long fetch64(byte[] input, int offset) { + return (long) LONG_HANDLE.get(input, offset); + } + + /** + * Reads a 32-bit unsigned integer, returned as long. + */ + private static long fetch32(byte[] input, int offset) { + return (int) INT_HANDLE.get(input, offset) & 0xFFFFFFFFL; + } + + /** + * Reads a 16-bit unsigned short, returned as long. + */ + private static long fetch16(byte[] input, int offset) { + return (short) SHORT_HANDLE.get(input, offset) & 0xFFFFL; + } + + /** + * Reads an 8-bit unsigned byte, returned as long. + */ + private static long fetch8(byte[] input, int offset) { + return input[offset] & 0xFFL; + } + + /** + * The implementation of mux64. + */ + @FunctionalInterface + private interface Mux64 { + long mux64(long a, long b); + } + + /** + * Provides the fastest available implementation of mux64 on this platform. + * + *

    + * Ideally, the following should be returned to match the reference implementation: + * {@code Math.unsignedMultiplyHigh(a, b) ^ (a * b)} + * + *

    + * Since unsignedMultiplyHigh isn't available before JDK 18, and calculating it without intrinsics is quite slow, + * the multiplyHigh method is used instead. Slight loss in quality is imperceptible for our use-case: a hash table. + * {@code Math.multiplyHigh(a, b) ^ (a * b)} + * + *

    + * This indirection can be removed once we stop supporting older JDKs. + */ + private static Mux64 fastestMux64Impl() { + try { + final MethodHandle unsignedMultiplyHigh = MethodHandles.publicLookup() + .findStatic(Math.class, "unsignedMultiplyHigh", MethodType.methodType(long.class, long.class, long.class)); + return (a, b) -> { + try { + return (long) unsignedMultiplyHigh.invokeExact(a, b) ^ (a * b); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }; + } catch (NoSuchMethodException e) { + return (a, b) -> Math.multiplyHigh(a, b) ^ (a * b); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/libs/common/src/main/java/org/opensearch/common/hash/package-info.java b/libs/common/src/main/java/org/opensearch/common/hash/package-info.java new file mode 100644 index 0000000000000..bd393b8b921ed --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/hash/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Common hashing utilities. + */ +package org.opensearch.common.hash; diff --git a/libs/common/src/main/java/org/opensearch/common/io/InputStreamContainer.java b/libs/common/src/main/java/org/opensearch/common/io/InputStreamContainer.java new file mode 100644 index 0000000000000..eb8a4e1382497 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/io/InputStreamContainer.java @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.io; + +import java.io.InputStream; + +/** + * Model composed of an input stream and the total content length of the stream + * + * @opensearch.internal + */ +public class InputStreamContainer { + + private final InputStream inputStream; + private final long contentLength; + private final long offset; + + /** + * Construct a new stream object + * + * @param inputStream The input stream that is to be encapsulated + * @param contentLength The total content length that is to be read from the stream + */ + public InputStreamContainer(InputStream inputStream, long contentLength, long offset) { + this.inputStream = inputStream; + this.contentLength = contentLength; + this.offset = offset; + } + + /** + * @return The input stream this object is reading from + */ + public InputStream getInputStream() { + return inputStream; + } + + /** + * @return The total length of the content that has to be read from this stream + */ + public long getContentLength() { + return contentLength; + } + + /** + * @return offset of the source content. + */ + public long getOffset() { + return offset; + } +} diff --git a/libs/core/src/main/java/org/opensearch/common/io/PathUtils.java b/libs/common/src/main/java/org/opensearch/common/io/PathUtils.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/io/PathUtils.java rename to libs/common/src/main/java/org/opensearch/common/io/PathUtils.java index 85b623ef4d06d..b3526859933ec 100644 --- a/libs/core/src/main/java/org/opensearch/common/io/PathUtils.java +++ b/libs/common/src/main/java/org/opensearch/common/io/PathUtils.java @@ -46,6 +46,8 @@ *

    * This class allows the default filesystem to * be changed during tests. + * + * @opensearch.internal */ @SuppressForbidden(reason = "accesses the default filesystem by design") // TODO: can we move this to the .env package and make it package-private? diff --git a/libs/common/src/main/java/org/opensearch/common/io/package-info.java b/libs/common/src/main/java/org/opensearch/common/io/package-info.java new file mode 100644 index 0000000000000..e79aeb830761b --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/io/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common i/o utilities used across opensearch. */ +package org.opensearch.common.io; diff --git a/server/src/main/java/org/opensearch/common/lease/Releasable.java b/libs/common/src/main/java/org/opensearch/common/lease/Releasable.java similarity index 89% rename from server/src/main/java/org/opensearch/common/lease/Releasable.java rename to libs/common/src/main/java/org/opensearch/common/lease/Releasable.java index e7f854d7d10b2..30bea6185febc 100644 --- a/server/src/main/java/org/opensearch/common/lease/Releasable.java +++ b/libs/common/src/main/java/org/opensearch/common/lease/Releasable.java @@ -32,12 +32,12 @@ package org.opensearch.common.lease; -import org.opensearch.OpenSearchException; - import java.io.Closeable; /** - * Specialization of {@link AutoCloseable} that may only throw an {@link OpenSearchException}. + * Specialization of {@link AutoCloseable} for calls that might not throw a checked exception. + * + * @opensearch.internal */ public interface Releasable extends Closeable { diff --git a/server/src/main/java/org/opensearch/common/lease/Releasables.java b/libs/common/src/main/java/org/opensearch/common/lease/Releasables.java similarity index 97% rename from server/src/main/java/org/opensearch/common/lease/Releasables.java rename to libs/common/src/main/java/org/opensearch/common/lease/Releasables.java index bed26c6facd36..31bfa9a2dd7ab 100644 --- a/server/src/main/java/org/opensearch/common/lease/Releasables.java +++ b/libs/common/src/main/java/org/opensearch/common/lease/Releasables.java @@ -33,14 +33,18 @@ package org.opensearch.common.lease; import org.opensearch.common.Nullable; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import java.io.IOException; import java.io.UncheckedIOException; import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; -/** Utility methods to work with {@link Releasable}s. */ +/** + * Utility methods to work with {@link Releasable}s. + * + * @opensearch.internal + */ public enum Releasables { ; diff --git a/libs/common/src/main/java/org/opensearch/common/lease/package-info.java b/libs/common/src/main/java/org/opensearch/common/lease/package-info.java new file mode 100644 index 0000000000000..f7097486a9c64 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/lease/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Base Releasables package. */ +package org.opensearch.common.lease; diff --git a/server/src/main/java/org/opensearch/common/component/AbstractLifecycleComponent.java b/libs/common/src/main/java/org/opensearch/common/lifecycle/AbstractLifecycleComponent.java similarity index 96% rename from server/src/main/java/org/opensearch/common/component/AbstractLifecycleComponent.java rename to libs/common/src/main/java/org/opensearch/common/lifecycle/AbstractLifecycleComponent.java index cae81cb0931b3..111556fbe43cf 100644 --- a/server/src/main/java/org/opensearch/common/component/AbstractLifecycleComponent.java +++ b/libs/common/src/main/java/org/opensearch/common/lifecycle/AbstractLifecycleComponent.java @@ -30,13 +30,18 @@ * GitHub history for details. */ -package org.opensearch.common.component; +package org.opensearch.common.lifecycle; import java.io.IOException; import java.io.UncheckedIOException; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +/** + * Base class for a lifecycle component. + * + * @opensearch.internal + */ public abstract class AbstractLifecycleComponent implements LifecycleComponent { protected final Lifecycle lifecycle = new Lifecycle(); diff --git a/server/src/main/java/org/opensearch/common/component/Lifecycle.java b/libs/common/src/main/java/org/opensearch/common/lifecycle/Lifecycle.java similarity index 96% rename from server/src/main/java/org/opensearch/common/component/Lifecycle.java rename to libs/common/src/main/java/org/opensearch/common/lifecycle/Lifecycle.java index afeb3c97cc027..c1cf9b2998a13 100644 --- a/server/src/main/java/org/opensearch/common/component/Lifecycle.java +++ b/libs/common/src/main/java/org/opensearch/common/lifecycle/Lifecycle.java @@ -30,7 +30,9 @@ * GitHub history for details. */ -package org.opensearch.common.component; +package org.opensearch.common.lifecycle; + +import org.opensearch.common.annotation.PublicApi; /** * Lifecycle state. Allows the following transitions: @@ -72,9 +74,18 @@ * // perform close logic here * } * + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public class Lifecycle { + /** + * State in the lifecycle + * + * @opensearch.api + */ + @PublicApi(since = "1.0.0") public enum State { INITIALIZED, STOPPED, diff --git a/server/src/main/java/org/opensearch/common/component/LifecycleComponent.java b/libs/common/src/main/java/org/opensearch/common/lifecycle/LifecycleComponent.java similarity index 87% rename from server/src/main/java/org/opensearch/common/component/LifecycleComponent.java rename to libs/common/src/main/java/org/opensearch/common/lifecycle/LifecycleComponent.java index d0691c54dc220..781c276fefe13 100644 --- a/server/src/main/java/org/opensearch/common/component/LifecycleComponent.java +++ b/libs/common/src/main/java/org/opensearch/common/lifecycle/LifecycleComponent.java @@ -30,10 +30,17 @@ * GitHub history for details. */ -package org.opensearch.common.component; +package org.opensearch.common.lifecycle; +import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.lease.Releasable; +/** + * Base interface for a lifecycle component. + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") public interface LifecycleComponent extends Releasable { Lifecycle.State lifecycleState(); diff --git a/server/src/main/java/org/opensearch/common/component/LifecycleListener.java b/libs/common/src/main/java/org/opensearch/common/lifecycle/LifecycleListener.java similarity index 92% rename from server/src/main/java/org/opensearch/common/component/LifecycleListener.java rename to libs/common/src/main/java/org/opensearch/common/lifecycle/LifecycleListener.java index dedf9a4899739..7ac41a5eb0df0 100644 --- a/server/src/main/java/org/opensearch/common/component/LifecycleListener.java +++ b/libs/common/src/main/java/org/opensearch/common/lifecycle/LifecycleListener.java @@ -30,8 +30,13 @@ * GitHub history for details. */ -package org.opensearch.common.component; +package org.opensearch.common.lifecycle; +/** + * Base lifecycle listener. + * + * @opensearch.internal + */ public abstract class LifecycleListener { public void beforeStart() { diff --git a/libs/common/src/main/java/org/opensearch/common/lifecycle/package-info.java b/libs/common/src/main/java/org/opensearch/common/lifecycle/package-info.java new file mode 100644 index 0000000000000..1bedde5585e36 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/lifecycle/package-info.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Foundation implementation for a object lifecycle. + * + * See {@link org.opensearch.common.lifecycle.Lifecycle} for example usage + * + * @opensearch.internal + */ +package org.opensearch.common.lifecycle; diff --git a/server/src/main/java/org/opensearch/common/network/Cidrs.java b/libs/common/src/main/java/org/opensearch/common/network/Cidrs.java similarity index 99% rename from server/src/main/java/org/opensearch/common/network/Cidrs.java rename to libs/common/src/main/java/org/opensearch/common/network/Cidrs.java index 86034fa8cac9c..651ab8883a9aa 100644 --- a/server/src/main/java/org/opensearch/common/network/Cidrs.java +++ b/libs/common/src/main/java/org/opensearch/common/network/Cidrs.java @@ -36,6 +36,11 @@ import java.util.Locale; import java.util.Objects; +/** + * Network Cidrs + * + * @opensearch.internal + */ public final class Cidrs { private Cidrs() {} diff --git a/server/src/main/java/org/opensearch/common/network/InetAddresses.java b/libs/common/src/main/java/org/opensearch/common/network/InetAddresses.java similarity index 99% rename from server/src/main/java/org/opensearch/common/network/InetAddresses.java rename to libs/common/src/main/java/org/opensearch/common/network/InetAddresses.java index 87a18f41deecc..a4fbc6cb65b0d 100644 --- a/server/src/main/java/org/opensearch/common/network/InetAddresses.java +++ b/libs/common/src/main/java/org/opensearch/common/network/InetAddresses.java @@ -39,6 +39,11 @@ import java.util.Arrays; import java.util.Locale; +/** + * Network addresses. + * + * @opensearch.internal + */ public class InetAddresses { private static int IPV4_PART_COUNT = 4; private static int IPV6_PART_COUNT = 8; diff --git a/server/src/main/java/org/opensearch/common/network/NetworkAddress.java b/libs/common/src/main/java/org/opensearch/common/network/NetworkAddress.java similarity index 99% rename from server/src/main/java/org/opensearch/common/network/NetworkAddress.java rename to libs/common/src/main/java/org/opensearch/common/network/NetworkAddress.java index 324e35f683a11..f5bc3face6e45 100644 --- a/server/src/main/java/org/opensearch/common/network/NetworkAddress.java +++ b/libs/common/src/main/java/org/opensearch/common/network/NetworkAddress.java @@ -60,6 +60,8 @@ * This class provides sane address formatting instead, e.g. * {@code 127.0.0.1} and {@code ::1} respectively. No methods do reverse * lookups. + * + * @opensearch.internal */ public final class NetworkAddress { /** No instantiation */ diff --git a/libs/common/src/main/java/org/opensearch/common/network/package-info.java b/libs/common/src/main/java/org/opensearch/common/network/package-info.java new file mode 100644 index 0000000000000..92e4eac5bde42 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/network/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** common network layer classes used across the code base */ +package org.opensearch.common.network; diff --git a/libs/common/src/main/java/org/opensearch/common/package-info.java b/libs/common/src/main/java/org/opensearch/common/package-info.java new file mode 100644 index 0000000000000..f91bac5192f63 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common Library of Utilties and Data Structures used across OpenSearch. */ +package org.opensearch.common; diff --git a/server/src/main/java/org/opensearch/common/recycler/AbstractRecycler.java b/libs/common/src/main/java/org/opensearch/common/recycler/AbstractRecycler.java similarity index 96% rename from server/src/main/java/org/opensearch/common/recycler/AbstractRecycler.java rename to libs/common/src/main/java/org/opensearch/common/recycler/AbstractRecycler.java index 66adee7ae45f0..321062556ef86 100644 --- a/server/src/main/java/org/opensearch/common/recycler/AbstractRecycler.java +++ b/libs/common/src/main/java/org/opensearch/common/recycler/AbstractRecycler.java @@ -32,6 +32,11 @@ package org.opensearch.common.recycler; +/** + * Base recycler. + * + * @opensearch.internal + */ abstract class AbstractRecycler implements Recycler { protected final Recycler.C c; diff --git a/server/src/main/java/org/opensearch/common/recycler/AbstractRecyclerC.java b/libs/common/src/main/java/org/opensearch/common/recycler/AbstractRecyclerC.java similarity index 96% rename from server/src/main/java/org/opensearch/common/recycler/AbstractRecyclerC.java rename to libs/common/src/main/java/org/opensearch/common/recycler/AbstractRecyclerC.java index 16a85ad6eda7f..8957880c27cdf 100644 --- a/server/src/main/java/org/opensearch/common/recycler/AbstractRecyclerC.java +++ b/libs/common/src/main/java/org/opensearch/common/recycler/AbstractRecyclerC.java @@ -32,6 +32,11 @@ package org.opensearch.common.recycler; +/** + * Base recycler. + * + * @opensearch.internal + */ public abstract class AbstractRecyclerC implements Recycler.C { @Override diff --git a/server/src/main/java/org/opensearch/common/recycler/ConcurrentDequeRecycler.java b/libs/common/src/main/java/org/opensearch/common/recycler/ConcurrentDequeRecycler.java similarity index 98% rename from server/src/main/java/org/opensearch/common/recycler/ConcurrentDequeRecycler.java rename to libs/common/src/main/java/org/opensearch/common/recycler/ConcurrentDequeRecycler.java index c279a554890a2..68d108d58603f 100644 --- a/server/src/main/java/org/opensearch/common/recycler/ConcurrentDequeRecycler.java +++ b/libs/common/src/main/java/org/opensearch/common/recycler/ConcurrentDequeRecycler.java @@ -39,6 +39,8 @@ /** * A {@link Recycler} implementation based on a concurrent {@link Deque}. This implementation is thread-safe. + * + * @opensearch.internal */ public class ConcurrentDequeRecycler extends DequeRecycler { diff --git a/server/src/main/java/org/opensearch/common/recycler/DequeRecycler.java b/libs/common/src/main/java/org/opensearch/common/recycler/DequeRecycler.java similarity index 99% rename from server/src/main/java/org/opensearch/common/recycler/DequeRecycler.java rename to libs/common/src/main/java/org/opensearch/common/recycler/DequeRecycler.java index 44a6c86f791a7..1d6588119e7f5 100644 --- a/server/src/main/java/org/opensearch/common/recycler/DequeRecycler.java +++ b/libs/common/src/main/java/org/opensearch/common/recycler/DequeRecycler.java @@ -36,6 +36,8 @@ /** * A {@link Recycler} implementation based on a {@link Deque}. This implementation is NOT thread-safe. + * + * @opensearch.internal */ public class DequeRecycler extends AbstractRecycler { diff --git a/server/src/main/java/org/opensearch/common/recycler/FilterRecycler.java b/libs/common/src/main/java/org/opensearch/common/recycler/FilterRecycler.java similarity index 96% rename from server/src/main/java/org/opensearch/common/recycler/FilterRecycler.java rename to libs/common/src/main/java/org/opensearch/common/recycler/FilterRecycler.java index 77a9a8cbc224d..e3825eb53c501 100644 --- a/server/src/main/java/org/opensearch/common/recycler/FilterRecycler.java +++ b/libs/common/src/main/java/org/opensearch/common/recycler/FilterRecycler.java @@ -32,6 +32,11 @@ package org.opensearch.common.recycler; +/** + * Base filter recycler. + * + * @opensearch.internal + */ abstract class FilterRecycler implements Recycler { /** Get the delegate instance to forward calls to. */ diff --git a/server/src/main/java/org/opensearch/common/recycler/NoneRecycler.java b/libs/common/src/main/java/org/opensearch/common/recycler/NoneRecycler.java similarity index 93% rename from server/src/main/java/org/opensearch/common/recycler/NoneRecycler.java rename to libs/common/src/main/java/org/opensearch/common/recycler/NoneRecycler.java index cab62c7fc218d..d3d74138d3d6a 100644 --- a/server/src/main/java/org/opensearch/common/recycler/NoneRecycler.java +++ b/libs/common/src/main/java/org/opensearch/common/recycler/NoneRecycler.java @@ -32,6 +32,11 @@ package org.opensearch.common.recycler; +/** + * No value recycler + * + * @opensearch.internal + */ public class NoneRecycler extends AbstractRecycler { public NoneRecycler(C c) { @@ -43,6 +48,11 @@ public V obtain() { return new NV<>(c.newInstance()); } + /** + * Generic no value recycler + * + * @opensearch.internal + */ public static class NV implements Recycler.V { T value; diff --git a/server/src/main/java/org/opensearch/common/recycler/Recycler.java b/libs/common/src/main/java/org/opensearch/common/recycler/Recycler.java similarity index 88% rename from server/src/main/java/org/opensearch/common/recycler/Recycler.java rename to libs/common/src/main/java/org/opensearch/common/recycler/Recycler.java index e700a324f38f3..0b0c98772a77c 100644 --- a/server/src/main/java/org/opensearch/common/recycler/Recycler.java +++ b/libs/common/src/main/java/org/opensearch/common/recycler/Recycler.java @@ -37,13 +37,25 @@ /** * A recycled object, note, implementations should support calling obtain and then recycle * on different threads. + * + * @opensearch.internal */ public interface Recycler { + /** + * Base factory interface + * + * @opensearch.internal + */ interface Factory { Recycler build(); } + /** + * Generic for recycler + * + * @opensearch.internal + */ interface C { /** Create a new empty instance of the given size. */ @@ -56,6 +68,11 @@ interface C { void destroy(T value); } + /** + * Generic releasable + * + * @opensearch.internal + */ interface V extends Releasable { /** Reference to the value. */ diff --git a/server/src/main/java/org/opensearch/common/recycler/Recyclers.java b/libs/common/src/main/java/org/opensearch/common/recycler/Recyclers.java similarity index 95% rename from server/src/main/java/org/opensearch/common/recycler/Recyclers.java rename to libs/common/src/main/java/org/opensearch/common/recycler/Recyclers.java index 6f7a254ec015c..52587144369f1 100644 --- a/server/src/main/java/org/opensearch/common/recycler/Recyclers.java +++ b/libs/common/src/main/java/org/opensearch/common/recycler/Recyclers.java @@ -32,10 +32,15 @@ package org.opensearch.common.recycler; -import com.carrotsearch.hppc.BitMixer; +import org.opensearch.common.util.BitMixer; import java.util.ArrayDeque; +/** + * Utility class of recyclers. + * + * @opensearch.internal + */ public enum Recyclers { ; @@ -70,6 +75,8 @@ public static Recycler.Factory dequeFactory(final Recycler.C c, final /** * Wrap the provided recycler so that calls to {@link Recycler#obtain()} and {@link Recycler.V#close()} are protected by * a lock. + * + * @opensearch.internal */ public static Recycler locked(final Recycler recycler) { return new FilterRecycler() { @@ -135,7 +142,7 @@ public static Recycler concurrent(final Recycler.Factory factory, fina private final Recycler[] recyclers; { - @SuppressWarnings("unchecked") + @SuppressWarnings({ "rawtypes", "unchecked" }) final Recycler[] recyclers = new Recycler[concurrencyLevel]; this.recyclers = recyclers; for (int i = 0; i < concurrencyLevel; ++i) { diff --git a/libs/common/src/main/java/org/opensearch/common/recycler/package-info.java b/libs/common/src/main/java/org/opensearch/common/recycler/package-info.java new file mode 100644 index 0000000000000..fec3c5d5e52d3 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/recycler/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common Recycler functionality for recycling objects */ +package org.opensearch.common.recycler; diff --git a/server/src/main/java/org/opensearch/common/transport/NetworkExceptionHelper.java b/libs/common/src/main/java/org/opensearch/common/transport/NetworkExceptionHelper.java similarity index 97% rename from server/src/main/java/org/opensearch/common/transport/NetworkExceptionHelper.java rename to libs/common/src/main/java/org/opensearch/common/transport/NetworkExceptionHelper.java index c950749b4979d..72f8146cb969d 100644 --- a/server/src/main/java/org/opensearch/common/transport/NetworkExceptionHelper.java +++ b/libs/common/src/main/java/org/opensearch/common/transport/NetworkExceptionHelper.java @@ -35,6 +35,11 @@ import java.net.ConnectException; import java.nio.channels.ClosedChannelException; +/** + * Helper class for network exceptions. + * + * @opensearch.internal + */ public class NetworkExceptionHelper { public static boolean isConnectException(Throwable e) { diff --git a/server/src/main/java/org/opensearch/common/transport/PortsRange.java b/libs/common/src/main/java/org/opensearch/common/transport/PortsRange.java similarity index 87% rename from server/src/main/java/org/opensearch/common/transport/PortsRange.java rename to libs/common/src/main/java/org/opensearch/common/transport/PortsRange.java index c2f164680463a..daf3e00c062fa 100644 --- a/server/src/main/java/org/opensearch/common/transport/PortsRange.java +++ b/libs/common/src/main/java/org/opensearch/common/transport/PortsRange.java @@ -32,10 +32,15 @@ package org.opensearch.common.transport; -import com.carrotsearch.hppc.IntArrayList; - +import java.util.ArrayList; +import java.util.List; import java.util.StringTokenizer; +/** + * Port range utility classes + * + * @opensearch.internal + */ public class PortsRange { private final String portRange; @@ -49,15 +54,12 @@ public String getPortRangeString() { } public int[] ports() throws NumberFormatException { - final IntArrayList ports = new IntArrayList(); - iterate(new PortCallback() { - @Override - public boolean onPortNumber(int portNumber) { - ports.add(portNumber); - return false; - } + final List ports = new ArrayList<>(); + iterate(portNumber -> { + ports.add(portNumber); + return false; }); - return ports.toArray(); + return ports.stream().mapToInt(Integer::intValue).toArray(); } public boolean iterate(PortCallback callback) throws NumberFormatException { @@ -89,6 +91,11 @@ public boolean iterate(PortCallback callback) throws NumberFormatException { return success; } + /** + * Callback for the port + * + * @opensearch.internal + */ public interface PortCallback { boolean onPortNumber(int portNumber); } diff --git a/libs/common/src/main/java/org/opensearch/common/transport/package-info.java b/libs/common/src/main/java/org/opensearch/common/transport/package-info.java new file mode 100644 index 0000000000000..7d28ac6c60a14 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/transport/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** common transport layer classes used across the code base */ +package org.opensearch.common.transport; diff --git a/libs/core/src/main/java/org/opensearch/common/unit/TimeValue.java b/libs/common/src/main/java/org/opensearch/common/unit/TimeValue.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/unit/TimeValue.java rename to libs/common/src/main/java/org/opensearch/common/unit/TimeValue.java index ee150bf4ff85a..a3fcffb1d6a4c 100644 --- a/libs/core/src/main/java/org/opensearch/common/unit/TimeValue.java +++ b/libs/common/src/main/java/org/opensearch/common/unit/TimeValue.java @@ -32,10 +32,18 @@ package org.opensearch.common.unit; +import org.opensearch.common.annotation.PublicApi; + import java.util.Locale; import java.util.Objects; import java.util.concurrent.TimeUnit; +/** + * Time value unit of measurement + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") public class TimeValue implements Comparable { /** How many nano-seconds in one milli-second */ diff --git a/libs/common/src/main/java/org/opensearch/common/unit/package-info.java b/libs/common/src/main/java/org/opensearch/common/unit/package-info.java new file mode 100644 index 0000000000000..f2387c4349cc5 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/unit/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common units of measurement used across opensearch. */ +package org.opensearch.common.unit; diff --git a/libs/common/src/main/java/org/opensearch/common/util/BitMixer.java b/libs/common/src/main/java/org/opensearch/common/util/BitMixer.java new file mode 100644 index 0000000000000..8762217916c7a --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/util/BitMixer.java @@ -0,0 +1,172 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * HPPC + * + * Copyright (C) 2010-2022 Carrot Search s.c. + * All rights reserved. + * + * Refer to the full license file "LICENSE.txt": + * https://github.com/carrotsearch/hppc/blob/master/LICENSE.txt + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common.util; + +/** + * Bit mixing utilities from carrotsearch.hppc. + * + * Licensed under ALv2. This is pulled in directly to avoid a full hppc dependency. + * + * The purpose of these methods is to evenly distribute key space over int32 + * range. + */ +public final class BitMixer { + + // Don't bother mixing very small key domains much. + public static int mix(byte key) { + return key * PHI_C32; + } + + public static int mix(byte key, int seed) { + return (key ^ seed) * PHI_C32; + } + + public static int mix(short key) { + return mixPhi(key); + } + + public static int mix(short key, int seed) { + return mixPhi(key ^ seed); + } + + public static int mix(char key) { + return mixPhi(key); + } + + public static int mix(char key, int seed) { + return mixPhi(key ^ seed); + } + + // Better mix for larger key domains. + public static int mix(int key) { + return mix32(key); + } + + public static int mix(int key, int seed) { + return mix32(key ^ seed); + } + + public static int mix(float key) { + return mix32(Float.floatToIntBits(key)); + } + + public static int mix(float key, int seed) { + return mix32(Float.floatToIntBits(key) ^ seed); + } + + public static int mix(double key) { + return (int) mix64(Double.doubleToLongBits(key)); + } + + public static int mix(double key, int seed) { + return (int) mix64(Double.doubleToLongBits(key) ^ seed); + } + + public static int mix(long key) { + return (int) mix64(key); + } + + public static int mix(long key, int seed) { + return (int) mix64(key ^ seed); + } + + public static int mix(Object key) { + return key == null ? 0 : mix32(key.hashCode()); + } + + public static int mix(Object key, int seed) { + return key == null ? 0 : mix32(key.hashCode() ^ seed); + } + + /** + * MH3's plain finalization step. + */ + public static int mix32(int k) { + k = (k ^ (k >>> 16)) * 0x85ebca6b; + k = (k ^ (k >>> 13)) * 0xc2b2ae35; + return k ^ (k >>> 16); + } + + /** + * Computes David Stafford variant 9 of 64bit mix function (MH3 finalization step, + * with different shifts and constants). + * + * Variant 9 is picked because it contains two 32-bit shifts which could be possibly + * optimized into better machine code. + * + * @see "http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html" + */ + public static long mix64(long z) { + z = (z ^ (z >>> 32)) * 0x4cd6944c5cc20b6dL; + z = (z ^ (z >>> 29)) * 0xfc12c5b19d3259e9L; + return z ^ (z >>> 32); + } + + /* + * Golden ratio bit mixers. + */ + + private static final int PHI_C32 = 0x9e3779b9; + private static final long PHI_C64 = 0x9e3779b97f4a7c15L; + + public static int mixPhi(byte k) { + final int h = k * PHI_C32; + return h ^ (h >>> 16); + } + + public static int mixPhi(char k) { + final int h = k * PHI_C32; + return h ^ (h >>> 16); + } + + public static int mixPhi(short k) { + final int h = k * PHI_C32; + return h ^ (h >>> 16); + } + + public static int mixPhi(int k) { + final int h = k * PHI_C32; + return h ^ (h >>> 16); + } + + public static int mixPhi(float k) { + final int h = Float.floatToIntBits(k) * PHI_C32; + return h ^ (h >>> 16); + } + + public static int mixPhi(double k) { + final long h = Double.doubleToLongBits(k) * PHI_C64; + return (int) (h ^ (h >>> 32)); + } + + public static int mixPhi(long k) { + final long h = k * PHI_C64; + return (int) (h ^ (h >>> 32)); + } + + public static int mixPhi(Object k) { + final int h = (k == null ? 0 : k.hashCode() * PHI_C32); + return h ^ (h >>> 16); + } +} diff --git a/libs/core/src/main/java/org/opensearch/common/util/FastMath.java b/libs/common/src/main/java/org/opensearch/common/util/FastMath.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/util/FastMath.java rename to libs/common/src/main/java/org/opensearch/common/util/FastMath.java index f3b04fda2cadb..ac95a39698787 100644 --- a/libs/core/src/main/java/org/opensearch/common/util/FastMath.java +++ b/libs/common/src/main/java/org/opensearch/common/util/FastMath.java @@ -47,6 +47,8 @@ /** * Additions or modifications to this class should only come from the original org.math.plot.utils.FastMath source + * + * @opensearch.api */ final class FastMath { diff --git a/libs/core/src/main/java/org/opensearch/common/util/OpenSearchSloppyMath.java b/libs/common/src/main/java/org/opensearch/common/util/OpenSearchSloppyMath.java similarity index 98% rename from libs/core/src/main/java/org/opensearch/common/util/OpenSearchSloppyMath.java rename to libs/common/src/main/java/org/opensearch/common/util/OpenSearchSloppyMath.java index 4c24e87fe74b4..154b9c3a3ffc8 100644 --- a/libs/core/src/main/java/org/opensearch/common/util/OpenSearchSloppyMath.java +++ b/libs/common/src/main/java/org/opensearch/common/util/OpenSearchSloppyMath.java @@ -34,6 +34,8 @@ /** * Similar to Lucene's SloppyMath, but for additional math functions. + * + * @opensearch.api */ public class OpenSearchSloppyMath { diff --git a/libs/core/src/main/java/org/opensearch/common/util/concurrent/AbstractRefCounted.java b/libs/common/src/main/java/org/opensearch/common/util/concurrent/AbstractRefCounted.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/util/concurrent/AbstractRefCounted.java rename to libs/common/src/main/java/org/opensearch/common/util/concurrent/AbstractRefCounted.java index e55c87c34c795..2e23e87bea534 100644 --- a/libs/core/src/main/java/org/opensearch/common/util/concurrent/AbstractRefCounted.java +++ b/libs/common/src/main/java/org/opensearch/common/util/concurrent/AbstractRefCounted.java @@ -38,6 +38,8 @@ * A basic RefCounted implementation that is initialized with a * ref count of 1 and calls {@link #closeInternal()} once it reaches * a 0 ref count + * + * @opensearch.api */ public abstract class AbstractRefCounted implements RefCounted { private final AtomicInteger refCount = new AtomicInteger(1); diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentCollections.java b/libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentCollections.java similarity index 97% rename from server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentCollections.java rename to libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentCollections.java index 758682fb9ff96..c646d476e17ab 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentCollections.java +++ b/libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentCollections.java @@ -43,6 +43,11 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.LinkedTransferQueue; +/** + * Thread safe collection base class. + * + * @opensearch.internal + */ public abstract class ConcurrentCollections { static final int aggressiveConcurrencyLevel; diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentHashMapLong.java b/libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentHashMapLong.java similarity index 98% rename from server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentHashMapLong.java rename to libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentHashMapLong.java index d93d55d106e6e..6ee1b0a38248a 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentHashMapLong.java +++ b/libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentHashMapLong.java @@ -37,6 +37,11 @@ import java.util.Set; import java.util.concurrent.ConcurrentMap; +/** + * Thread safe hash map of longs. + * + * @opensearch.internal + */ public class ConcurrentHashMapLong implements ConcurrentMapLong { private final ConcurrentMap map; diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentMapLong.java b/libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentMapLong.java similarity index 95% rename from server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentMapLong.java rename to libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentMapLong.java index fb1c752dd7a55..7656a957dc534 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/ConcurrentMapLong.java +++ b/libs/common/src/main/java/org/opensearch/common/util/concurrent/ConcurrentMapLong.java @@ -34,6 +34,11 @@ import java.util.concurrent.ConcurrentMap; +/** + * Thread safe long value hash map + * + * @opensearch.internal + */ public interface ConcurrentMapLong extends ConcurrentMap { T get(long key); diff --git a/libs/core/src/main/java/org/opensearch/common/util/concurrent/RefCounted.java b/libs/common/src/main/java/org/opensearch/common/util/concurrent/RefCounted.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/common/util/concurrent/RefCounted.java rename to libs/common/src/main/java/org/opensearch/common/util/concurrent/RefCounted.java index 20a2222614343..1153df7225a05 100644 --- a/libs/core/src/main/java/org/opensearch/common/util/concurrent/RefCounted.java +++ b/libs/common/src/main/java/org/opensearch/common/util/concurrent/RefCounted.java @@ -49,6 +49,8 @@ * inst.decRef(); * } * + * + * @opensearch.api */ public interface RefCounted { diff --git a/libs/common/src/main/java/org/opensearch/common/util/concurrent/package-info.java b/libs/common/src/main/java/org/opensearch/common/util/concurrent/package-info.java new file mode 100644 index 0000000000000..b17aeac7e4c4e --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/util/concurrent/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common concurrency utilities used across opensearch. */ +package org.opensearch.common.util.concurrent; diff --git a/libs/core/src/main/java/org/opensearch/core/internal/io/IOUtils.java b/libs/common/src/main/java/org/opensearch/common/util/io/IOUtils.java similarity index 99% rename from libs/core/src/main/java/org/opensearch/core/internal/io/IOUtils.java rename to libs/common/src/main/java/org/opensearch/common/util/io/IOUtils.java index 00ecb8b82d744..6b72a0f92c14d 100644 --- a/libs/core/src/main/java/org/opensearch/core/internal/io/IOUtils.java +++ b/libs/common/src/main/java/org/opensearch/common/util/io/IOUtils.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.core.internal.io; +package org.opensearch.common.util.io; import org.opensearch.common.Nullable; @@ -52,6 +52,8 @@ /** * Utilities for common I/O methods. Borrowed heavily from Lucene (org.apache.lucene.util.IOUtils). + * + * @opensearch.internal */ public final class IOUtils { diff --git a/libs/common/src/main/java/org/opensearch/common/util/io/Streams.java b/libs/common/src/main/java/org/opensearch/common/util/io/Streams.java new file mode 100644 index 0000000000000..96672a3be3dc1 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/util/io/Streams.java @@ -0,0 +1,103 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common.util.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Simple utility methods for file and stream copying. + * All copy methods close all affected streams when done. + *

    + * Mainly for use within the framework, + * but also useful for application code. + * + * @opensearch.internal + */ +public abstract class Streams { + + private static final ThreadLocal buffer = ThreadLocal.withInitial(() -> new byte[8 * 1024]); + + /** + * Copy the contents of the given InputStream to the given OutputStream. Optionally, closes both streams when done. + * + * @param in the stream to copy from + * @param out the stream to copy to + * @param close whether to close both streams after copying + * @param buffer buffer to use for copying + * @return the number of bytes copied + * @throws IOException in case of I/O errors + */ + public static long copy(final InputStream in, final OutputStream out, byte[] buffer, boolean close) throws IOException { + Exception err = null; + try { + long byteCount = 0; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + byteCount += bytesRead; + } + out.flush(); + return byteCount; + } catch (IOException | RuntimeException e) { + err = e; + throw e; + } finally { + if (close) { + IOUtils.close(err, in, out); + } + } + } + + /** + * @see #copy(InputStream, OutputStream, byte[], boolean) + */ + public static long copy(final InputStream in, final OutputStream out, boolean close) throws IOException { + return copy(in, out, buffer.get(), close); + } + + /** + * @see #copy(InputStream, OutputStream, byte[], boolean) + */ + public static long copy(final InputStream in, final OutputStream out, byte[] buffer) throws IOException { + return copy(in, out, buffer, true); + } + + /** + * @see #copy(InputStream, OutputStream, byte[], boolean) + */ + public static long copy(final InputStream in, final OutputStream out) throws IOException { + return copy(in, out, buffer.get(), true); + } +} diff --git a/libs/common/src/main/java/org/opensearch/common/util/io/package-info.java b/libs/common/src/main/java/org/opensearch/common/util/io/package-info.java new file mode 100644 index 0000000000000..5789028538a48 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/util/io/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common internal I/O utility classes */ +package org.opensearch.common.util.io; diff --git a/libs/core/src/main/java/org/opensearch/core/internal/net/NetUtils.java b/libs/common/src/main/java/org/opensearch/common/util/net/NetUtils.java similarity index 98% rename from libs/core/src/main/java/org/opensearch/core/internal/net/NetUtils.java rename to libs/common/src/main/java/org/opensearch/common/util/net/NetUtils.java index 6ea3b6f953c50..708b62c290949 100644 --- a/libs/core/src/main/java/org/opensearch/core/internal/net/NetUtils.java +++ b/libs/common/src/main/java/org/opensearch/common/util/net/NetUtils.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.core.internal.net; +package org.opensearch.common.util.net; import java.io.IOException; import java.lang.reflect.Field; @@ -41,6 +41,8 @@ /** * Utilities for network-related methods. + * + * @opensearch.internal */ public class NetUtils { diff --git a/libs/common/src/main/java/org/opensearch/common/util/net/package-info.java b/libs/common/src/main/java/org/opensearch/common/util/net/package-info.java new file mode 100644 index 0000000000000..126b0939eab32 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/util/net/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common Network Utility Classes */ +package org.opensearch.common.util.net; diff --git a/libs/common/src/main/java/org/opensearch/common/util/package-info.java b/libs/common/src/main/java/org/opensearch/common/util/package-info.java new file mode 100644 index 0000000000000..98f786e2e82bc --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/util/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common utilities used across opensearch. */ +package org.opensearch.common.util; diff --git a/server/src/main/java/org/opensearch/common/util/set/Sets.java b/libs/common/src/main/java/org/opensearch/common/util/set/Sets.java similarity index 97% rename from server/src/main/java/org/opensearch/common/util/set/Sets.java rename to libs/common/src/main/java/org/opensearch/common/util/set/Sets.java index 1d7175ad424b2..2dc2fb3175d27 100644 --- a/server/src/main/java/org/opensearch/common/util/set/Sets.java +++ b/libs/common/src/main/java/org/opensearch/common/util/set/Sets.java @@ -49,6 +49,11 @@ import java.util.stream.Collector; import java.util.stream.Collectors; +/** + * OpenSearch sets. + * + * @opensearch.internal + */ public final class Sets { private Sets() {} @@ -114,6 +119,11 @@ public static SortedSet sortedDifference(Set left, Set right) { return left.stream().filter(k -> !right.contains(k)).collect(new SortedSetCollector<>()); } + /** + * A sorted set collector + * + * @opensearch.internal + */ private static class SortedSetCollector implements Collector, SortedSet> { @Override diff --git a/libs/common/src/main/java/org/opensearch/common/util/set/package-info.java b/libs/common/src/main/java/org/opensearch/common/util/set/package-info.java new file mode 100644 index 0000000000000..298e5f53ff215 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/common/util/set/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Base Set collection utility package. */ +package org.opensearch.common.util.set; diff --git a/libs/common/src/main/java/org/opensearch/package-info.java b/libs/common/src/main/java/org/opensearch/package-info.java new file mode 100644 index 0000000000000..6a75558502c72 --- /dev/null +++ b/libs/common/src/main/java/org/opensearch/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Base opensearch package. */ +package org.opensearch; diff --git a/libs/core/src/test/java/org/opensearch/bootstrap/JarHellTests.java b/libs/common/src/test/java/org/opensearch/bootstrap/JarHellTests.java similarity index 96% rename from libs/core/src/test/java/org/opensearch/bootstrap/JarHellTests.java rename to libs/common/src/test/java/org/opensearch/bootstrap/JarHellTests.java index d0e411ae8e3c2..d1851850e78e1 100644 --- a/libs/core/src/test/java/org/opensearch/bootstrap/JarHellTests.java +++ b/libs/common/src/test/java/org/opensearch/bootstrap/JarHellTests.java @@ -32,11 +32,12 @@ package org.opensearch.bootstrap; -import org.opensearch.common.Strings; import org.opensearch.common.io.PathUtils; +import org.opensearch.core.common.Strings; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; +import java.lang.Runtime.Version; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -156,12 +157,12 @@ public void testXmlBeansLeniency() throws Exception { public void testRequiredJDKVersionTooOld() throws Exception { Path dir = createTempDir(); - List current = JavaVersion.current().getVersion(); + List current = Runtime.version().version(); List target = new ArrayList<>(current.size()); for (int i = 0; i < current.size(); i++) { target.add(current.get(i) + 1); } - JavaVersion targetVersion = JavaVersion.parse(Strings.collectionToDelimitedString(target, ".")); + Version targetVersion = Version.parse(Strings.collectionToDelimitedString(target, ".")); Manifest manifest = new Manifest(); Attributes attributes = manifest.getMainAttributes(); @@ -173,7 +174,7 @@ public void testRequiredJDKVersionTooOld() throws Exception { fail("did not get expected exception"); } catch (IllegalStateException e) { assertTrue(e.getMessage().contains("requires Java " + targetVersion.toString())); - assertTrue(e.getMessage().contains("your system: " + JavaVersion.current().toString())); + assertTrue(e.getMessage().contains("your system: " + Runtime.version().toString())); } } @@ -209,7 +210,7 @@ public void testRequiredJDKVersionIsOK() throws Exception { } public void testValidVersions() { - String[] versions = new String[] { "1.7", "1.7.0", "0.1.7", "1.7.0.80" }; + String[] versions = new String[] { "12-ea", "13.0.2.3-ea", "14-something", "11.0.2-21002", "11.0.14.1+1", "17.0.2+8" }; for (String version : versions) { try { JarHell.checkVersionFormat(version); @@ -220,7 +221,7 @@ public void testValidVersions() { } public void testInvalidVersions() { - String[] versions = new String[] { "", "1.7.0_80", "1.7." }; + String[] versions = new String[] { "", "1.7.0_80", "1.7.", "11.2+something-else" }; for (String version : versions) { try { JarHell.checkVersionFormat(version); diff --git a/libs/core/src/test/java/org/opensearch/common/CharArraysTests.java b/libs/common/src/test/java/org/opensearch/common/CharArraysTests.java similarity index 100% rename from libs/core/src/test/java/org/opensearch/common/CharArraysTests.java rename to libs/common/src/test/java/org/opensearch/common/CharArraysTests.java diff --git a/libs/common/src/test/java/org/opensearch/common/NumbersTests.java b/libs/common/src/test/java/org/opensearch/common/NumbersTests.java new file mode 100644 index 0000000000000..7990ba74f162a --- /dev/null +++ b/libs/common/src/test/java/org/opensearch/common/NumbersTests.java @@ -0,0 +1,245 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common; + +import com.carrotsearch.randomizedtesting.annotations.Timeout; + +import org.opensearch.test.OpenSearchTestCase; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.concurrent.atomic.AtomicInteger; + +public class NumbersTests extends OpenSearchTestCase { + + @Timeout(millis = 10000) + public void testToLong() { + assertEquals(3L, Numbers.toLong("3", false)); + assertEquals(3L, Numbers.toLong("3.1", true)); + assertEquals(9223372036854775807L, Numbers.toLong("9223372036854775807.00", false)); + assertEquals(-9223372036854775808L, Numbers.toLong("-9223372036854775808.00", false)); + assertEquals(9223372036854775807L, Numbers.toLong("9223372036854775807.00", true)); + assertEquals(-9223372036854775808L, Numbers.toLong("-9223372036854775808.00", true)); + assertEquals(9223372036854775807L, Numbers.toLong("9223372036854775807.99", true)); + assertEquals(-9223372036854775808L, Numbers.toLong("-9223372036854775808.99", true)); + + assertEquals( + "Value [9223372036854775808] is out of range for a long", + expectThrows(IllegalArgumentException.class, () -> Numbers.toLong("9223372036854775808", false)).getMessage() + ); + assertEquals( + "Value [-9223372036854775809] is out of range for a long", + expectThrows(IllegalArgumentException.class, () -> Numbers.toLong("-9223372036854775809", false)).getMessage() + ); + + assertEquals( + "Value [1e99999999] is out of range for a long", + expectThrows(IllegalArgumentException.class, () -> Numbers.toLong("1e99999999", false)).getMessage() + ); + assertEquals( + "Value [-1e99999999] is out of range for a long", + expectThrows(IllegalArgumentException.class, () -> Numbers.toLong("-1e99999999", false)).getMessage() + ); + } + + public void testToLongExact() { + assertEquals(3L, Numbers.toLongExact(Long.valueOf(3L))); + assertEquals(3L, Numbers.toLongExact(Integer.valueOf(3))); + assertEquals(3L, Numbers.toLongExact(Short.valueOf((short) 3))); + assertEquals(3L, Numbers.toLongExact(Byte.valueOf((byte) 3))); + assertEquals(3L, Numbers.toLongExact(3d)); + assertEquals(3L, Numbers.toLongExact(3f)); + assertEquals(3L, Numbers.toLongExact(BigInteger.valueOf(3L))); + assertEquals(3L, Numbers.toLongExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage()); + } + + public void testToIntExact() { + assertEquals(3L, Numbers.toIntExact(Long.valueOf(3L))); + assertEquals(3L, Numbers.toIntExact(Integer.valueOf(3))); + assertEquals(3L, Numbers.toIntExact(Short.valueOf((short) 3))); + assertEquals(3L, Numbers.toIntExact(Byte.valueOf((byte) 3))); + assertEquals(3L, Numbers.toIntExact(3d)); + assertEquals(3L, Numbers.toIntExact(3f)); + assertEquals(3L, Numbers.toIntExact(BigInteger.valueOf(3L))); + assertEquals(3L, Numbers.toIntExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> Numbers.toIntExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toIntExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + ArithmeticException ae = expectThrows(ArithmeticException.class, () -> Numbers.toIntExact(1L << 40)); + assertEquals("integer overflow", ae.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toIntExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage()); + } + + public void testToShortExact() { + assertEquals(3L, Numbers.toShortExact(Long.valueOf(3L))); + assertEquals(3L, Numbers.toShortExact(Integer.valueOf(3))); + assertEquals(3L, Numbers.toShortExact(Short.valueOf((short) 3))); + assertEquals(3L, Numbers.toShortExact(Byte.valueOf((byte) 3))); + assertEquals(3L, Numbers.toShortExact(3d)); + assertEquals(3L, Numbers.toShortExact(3f)); + assertEquals(3L, Numbers.toShortExact(BigInteger.valueOf(3L))); + assertEquals(3L, Numbers.toShortExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> Numbers.toShortExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toShortExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + ArithmeticException ae = expectThrows(ArithmeticException.class, () -> Numbers.toShortExact(100000)); + assertEquals("short overflow: " + 100000, ae.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toShortExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage()); + } + + public void testToByteExact() { + assertEquals(3L, Numbers.toByteExact(Long.valueOf(3L))); + assertEquals(3L, Numbers.toByteExact(Integer.valueOf(3))); + assertEquals(3L, Numbers.toByteExact(Short.valueOf((short) 3))); + assertEquals(3L, Numbers.toByteExact(Byte.valueOf((byte) 3))); + assertEquals(3L, Numbers.toByteExact(3d)); + assertEquals(3L, Numbers.toByteExact(3f)); + assertEquals(3L, Numbers.toByteExact(BigInteger.valueOf(3L))); + assertEquals(3L, Numbers.toByteExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> Numbers.toByteExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toByteExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + ArithmeticException ae = expectThrows(ArithmeticException.class, () -> Numbers.toByteExact(300)); + assertEquals("byte overflow: " + 300, ae.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toByteExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot check whether [3] of class [java.util.concurrent.atomic.AtomicInteger] is actually a long", e.getMessage()); + } + + public void testToUnsignedLong() { + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLong("3", false)); + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLong("3.1", true)); + assertEquals(new BigInteger("17223372036854775807"), Numbers.toUnsignedLong("17223372036854775807.00", false)); + assertEquals(new BigInteger("17223372036854775807"), Numbers.toUnsignedLong("17223372036854775807.00", true)); + assertEquals(new BigInteger("17223372036854775807"), Numbers.toUnsignedLong("17223372036854775807.99", true)); + assertEquals(new BigInteger("18446744073709551000"), Numbers.toUnsignedLong("1.8446744073709551E19", true)); + + assertEquals( + "Value [19223372036854775808] is out of range for an unsigned long", + expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLong("19223372036854775808", false)).getMessage() + ); + assertEquals( + "Value [-1] is out of range for an unsigned long", + expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLong("-1", false)).getMessage() + ); + + assertEquals( + "Value [1.8446744073709552E19] is out of range for an unsigned long", + expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLong("1.8446744073709552E19", false)).getMessage() + ); + + assertEquals( + "Value [1e99999999] is out of range for an unsigned long", + expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLong("1e99999999", false)).getMessage() + ); + } + + public void testToUnsignedLongExact() { + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLongExact(Long.valueOf(3L))); + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLongExact(Integer.valueOf(3))); + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLongExact(Short.valueOf((short) 3))); + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLongExact(Byte.valueOf((byte) 3))); + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLongExact(3d)); + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLongExact(3f)); + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLongExact(BigInteger.valueOf(3L))); + assertEquals(BigInteger.valueOf(3L), Numbers.toUnsignedLongExact(BigDecimal.valueOf(3L))); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLongExact(3.1d)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLongExact(Double.NaN)); + assertEquals("NaN is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLongExact(Double.POSITIVE_INFINITY)); + assertEquals("Infinity is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLongExact(3.1f)); + assertEquals("3.1 is not an integer value", e.getMessage()); + e = expectThrows(IllegalArgumentException.class, () -> Numbers.toUnsignedLongExact(new AtomicInteger(3))); // not supported + assertEquals("Cannot convert [3] of class [java.util.concurrent.atomic.AtomicInteger] to a BigInteger", e.getMessage()); + } + + public void testToUnsignedBigInteger() { + final BigInteger random = randomUnsignedLong(); + assertEquals(random, Numbers.toUnsignedBigInteger(random.longValue())); + assertEquals(Numbers.MAX_UNSIGNED_LONG_VALUE, Numbers.toUnsignedBigInteger(Numbers.MAX_UNSIGNED_LONG_VALUE.longValue())); + } + + public void testNextPowerOfTwo() { + // Negative values: + for (int i = 0; i < 1000; i++) { + long value = randomLongBetween(-500000, -1); + assertEquals(1, Numbers.nextPowerOfTwo(value)); + } + + // Zero value: + assertEquals(1, Numbers.nextPowerOfTwo(0L)); + + // Positive values: + for (int i = 0; i < 1000; i++) { + long value = randomLongBetween(1, 500000); + long nextPowerOfTwo = Numbers.nextPowerOfTwo(value); + + assertTrue(nextPowerOfTwo > value); // must be strictly greater + assertTrue((nextPowerOfTwo >>> 1) <= value); // must be greater by no more than one power of two + assertEquals(0, nextPowerOfTwo & (nextPowerOfTwo - 1)); // must be a power of two + } + } +} diff --git a/libs/common/src/test/java/org/opensearch/common/SetOnceTests.java b/libs/common/src/test/java/org/opensearch/common/SetOnceTests.java new file mode 100644 index 0000000000000..0392ff62dd115 --- /dev/null +++ b/libs/common/src/test/java/org/opensearch/common/SetOnceTests.java @@ -0,0 +1,120 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common; + +import org.opensearch.common.SetOnce.AlreadySetException; +import org.opensearch.test.OpenSearchTestCase; + +import java.util.Random; + +import static org.hamcrest.CoreMatchers.containsString; + +public class SetOnceTests extends OpenSearchTestCase { + private static final class SetOnceThread extends Thread { + SetOnce set; + boolean success = false; + final Random RAND; + + public SetOnceThread(Random random) { + RAND = new Random(random.nextLong()); + } + + @Override + public void run() { + try { + sleep(RAND.nextInt(10)); // sleep for a short time + set.set(Integer.valueOf(getName().substring(2))); + success = true; + } catch (@SuppressWarnings("unused") InterruptedException e) { + // ignore + } catch (@SuppressWarnings("unused") RuntimeException e) { + // TODO: change exception type + // expected. + success = false; + } + } + } + + public void testEmptyCtor() { + SetOnce set = new SetOnce<>(); + assertNull(set.get()); + } + + public void testSettingCtor() { + SetOnce set = new SetOnce<>(5); + assertEquals(5, set.get().intValue()); + + AlreadySetException alreadySetException = expectThrows(AlreadySetException.class, () -> set.set(7)); + assertThat(alreadySetException.getMessage(), containsString("The object cannot be set twice!")); + } + + public void testSetOnce() { + SetOnce set = new SetOnce<>(); + set.set(5); + assertEquals(5, set.get().intValue()); + + AlreadySetException alreadySetException = expectThrows(AlreadySetException.class, () -> set.set(7)); + assertThat(alreadySetException.getMessage(), containsString("The object cannot be set twice!")); + } + + public void testTrySet() { + SetOnce set = new SetOnce<>(); + assertTrue(set.trySet(5)); + assertEquals(5, set.get().intValue()); + assertFalse(set.trySet(7)); + assertEquals(5, set.get().intValue()); + } + + public void testSetMultiThreaded() throws Exception { + final SetOnce set = new SetOnce<>(); + SetOnceThread[] threads = new SetOnceThread[10]; + for (int i = 0; i < threads.length; i++) { + threads[i] = new SetOnceThread(random()); + threads[i].setName("t-" + (i + 1)); + threads[i].set = set; + } + + for (Thread t : threads) { + t.start(); + } + + for (Thread t : threads) { + t.join(); + } + + for (SetOnceThread t : threads) { + if (t.success) { + int expectedVal = Integer.parseInt(t.getName().substring(2)); + assertEquals("thread " + t.getName(), expectedVal, t.set.get().intValue()); + } + } + } +} diff --git a/server/src/test/java/org/opensearch/common/collect/IteratorsTests.java b/libs/common/src/test/java/org/opensearch/common/collect/IteratorsTests.java similarity index 98% rename from server/src/test/java/org/opensearch/common/collect/IteratorsTests.java rename to libs/common/src/test/java/org/opensearch/common/collect/IteratorsTests.java index 155391aaaf62f..1ad6751014073 100644 --- a/server/src/test/java/org/opensearch/common/collect/IteratorsTests.java +++ b/libs/common/src/test/java/org/opensearch/common/collect/IteratorsTests.java @@ -83,6 +83,7 @@ public void testRandomSingleton() { int numberOfIterators = randomIntBetween(1, 1000); int singletonIndex = randomIntBetween(0, numberOfIterators - 1); int value = randomInt(); + @SuppressWarnings("rawtypes") Iterator[] iterators = new Iterator[numberOfIterators]; for (int i = 0; i < numberOfIterators; i++) { iterators[i] = i != singletonIndex ? empty() : singletonIterator(value); @@ -92,6 +93,7 @@ public void testRandomSingleton() { public void testRandomIterators() { int numberOfIterators = randomIntBetween(1, 1000); + @SuppressWarnings("rawtypes") Iterator[] iterators = new Iterator[numberOfIterators]; List values = new ArrayList<>(); for (int i = 0; i < numberOfIterators; i++) { diff --git a/libs/core/src/test/java/org/opensearch/common/collect/TupleTests.java b/libs/common/src/test/java/org/opensearch/common/collect/TupleTests.java similarity index 100% rename from libs/core/src/test/java/org/opensearch/common/collect/TupleTests.java rename to libs/common/src/test/java/org/opensearch/common/collect/TupleTests.java diff --git a/libs/common/src/test/java/org/opensearch/common/hash/T1Ha1Tests.java b/libs/common/src/test/java/org/opensearch/common/hash/T1Ha1Tests.java new file mode 100644 index 0000000000000..e348fbf759bdd --- /dev/null +++ b/libs/common/src/test/java/org/opensearch/common/hash/T1Ha1Tests.java @@ -0,0 +1,312 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.hash; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; + +public class T1Ha1Tests extends HashFunctionTestCase { + private static final VarHandle LONG_HANDLE = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + private final byte[] scratch = new byte[8]; + + /** + * Inspired from the tests defined in the reference implementation: + * t1ha_selfcheck.c + */ + public void testSelfCheck() { + byte[] testPattern = { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + (byte) 0xFF, + 0x7F, + 0x3F, + 0x1F, + 0xF, + 8, + 16, + 32, + 64, + (byte) 0x80, + (byte) 0xFE, + (byte) 0xFC, + (byte) 0xF8, + (byte) 0xF0, + (byte) 0xE0, + (byte) 0xC0, + (byte) 0xFD, + (byte) 0xFB, + (byte) 0xF7, + (byte) 0xEF, + (byte) 0xDF, + (byte) 0xBF, + 0x55, + (byte) 0xAA, + 11, + 17, + 19, + 23, + 29, + 37, + 42, + 43, + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x' }; + + // Reference hashes when using {@link Math::unsignedMultiplyHigh} in the mux64 step. + // These values match the ones defined in the reference implementation: + // https://github.com/erthink/t1ha/blob/master/src/t1ha1_selfcheck.c#L51-L72 + long[] referenceUnsignedMultiplyHigh = { + 0L, + 0x6A580668D6048674L, + 0xA2FE904AFF0D0879L, + 0xE3AB9C06FAF4D023L, + 0x6AF1C60874C95442L, + 0xB3557E561A6C5D82L, + 0x0AE73C696F3D37C0L, + 0x5EF25F7062324941L, + 0x9B784F3B4CE6AF33L, + 0x6993BB206A74F070L, + 0xF1E95DF109076C4CL, + 0x4E1EB70C58E48540L, + 0x5FDD7649D8EC44E4L, + 0x559122C706343421L, + 0x380133D58665E93DL, + 0x9CE74296C8C55AE4L, + 0x3556F9A5757AB6D0L, + 0xF62751F7F25C469EL, + 0x851EEC67F6516D94L, + 0xED463EE3848A8695L, + 0xDC8791FEFF8ED3ACL, + 0x2569C744E1A282CFL, + 0xF90EB7C1D70A80B9L, + 0x68DFA6A1B8050A4CL, + 0x94CCA5E8210D2134L, + 0xF5CC0BEABC259F52L, + 0x40DBC1F51618FDA7L, + 0x0807945BF0FB52C6L, + 0xE5EF7E09DE70848DL, + 0x63E1DF35FEBE994AL, + 0x2025E73769720D5AL, + 0xAD6120B2B8A152E1L, + 0x2A71D9F13959F2B7L, + 0x8A20849A27C32548L, + 0x0BCBC9FE3B57884EL, + 0x0E028D255667AEADL, + 0xBE66DAD3043AB694L, + 0xB00E4C1238F9E2D4L, + 0x5C54BDE5AE280E82L, + 0x0E22B86754BC3BC4L, + 0x016707EBF858B84DL, + 0x990015FBC9E095EEL, + 0x8B9AF0A3E71F042FL, + 0x6AA56E88BD380564L, + 0xAACE57113E681A0FL, + 0x19F81514AFA9A22DL, + 0x80DABA3D62BEAC79L, + 0x715210412CABBF46L, + 0xD8FA0B9E9D6AA93FL, + 0x6C2FC5A4109FD3A2L, + 0x5B3E60EEB51DDCD8L, + 0x0A7C717017756FE7L, + 0xA73773805CA31934L, + 0x4DBD6BB7A31E85FDL, + 0x24F619D3D5BC2DB4L, + 0x3E4AF35A1678D636L, + 0x84A1A8DF8D609239L, + 0x359C862CD3BE4FCDL, + 0xCF3A39F5C27DC125L, + 0xC0FF62F8FD5F4C77L, + 0x5E9F2493DDAA166CL, + 0x17424152BE1CA266L, + 0xA78AFA5AB4BBE0CDL, + 0x7BFB2E2CEF118346L, + 0x647C3E0FF3E3D241L, + 0x0352E4055C13242EL, + 0x6F42FC70EB660E38L, + 0x0BEBAD4FABF523BAL, + 0x9269F4214414D61DL, + 0x1CA8760277E6006CL, + 0x7BAD25A859D87B5DL, + 0xAD645ADCF7414F1DL, + 0xB07F517E88D7AFB3L, + 0xB321C06FB5FFAB5CL, + 0xD50F162A1EFDD844L, + 0x1DFD3D1924FBE319L, + 0xDFAEAB2F09EF7E78L, + 0xA7603B5AF07A0B1EL, + 0x41CD044C0E5A4EE3L, + 0xF64D2F86E813BF33L, + 0xFF9FDB99305EB06AL }; + + // Reference hashes when using {@link Math::multiplyHigh} in the mux64 step. + long[] referenceMultiplyHigh = { + 0L, + 0xCE510B7405E0A2CAL, + 0xC0A2DA74A8271FCBL, + 0x1C549C06FAF4D023L, + 0x084CDA0ED41CD2D4L, + 0xD05BA7AA9FEECE5BL, + 0x7D6128AB2CCC4EB1L, + 0x62332FA6EC1B50AAL, + 0x1B66C81767870EF2L, + 0xEC6B92A37AED73B8L, + 0x1712987232EF4ED3L, + 0xAA503A04AE2450B5L, + 0x15D25DE445730A6CL, + 0xAB87E38AA8D21746L, + 0x18CAE735BBF62D15L, + 0x0D56DFF9914CA656L, + 0xCB4F5859A9AE5B52L, + 0xEE97003F7B1283E1L, + 0x50CFB2AF0F54BA6DL, + 0x570B4D6AE4C67814L, + 0x1ED59274A97497EBL, + 0x8608D03D165C59BFL, + 0x6CBE0E537BE04C02L, + 0xD4C8FCFD4179A874L, + 0xFB4E677D876118A1L, + 0x6B1A96F1B4765D79L, + 0x1075B9B89BDFE5F8L, + 0x02771D08F2891CB1L, + 0x4BB8E16FF410F19EL, + 0x3EB7849C0DFAF566L, + 0x173B09359DE422CFL, + 0xFE212C6DB7474306L, + 0xA74E7C2D632664EFL, + 0x56ECDED6546F0914L, + 0x08DEF866EF20A94BL, + 0x7D0BAC64606521F1L, + 0xCA6BA9817A357FA9L, + 0x0873B834A6E2AAE4L, + 0x45EE02D6DCF8992EL, + 0x3EA060225B3E1C1FL, + 0x24DBB6D02D5CC531L, + 0xE5E91A7340BF9382L, + 0x28975F86E2E2177FL, + 0x80E48374A6B42E85L, + 0xDF40392265BB4A66L, + 0x43750475A48C7023L, + 0x5648BD3E391C01D3L, + 0x9BE9E11AD1A6C369L, + 0x2E079CB8C1A11F50L, + 0xB2D538403F1020F1L, + 0x297518A4EF6AF5F1L, + 0xA8CE1B90167A6F8BL, + 0xB926B2FA50541BA9L, + 0xC46A2D3BD6925A35L, + 0x3071BC8E6C400487L, + 0x300D3885894BA47FL, + 0x840BFF3BEB7EEADDL, + 0xDC9E04DF744BDC0CL, + 0xBE01CF6841412C77L, + 0x6C55B2DC74B816A1L, + 0x4D4C63128A344F82L, + 0xC6227497E100B463L, + 0x53C9987705EA71C0L, + 0x3E355394668C3559L, + 0x05984B7D358B107AL, + 0x4D32FA1D79002A57L, + 0x910B0DAD1440EC24L, + 0x025BDE6A7BEBF320L, + 0x0D33817EF345D999L, + 0xBA0DE64B3F4DB34AL, + 0x54666461D0EB4FD7L, + 0x746ECFA92D1CAF81L, + 0x6E6A774ACD266DF2L, + 0x1A86161AE8E82A85L, + 0xFFF7C351A4CEC13DL, + 0xFFF05844F57498B8L, + 0x8DB71789127C6C13L, + 0x4A52ACF805F370ABL, + 0xFE13F90A1ACFBD58L, + 0x615730E301ED12E2L, + 0x1A2D4AA43B6C0103L }; + + long[] reference = hasUnsignedMultiplyHigh() ? referenceUnsignedMultiplyHigh : referenceMultiplyHigh; + + int offset = 0; + assertEquals(reference[offset++], T1ha1.hash(null, 0, 0, 0L)); // empty-zero + assertEquals(reference[offset++], T1ha1.hash(null, 0, 0, ~0L)); // empty-all1 + assertEquals(reference[offset++], T1ha1.hash(testPattern, 0, 64, 0L)); // bin64-zero + + long seed = 1; + for (int i = 1; i < 64; i++) { + assertEquals(reference[offset++], T1ha1.hash(testPattern, 0, i, seed)); // bin%i-1p%i + seed <<= 1; + } + + seed = ~0L; + for (int i = 1; i <= 7; i++) { + seed <<= 1; + assertEquals(reference[offset++], T1ha1.hash(testPattern, i, 64 - i, seed)); // align%i_F%i + } + + byte[] testPatternLong = new byte[512]; + for (int i = 0; i < testPatternLong.length; i++) { + testPatternLong[i] = (byte) i; + } + for (int i = 0; i <= 7; i++) { + assertEquals(reference[offset++], T1ha1.hash(testPatternLong, i, 128 + i * 17, seed)); // long-%05i + } + } + + @Override + public byte[] hash(byte[] input) { + long hash = T1ha1.hash(input, 0, input.length); + LONG_HANDLE.set(scratch, 0, hash); + return scratch; + } + + @Override + public int outputBits() { + return 64; + } + + private static boolean hasUnsignedMultiplyHigh() { + try { + MethodHandles.publicLookup() + .findStatic(Math.class, "unsignedMultiplyHigh", MethodType.methodType(long.class, long.class, long.class)); + return true; + } catch (NoSuchMethodException e) { + return false; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } +} diff --git a/server/src/test/java/org/opensearch/common/network/CidrsTests.java b/libs/common/src/test/java/org/opensearch/common/network/CidrsTests.java similarity index 100% rename from server/src/test/java/org/opensearch/common/network/CidrsTests.java rename to libs/common/src/test/java/org/opensearch/common/network/CidrsTests.java diff --git a/server/src/test/java/org/opensearch/common/network/InetAddressesTests.java b/libs/common/src/test/java/org/opensearch/common/network/InetAddressesTests.java similarity index 100% rename from server/src/test/java/org/opensearch/common/network/InetAddressesTests.java rename to libs/common/src/test/java/org/opensearch/common/network/InetAddressesTests.java diff --git a/server/src/test/java/org/opensearch/common/network/NetworkAddressTests.java b/libs/common/src/test/java/org/opensearch/common/network/NetworkAddressTests.java similarity index 100% rename from server/src/test/java/org/opensearch/common/network/NetworkAddressTests.java rename to libs/common/src/test/java/org/opensearch/common/network/NetworkAddressTests.java diff --git a/server/src/test/java/org/opensearch/common/recycler/AbstractRecyclerTestCase.java b/libs/common/src/test/java/org/opensearch/common/recycler/AbstractRecyclerTestCase.java similarity index 100% rename from server/src/test/java/org/opensearch/common/recycler/AbstractRecyclerTestCase.java rename to libs/common/src/test/java/org/opensearch/common/recycler/AbstractRecyclerTestCase.java diff --git a/server/src/test/java/org/opensearch/common/recycler/ConcurrentRecyclerTests.java b/libs/common/src/test/java/org/opensearch/common/recycler/ConcurrentRecyclerTests.java similarity index 100% rename from server/src/test/java/org/opensearch/common/recycler/ConcurrentRecyclerTests.java rename to libs/common/src/test/java/org/opensearch/common/recycler/ConcurrentRecyclerTests.java diff --git a/server/src/test/java/org/opensearch/common/recycler/LockedRecyclerTests.java b/libs/common/src/test/java/org/opensearch/common/recycler/LockedRecyclerTests.java similarity index 100% rename from server/src/test/java/org/opensearch/common/recycler/LockedRecyclerTests.java rename to libs/common/src/test/java/org/opensearch/common/recycler/LockedRecyclerTests.java diff --git a/server/src/test/java/org/opensearch/common/recycler/NoneRecyclerTests.java b/libs/common/src/test/java/org/opensearch/common/recycler/NoneRecyclerTests.java similarity index 100% rename from server/src/test/java/org/opensearch/common/recycler/NoneRecyclerTests.java rename to libs/common/src/test/java/org/opensearch/common/recycler/NoneRecyclerTests.java diff --git a/server/src/test/java/org/opensearch/common/recycler/QueueRecyclerTests.java b/libs/common/src/test/java/org/opensearch/common/recycler/QueueRecyclerTests.java similarity index 100% rename from server/src/test/java/org/opensearch/common/recycler/QueueRecyclerTests.java rename to libs/common/src/test/java/org/opensearch/common/recycler/QueueRecyclerTests.java diff --git a/libs/core/src/test/java/org/opensearch/common/unit/TimeValueTests.java b/libs/common/src/test/java/org/opensearch/common/unit/TimeValueTests.java similarity index 100% rename from libs/core/src/test/java/org/opensearch/common/unit/TimeValueTests.java rename to libs/common/src/test/java/org/opensearch/common/unit/TimeValueTests.java diff --git a/libs/core/src/test/java/org/opensearch/common/util/OpenSearchSloppyMathTests.java b/libs/common/src/test/java/org/opensearch/common/util/OpenSearchSloppyMathTests.java similarity index 100% rename from libs/core/src/test/java/org/opensearch/common/util/OpenSearchSloppyMathTests.java rename to libs/common/src/test/java/org/opensearch/common/util/OpenSearchSloppyMathTests.java diff --git a/libs/core/src/test/java/org/opensearch/common/util/concurrent/RefCountedTests.java b/libs/common/src/test/java/org/opensearch/common/util/concurrent/RefCountedTests.java similarity index 100% rename from libs/core/src/test/java/org/opensearch/common/util/concurrent/RefCountedTests.java rename to libs/common/src/test/java/org/opensearch/common/util/concurrent/RefCountedTests.java diff --git a/libs/core/src/test/resources/org/opensearch/bootstrap/duplicate-classes.jar b/libs/common/src/test/resources/org/opensearch/bootstrap/duplicate-classes.jar similarity index 100% rename from libs/core/src/test/resources/org/opensearch/bootstrap/duplicate-classes.jar rename to libs/common/src/test/resources/org/opensearch/bootstrap/duplicate-classes.jar diff --git a/libs/core/src/test/resources/org/opensearch/bootstrap/duplicate-xmlbeans-classes.jar b/libs/common/src/test/resources/org/opensearch/bootstrap/duplicate-xmlbeans-classes.jar similarity index 100% rename from libs/core/src/test/resources/org/opensearch/bootstrap/duplicate-xmlbeans-classes.jar rename to libs/common/src/test/resources/org/opensearch/bootstrap/duplicate-xmlbeans-classes.jar diff --git a/libs/compress/build.gradle b/libs/compress/build.gradle new file mode 100644 index 0000000000000..7a5bc2f573dea --- /dev/null +++ b/libs/compress/build.gradle @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +apply plugin: 'opensearch.build' +apply plugin: 'opensearch.publish' + +base { + archivesName = 'opensearch-compress' +} + +dependencies { + api project(':libs:opensearch-common') + api project(':libs:opensearch-core') + + //zstd + api "com.github.luben:zstd-jni:${versions.zstd}" + + testImplementation(project(":test:framework")) { + // tests use the locally compiled version of server + exclude group: 'org.opensearch', module: 'opensearch-compress' + } +} + +tasks.named('forbiddenApisMain').configure { + // :libs:opensearch-compress does not depend on server + // TODO: Need to decide how we want to handle for forbidden signatures with the changes to server + replaceSignatureFiles 'jdk-signatures' +} + +jarHell.enabled = false diff --git a/libs/compress/licenses/zstd-jni-1.5.5-5.jar.sha1 b/libs/compress/licenses/zstd-jni-1.5.5-5.jar.sha1 new file mode 100644 index 0000000000000..498c60c34e3da --- /dev/null +++ b/libs/compress/licenses/zstd-jni-1.5.5-5.jar.sha1 @@ -0,0 +1 @@ +74ffdc5f140080adacf5278287aadd950179f848 \ No newline at end of file diff --git a/libs/compress/licenses/zstd-jni-LICENSE.txt b/libs/compress/licenses/zstd-jni-LICENSE.txt new file mode 100644 index 0000000000000..c4dd507c1c72f --- /dev/null +++ b/libs/compress/licenses/zstd-jni-LICENSE.txt @@ -0,0 +1,29 @@ +----------------------------------------------------------------------------- +** Beginning of "BSD License" text. ** + +Zstd-jni: JNI bindings to Zstd Library + +Copyright (c) 2015-present, Luben Karavelov/ All rights reserved. + +BSD License + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/compress/licenses/zstd-jni-NOTICE.txt b/libs/compress/licenses/zstd-jni-NOTICE.txt new file mode 100644 index 0000000000000..389c97cbc892d --- /dev/null +++ b/libs/compress/licenses/zstd-jni-NOTICE.txt @@ -0,0 +1 @@ +The code for the JNI bindings to Zstd library was originally authored by Luben Karavelov diff --git a/libs/compress/src/main/java/org/opensearch/compress/ZstdCompressor.java b/libs/compress/src/main/java/org/opensearch/compress/ZstdCompressor.java new file mode 100644 index 0000000000000..e2a740f72be93 --- /dev/null +++ b/libs/compress/src/main/java/org/opensearch/compress/ZstdCompressor.java @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.compress; + +import com.github.luben.zstd.RecyclingBufferPool; +import com.github.luben.zstd.ZstdInputStreamNoFinalizer; +import com.github.luben.zstd.ZstdOutputStreamNoFinalizer; + +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.compress.Compressor; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * {@link Compressor} implementation based on the ZSTD compression algorithm. + * + * @opensearch.api - registered name requires BWC support + * @opensearch.experimental - class methods might change + */ +public class ZstdCompressor implements Compressor { + + /** + * An arbitrary header that we use to identify compressed streams + * It needs to be different from other compressors and to not be specific + * enough so that no stream starting with these bytes could be detected as + * a XContent + * */ + private static final byte[] HEADER = new byte[] { 'Z', 'S', 'T', 'D', '\0' }; + + /** + * The name to register the compressor by + * + * @opensearch.api - requires BWC support + */ + @PublicApi(since = "2.10.0") + public static final String NAME = "ZSTD"; + + /** + * The compression level for {@link ZstdOutputStreamNoFinalizer} + */ + private static final int LEVEL = 3; + + /** The buffer size for {@link BufferedInputStream} and {@link BufferedOutputStream} + */ + private static final int BUFFER_SIZE = 4096; + + /** + * Compares the given bytes with the {@link ZstdCompressor#HEADER} of a compressed stream + * @param bytes the bytes to compare to ({@link ZstdCompressor#HEADER}) + * @return true if the bytes are the {@link ZstdCompressor#HEADER}, false otherwise + */ + @Override + public boolean isCompressed(BytesReference bytes) { + if (bytes.length() < HEADER.length) { + return false; + } + for (int i = 0; i < HEADER.length; ++i) { + if (bytes.get(i) != HEADER[i]) { + return false; + } + } + return true; + } + + /** + * Returns the length of the {@link ZstdCompressor#HEADER} + * @return the {@link ZstdCompressor#HEADER} length + */ + @Override + public int headerLength() { + return HEADER.length; + } + + /** + * Returns a new {@link ZstdInputStreamNoFinalizer} from the given compressed {@link InputStream} + * @param in the compressed {@link InputStream} + * @return a new {@link ZstdInputStreamNoFinalizer} from the given compressed {@link InputStream} + * @throws IOException if an I/O error occurs + * @throws IllegalArgumentException if the input stream is not compressed with ZSTD + */ + @Override + public InputStream threadLocalInputStream(InputStream in) throws IOException { + final byte[] header = in.readNBytes(HEADER.length); + if (Arrays.equals(header, HEADER) == false) { + throw new IllegalArgumentException("Input stream is not compressed with ZSTD!"); + } + return new ZstdInputStreamNoFinalizer(new BufferedInputStream(in, BUFFER_SIZE), RecyclingBufferPool.INSTANCE); + } + + /** + * Returns a new {@link ZstdOutputStreamNoFinalizer} from the given {@link OutputStream} + * @param out the {@link OutputStream} + * @return a new {@link ZstdOutputStreamNoFinalizer} from the given {@link OutputStream} + * @throws IOException if an I/O error occurs + */ + @Override + public OutputStream threadLocalOutputStream(OutputStream out) throws IOException { + out.write(HEADER); + return new ZstdOutputStreamNoFinalizer(new BufferedOutputStream(out, BUFFER_SIZE), RecyclingBufferPool.INSTANCE, LEVEL); + } + + /** + * Always throws an {@link UnsupportedOperationException} as ZSTD compression is supported only for snapshotting + * @param bytesReference a reference to the bytes to uncompress + * @return always throws an exception + * @throws UnsupportedOperationException if the method is called + * @throws IOException is never thrown + */ + @Override + public BytesReference uncompress(BytesReference bytesReference) throws IOException { + throw new UnsupportedOperationException("ZSTD compression is supported only for snapshotting"); + } + + /** + * Always throws an {@link UnsupportedOperationException} as ZSTD compression is supported only for snapshotting + * @param bytesReference a reference to the bytes to compress + * @return always throws an exception + * @throws UnsupportedOperationException if the method is called + */ + @Override + public BytesReference compress(BytesReference bytesReference) throws IOException { + throw new UnsupportedOperationException("ZSTD compression is supported only for snapshotting"); + } +} diff --git a/libs/compress/src/main/java/org/opensearch/compress/package-info.java b/libs/compress/src/main/java/org/opensearch/compress/package-info.java new file mode 100644 index 0000000000000..3ffa53079fa69 --- /dev/null +++ b/libs/compress/src/main/java/org/opensearch/compress/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Concrete {@link org.opensearch.core.compress.Compressor} implementations + */ +package org.opensearch.compress; diff --git a/libs/compress/src/main/java/org/opensearch/compress/spi/CompressionProvider.java b/libs/compress/src/main/java/org/opensearch/compress/spi/CompressionProvider.java new file mode 100644 index 0000000000000..f0c6969377d78 --- /dev/null +++ b/libs/compress/src/main/java/org/opensearch/compress/spi/CompressionProvider.java @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.compress.spi; + +import org.opensearch.compress.ZstdCompressor; +import org.opensearch.core.compress.Compressor; +import org.opensearch.core.compress.spi.CompressorProvider; + +import java.util.AbstractMap.SimpleEntry; +import java.util.List; +import java.util.Map.Entry; + +/** + * Additional "optional" compressor implementations provided by the opensearch compress library + * + * @opensearch.internal + */ +public class CompressionProvider implements CompressorProvider { + + /** + * Returns the concrete {@link Compressor}s provided by the compress library + * @return a list of {@link Compressor}s + * */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public List> getCompressors() { + return List.of(new SimpleEntry<>(ZstdCompressor.NAME, new ZstdCompressor())); + } +} diff --git a/libs/compress/src/main/java/org/opensearch/compress/spi/package-info.java b/libs/compress/src/main/java/org/opensearch/compress/spi/package-info.java new file mode 100644 index 0000000000000..47d982a7ca2f9 --- /dev/null +++ b/libs/compress/src/main/java/org/opensearch/compress/spi/package-info.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Service Provider Interface for registering concrete {@link org.opensearch.core.compress.Compressor} + * implementations. + * + * See {@link org.opensearch.compress.ZstdCompressor} + */ +package org.opensearch.compress.spi; diff --git a/libs/compress/src/main/java/org/opensearch/package-info.java b/libs/compress/src/main/java/org/opensearch/package-info.java new file mode 100644 index 0000000000000..264680e9cb271 --- /dev/null +++ b/libs/compress/src/main/java/org/opensearch/package-info.java @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * This is the compress library for registering optional + * {@link org.opensearch.core.compress.Compressor} implementations + */ +package org.opensearch; diff --git a/libs/compress/src/main/resources/META-INF/services/org.opensearch.core.compress.spi.CompressorProvider b/libs/compress/src/main/resources/META-INF/services/org.opensearch.core.compress.spi.CompressorProvider new file mode 100644 index 0000000000000..a9ea063e24436 --- /dev/null +++ b/libs/compress/src/main/resources/META-INF/services/org.opensearch.core.compress.spi.CompressorProvider @@ -0,0 +1,9 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# + +org.opensearch.compress.spi.CompressionProvider diff --git a/libs/compress/src/test/java/org/opensearch/compress/ZstdCompressTests.java b/libs/compress/src/test/java/org/opensearch/compress/ZstdCompressTests.java new file mode 100644 index 0000000000000..54864054a0e02 --- /dev/null +++ b/libs/compress/src/test/java/org/opensearch/compress/ZstdCompressTests.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.compress; + +import org.opensearch.core.compress.Compressor; +import org.opensearch.test.core.compress.AbstractCompressorTestCase; + +/** + * Test streaming compression + */ +public class ZstdCompressTests extends AbstractCompressorTestCase { + + private final Compressor compressor = new ZstdCompressor(); + + @Override + protected Compressor compressor() { + return compressor; + } +} diff --git a/libs/core/.classpath1 b/libs/core/.classpath1 index 3a20aa700e9e9..3586b0ee921fb 100644 --- a/libs/core/.classpath1 +++ b/libs/core/.classpath1 @@ -253,12 +253,6 @@ - - - - - - diff --git a/libs/core/build.gradle b/libs/core/build.gradle index 374f2fe572a12..1ab2073759788 100644 --- a/libs/core/build.gradle +++ b/libs/core/build.gradle @@ -30,10 +30,11 @@ import org.opensearch.gradle.info.BuildParams -apply plugin: 'nebula.optional-base' apply plugin: 'opensearch.publish' -archivesBaseName = 'opensearch-core' +base { + archivesBaseName = 'opensearch-core' +} // we want to keep the JDKs in our IDEs set to JDK 8 until minimum JDK is bumped to 11 so we do not include this source set in our IDEs if (!isEclipse) { @@ -75,8 +76,15 @@ if (!isEclipse) { } dependencies { - // This dependency is used only by :libs:core for null-checking interop with other tools - compileOnly "com.google.code.findbugs:jsr305:3.0.2" + api project(':libs:opensearch-common') + + api "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" + + // lucene + api "org.apache.lucene:lucene-core:${versions.lucene}" + + // logging + api "org.apache.logging.log4j:log4j-api:${versions.log4j}" testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" testImplementation "junit:junit:${versions.junit}" @@ -93,15 +101,23 @@ tasks.named('forbiddenApisMain').configure { replaceSignatureFiles 'jdk-signatures' } -thirdPartyAudit.ignoreMissingClasses( - // from log4j - 'org/osgi/framework/AdaptPermission', - 'org/osgi/framework/AdminPermission', - 'org/osgi/framework/Bundle', - 'org/osgi/framework/BundleActivator', - 'org/osgi/framework/BundleContext', - 'org/osgi/framework/BundleEvent', - 'org/osgi/framework/SynchronousBundleListener', - 'org/osgi/framework/wiring/BundleWire', - 'org/osgi/framework/wiring/BundleWiring' -) +tasks.named("thirdPartyAudit").configure { + ignoreMissingClasses( + // from log4j + 'org.osgi.framework.Bundle', + 'org.osgi.framework.BundleActivator', + 'org.osgi.framework.BundleContext', + 'org.osgi.framework.BundleEvent', + 'org.osgi.framework.FrameworkUtil', + 'org.osgi.framework.ServiceReference', + 'org.osgi.framework.ServiceRegistration', + 'org.osgi.framework.SynchronousBundleListener', + 'org.osgi.framework.wiring.BundleWire', + 'org.osgi.framework.wiring.BundleWiring' + ) +} + +tasks.named("dependencyLicenses").configure { + mapping from: /jackson-.*/, to: 'jackson' + mapping from: /lucene-.*/, to: 'lucene' +} diff --git a/plugins/repository-azure/licenses/jackson-LICENSE b/libs/core/licenses/jackson-LICENSE similarity index 100% rename from plugins/repository-azure/licenses/jackson-LICENSE rename to libs/core/licenses/jackson-LICENSE diff --git a/plugins/repository-azure/licenses/jackson-NOTICE b/libs/core/licenses/jackson-NOTICE similarity index 100% rename from plugins/repository-azure/licenses/jackson-NOTICE rename to libs/core/licenses/jackson-NOTICE diff --git a/libs/core/licenses/jackson-core-2.15.2.jar.sha1 b/libs/core/licenses/jackson-core-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..ec6781b968eed --- /dev/null +++ b/libs/core/licenses/jackson-core-2.15.2.jar.sha1 @@ -0,0 +1 @@ +a6fe1836469a69b3ff66037c324d75fc66ef137c \ No newline at end of file diff --git a/libs/core/licenses/log4j-api-2.20.0.jar.sha1 b/libs/core/licenses/log4j-api-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..37154d9861ac0 --- /dev/null +++ b/libs/core/licenses/log4j-api-2.20.0.jar.sha1 @@ -0,0 +1 @@ +1fe6082e660daf07c689a89c94dc0f49c26b44bb \ No newline at end of file diff --git a/libs/core/licenses/log4j-api-LICENSE.txt b/libs/core/licenses/log4j-api-LICENSE.txt new file mode 100644 index 0000000000000..6279e5206de13 --- /dev/null +++ b/libs/core/licenses/log4j-api-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 1999-2005 The Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/libs/core/licenses/log4j-api-NOTICE.txt b/libs/core/licenses/log4j-api-NOTICE.txt new file mode 100644 index 0000000000000..0375732360047 --- /dev/null +++ b/libs/core/licenses/log4j-api-NOTICE.txt @@ -0,0 +1,5 @@ +Apache log4j +Copyright 2007 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). \ No newline at end of file diff --git a/libs/core/licenses/lucene-LICENSE.txt b/libs/core/licenses/lucene-LICENSE.txt new file mode 100644 index 0000000000000..28b134f5f8e4d --- /dev/null +++ b/libs/core/licenses/lucene-LICENSE.txt @@ -0,0 +1,475 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from unicode conversion examples available at +http://www.unicode.org/Public/PROGRAMS/CVTUTF. Here is the copyright +from those sources: + +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + + +Some code in core/src/java/org/apache/lucene/util/ArrayUtil.java was +derived from Python 2.4.2 sources available at +http://www.python.org. Full license is here: + + http://www.python.org/download/releases/2.4.2/license/ + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from Python 3.1.2 sources available at +http://www.python.org. Full license is here: + + http://www.python.org/download/releases/3.1.2/license/ + +Some code in core/src/java/org/apache/lucene/util/automaton was +derived from Brics automaton sources available at +www.brics.dk/automaton/. Here is the copyright from those sources: + +/* + * Copyright (c) 2001-2009 Anders Moeller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +The levenshtein automata tables in core/src/java/org/apache/lucene/util/automaton +were automatically generated with the moman/finenight FSA package. +Here is the copyright for those sources: + +# Copyright (c) 2010, Jean-Philippe Barrette-LaPierre, +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from ICU (http://www.icu-project.org) +The full license is available here: + http://source.icu-project.org/repos/icu/icu/trunk/license.html + +/* + * Copyright (C) 1999-2010, International Business Machines + * Corporation and others. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * provided that the above copyright notice(s) and this permission notice appear + * in all copies of the Software and that both the above copyright notice(s) and + * this permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE + * LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR + * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall not + * be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +The following license applies to the Snowball stemmers: + +Copyright (c) 2001, Dr Martin Porter +Copyright (c) 2002, Richard Boulton +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holders nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The following license applies to the KStemmer: + +Copyright © 2003, +Center for Intelligent Information Retrieval, +University of Massachusetts, Amherst. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. The names "Center for Intelligent Information Retrieval" and +"University of Massachusetts" must not be used to endorse or promote products +derived from this software without prior written permission. To obtain +permission, contact info@ciir.cs.umass.edu. + +THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF MASSACHUSETTS AND OTHER CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The following license applies to the Morfologik project: + +Copyright (c) 2006 Dawid Weiss +Copyright (c) 2007-2011 Dawid Weiss, Marcin Miłkowski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Morfologik nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- + +The dictionary comes from Morfologik project. Morfologik uses data from +Polish ispell/myspell dictionary hosted at http://www.sjp.pl/slownik/en/ and +is licenced on the terms of (inter alia) LGPL and Creative Commons +ShareAlike. The part-of-speech tags were added in Morfologik project and +are not found in the data from sjp.pl. The tagset is similar to IPI PAN +tagset. + +--- + +The following license applies to the Morfeusz project, +used by org.apache.lucene.analysis.morfologik. + +BSD-licensed dictionary of Polish (SGJP) +http://sgjp.pl/morfeusz/ + +Copyright © 2011 Zygmunt Saloni, Włodzimierz Gruszczyński, + Marcin Woliński, Robert Wołosz + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS “AS IS” AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/core/licenses/lucene-NOTICE.txt b/libs/core/licenses/lucene-NOTICE.txt new file mode 100644 index 0000000000000..1a1d51572432a --- /dev/null +++ b/libs/core/licenses/lucene-NOTICE.txt @@ -0,0 +1,192 @@ +Apache Lucene +Copyright 2014 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Includes software from other Apache Software Foundation projects, +including, but not limited to: + - Apache Ant + - Apache Jakarta Regexp + - Apache Commons + - Apache Xerces + +ICU4J, (under analysis/icu) is licensed under an MIT styles license +and Copyright (c) 1995-2008 International Business Machines Corporation and others + +Some data files (under analysis/icu/src/data) are derived from Unicode data such +as the Unicode Character Database. See http://unicode.org/copyright.html for more +details. + +Brics Automaton (under core/src/java/org/apache/lucene/util/automaton) is +BSD-licensed, created by Anders Møller. See http://www.brics.dk/automaton/ + +The levenshtein automata tables (under core/src/java/org/apache/lucene/util/automaton) were +automatically generated with the moman/finenight FSA library, created by +Jean-Philippe Barrette-LaPierre. This library is available under an MIT license, +see http://sites.google.com/site/rrettesite/moman and +http://bitbucket.org/jpbarrette/moman/overview/ + +The class org.apache.lucene.util.WeakIdentityMap was derived from +the Apache CXF project and is Apache License 2.0. + +The Google Code Prettify is Apache License 2.0. +See http://code.google.com/p/google-code-prettify/ + +JUnit (junit-4.10) is licensed under the Common Public License v. 1.0 +See http://junit.sourceforge.net/cpl-v10.html + +This product includes code (JaspellTernarySearchTrie) from Java Spelling Checkin +g Package (jaspell): http://jaspell.sourceforge.net/ +License: The BSD License (http://www.opensource.org/licenses/bsd-license.php) + +The snowball stemmers in + analysis/common/src/java/net/sf/snowball +were developed by Martin Porter and Richard Boulton. +The snowball stopword lists in + analysis/common/src/resources/org/apache/lucene/analysis/snowball +were developed by Martin Porter and Richard Boulton. +The full snowball package is available from + http://snowball.tartarus.org/ + +The KStem stemmer in + analysis/common/src/org/apache/lucene/analysis/en +was developed by Bob Krovetz and Sergio Guzman-Lara (CIIR-UMass Amherst) +under the BSD-license. + +The Arabic,Persian,Romanian,Bulgarian, Hindi and Bengali analyzers (common) come with a default +stopword list that is BSD-licensed created by Jacques Savoy. These files reside in: +analysis/common/src/resources/org/apache/lucene/analysis/ar/stopwords.txt, +analysis/common/src/resources/org/apache/lucene/analysis/fa/stopwords.txt, +analysis/common/src/resources/org/apache/lucene/analysis/ro/stopwords.txt, +analysis/common/src/resources/org/apache/lucene/analysis/bg/stopwords.txt, +analysis/common/src/resources/org/apache/lucene/analysis/hi/stopwords.txt, +analysis/common/src/resources/org/apache/lucene/analysis/bn/stopwords.txt +See http://members.unine.ch/jacques.savoy/clef/index.html. + +The German,Spanish,Finnish,French,Hungarian,Italian,Portuguese,Russian and Swedish light stemmers +(common) are based on BSD-licensed reference implementations created by Jacques Savoy and +Ljiljana Dolamic. These files reside in: +analysis/common/src/java/org/apache/lucene/analysis/de/GermanLightStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/de/GermanMinimalStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/es/SpanishLightStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/fi/FinnishLightStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/fr/FrenchLightStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/fr/FrenchMinimalStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/hu/HungarianLightStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/it/ItalianLightStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/pt/PortugueseLightStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/ru/RussianLightStemmer.java +analysis/common/src/java/org/apache/lucene/analysis/sv/SwedishLightStemmer.java + +The Stempel analyzer (stempel) includes BSD-licensed software developed +by the Egothor project http://egothor.sf.net/, created by Leo Galambos, Martin Kvapil, +and Edmond Nolan. + +The Polish analyzer (stempel) comes with a default +stopword list that is BSD-licensed created by the Carrot2 project. The file resides +in stempel/src/resources/org/apache/lucene/analysis/pl/stopwords.txt. +See http://project.carrot2.org/license.html. + +The SmartChineseAnalyzer source code (smartcn) was +provided by Xiaoping Gao and copyright 2009 by www.imdict.net. + +WordBreakTestUnicode_*.java (under modules/analysis/common/src/test/) +is derived from Unicode data such as the Unicode Character Database. +See http://unicode.org/copyright.html for more details. + +The Morfologik analyzer (morfologik) includes BSD-licensed software +developed by Dawid Weiss and Marcin Miłkowski (http://morfologik.blogspot.com/). + +Morfologik uses data from Polish ispell/myspell dictionary +(http://www.sjp.pl/slownik/en/) licenced on the terms of (inter alia) +LGPL and Creative Commons ShareAlike. + +Morfologic includes data from BSD-licensed dictionary of Polish (SGJP) +(http://sgjp.pl/morfeusz/) + +Servlet-api.jar and javax.servlet-*.jar are under the CDDL license, the original +source code for this can be found at http://www.eclipse.org/jetty/downloads.php + +=========================================================================== +Kuromoji Japanese Morphological Analyzer - Apache Lucene Integration +=========================================================================== + +This software includes a binary and/or source version of data from + + mecab-ipadic-2.7.0-20070801 + +which can be obtained from + + http://atilika.com/releases/mecab-ipadic/mecab-ipadic-2.7.0-20070801.tar.gz + +or + + http://jaist.dl.sourceforge.net/project/mecab/mecab-ipadic/2.7.0-20070801/mecab-ipadic-2.7.0-20070801.tar.gz + +=========================================================================== +mecab-ipadic-2.7.0-20070801 Notice +=========================================================================== + +Nara Institute of Science and Technology (NAIST), +the copyright holders, disclaims all warranties with regard to this +software, including all implied warranties of merchantability and +fitness, in no event shall NAIST be liable for +any special, indirect or consequential damages or any damages +whatsoever resulting from loss of use, data or profits, whether in an +action of contract, negligence or other tortuous action, arising out +of or in connection with the use or performance of this software. + +A large portion of the dictionary entries +originate from ICOT Free Software. The following conditions for ICOT +Free Software applies to the current dictionary as well. + +Each User may also freely distribute the Program, whether in its +original form or modified, to any third party or parties, PROVIDED +that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear +on, or be attached to, the Program, which is distributed substantially +in the same form as set out herein and that such intended +distribution, if actually made, will neither violate or otherwise +contravene any of the laws and regulations of the countries having +jurisdiction over the User or the intended distribution itself. + +NO WARRANTY + +The program was produced on an experimental basis in the course of the +research and development conducted during the project and is provided +to users as so produced on an experimental basis. Accordingly, the +program is provided without any warranty whatsoever, whether express, +implied, statutory or otherwise. The term "warranty" used herein +includes, but is not limited to, any warranty of the quality, +performance, merchantability and fitness for a particular purpose of +the program and the nonexistence of any infringement or violation of +any right of any third party. + +Each user of the program will agree and understand, and be deemed to +have agreed and understood, that there is no warranty whatsoever for +the program and, accordingly, the entire risk arising from or +otherwise connected with the program is assumed by the user. + +Therefore, neither ICOT, the copyright holder, or any other +organization that participated in or was otherwise related to the +development of the program and their respective officials, directors, +officers and other employees shall be held liable for any and all +damages, including, without limitation, general, special, incidental +and consequential damages, arising out of or otherwise in connection +with the use or inability to use the program or any product, material +or result produced or otherwise obtained by using the program, +regardless of whether they have been advised of, or otherwise had +knowledge of, the possibility of such damages at any time during the +project or thereafter. Each user will be deemed to have agreed to the +foregoing by his or her commencement of use of the program. The term +"use" as used herein includes, but is not limited to, the use, +modification, copying and distribution of the program and the +production of secondary products from the program. + +In the case where the program, whether in its original form or +modified, was distributed or delivered to or received by a user from +any person, organization or entity other than ICOT, unless it makes or +grants independently of ICOT any specific warranty to the user in +writing, such person, organization or entity, will also be exempted +from and not be held liable to the user for any such damages as noted +above as far as the program is concerned. diff --git a/libs/core/licenses/lucene-core-9.7.0.jar.sha1 b/libs/core/licenses/lucene-core-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..2b0f77275c0ab --- /dev/null +++ b/libs/core/licenses/lucene-core-9.7.0.jar.sha1 @@ -0,0 +1 @@ +ad391210ffd806931334be9670a35af00c56f959 \ No newline at end of file diff --git a/server/src/main/java/org/opensearch/Build.java b/libs/core/src/main/java/org/opensearch/Build.java similarity index 75% rename from server/src/main/java/org/opensearch/Build.java rename to libs/core/src/main/java/org/opensearch/Build.java index 9508a63de5991..67a50a8a31a0e 100644 --- a/server/src/main/java/org/opensearch/Build.java +++ b/libs/core/src/main/java/org/opensearch/Build.java @@ -33,9 +33,7 @@ package org.opensearch; import org.opensearch.common.Booleans; -import org.opensearch.common.io.FileSystemUtils; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.util.FileSystemUtils; import java.io.IOException; import java.net.URL; @@ -46,6 +44,8 @@ /** * Information about a build of OpenSearch. + * + * @opensearch.internal */ public class Build { /** @@ -54,6 +54,11 @@ public class Build { */ public static final Build CURRENT; + /** + * The type of build + * + * @opensearch.internal + */ public enum Type { DEB("deb"), @@ -109,12 +114,14 @@ public static Type fromDisplayName(final String displayName, final boolean stric // these are parsed at startup, and we require that we are able to recognize the values passed in by the startup scripts type = Type.fromDisplayName(System.getProperty("opensearch.distribution.type", "unknown"), true); - final String opensearchPrefix = distribution + "-" + Version.CURRENT; + final String opensearchPrefix = distribution; final URL url = getOpenSearchCodeSourceLocation(); final String urlStr = url == null ? "" : url.toString(); if (urlStr.startsWith("file:/") - && (urlStr.endsWith(opensearchPrefix + ".jar") - || urlStr.matches("(.*)" + opensearchPrefix + "(-)?((alpha|beta|rc)[0-9]+)?(-SNAPSHOT)?.jar"))) { + && (urlStr.endsWith(opensearchPrefix + "-" + Version.CURRENT + ".jar") + || urlStr.matches( + "(.*)" + opensearchPrefix + "(-)?(.*?)" + Version.CURRENT + "(-)?((alpha|beta|rc)[0-9]+)?(-SNAPSHOT)?.jar" + ))) { try (JarInputStream jar = new JarInputStream(FileSystemUtils.openFileURLStream(url))) { Manifest manifest = jar.getManifest(); hash = manifest.getMainAttributes().getValue("Change"); @@ -199,61 +206,6 @@ public String date() { return date; } - public static Build readBuild(StreamInput in) throws IOException { - final String distribution; - final Type type; - // the following is new for opensearch: we write the distribution to support any "forks" - if (in.getVersion().onOrAfter(Version.V_1_0_0)) { - distribution = in.readString(); - } else { - distribution = "other"; - } - - // The following block is kept for existing BWS tests to pass. - // TODO - clean up this code when we remove all v6 bwc tests. - // TODO - clean this up when OSS flavor is removed in all of the code base - // (Integ test zip still write OSS as distribution) - // See issue: https://github.com/opendistro-for-elasticsearch/search/issues/159 - if (in.getVersion().before(Version.V_1_3_0)) { - String flavor = in.readString(); - } - // be lenient when reading on the wire, the enumeration values from other versions might be different than what we know - type = Type.fromDisplayName(in.readString(), false); - String hash = in.readString(); - String date = in.readString(); - boolean snapshot = in.readBoolean(); - - final String version; - if (in.getVersion().onOrAfter(LegacyESVersion.V_7_0_0)) { - version = in.readString(); - } else { - version = in.getVersion().toString(); - } - return new Build(type, hash, date, snapshot, version, distribution); - } - - public static void writeBuild(Build build, StreamOutput out) throws IOException { - // the following is new for opensearch: we write the distribution name to support any "forks" of the code - if (out.getVersion().onOrAfter(Version.V_1_0_0)) { - out.writeString(build.distribution); - } - - // The following block is kept for existing BWS tests to pass. - // TODO - clean up this code when we remove all v6 bwc tests. - // TODO - clean this up when OSS flavor is removed in all of the code base - if (out.getVersion().before(Version.V_1_3_0)) { - out.writeString("oss"); - } - final Type buildType = build.type(); - out.writeString(buildType.displayName()); - out.writeString(build.hash()); - out.writeString(build.date()); - out.writeBoolean(build.isSnapshot()); - if (out.getVersion().onOrAfter(LegacyESVersion.V_7_0_0)) { - out.writeString(build.getQualifiedVersion()); - } - } - /** * Get the distribution name (expected to be OpenSearch; empty if legacy; something else if forked) * @return distribution name as a string diff --git a/server/src/main/java/org/opensearch/ExceptionsHelper.java b/libs/core/src/main/java/org/opensearch/ExceptionsHelper.java similarity index 86% rename from server/src/main/java/org/opensearch/ExceptionsHelper.java rename to libs/core/src/main/java/org/opensearch/ExceptionsHelper.java index 418bf9811a7b3..fa1da858d0b42 100644 --- a/server/src/main/java/org/opensearch/ExceptionsHelper.java +++ b/libs/core/src/main/java/org/opensearch/ExceptionsHelper.java @@ -33,16 +33,19 @@ package org.opensearch; import com.fasterxml.jackson.core.JsonParseException; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexFormatTooNewException; import org.apache.lucene.index.IndexFormatTooOldException; -import org.opensearch.action.ShardOperationFailedException; +import org.opensearch.common.CheckedRunnable; +import org.opensearch.common.CheckedSupplier; import org.opensearch.common.Nullable; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.index.Index; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.action.ShardOperationFailedException; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.index.Index; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; import java.io.PrintWriter; @@ -61,10 +64,19 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -public final class ExceptionsHelper { +import static org.opensearch.OpenSearchException.getExceptionSimpleClassName; +/** + * Helper class for OpenSearch Exceptions + * + * @opensearch.internal + */ +public final class ExceptionsHelper { private static final Logger logger = LogManager.getLogger(ExceptionsHelper.class); + // utility class: no ctor + private ExceptionsHelper() {} + public static RuntimeException convertToRuntime(Exception e) { if (e instanceof RuntimeException) { return (RuntimeException) e; @@ -94,6 +106,21 @@ public static RestStatus status(Throwable t) { return RestStatus.INTERNAL_SERVER_ERROR; } + public static String summaryMessage(Throwable t) { + if (t != null) { + if (t instanceof OpenSearchException) { + return getExceptionSimpleClassName(t) + "[" + t.getMessage() + "]"; + } else if (t instanceof IllegalArgumentException) { + return "Invalid argument"; + } else if (t instanceof JsonParseException) { + return "Failed to parse JSON"; + } else if (t instanceof OpenSearchRejectedExecutionException) { + return "Too many requests"; + } + } + return "Internal failure"; + } + public static Throwable unwrapCause(Throwable t) { int counter = 0; Throwable result = t; @@ -125,7 +152,7 @@ public static String detailedMessage(Throwable t) { if (t.getCause() != null) { StringBuilder sb = new StringBuilder(); while (t != null) { - sb.append(t.getClass().getSimpleName()); + sb.append(getExceptionSimpleClassName(t)); if (t.getMessage() != null) { sb.append("["); sb.append(t.getMessage()); @@ -139,7 +166,7 @@ public static String detailedMessage(Throwable t) { } return sb.toString(); } else { - return t.getClass().getSimpleName() + "[" + t.getMessage() + "]"; + return getExceptionSimpleClassName(t) + "[" + t.getMessage() + "]"; } } @@ -315,6 +342,32 @@ public static void maybeDieOnAnotherThread(final Throwable throwable) { }); } + /** + * Run passed runnable and catch exception and translate exception into runtime exception using + * {@link ExceptionsHelper#convertToRuntime(Exception)} + * @param supplier to run + */ + public static R catchAsRuntimeException(CheckedSupplier supplier) { + try { + return supplier.get(); + } catch (Exception e) { + throw convertToRuntime(e); + } + } + + /** + * Run passed runnable and catch exception and translate exception into runtime exception using + * {@link ExceptionsHelper#convertToRuntime(Exception)} + * @param runnable to run + */ + public static void catchAsRuntimeException(CheckedRunnable runnable) { + try { + runnable.run(); + } catch (Exception e) { + throw convertToRuntime(e); + } + } + /** * Deduplicate the failures by exception message and index. */ diff --git a/server/src/main/java/org/opensearch/LegacyESVersion.java b/libs/core/src/main/java/org/opensearch/LegacyESVersion.java similarity index 95% rename from server/src/main/java/org/opensearch/LegacyESVersion.java rename to libs/core/src/main/java/org/opensearch/LegacyESVersion.java index 38b655b09b151..dafd071ef935a 100644 --- a/server/src/main/java/org/opensearch/LegacyESVersion.java +++ b/libs/core/src/main/java/org/opensearch/LegacyESVersion.java @@ -32,17 +32,19 @@ package org.opensearch; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenIntMap; -import org.opensearch.common.collect.ImmutableOpenMap; +import org.opensearch.core.Assertions; import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; /** * The Contents of this file were originally moved from {@link Version}. * * This class keeps all the supported OpenSearch predecessor versions for * backward compatibility purpose. + * + * @opensearch.internal */ public class LegacyESVersion extends Version { @@ -77,11 +79,11 @@ public class LegacyESVersion extends Version { public static final LegacyESVersion V_7_10_2 = new LegacyESVersion(7100299, org.apache.lucene.util.Version.LUCENE_8_7_0); // todo move back to Version.java if retiring legacy version support - protected static final ImmutableOpenIntMap idToVersion; - protected static final ImmutableOpenMap stringToVersion; + protected static final Map idToVersion; + protected static final Map stringToVersion; static { - final ImmutableOpenIntMap.Builder builder = ImmutableOpenIntMap.builder(); - final ImmutableOpenMap.Builder builderByString = ImmutableOpenMap.builder(); + final Map builder = new HashMap<>(); + final Map builderByString = new HashMap<>(); for (final Field declaredField : LegacyESVersion.class.getFields()) { if (declaredField.getType().equals(Version.class) || declaredField.getType().equals(LegacyESVersion.class)) { @@ -143,8 +145,8 @@ public class LegacyESVersion extends Version { builder.put(V_EMPTY_ID, V_EMPTY); builderByString.put(V_EMPTY.toString(), V_EMPTY); - idToVersion = builder.build(); - stringToVersion = builderByString.build(); + idToVersion = Map.copyOf(builder); + stringToVersion = Map.copyOf(builderByString); } protected LegacyESVersion(int id, org.apache.lucene.util.Version luceneVersion) { @@ -173,7 +175,7 @@ public boolean isAlpha() { * Returns the version given its string representation, current version if the argument is null or empty */ public static Version fromString(String version) { - if (!Strings.hasLength(version)) { + if (stringHasLength(version) == false) { return Version.CURRENT; } final Version cached = stringToVersion.get(version); diff --git a/libs/core/src/main/java/org/opensearch/OpenSearchException.java b/libs/core/src/main/java/org/opensearch/OpenSearchException.java new file mode 100644 index 0000000000000..5bad711a15032 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/OpenSearchException.java @@ -0,0 +1,1003 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ +package org.opensearch; + +import org.opensearch.common.CheckedFunction; +import org.opensearch.common.Nullable; +import org.opensearch.common.collect.Tuple; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.logging.LoggerMessageFormat; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static org.opensearch.OpenSearchException.OpenSearchExceptionHandleRegistry.registerExceptionHandle; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureFieldName; + +/** + * A core library base class for all opensearch exceptions. + * + * @opensearch.internal + */ +public class OpenSearchException extends RuntimeException implements Writeable, ToXContentFragment { + + protected static final Version UNKNOWN_VERSION_ADDED = Version.fromId(0); + + /** + * Passed in the {@link ToXContent.Params} of {@link #generateThrowableXContent(XContentBuilder, ToXContent.Params, Throwable)} + * to control if the {@code caused_by} element should render. Unlike most parameters to {@code toXContent} methods this parameter is + * internal only and not available as a URL parameter. + */ + private static final String REST_EXCEPTION_SKIP_CAUSE = "rest.exception.cause.skip"; + /** + * Passed in the {@link ToXContent.Params} of {@link #generateThrowableXContent(XContentBuilder, ToXContent.Params, Throwable)} + * to control if the {@code stack_trace} element should render. Unlike most parameters to {@code toXContent} methods this parameter is + * internal only and not available as a URL parameter. Use the {@code error_trace} parameter instead. + */ + public static final String REST_EXCEPTION_SKIP_STACK_TRACE = "rest.exception.stacktrace.skip"; + public static final boolean REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT = true; + private static final boolean REST_EXCEPTION_SKIP_CAUSE_DEFAULT = false; + private static final String RESOURCE_METADATA_TYPE_KEY = "opensearch.resource.type"; + private static final String RESOURCE_METADATA_ID_KEY = "opensearch.resource.id"; + private static final String INDEX_METADATA_KEY = "opensearch.index"; + private static final String INDEX_METADATA_KEY_UUID = "opensearch.index_uuid"; + private static final String SHARD_METADATA_KEY = "opensearch.shard"; + private static final String OPENSEARCH_PREFIX_KEY = "opensearch."; + + private static final String TYPE = "type"; + private static final String REASON = "reason"; + private static final String CAUSED_BY = "caused_by"; + private static final ParseField SUPPRESSED = new ParseField("suppressed"); + public static final String STACK_TRACE = "stack_trace"; + private static final String HEADER = "header"; + private static final String ERROR = "error"; + private static final String ROOT_CAUSE = "root_cause"; + + protected final Map> metadata = new HashMap<>(); + protected final Map> headers = new HashMap<>(); + + static { + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.core.index.snapshots.IndexShardSnapshotFailedException.class, + org.opensearch.core.index.snapshots.IndexShardSnapshotFailedException::new, + 0, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.OpenSearchParseException.class, + org.opensearch.OpenSearchParseException::new, + 35, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.core.common.ParsingException.class, + org.opensearch.core.common.ParsingException::new, + 40, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.core.common.io.stream.NotSerializableExceptionWrapper.class, + org.opensearch.core.common.io.stream.NotSerializableExceptionWrapper::new, + 62, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.core.common.breaker.CircuitBreakingException.class, + org.opensearch.core.common.breaker.CircuitBreakingException::new, + 133, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.core.tasks.TaskCancelledException.class, + org.opensearch.core.tasks.TaskCancelledException::new, + 146, + UNKNOWN_VERSION_ADDED + ) + ); + } + + /** + * Construct a OpenSearchException with the specified cause exception. + */ + public OpenSearchException(Throwable cause) { + super(cause); + } + + /** + * Construct a OpenSearchException with the specified detail message. + * + * The message can be parameterized using {} as placeholders for the given + * arguments + * + * @param msg the detail message + * @param args the arguments for the message + */ + public OpenSearchException(String msg, Object... args) { + super(LoggerMessageFormat.format(msg, args)); + } + + /** + * Construct a OpenSearchException with the specified detail message + * and nested exception. + * + * The message can be parameterized using {} as placeholders for the given + * arguments + * + * @param msg the detail message + * @param cause the nested exception + * @param args the arguments for the message + */ + public OpenSearchException(String msg, Throwable cause, Object... args) { + super(LoggerMessageFormat.format(msg, args), cause); + } + + public OpenSearchException(StreamInput in) throws IOException { + this(in.readOptionalString(), in.readException()); + readStackTrace(this, in); + headers.putAll(in.readMapOfLists(StreamInput::readString, StreamInput::readString)); + metadata.putAll(in.readMapOfLists(StreamInput::readString, StreamInput::readString)); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + out.writeOptionalString(this.getMessage()); + out.writeException(this.getCause()); + writeStackTraces(this, out, StreamOutput::writeException); + out.writeMapOfLists(headers, StreamOutput::writeString, StreamOutput::writeString); + out.writeMapOfLists(metadata, StreamOutput::writeString, StreamOutput::writeString); + } + + /** + * Adds a new piece of metadata with the given key. + * If the provided key is already present, the corresponding metadata will be replaced + */ + public void addMetadata(String key, String... values) { + addMetadata(key, Arrays.asList(values)); + } + + /** + * Adds a new piece of metadata with the given key. + * If the provided key is already present, the corresponding metadata will be replaced + */ + public void addMetadata(String key, List values) { + // we need to enforce this otherwise bw comp doesn't work properly, as "opensearch." + // was the previous criteria to split headers in two sets + if (key.startsWith(OPENSEARCH_PREFIX_KEY) == false) { + throw new IllegalArgumentException("exception metadata must start with [opensearch.], found [" + key + "] instead"); + } + this.metadata.put(key, values); + } + + /** + * Returns a set of all metadata keys on this exception + */ + public Set getMetadataKeys() { + return metadata.keySet(); + } + + /** + * Returns the list of metadata values for the given key or {@code null} if no metadata for the + * given key exists. + */ + public List getMetadata(String key) { + return metadata.get(key); + } + + protected Map> getMetadata() { + return metadata; + } + + /** + * Adds a new header with the given key. + * This method will replace existing header if a header with the same key already exists + */ + public void addHeader(String key, List value) { + // we need to enforce this otherwise bw comp doesn't work properly, as "opensearch." + // was the previous criteria to split headers in two sets + if (key.startsWith(OPENSEARCH_PREFIX_KEY)) { + throw new IllegalArgumentException("exception headers must not start with [opensearch.], found [" + key + "] instead"); + } + this.headers.put(key, value); + } + + /** + * Adds a new header with the given key. + * This method will replace existing header if a header with the same key already exists + */ + public void addHeader(String key, String... value) { + addHeader(key, Arrays.asList(value)); + } + + /** + * Returns a set of all header keys on this exception + */ + public Set getHeaderKeys() { + return headers.keySet(); + } + + /** + * Returns the list of header values for the given key or {@code null} if no header for the + * given key exists. + */ + public List getHeader(String key) { + return headers.get(key); + } + + protected Map> getHeaders() { + return headers; + } + + /** + * Returns the rest status code associated with this exception. + */ + public RestStatus status() { + Throwable cause = unwrapCause(); + if (cause == this) { + return RestStatus.INTERNAL_SERVER_ERROR; + } else { + return ExceptionsHelper.status(cause); + } + } + + /** + * Unwraps the actual cause from the exception for cases when the exception is a + * {@link OpenSearchWrapperException}. + * + * @see ExceptionsHelper#unwrapCause(Throwable) + */ + public Throwable unwrapCause() { + return ExceptionsHelper.unwrapCause(this); + } + + /** + * Return the detail message, including the message from the nested exception + * if there is one. + */ + public String getDetailedMessage() { + if (getCause() != null) { + StringBuilder sb = new StringBuilder(); + sb.append(toString()).append("; "); + if (getCause() instanceof OpenSearchException) { + sb.append(((OpenSearchException) getCause()).getDetailedMessage()); + } else { + sb.append(getCause()); + } + return sb.toString(); + } else { + return toString(); + } + } + + /** + * Retrieve the innermost cause of this exception, if none, returns the current exception. + */ + public Throwable getRootCause() { + Throwable rootCause = this; + Throwable cause = getCause(); + while (cause != null && cause != rootCause) { + rootCause = cause; + cause = cause.getCause(); + } + return rootCause; + } + + @SuppressWarnings("unchecked") + public static OpenSearchException readException(T input, int id) throws IOException { + CheckedFunction opensearchException = (CheckedFunction< + T, + ? extends OpenSearchException, + IOException>) OpenSearchExceptionHandleRegistry.getSupplier(id); + if (opensearchException == null) { + throw new IllegalStateException("unknown exception for id: " + id); + } + return opensearchException.apply(input); + } + + /** + * Returns true iff the given class is a registered for an exception to be read. + */ + public static boolean isRegistered(final Class exception, Version version) { + return OpenSearchExceptionHandleRegistry.isRegistered(exception, version); + } + + static Set> getRegisteredKeys() { // for testing + return OpenSearchExceptionHandleRegistry.getRegisteredKeys(); + } + + /** + * Returns the serialization id the given exception. + */ + public static int getId(final Class exception) { + return OpenSearchExceptionHandleRegistry.getId(exception); + } + + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + Throwable ex = ExceptionsHelper.unwrapCause(this); + if (ex != this) { + generateThrowableXContent(builder, params, this); + } else { + innerToXContent(builder, params, this, getExceptionName(), getMessage(), headers, metadata, getCause()); + } + return builder; + } + + protected static void innerToXContent( + XContentBuilder builder, + ToXContent.Params params, + Throwable throwable, + String type, + String message, + Map> headers, + Map> metadata, + Throwable cause + ) throws IOException { + builder.field(TYPE, type); + builder.field(REASON, message); + + for (Map.Entry> entry : metadata.entrySet()) { + headerToXContent(builder, entry.getKey().substring(OPENSEARCH_PREFIX_KEY.length()), entry.getValue()); + } + + if (throwable instanceof OpenSearchException) { + OpenSearchException exception = (OpenSearchException) throwable; + exception.metadataToXContent(builder, params); + } + + if (params.paramAsBoolean(REST_EXCEPTION_SKIP_CAUSE, REST_EXCEPTION_SKIP_CAUSE_DEFAULT) == false) { + if (cause != null) { + builder.field(CAUSED_BY); + builder.startObject(); + generateThrowableXContent(builder, params, cause); + builder.endObject(); + } + } + + if (headers.isEmpty() == false) { + builder.startObject(HEADER); + for (Map.Entry> entry : headers.entrySet()) { + headerToXContent(builder, entry.getKey(), entry.getValue()); + } + builder.endObject(); + } + + if (params.paramAsBoolean(REST_EXCEPTION_SKIP_STACK_TRACE, REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT) == false) { + builder.field(STACK_TRACE, ExceptionsHelper.stackTrace(throwable)); + } + + Throwable[] allSuppressed = throwable.getSuppressed(); + if (allSuppressed.length > 0) { + builder.startArray(SUPPRESSED.getPreferredName()); + for (Throwable suppressed : allSuppressed) { + builder.startObject(); + generateThrowableXContent(builder, params, suppressed); + builder.endObject(); + } + builder.endArray(); + } + } + + protected static void headerToXContent(XContentBuilder builder, String key, List values) throws IOException { + if (values != null && values.isEmpty() == false) { + if (values.size() == 1) { + builder.field(key, values.get(0)); + } else { + builder.startArray(key); + for (String value : values) { + builder.value(value); + } + builder.endArray(); + } + } + } + + /** + * Renders additional per exception information into the XContent + */ + protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {} + + /** + * Generate a {@link OpenSearchException} from a {@link XContentParser}. This does not + * return the original exception type (ie NodeClosedException for example) but just wraps + * the type, the reason and the cause of the exception. It also recursively parses the + * tree structure of the cause, returning it as a tree structure of {@link OpenSearchException} + * instances. + */ + public static OpenSearchException fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token = parser.nextToken(); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser); + return innerFromXContent(parser, false); + } + + public static OpenSearchException innerFromXContent(XContentParser parser, boolean parseRootCauses) throws IOException { + XContentParser.Token token = parser.currentToken(); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser); + + String type = null, reason = null, stack = null; + OpenSearchException cause = null; + Map> metadata = new HashMap<>(); + Map> headers = new HashMap<>(); + List rootCauses = new ArrayList<>(); + List suppressed = new ArrayList<>(); + + for (; token == XContentParser.Token.FIELD_NAME; token = parser.nextToken()) { + String currentFieldName = parser.currentName(); + token = parser.nextToken(); + + if (token.isValue()) { + if (TYPE.equals(currentFieldName)) { + type = parser.text(); + } else if (REASON.equals(currentFieldName)) { + reason = parser.text(); + } else if (STACK_TRACE.equals(currentFieldName)) { + stack = parser.text(); + } else if (token == XContentParser.Token.VALUE_STRING) { + metadata.put(currentFieldName, Collections.singletonList(parser.text())); + } + } else if (token == XContentParser.Token.START_OBJECT) { + if (CAUSED_BY.equals(currentFieldName)) { + cause = fromXContent(parser); + } else if (HEADER.equals(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else { + List values = headers.getOrDefault(currentFieldName, new ArrayList<>()); + if (token == XContentParser.Token.VALUE_STRING) { + values.add(parser.text()); + } else if (token == XContentParser.Token.START_ARRAY) { + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (token == XContentParser.Token.VALUE_STRING) { + values.add(parser.text()); + } else { + parser.skipChildren(); + } + } + } else if (token == XContentParser.Token.START_OBJECT) { + parser.skipChildren(); + } + headers.put(currentFieldName, values); + } + } + } else { + // Any additional metadata object added by the metadataToXContent method is ignored + // and skipped, so that the parser does not fail on unknown fields. The parser only + // support metadata key-pairs and metadata arrays of values. + parser.skipChildren(); + } + } else if (token == XContentParser.Token.START_ARRAY) { + if (parseRootCauses && ROOT_CAUSE.equals(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + rootCauses.add(fromXContent(parser)); + } + } else if (SUPPRESSED.match(currentFieldName, parser.getDeprecationHandler())) { + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + suppressed.add(fromXContent(parser)); + } + } else { + // Parse the array and add each item to the corresponding list of metadata. + // Arrays of objects are not supported yet and just ignored and skipped. + List values = new ArrayList<>(); + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (token == XContentParser.Token.VALUE_STRING) { + values.add(parser.text()); + } else { + parser.skipChildren(); + } + } + if (values.size() > 0) { + if (metadata.containsKey(currentFieldName)) { + values.addAll(metadata.get(currentFieldName)); + } + metadata.put(currentFieldName, values); + } + } + } + } + + OpenSearchException e = new OpenSearchException(buildMessage(type, reason, stack), cause) { + }; + for (Map.Entry> entry : metadata.entrySet()) { + // subclasses can print out additional metadata through the metadataToXContent method. Simple key-value pairs will be + // parsed back and become part of this metadata set, while objects and arrays are not supported when parsing back. + // Those key-value pairs become part of the metadata set and inherit the "opensearch." prefix as that is currently required + // by addMetadata. The prefix will get stripped out when printing metadata out so it will be effectively invisible. + // TODO move subclasses that print out simple metadata to using addMetadata directly and support also numbers and booleans. + // TODO rename metadataToXContent and have only SearchPhaseExecutionException use it, which prints out complex objects + e.addMetadata(OPENSEARCH_PREFIX_KEY + entry.getKey(), entry.getValue()); + } + for (Map.Entry> header : headers.entrySet()) { + e.addHeader(header.getKey(), header.getValue()); + } + + // Adds root causes as suppressed exception. This way they are not lost + // after parsing and can be retrieved using getSuppressed() method. + for (OpenSearchException rootCause : rootCauses) { + e.addSuppressed(rootCause); + } + for (OpenSearchException s : suppressed) { + e.addSuppressed(s); + } + return e; + } + + /** + * Static toXContent helper method that renders {@link OpenSearchException} or {@link Throwable} instances + * as XContent, delegating the rendering to {@link OpenSearchException#toXContent(XContentBuilder, ToXContent.Params)} + * or {@link #innerToXContent(XContentBuilder, ToXContent.Params, Throwable, String, String, Map, Map, Throwable)}. + * + * This method is usually used when the {@link Throwable} is rendered as a part of another XContent object, and its result can + * be parsed back using the {@code OpenSearchException.fromXContent(XContentParser)} method. + */ + public static void generateThrowableXContent(XContentBuilder builder, ToXContent.Params params, Throwable t) throws IOException { + t = ExceptionsHelper.unwrapCause(t); + + if (t instanceof OpenSearchException) { + ((OpenSearchException) t).toXContent(builder, params); + } else { + innerToXContent(builder, params, t, getExceptionName(t), t.getMessage(), emptyMap(), emptyMap(), t.getCause()); + } + } + + /** + * Render any exception as a xcontent, encapsulated within a field or object named "error". The level of details that are rendered + * depends on the value of the "detailed" parameter: when it's false only a simple message based on the type and message of the + * exception is rendered. When it's true all detail are provided including guesses root causes, cause and potentially stack + * trace. + * + * This method is usually used when the {@link Exception} is rendered as a full XContent object, and its output can be parsed + * by the {@code #OpenSearchException.failureFromXContent(XContentParser)} method. + */ + public static void generateFailureXContent(XContentBuilder builder, ToXContent.Params params, @Nullable Exception e, boolean detailed) + throws IOException { + // No exception to render as an error + if (e == null) { + builder.field(ERROR, "unknown"); + return; + } + + // Render the exception with a simple message + if (detailed == false) { + Throwable t = e; + for (int counter = 0; counter < 10 && t != null; counter++) { + if (t instanceof OpenSearchException) { + break; + } + t = t.getCause(); + } + builder.field(ERROR, ExceptionsHelper.summaryMessage(t != null ? t : e)); + return; + } + + // Render the exception with all details + final OpenSearchException[] rootCauses = OpenSearchException.guessRootCauses(e); + builder.startObject(ERROR); + { + builder.startArray(ROOT_CAUSE); + for (OpenSearchException rootCause : rootCauses) { + builder.startObject(); + rootCause.toXContent(builder, new ToXContent.DelegatingMapParams(singletonMap(REST_EXCEPTION_SKIP_CAUSE, "true"), params)); + builder.endObject(); + } + builder.endArray(); + } + generateThrowableXContent(builder, params, e); + builder.endObject(); + } + + /** + * Parses the output of {@link #generateFailureXContent(XContentBuilder, Params, Exception, boolean)} + */ + public static OpenSearchException failureFromXContent(XContentParser parser) throws IOException { + XContentParser.Token token = parser.currentToken(); + ensureFieldName(parser, token, ERROR); + + token = parser.nextToken(); + if (token.isValue()) { + return new OpenSearchException(buildMessage("exception", parser.text(), null)) { + }; + } + + ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser); + token = parser.nextToken(); + + // Root causes are parsed in the innerFromXContent() and are added as suppressed exceptions. + return innerFromXContent(parser, true); + } + + /** + * Returns the root cause of this exception or multiple if different shards caused different exceptions + */ + public OpenSearchException[] guessRootCauses() { + final Throwable cause = getCause(); + if (cause != null && cause instanceof OpenSearchException) { + return ((OpenSearchException) cause).guessRootCauses(); + } + return new OpenSearchException[] { this }; + } + + /** + * Returns the root cause of this exception or multiple if different shards caused different exceptions. + * If the given exception is not an instance of {@link OpenSearchException} an empty array + * is returned. + */ + public static OpenSearchException[] guessRootCauses(Throwable t) { + Throwable ex = ExceptionsHelper.unwrapCause(t); + if (ex instanceof OpenSearchException) { + // OpenSearchException knows how to guess its own root cause + return ((OpenSearchException) ex).guessRootCauses(); + } + if (ex instanceof XContentParseException) { + /* + * We'd like to unwrap parsing exceptions to the inner-most + * parsing exception because that is generally the most interesting + * exception to return to the user. If that exception is caused by + * an OpenSearchException we'd like to keep unwrapping because + * OpenSearchException instances tend to contain useful information + * for the user. + */ + Throwable cause = ex.getCause(); + if (cause != null) { + if (cause instanceof XContentParseException || cause instanceof OpenSearchException) { + return OpenSearchException.guessRootCauses(ex.getCause()); + } + } + } + return new OpenSearchException[] { new OpenSearchException(ex.getMessage(), ex) { + @Override + protected String getExceptionName() { + return getExceptionName(getCause()); + } + } }; + } + + protected String getExceptionName() { + return getExceptionName(this); + } + + /** + * Returns an underscore case name for the given exception. This method strips {@code OpenSearch} prefixes from exception names. + */ + public static String getExceptionName(Throwable ex) { + String simpleName = getExceptionSimpleClassName(ex); + if (simpleName.startsWith("OpenSearch")) { + simpleName = simpleName.substring("OpenSearch".length()); + } + // TODO: do we really need to make the exception name in underscore casing? + return toUnderscoreCase(simpleName); + } + + public static String getExceptionSimpleClassName(final Throwable ex) { + String simpleName = ex.getClass().getSimpleName(); + if (Strings.isEmpty(simpleName)) { + simpleName = "OpenSearchException"; + } + return simpleName; + } + + static String buildMessage(String type, String reason, String stack) { + StringBuilder message = new StringBuilder("OpenSearch exception ["); + message.append(TYPE).append('=').append(type).append(", "); + message.append(REASON).append('=').append(reason); + if (stack != null) { + message.append(", ").append(STACK_TRACE).append('=').append(stack); + } + message.append(']'); + return message.toString(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (metadata.containsKey(INDEX_METADATA_KEY)) { + builder.append(getIndex()); + if (metadata.containsKey(SHARD_METADATA_KEY)) { + builder.append('[').append(getShardId()).append(']'); + } + builder.append(' '); + } + return builder.append(ExceptionsHelper.detailedMessage(this).trim()).toString(); + } + + /** + * Deserializes stacktrace elements as well as suppressed exceptions from the given output stream and + * adds it to the given exception. + */ + public static T readStackTrace(T throwable, StreamInput in) throws IOException { + throwable.setStackTrace(in.readArray(i -> { + final String declaringClasss = i.readString(); + final String fileName = i.readOptionalString(); + final String methodName = i.readString(); + final int lineNumber = i.readVInt(); + return new StackTraceElement(declaringClasss, methodName, fileName, lineNumber); + }, StackTraceElement[]::new)); + + int numSuppressed = in.readVInt(); + for (int i = 0; i < numSuppressed; i++) { + throwable.addSuppressed(in.readException()); + } + return throwable; + } + + /** + * Serializes the given exceptions stacktrace elements as well as it's suppressed exceptions to the given output stream. + */ + public static T writeStackTraces( + T throwable, + StreamOutput out, + Writer exceptionWriter + ) throws IOException { + out.writeArray((o, v) -> { + o.writeString(v.getClassName()); + o.writeOptionalString(v.getFileName()); + o.writeString(v.getMethodName()); + o.writeVInt(v.getLineNumber()); + }, throwable.getStackTrace()); + out.writeArray(exceptionWriter, throwable.getSuppressed()); + return throwable; + } + + public void setResources(String type, String... id) { + assert type != null; + addMetadata(RESOURCE_METADATA_ID_KEY, id); + addMetadata(RESOURCE_METADATA_TYPE_KEY, type); + } + + public List getResourceId() { + return getMetadata(RESOURCE_METADATA_ID_KEY); + } + + public String getResourceType() { + List header = getMetadata(RESOURCE_METADATA_TYPE_KEY); + if (header != null && header.isEmpty() == false) { + assert header.size() == 1; + return header.get(0); + } + return null; + } + + // lower cases and adds underscores to transitions in a name + private static String toUnderscoreCase(String value) { + StringBuilder sb = new StringBuilder(); + boolean changed = false; + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + if (Character.isUpperCase(c)) { + if (!changed) { + // copy it over here + for (int j = 0; j < i; j++) { + sb.append(value.charAt(j)); + } + changed = true; + if (i == 0) { + sb.append(Character.toLowerCase(c)); + } else { + sb.append('_'); + sb.append(Character.toLowerCase(c)); + } + } else { + sb.append('_'); + sb.append(Character.toLowerCase(c)); + } + } else { + if (changed) { + sb.append(c); + } + } + } + if (!changed) { + return value; + } + return sb.toString(); + } + + /** + * Returns an array of all registered handle IDs. These are the IDs for every registered + * exception. + * + * @return an array of all registered handle IDs + */ + static int[] ids() { + return OpenSearchExceptionHandleRegistry.ids().stream().mapToInt(i -> i).toArray(); + } + + /** + * Returns an array of all registered pairs of handle IDs and exception classes. These pairs are + * provided for every registered exception. + * + * @return an array of all registered pairs of handle IDs and exception classes + */ + @SuppressWarnings("unchecked") + static Tuple>[] classes() { + final Tuple>[] ts = OpenSearchExceptionHandleRegistry.handles() + .stream() + .map(h -> Tuple.tuple(h.id, h.exceptionClass)) + .toArray(Tuple[]::new); + return ts; + } + + public Index getIndex() { + List index = getMetadata(INDEX_METADATA_KEY); + if (index != null && index.isEmpty() == false) { + List index_uuid = getMetadata(INDEX_METADATA_KEY_UUID); + return new Index(index.get(0), index_uuid.get(0)); + } + + return null; + } + + public void setIndex(Index index) { + if (index != null) { + addMetadata(INDEX_METADATA_KEY, index.getName()); + addMetadata(INDEX_METADATA_KEY_UUID, index.getUUID()); + } + } + + public void setIndex(String index) { + if (index != null) { + setIndex(new Index(index, Strings.UNKNOWN_UUID_VALUE)); + } + } + + public ShardId getShardId() { + List shard = getMetadata(SHARD_METADATA_KEY); + if (shard != null && shard.isEmpty() == false) { + return new ShardId(getIndex(), Integer.parseInt(shard.get(0))); + } + return null; + } + + public void setShard(ShardId shardId) { + if (shardId != null) { + setIndex(shardId.getIndex()); + addMetadata(SHARD_METADATA_KEY, Integer.toString(shardId.id())); + } + } + + /** + * This is the list of Exceptions OpenSearch can throw over the wire or save into a corruption marker. Each value in the enum is a + * single exception tying the Class to an id for use of the encode side and the id back to a constructor for use on the decode side. As + * such its ok if the exceptions to change names so long as their constructor can still read the exception. Each exception is listed + * in id order. If you want to remove an exception leave a tombstone comment and mark the id as null in + * ExceptionSerializationTests.testIds.ids. + * + * @opensearch.internal + */ + protected static class OpenSearchExceptionHandle { + final Class exceptionClass; + final CheckedFunction constructor; + final int id; + final Version versionAdded; + + OpenSearchExceptionHandle( + Class exceptionClass, + CheckedFunction constructor, + int id, + Version versionAdded + ) { + // We need the exceptionClass because you can't dig it out of the constructor reliably. + this.exceptionClass = exceptionClass; + this.constructor = constructor; + this.versionAdded = versionAdded; + this.id = id; + } + } + + /** + * Registry of ExceptionHandlers + * + * @opensearch.internal + */ + public static class OpenSearchExceptionHandleRegistry { + /** Registry mapping from unique Ordinal to the Exception Constructor */ + private static final Map< + Integer, + CheckedFunction> ID_TO_SUPPLIER_REGISTRY = new ConcurrentHashMap<>(); + /** Registry mapping from Exception class to the Exception Handler */ + private static final Map< + Class, + OpenSearchExceptionHandle> CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE_REGISTRY = new ConcurrentHashMap<>(); + + /** returns the Exception constructor function from a given ordinal */ + public static CheckedFunction getSupplier(final int id) { + return ID_TO_SUPPLIER_REGISTRY.get(id); + } + + /** registers the Exception handler */ + public static void registerExceptionHandle(final OpenSearchExceptionHandle handle) { + ID_TO_SUPPLIER_REGISTRY.put(handle.id, handle.constructor); + CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE_REGISTRY.put(handle.exceptionClass, handle); + } + + /** Gets the unique ordinal id of the Exception from the given class */ + public static int getId(final Class exception) { + return CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE_REGISTRY.get(exception).id; + } + + /** returns a set of ids */ + public static Set ids() { + return ID_TO_SUPPLIER_REGISTRY.keySet(); + } + + /** returns a collection of handles */ + public static Collection handles() { + return CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE_REGISTRY.values(); + } + + /** checks that the exception class is registered */ + public static boolean isRegistered(final Class exception, final Version version) { + OpenSearchExceptionHandle openSearchExceptionHandle = CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE_REGISTRY.get(exception); + if (openSearchExceptionHandle != null) { + return version.onOrAfter(openSearchExceptionHandle.versionAdded); + } + return false; + } + + /** returns a set of registered exception classes */ + public static Set> getRegisteredKeys() { // for testing + return CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE_REGISTRY.keySet(); + } + } +} diff --git a/server/src/main/java/org/opensearch/OpenSearchParseException.java b/libs/core/src/main/java/org/opensearch/OpenSearchParseException.java similarity index 93% rename from server/src/main/java/org/opensearch/OpenSearchParseException.java rename to libs/core/src/main/java/org/opensearch/OpenSearchParseException.java index 0cc537f36a3ec..c2516402b0d30 100644 --- a/server/src/main/java/org/opensearch/OpenSearchParseException.java +++ b/libs/core/src/main/java/org/opensearch/OpenSearchParseException.java @@ -32,13 +32,15 @@ package org.opensearch; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; /** * Unchecked exception that is translated into a {@code 400 BAD REQUEST} error when it bubbles out over HTTP. + * + * @opensearch.internal */ public class OpenSearchParseException extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/OpenSearchWrapperException.java b/libs/core/src/main/java/org/opensearch/OpenSearchWrapperException.java similarity index 98% rename from server/src/main/java/org/opensearch/OpenSearchWrapperException.java rename to libs/core/src/main/java/org/opensearch/OpenSearchWrapperException.java index aa3e857f96901..3ef5ec4feac2d 100644 --- a/server/src/main/java/org/opensearch/OpenSearchWrapperException.java +++ b/libs/core/src/main/java/org/opensearch/OpenSearchWrapperException.java @@ -36,6 +36,8 @@ * An exception that is meant to be "unwrapped" when sent back to the user * as an error because its is {@link #getCause() cause}, if non-null is * always more useful to the user than the exception itself. + * + * @opensearch.internal */ public interface OpenSearchWrapperException { Throwable getCause(); diff --git a/server/src/main/java/org/opensearch/Version.java b/libs/core/src/main/java/org/opensearch/Version.java similarity index 76% rename from server/src/main/java/org/opensearch/Version.java rename to libs/core/src/main/java/org/opensearch/Version.java index f74e529c442bb..7b8d9e468c845 100644 --- a/server/src/main/java/org/opensearch/Version.java +++ b/libs/core/src/main/java/org/opensearch/Version.java @@ -32,15 +32,10 @@ package org.opensearch; -import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Strings; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.monitor.jvm.JvmInfo; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.lang.reflect.Field; @@ -51,6 +46,12 @@ import java.util.Locale; import java.util.Objects; +/** + * OpenSearch Version Class + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") public class Version implements Comparable, ToXContentFragment { /* * The logic for ID is: XXYYZZAA, where XX is major version, YY is minor version, ZZ is revision, and AA is alpha/beta/rc indicator AA @@ -80,13 +81,45 @@ public class Version implements Comparable, ToXContentFragment { public static final Version V_1_2_5 = new Version(1020599, org.apache.lucene.util.Version.LUCENE_8_10_1); public static final Version V_1_3_0 = new Version(1030099, org.apache.lucene.util.Version.LUCENE_8_10_1); public static final Version V_1_3_1 = new Version(1030199, org.apache.lucene.util.Version.LUCENE_8_10_1); - public static final Version V_1_4_0 = new Version(1040099, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_2 = new Version(1030299, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_3 = new Version(1030399, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_4 = new Version(1030499, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_5 = new Version(1030599, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_6 = new Version(1030699, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_7 = new Version(1030799, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_8 = new Version(1030899, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_9 = new Version(1030999, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_10 = new Version(1031099, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_11 = new Version(1031199, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_12 = new Version(1031299, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_13 = new Version(1031399, org.apache.lucene.util.Version.LUCENE_8_10_1); + public static final Version V_1_3_14 = new Version(1031499, org.apache.lucene.util.Version.LUCENE_8_10_1); public static final Version V_2_0_0 = new Version(2000099, org.apache.lucene.util.Version.LUCENE_9_1_0); - public static final Version CURRENT = V_2_0_0; - - public static Version readVersion(StreamInput in) throws IOException { - return fromId(in.readVInt()); - } + public static final Version V_2_0_1 = new Version(2000199, org.apache.lucene.util.Version.LUCENE_9_1_0); + public static final Version V_2_0_2 = new Version(2000299, org.apache.lucene.util.Version.LUCENE_9_1_0); + public static final Version V_2_1_0 = new Version(2010099, org.apache.lucene.util.Version.LUCENE_9_2_0); + public static final Version V_2_1_1 = new Version(2010199, org.apache.lucene.util.Version.LUCENE_9_2_0); + public static final Version V_2_2_0 = new Version(2020099, org.apache.lucene.util.Version.LUCENE_9_3_0); + public static final Version V_2_2_1 = new Version(2020199, org.apache.lucene.util.Version.LUCENE_9_3_0); + public static final Version V_2_2_2 = new Version(2020299, org.apache.lucene.util.Version.LUCENE_9_3_0); + public static final Version V_2_3_0 = new Version(2030099, org.apache.lucene.util.Version.LUCENE_9_3_0); + public static final Version V_2_3_1 = new Version(2030199, org.apache.lucene.util.Version.LUCENE_9_3_0); + public static final Version V_2_4_0 = new Version(2040099, org.apache.lucene.util.Version.LUCENE_9_4_1); + public static final Version V_2_4_1 = new Version(2040199, org.apache.lucene.util.Version.LUCENE_9_4_2); + public static final Version V_2_4_2 = new Version(2040299, org.apache.lucene.util.Version.LUCENE_9_4_2); + public static final Version V_2_5_0 = new Version(2050099, org.apache.lucene.util.Version.LUCENE_9_4_2); + public static final Version V_2_5_1 = new Version(2050199, org.apache.lucene.util.Version.LUCENE_9_4_2); + public static final Version V_2_6_0 = new Version(2060099, org.apache.lucene.util.Version.LUCENE_9_5_0); + public static final Version V_2_6_1 = new Version(2060199, org.apache.lucene.util.Version.LUCENE_9_5_0); + public static final Version V_2_7_0 = new Version(2070099, org.apache.lucene.util.Version.LUCENE_9_5_0); + public static final Version V_2_7_1 = new Version(2070199, org.apache.lucene.util.Version.LUCENE_9_5_0); + public static final Version V_2_8_0 = new Version(2080099, org.apache.lucene.util.Version.LUCENE_9_6_0); + public static final Version V_2_8_1 = new Version(2080199, org.apache.lucene.util.Version.LUCENE_9_6_0); + public static final Version V_2_9_0 = new Version(2090099, org.apache.lucene.util.Version.LUCENE_9_7_0); + public static final Version V_2_9_1 = new Version(2090199, org.apache.lucene.util.Version.LUCENE_9_7_0); + public static final Version V_2_10_0 = new Version(2100099, org.apache.lucene.util.Version.LUCENE_9_7_0); + public static final Version V_2_11_0 = new Version(2110099, org.apache.lucene.util.Version.LUCENE_9_7_0); + public static final Version CURRENT = V_2_11_0; public static Version fromId(int id) { final Version known = LegacyESVersion.idToVersion.get(id); @@ -124,30 +157,6 @@ private static Version fromIdSlow(int id) { return id < MASK ? new LegacyESVersion(id, luceneVersion) : new Version(id ^ MASK, luceneVersion); } - /** - * Return the {@link Version} of OpenSearch that has been used to create an index given its settings. - * - * @throws IllegalStateException if the given index settings doesn't contain a value for the key - * {@value IndexMetadata#SETTING_VERSION_CREATED} - */ - public static Version indexCreated(Settings indexSettings) { - final Version indexVersion = IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(indexSettings); - if (indexVersion.equals(V_EMPTY)) { - final String message = String.format( - Locale.ROOT, - "[%s] is not present in the index settings for index with UUID [%s]", - IndexMetadata.SETTING_INDEX_VERSION_CREATED.getKey(), - indexSettings.get(IndexMetadata.SETTING_INDEX_UUID) - ); - throw new IllegalStateException(message); - } - return indexVersion; - } - - public static void writeVersion(Version version, StreamOutput out) throws IOException { - out.writeVInt(version.id); - } - public static int computeLegacyID(int major, int minor, int revision, int build) { return major * 1000000 + minor * 10000 + revision * 100 + build; } @@ -174,7 +183,7 @@ public static Version max(Version version1, Version version2) { * Returns the version given its string representation, current version if the argument is null or empty */ public static Version fromString(String version) { - if (!Strings.hasLength(version)) { + if (stringHasLength(version) == false) { // TODO replace with Strings.hasLength after refactoring Strings to core lib return Version.CURRENT; } final Version cached = LegacyESVersion.stringToVersion.get(version); @@ -278,11 +287,16 @@ public boolean onOrBefore(Version version) { return version.id >= id; } - // LegacyESVersion major 7 is equivalent to Version major 1 public int compareMajor(Version other) { - int m = major == 1 ? 7 : major == 2 ? 8 : major; - int om = other.major == 1 ? 7 : other.major == 2 ? 8 : other.major; - return Integer.compare(m, om); + // comparing Legacy 7x for bwc + // todo: remove the following when removing legacy support in 3.0.0 + if (major == 7 || other.major == 7 || major == 6 || other.major == 6) { + // opensearch v1.x and v2.x need major translation to compare w/ legacy versions + int m = major == 1 ? 7 : major == 2 ? 8 : major; + int om = other.major == 1 ? 7 : other.major == 2 ? 8 : other.major; + return Integer.compare(m, om); + } + return Integer.compare(major, other.major); } @Override @@ -338,12 +352,9 @@ protected Version computeMinCompatVersion() { } else if (major == 6) { // force the minimum compatibility for version 6 to 5.6 since we don't reference version 5 anymore return LegacyESVersion.fromId(5060099); - } - /* - * TODO - uncomment this logic from OpenSearch version 3 onwards - * - else if (major >= 3) { + } else if (major >= 3 && major < 5) { // all major versions from 3 onwards are compatible with last minor series of the previous major + // todo: remove 5 check when removing LegacyESVersionTests Version bwcVersion = null; for (int i = DeclaredVersionsHolder.DECLARED_VERSIONS.size() - 1; i >= 0; i--) { @@ -357,7 +368,6 @@ else if (major >= 3) { } return bwcVersion == null ? this : bwcVersion; } - */ return Version.min(this, fromId(maskId((int) major * 1000000 + 0 * 10000 + 99))); } @@ -395,6 +405,10 @@ private Version computeMinIndexCompatVersion() { bwcMajor = major - 1; } final int bwcMinor = 0; + if (major == 3) { + return Version.min(this, fromId((bwcMajor * 1000000 + bwcMinor * 10000 + 99) ^ MASK)); + } + // todo remove below when LegacyESVersion is removed in 3.0 return Version.min(this, fromId((bwcMajor * 1000000 + bwcMinor * 10000 + 99))); } @@ -408,16 +422,15 @@ public boolean isCompatible(Version version) { // OpenSearch version 2 is the functional equivalent of predecessor unreleased version "8" // todo refactor this logic after removing deprecated features int a = major; - if (major == 1) { - a = 7; - } else if (major == 2) { - a = 8; - } int b = version.major; - if (version.major == 1) { - b = 7; - } else if (version.major == 2) { - b = 8; + + if (a == 7 || b == 7 || a == 6 || b == 6) { + if (major <= 2) { + a += 6; // for legacy compatibility up to version 2.x (to compare minCompat) + } + if (version.major <= 2) { + b += 6; // for legacy compatibility up to version 2.x (to compare minCompat) + } } assert compatible == false || Math.max(a, b) - Math.min(a, b) <= 1; @@ -433,7 +446,7 @@ public static void main(String[] args) { Build.CURRENT.type().displayName(), Build.CURRENT.hash(), Build.CURRENT.date(), - JvmInfo.jvmInfo().version() + System.getProperty("java.version") // TODO switch back to JvmInfo.jvmInfo().version() after refactoring to core lib ); System.out.println(versionOutput); } @@ -530,4 +543,12 @@ public static List getDeclaredVersions(final Class versionClass) { Collections.sort(versions); return versions; } + + /** + * Check that the given String is neither null nor of length 0. + * Note: Will return true for a String that purely consists of whitespace. + */ + public static boolean stringHasLength(String str) { + return (str != null && str.length() > 0); + } } diff --git a/libs/core/src/main/java/org/opensearch/bootstrap/JavaVersion.java b/libs/core/src/main/java/org/opensearch/bootstrap/JavaVersion.java deleted file mode 100644 index 236563bf8bd89..0000000000000 --- a/libs/core/src/main/java/org/opensearch/bootstrap/JavaVersion.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.bootstrap; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -public class JavaVersion implements Comparable { - - private final List version; - private final String prePart; - - public List getVersion() { - return version; - } - - private JavaVersion(List version, String prePart) { - this.prePart = prePart; - if (version.size() >= 2 && version.get(0) == 1 && version.get(1) == 8) { - // for Java 8 there is ambiguity since both 1.8 and 8 are supported, - // so we rewrite the former to the latter - version = new ArrayList<>(version.subList(1, version.size())); - } - this.version = Collections.unmodifiableList(version); - } - - /** - * Parses the Java version as it can be retrieved as the value of java.version or - * java.specification.version according to JEP 223. - * - * @param value The version String - */ - public static JavaVersion parse(String value) { - Objects.requireNonNull(value); - String prePart = null; - if (!isValid(value)) { - throw new IllegalArgumentException("Java version string [" + value + "] could not be parsed."); - } - List version = new ArrayList<>(); - String[] parts = value.split("-"); - String[] numericComponents; - if (parts.length == 1) { - numericComponents = value.split("\\."); - } else if (parts.length == 2) { - numericComponents = parts[0].split("\\."); - prePart = parts[1]; - } else { - throw new IllegalArgumentException("Java version string [" + value + "] could not be parsed."); - } - - for (String component : numericComponents) { - version.add(Integer.valueOf(component)); - } - return new JavaVersion(version, prePart); - } - - public static boolean isValid(String value) { - return value.matches("^0*[0-9]+(\\.[0-9]+)*(-[a-zA-Z0-9]+)?$"); - } - - private static final JavaVersion CURRENT = parse(System.getProperty("java.specification.version")); - - public static JavaVersion current() { - return CURRENT; - } - - @Override - public int compareTo(JavaVersion o) { - int len = Math.max(version.size(), o.version.size()); - for (int i = 0; i < len; i++) { - int d = (i < version.size() ? version.get(i) : 0); - int s = (i < o.version.size() ? o.version.get(i) : 0); - if (s < d) return 1; - if (s > d) return -1; - } - if (prePart != null && o.prePart == null) { - return -1; - } else if (prePart == null && o.prePart != null) { - return 1; - } else if (prePart != null && o.prePart != null) { - return comparePrePart(prePart, o.prePart); - } - return 0; - } - - private int comparePrePart(String prePart, String otherPrePart) { - if (prePart.matches("\\d+")) { - return otherPrePart.matches("\\d+") ? (new BigInteger(prePart)).compareTo(new BigInteger(otherPrePart)) : -1; - } else { - return otherPrePart.matches("\\d+") ? 1 : prePart.compareTo(otherPrePart); - } - } - - @Override - public boolean equals(Object o) { - if (o == null || o.getClass() != getClass()) { - return false; - } - return compareTo((JavaVersion) o) == 0; - } - - @Override - public int hashCode() { - return version.hashCode(); - } - - @Override - public String toString() { - final String versionString = version.stream().map(v -> Integer.toString(v)).collect(Collectors.joining(".")); - return prePart != null ? versionString + "-" + prePart : versionString; - } -} diff --git a/libs/core/src/main/java/org/opensearch/common/collect/List.java b/libs/core/src/main/java/org/opensearch/common/collect/List.java deleted file mode 100644 index 96bdacc276323..0000000000000 --- a/libs/core/src/main/java/org/opensearch/common/collect/List.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -public class List { - - /** - * Returns an unmodifiable list containing zero elements. - * - * @param the {@code List}'s element type - * @return an empty {@code List} - */ - public static java.util.List of() { - return Collections.emptyList(); - } - - /** - * Returns an unmodifiable list containing one element. - * - * @param the {@code List}'s element type - * @param e1 the single element - * @return a {@code List} containing the specified element - */ - public static java.util.List of(T e1) { - return Collections.singletonList(e1); - } - - /** - * Returns an unmodifiable list containing two elements. - * - * @param the {@code List}'s element type - * @param e1 the first element - * @param e2 the second element - * @return a {@code List} containing the specified element - */ - @SuppressWarnings("unchecked") - public static java.util.List of(T e1, T e2) { - return List.of((T[]) new Object[] { e1, e2 }); - } - - /** - * Returns an unmodifiable list containing an arbitrary number of elements. - * - * @param entries the elements to be contained in the list - * @param the {@code List}'s element type - * @return an unmodifiable list containing the specified elements. - */ - @SafeVarargs - @SuppressWarnings("varargs") - public static java.util.List of(T... entries) { - switch (entries.length) { - case 0: - return List.of(); - case 1: - return List.of(entries[0]); - default: - return Collections.unmodifiableList(Arrays.asList(entries)); - } - } - - /** - * Returns an unmodifiable {@code List} containing the elements of the given {@code Collection} in iteration order. - * - * @param the {@code List}'s element type - * @param coll a {@code Collection} from which elements are drawn, must be non-null - * @return a {@code List} containing the elements of the given {@code Collection} - */ - @SuppressWarnings("unchecked") - public static java.util.List copyOf(Collection coll) { - return (java.util.List) List.of(coll.toArray()); - } -} diff --git a/libs/core/src/main/java/org/opensearch/common/collect/Map.java b/libs/core/src/main/java/org/opensearch/common/collect/Map.java deleted file mode 100644 index 3b401ee0e1c1b..0000000000000 --- a/libs/core/src/main/java/org/opensearch/common/collect/Map.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -import java.util.AbstractMap; -import java.util.Collections; -import java.util.HashMap; - -public class Map { - - /** - * Returns an unmodifiable map containing one mapping. - */ - public static java.util.Map of() { - return Collections.emptyMap(); - } - - /** - * Returns an unmodifiable map containing one mapping. - */ - public static java.util.Map of(K k1, V v1) { - return Collections.singletonMap(k1, v1); - } - - /** - * Returns an unmodifiable map containing two mappings. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2) { - return mapN(k1, v1, k2, v2); - } - - /** - * Returns an unmodifiable map containing three mappings. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3) { - return mapN(k1, v1, k2, v2, k3, v3); - } - - /** - * Returns an unmodifiable map containing four mappings. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { - return mapN(k1, v1, k2, v2, k3, v3, k4, v4); - } - - /** - * Returns an unmodifiable map containing five mappings. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { - return mapN(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); - } - - /** - * Returns an unmodifiable map containing six mappings. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { - return mapN(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6); - } - - /** - * Returns an unmodifiable map containing seven mappings. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { - return mapN(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7); - } - - /** - * Returns an unmodifiable map containing eight mappings. - */ - public static java.util.Map of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8 - ) { - return mapN(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8); - } - - /** - * Returns an unmodifiable map containing nine mappings. - */ - public static java.util.Map of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8, - K k9, - V v9 - ) { - return mapN(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9); - } - - /** - * Returns an unmodifiable map containing ten mappings. - */ - public static java.util.Map of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8, - K k9, - V v9, - K k10, - V v10 - ) { - return mapN(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10); - } - - @SuppressWarnings("unchecked") - private static java.util.Map mapN(Object... objects) { - if (objects.length % 2 != 0) { - throw new IllegalStateException("Must provide an even number of arguments to Map::of method"); - } - switch (objects.length) { - case 0: - return Map.of(); - case 2: - return Map.of((K) objects[0], (V) objects[1]); - default: - HashMap map = new HashMap<>(); - for (int k = 0; k < objects.length / 2; k++) { - map.put((K) objects[k * 2], (V) objects[k * 2 + 1]); - } - return Collections.unmodifiableMap(map); - } - } - - /** - * Returns an unmodifiable map containing keys and values extracted from the given entries. - * - * @param the {@code Map}'s key type - * @param the {@code Map}'s value type - * @param entries {@code Map.Entry}s containing the keys and values from which the map is populated - * @return a {@code Map} containing the specified mappings - */ - @SafeVarargs - public static java.util.Map ofEntries(java.util.Map.Entry... entries) { - if (entries.length == 0) { - return Collections.emptyMap(); - } else if (entries.length == 1) { - return Collections.singletonMap(entries[0].getKey(), entries[0].getValue()); - } else { - HashMap map = new HashMap<>(); - for (java.util.Map.Entry entry : entries) { - map.put(entry.getKey(), entry.getValue()); - } - return Collections.unmodifiableMap(map); - } - } - - /** - * Returns an unmodifiable Map.Entry for the provided key and value. - */ - public static java.util.Map.Entry entry(K k, V v) { - return new AbstractMap.SimpleImmutableEntry<>(k, v); - } - - /** - * Returns an unmodifiable {@code Map} containing the entries of the given {@code Map}. - * - * @param the {@code Map}'s key type - * @param the {@code Map}'s value type - * @param map a {@code Map} from which entries are drawn, must be non-null - * @return a {@code Map} containing the entries of the given {@code Map} - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static java.util.Map copyOf(java.util.Map map) { - return (java.util.Map) Map.ofEntries(map.entrySet().toArray(new java.util.Map.Entry[0])); - } -} diff --git a/libs/core/src/main/java/org/opensearch/common/collect/Set.java b/libs/core/src/main/java/org/opensearch/common/collect/Set.java deleted file mode 100644 index 921408b88241f..0000000000000 --- a/libs/core/src/main/java/org/opensearch/common/collect/Set.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; - -public class Set { - - /** - * Returns an unmodifiable set containing zero elements. - * - * @param the {@code Set}'s element type - * @return an empty {@code Set} - */ - public static java.util.Set of() { - return Collections.emptySet(); - } - - /** - * Returns an unmodifiable set containing one element. - * - * @param the {@code Set}'s element type - * @param e1 the single element - * @return a {@code Set} containing the specified element - */ - public static java.util.Set of(T e1) { - return Collections.singleton(e1); - } - - /** - * Returns an unmodifiable set containing two elements. - * - * @param the {@code Set}'s element type - * @param e1 the first element - * @param e2 the second element - * @return a {@code Set} containing the specified element - */ - @SuppressWarnings("unchecked") - public static java.util.Set of(T e1, T e2) { - return Set.of((T[]) new Object[] { e1, e2 }); - } - - /** - * Returns an unmodifiable set containing an arbitrary number of elements. - * - * @param entries the elements to be contained in the set - * @param the {@code Set}'s element type - * @return an unmodifiable set containing the specified elements. - */ - @SafeVarargs - @SuppressWarnings("varargs") - public static java.util.Set of(T... entries) { - switch (entries.length) { - case 0: - return Set.of(); - case 1: - return Set.of(entries[0]); - default: - return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(entries))); - } - } - - /** - * Returns an unmodifiable {@code Set} containing the elements of the given Collection. - * - * @param the {@code Set}'s element type - * @param coll a {@code Collection} from which elements are drawn, must be non-null - * @return a {@code Set} containing the elements of the given {@code Collection} - * @throws NullPointerException if coll is null, or if it contains any nulls - * @since 10 - */ - @SuppressWarnings("unchecked") - public static java.util.Set copyOf(Collection coll) { - return (java.util.Set) Set.of(new HashSet<>(coll).toArray()); - } -} diff --git a/libs/core/src/main/java/org/opensearch/common/collect/Tuple.java b/libs/core/src/main/java/org/opensearch/common/collect/Tuple.java deleted file mode 100644 index cc82123056423..0000000000000 --- a/libs/core/src/main/java/org/opensearch/common/collect/Tuple.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -public class Tuple { - - public static Tuple tuple(V1 v1, V2 v2) { - return new Tuple<>(v1, v2); - } - - private final V1 v1; - private final V2 v2; - - public Tuple(V1 v1, V2 v2) { - this.v1 = v1; - this.v2 = v2; - } - - public V1 v1() { - return v1; - } - - public V2 v2() { - return v2; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Tuple tuple = (Tuple) o; - - if (v1 != null ? !v1.equals(tuple.v1) : tuple.v1 != null) return false; - if (v2 != null ? !v2.equals(tuple.v2) : tuple.v2 != null) return false; - - return true; - } - - @Override - public int hashCode() { - int result = v1 != null ? v1.hashCode() : 0; - result = 31 * result + (v2 != null ? v2.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Tuple [v1=" + v1 + ", v2=" + v2 + "]"; - } -} diff --git a/server/src/main/java/org/opensearch/Assertions.java b/libs/core/src/main/java/org/opensearch/core/Assertions.java similarity index 91% rename from server/src/main/java/org/opensearch/Assertions.java rename to libs/core/src/main/java/org/opensearch/core/Assertions.java index 6873364f7987d..da93f2f435441 100644 --- a/server/src/main/java/org/opensearch/Assertions.java +++ b/libs/core/src/main/java/org/opensearch/core/Assertions.java @@ -30,12 +30,14 @@ * GitHub history for details. */ -package org.opensearch; +package org.opensearch.core; /** * Provides a static final field that can be used to check if assertions are enabled. Since this field might be used elsewhere to check if * assertions are enabled, if you are running with assertions enabled for specific packages or classes, you should enable assertions on this - * class too (e.g., {@code -ea org.opensearch.Assertions -ea org.opensearch.cluster.service.MasterService}). + * class too (e.g., {@code -ea org.opensearch.core.Assertions -ea org.opensearch.cluster.service.ClusterManagerService}). + * + * @opensearch.internal */ public final class Assertions { diff --git a/libs/x-content/src/main/java/org/opensearch/common/ParseField.java b/libs/core/src/main/java/org/opensearch/core/ParseField.java similarity index 95% rename from libs/x-content/src/main/java/org/opensearch/common/ParseField.java rename to libs/core/src/main/java/org/opensearch/core/ParseField.java index 8673e25bf567b..6c04ec0a96361 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/ParseField.java +++ b/libs/core/src/main/java/org/opensearch/core/ParseField.java @@ -29,10 +29,11 @@ * GitHub history for details. */ -package org.opensearch.common; +package org.opensearch.core; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.XContentLocation; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.XContentLocation; import java.util.Collections; import java.util.HashSet; @@ -43,7 +44,11 @@ /** * Holds a field that can be found in a request while parsing and its different * variants, which may be deprecated. + * + * @opensearch.api + * */ +@PublicApi(since = "1.0.0") public class ParseField { private final String name; private final String[] deprecatedNames; @@ -193,6 +198,11 @@ public String[] getDeprecatedNames() { return deprecatedNames; } + /** + * Common fields shared throughout the project + * + * @opensearch.internal + **/ public static class CommonFields { public static final ParseField FIELD = new ParseField("field"); public static final ParseField FIELDS = new ParseField("fields"); diff --git a/server/src/main/java/org/opensearch/action/ActionListener.java b/libs/core/src/main/java/org/opensearch/core/action/ActionListener.java similarity index 98% rename from server/src/main/java/org/opensearch/action/ActionListener.java rename to libs/core/src/main/java/org/opensearch/core/action/ActionListener.java index 13c6aa96ceee6..372f71957fcc5 100644 --- a/server/src/main/java/org/opensearch/action/ActionListener.java +++ b/libs/core/src/main/java/org/opensearch/core/action/ActionListener.java @@ -30,13 +30,14 @@ * GitHub history for details. */ -package org.opensearch.action; +package org.opensearch.core.action; import org.opensearch.ExceptionsHelper; import org.opensearch.common.CheckedConsumer; import org.opensearch.common.CheckedFunction; import org.opensearch.common.CheckedRunnable; import org.opensearch.common.CheckedSupplier; +import org.opensearch.common.annotation.PublicApi; import java.util.ArrayList; import java.util.List; @@ -45,7 +46,10 @@ /** * A listener for action responses or failures. + * + * @opensearch.internal */ +@PublicApi(since = "1.0.0") public interface ActionListener { /** * Handle action response. This response may constitute a failure or a diff --git a/server/src/main/java/org/opensearch/action/ActionResponse.java b/libs/core/src/main/java/org/opensearch/core/action/ActionResponse.java similarity index 88% rename from server/src/main/java/org/opensearch/action/ActionResponse.java rename to libs/core/src/main/java/org/opensearch/core/action/ActionResponse.java index 65008e7e0e279..3bb98c1d58e81 100644 --- a/server/src/main/java/org/opensearch/action/ActionResponse.java +++ b/libs/core/src/main/java/org/opensearch/core/action/ActionResponse.java @@ -30,15 +30,17 @@ * GitHub history for details. */ -package org.opensearch.action; +package org.opensearch.core.action; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.transport.TransportResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.transport.TransportResponse; import java.io.IOException; /** * Base class for responses to action requests. + * + * @opensearch.internal */ public abstract class ActionResponse extends TransportResponse { diff --git a/server/src/main/java/org/opensearch/action/NotifyOnceListener.java b/libs/core/src/main/java/org/opensearch/core/action/NotifyOnceListener.java similarity index 96% rename from server/src/main/java/org/opensearch/action/NotifyOnceListener.java rename to libs/core/src/main/java/org/opensearch/core/action/NotifyOnceListener.java index 4935c34daa284..6af9ca005d171 100644 --- a/server/src/main/java/org/opensearch/action/NotifyOnceListener.java +++ b/libs/core/src/main/java/org/opensearch/core/action/NotifyOnceListener.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.action; +package org.opensearch.core.action; import java.util.concurrent.atomic.AtomicBoolean; @@ -38,6 +38,8 @@ * A listener that ensures that only one of onResponse or onFailure is called. And the method * the is called is only called once. Subclasses should implement notification logic with * innerOnResponse and innerOnFailure. + * + * @opensearch.internal */ public abstract class NotifyOnceListener implements ActionListener { diff --git a/server/src/main/java/org/opensearch/action/ShardOperationFailedException.java b/libs/core/src/main/java/org/opensearch/core/action/ShardOperationFailedException.java similarity index 92% rename from server/src/main/java/org/opensearch/action/ShardOperationFailedException.java rename to libs/core/src/main/java/org/opensearch/core/action/ShardOperationFailedException.java index f1f134c19115d..7456dcd335f72 100644 --- a/server/src/main/java/org/opensearch/action/ShardOperationFailedException.java +++ b/libs/core/src/main/java/org/opensearch/core/action/ShardOperationFailedException.java @@ -30,18 +30,19 @@ * GitHub history for details. */ -package org.opensearch.action; +package org.opensearch.core.action; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContentObject; import java.util.Objects; /** * An exception indicating that a failure occurred performing an operation on the shard. * + * @opensearch.internal */ public abstract class ShardOperationFailedException implements Writeable, ToXContentObject { diff --git a/libs/core/src/main/java/org/opensearch/core/action/package-info.java b/libs/core/src/main/java/org/opensearch/core/action/package-info.java new file mode 100644 index 0000000000000..88cfb3326f8ed --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/action/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Core action module */ +package org.opensearch.core.action; diff --git a/server/src/main/java/org/opensearch/action/support/DefaultShardOperationFailedException.java b/libs/core/src/main/java/org/opensearch/core/action/support/DefaultShardOperationFailedException.java similarity index 84% rename from server/src/main/java/org/opensearch/action/support/DefaultShardOperationFailedException.java rename to libs/core/src/main/java/org/opensearch/core/action/support/DefaultShardOperationFailedException.java index 19f2c22d79496..777f8d04758d0 100644 --- a/server/src/main/java/org/opensearch/action/support/DefaultShardOperationFailedException.java +++ b/libs/core/src/main/java/org/opensearch/core/action/support/DefaultShardOperationFailedException.java @@ -30,25 +30,31 @@ * GitHub history for details. */ -package org.opensearch.action.support; +package org.opensearch.core.action.support; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ShardOperationFailedException; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.OpenSearchException; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import static org.opensearch.ExceptionsHelper.detailedMessage; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.OpenSearchException.generateThrowableXContent; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +/** + * Exception for a default shard operation + * + * @opensearch.internal + */ public class DefaultShardOperationFailedException extends ShardOperationFailedException implements Writeable { private static final String INDEX = "index"; @@ -130,7 +136,7 @@ protected XContentBuilder innerToXContent(XContentBuilder builder, Params params builder.field("status", status.name()); if (reason != null) { builder.startObject("reason"); - OpenSearchException.generateThrowableXContent(builder, params, cause); + generateThrowableXContent(builder, params, cause); builder.endObject(); } return builder; diff --git a/libs/core/src/main/java/org/opensearch/core/action/support/package-info.java b/libs/core/src/main/java/org/opensearch/core/action/support/package-info.java new file mode 100644 index 0000000000000..d0a1150d0750d --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/action/support/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Support classes for core action module */ +package org.opensearch.core.action.support; diff --git a/server/src/main/java/org/opensearch/common/ParsingException.java b/libs/core/src/main/java/org/opensearch/core/common/ParsingException.java similarity index 90% rename from server/src/main/java/org/opensearch/common/ParsingException.java rename to libs/core/src/main/java/org/opensearch/core/common/ParsingException.java index 31129eaf1d882..b6dc7dc928b3e 100644 --- a/server/src/main/java/org/opensearch/common/ParsingException.java +++ b/libs/core/src/main/java/org/opensearch/core/common/ParsingException.java @@ -30,15 +30,15 @@ * GitHub history for details. */ -package org.opensearch.common; +package org.opensearch.core.common; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentLocation; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentLocation; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; @@ -46,6 +46,8 @@ * Exception that can be used when parsing queries with a given {@link * XContentParser}. * Can contain information about location of the error. + * + * @opensearch.internal */ public class ParsingException extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/common/Strings.java b/libs/core/src/main/java/org/opensearch/core/common/Strings.java similarity index 83% rename from server/src/main/java/org/opensearch/common/Strings.java rename to libs/core/src/main/java/org/opensearch/core/common/Strings.java index 139ace6c481c8..13468118abd38 100644 --- a/server/src/main/java/org/opensearch/common/Strings.java +++ b/libs/core/src/main/java/org/opensearch/core/common/Strings.java @@ -6,40 +6,17 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common; +package org.opensearch.core.common; import org.apache.lucene.util.BytesRefBuilder; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.util.CollectionUtils; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.OpenSearchException; +import org.opensearch.common.Nullable; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.BufferedReader; import java.io.IOException; @@ -59,9 +36,26 @@ import static java.util.Collections.unmodifiableSet; import static org.opensearch.common.util.set.Sets.newHashSet; +/** + * String utility class. + * + * TODO replace Strings in :server + * + * @opensearch.internal + */ public class Strings { - + public static final String UNKNOWN_UUID_VALUE = "_na_"; public static final String[] EMPTY_ARRAY = new String[0]; + public static final Set INVALID_FILENAME_CHARS = unmodifiableSet( + newHashSet('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',') + ); + + // no instance: + private Strings() {} + + // --------------------------------------------------------------------- + // General convenience methods for working with Strings + // --------------------------------------------------------------------- public static void spaceify(int spaces, String from, StringBuilder to) throws Exception { try (BufferedReader reader = new BufferedReader(new StringReader(from))) { @@ -75,71 +69,6 @@ public static void spaceify(int spaces, String from, StringBuilder to) throws Ex } } - /** - * Splits a backslash escaped string on the separator. - *

    - * Current backslash escaping supported: - *
    \n \t \r \b \f are escaped the same as a Java String - *
    Other characters following a backslash are produced verbatim (\c => c) - * - * @param s the string to split - * @param separator the separator to split on - * @param decode decode backslash escaping - */ - public static List splitSmart(String s, String separator, boolean decode) { - ArrayList lst = new ArrayList<>(2); - StringBuilder sb = new StringBuilder(); - int pos = 0, end = s.length(); - while (pos < end) { - if (s.startsWith(separator, pos)) { - if (sb.length() > 0) { - lst.add(sb.toString()); - sb = new StringBuilder(); - } - pos += separator.length(); - continue; - } - - char ch = s.charAt(pos++); - if (ch == '\\') { - if (!decode) sb.append(ch); - if (pos >= end) break; // ERROR, or let it go? - ch = s.charAt(pos++); - if (decode) { - switch (ch) { - case 'n': - ch = '\n'; - break; - case 't': - ch = '\t'; - break; - case 'r': - ch = '\r'; - break; - case 'b': - ch = '\b'; - break; - case 'f': - ch = '\f'; - break; - } - } - } - - sb.append(ch); - } - - if (sb.length() > 0) { - lst.add(sb.toString()); - } - - return lst; - } - - // --------------------------------------------------------------------- - // General convenience methods for working with Strings - // --------------------------------------------------------------------- - /** * Check that the given CharSequence is neither null nor of length 0. * Note: Will return true for a CharSequence that purely consists of whitespace. @@ -164,9 +93,9 @@ public static boolean hasLength(CharSequence str) { * * @param bytesReference the BytesReference to check (may be null) * @return true if the BytesReference is not null and has length - * @see #hasLength(CharSequence) + * @see Strings#hasLength(CharSequence) */ - public static boolean hasLength(BytesReference bytesReference) { + public static boolean hasLength(final BytesReference bytesReference) { return (bytesReference != null && bytesReference.length() > 0); } @@ -176,9 +105,9 @@ public static boolean hasLength(BytesReference bytesReference) { * * @param str the String to check (may be null) * @return true if the String is not null and has length - * @see #hasLength(CharSequence) + * @see Strings#hasLength(CharSequence) */ - public static boolean hasLength(String str) { + public static boolean hasLength(final String str) { return hasLength((CharSequence) str); } @@ -195,8 +124,8 @@ public static boolean hasLength(String str) { * @param str the CharSequence to check (may be null) * @return true if the CharSequence is either null or has a zero length */ - public static boolean isEmpty(CharSequence str) { - return !hasLength(str); + public static boolean isEmpty(final CharSequence str) { + return hasLength(str) == false; } /** @@ -214,7 +143,7 @@ public static boolean isEmpty(CharSequence str) { * @param str the CharSequence to check (may be null) * @return true if the CharSequence is not null, * its length is greater than 0, and it does not contain whitespace only - * @see java.lang.Character#isWhitespace + * @see Character#isWhitespace */ public static boolean hasText(CharSequence str) { if (!hasLength(str)) { @@ -237,7 +166,7 @@ public static boolean hasText(CharSequence str) { * @param str the String to check (may be null) * @return true if the String is not null, its length is * greater than 0, and it does not contain whitespace only - * @see #hasText(CharSequence) + * @see Strings#hasText(CharSequence) */ public static boolean hasText(String str) { return hasText((CharSequence) str); @@ -251,7 +180,7 @@ public static boolean hasText(String str) { * @return the trimmed String */ public static String trimLeadingCharacter(String str, char leadingCharacter) { - if (!hasLength(str)) { + if (hasLength(str) == false) { return str; } StringBuilder sb = new StringBuilder(str); @@ -328,7 +257,7 @@ public static String delete(String inString, String pattern) { * @return the resulting String */ public static String deleteAny(String inString, String charsToDelete) { - if (!hasLength(inString) || !hasLength(charsToDelete)) { + if (hasLength(inString) == false || hasLength(charsToDelete) == false) { return inString; } StringBuilder sb = new StringBuilder(); @@ -382,10 +311,6 @@ private static String changeFirstCharacterCase(String str, boolean capitalize) { return sb.toString(); } - public static final Set INVALID_FILENAME_CHARS = unmodifiableSet( - newHashSet('\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',') - ); - public static boolean validFileName(String fileName) { for (int i = 0; i < fileName.length(); i++) { char c = fileName.charAt(i); @@ -414,7 +339,7 @@ public static boolean validFileNameExcludingAstrix(String fileName) { * @return the String array (null if the passed-in * Collection was null) */ - public static String[] toStringArray(Collection collection) { + public static String[] toStringArray(final Collection collection) { if (collection == null) { return null; } @@ -455,7 +380,7 @@ public static String[] splitStringByCommaToArray(final String s) { * or null if the delimiter wasn't found in the given input String */ public static String[] split(String toSplit, String delimiter) { - if (!hasLength(toSplit) || !hasLength(delimiter)) { + if (hasLength(toSplit) == false || hasLength(delimiter) == false) { return null; } int offset = toSplit.indexOf(delimiter); @@ -479,13 +404,13 @@ public static String[] split(String toSplit, String delimiter) { * @param delimiters the delimiter characters, assembled as String * (each of those characters is individually considered as delimiter). * @return an array of the tokens - * @see java.util.StringTokenizer - * @see java.lang.String#trim() - * @see #delimitedListToStringArray + * @see StringTokenizer + * @see String#trim() + * @see Strings#delimitedListToStringArray */ public static String[] tokenizeToStringArray(final String s, final String delimiters) { if (s == null) { - return EMPTY_ARRAY; + return Strings.EMPTY_ARRAY; } return toStringArray(tokenizeToCollection(s, delimiters, ArrayList::new)); } @@ -499,7 +424,7 @@ public static String[] tokenizeToStringArray(final String s, final String delimi * @param supplier a collection supplier * @param the type of the collection * @return the tokens - * @see java.util.StringTokenizer + * @see StringTokenizer */ private static > T tokenizeToCollection( final String s, @@ -520,22 +445,6 @@ private static > T tokenizeToCollection( return tokens; } - /** - * Take a String which is a delimited list and convert it to a String array. - *

    A single delimiter can consists of more than one character: It will still - * be considered as single delimiter string, rather than as bunch of potential - * delimiter characters - in contrast to tokenizeToStringArray. - * - * @param str the input String - * @param delimiter the delimiter between elements (this is a single delimiter, - * rather than a bunch individual delimiter characters) - * @return an array of the tokens in the list - * @see #tokenizeToStringArray - */ - public static String[] delimitedListToStringArray(String str, String delimiter) { - return delimitedListToStringArray(str, delimiter, null); - } - /** * Take a String which is a delimited list and convert it to a String array. *

    A single delimiter can consists of more than one character: It will still @@ -552,7 +461,7 @@ public static String[] delimitedListToStringArray(String str, String delimiter) */ public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { if (str == null) { - return EMPTY_ARRAY; + return Strings.EMPTY_ARRAY; } if (delimiter == null) { return new String[] { str }; @@ -577,6 +486,22 @@ public static String[] delimitedListToStringArray(String str, String delimiter, return toStringArray(result); } + /** + * Take a String which is a delimited list and convert it to a String array. + *

    A single delimiter can consists of more than one character: It will still + * be considered as single delimiter string, rather than as bunch of potential + * delimiter characters - in contrast to tokenizeToStringArray. + * + * @param str the input String + * @param delimiter the delimiter between elements (this is a single delimiter, + * rather than a bunch individual delimiter characters) + * @return an array of the tokens in the list + * @see Strings#tokenizeToStringArray + */ + public static String[] delimitedListToStringArray(String str, String delimiter) { + return delimitedListToStringArray(str, delimiter, null); + } + /** * Convert a CSV list into an array of Strings. * @@ -716,12 +641,10 @@ public static String format1Decimals(double value, String suffix) { * * @param array the array to check */ - private static boolean isEmpty(Object[] array) { + private static boolean isEmpty(final Object[] array) { return (array == null || array.length == 0); } - private Strings() {} - public static byte[] toUTF8Bytes(CharSequence charSequence) { return toUTF8Bytes(charSequence, new BytesRefBuilder()); } @@ -768,8 +691,8 @@ public static boolean isAllOrWildcard(String data) { * Wraps the output into an anonymous object if needed. The content is not pretty-printed * nor human readable. */ - public static String toString(ToXContent toXContent) { - return toString(toXContent, false, false); + public static String toString(MediaType mediaType, ToXContent toXContent) { + return toString(mediaType, toXContent, false, false); } /** @@ -778,16 +701,8 @@ public static String toString(ToXContent toXContent) { * Allows to configure the params. * The content is not pretty-printed nor human readable. */ - public static String toString(ToXContent toXContent, ToXContent.Params params) { - return toString(toXContent, params, false, false); - } - - /** - * Returns a string representation of the builder (only applicable for text based xcontent). - * @param xContentBuilder builder containing an object to converted to a string - */ - public static String toString(XContentBuilder xContentBuilder) { - return BytesReference.bytes(xContentBuilder).utf8ToString(); + public static String toString(MediaType mediaType, ToXContent toXContent, ToXContent.Params params) { + return toString(mediaType, toXContent, params, false, false); } /** @@ -796,8 +711,8 @@ public static String toString(XContentBuilder xContentBuilder) { * json needs to be pretty printed and human readable. * */ - public static String toString(ToXContent toXContent, boolean pretty, boolean human) { - return toString(toXContent, ToXContent.EMPTY_PARAMS, pretty, human); + public static String toString(MediaType mediaType, ToXContent toXContent, boolean pretty, boolean human) { + return toString(mediaType, toXContent, ToXContent.EMPTY_PARAMS, pretty, human); } /** @@ -806,9 +721,9 @@ public static String toString(ToXContent toXContent, boolean pretty, boolean hum * Allows to configure the params. * Allows to control whether the outputted json needs to be pretty printed and human readable. */ - private static String toString(ToXContent toXContent, ToXContent.Params params, boolean pretty, boolean human) { + private static String toString(MediaType mediaType, ToXContent toXContent, ToXContent.Params params, boolean pretty, boolean human) { try { - XContentBuilder builder = createBuilder(pretty, human); + XContentBuilder builder = createBuilder(mediaType, pretty, human); if (toXContent.isFragment()) { builder.startObject(); } @@ -816,23 +731,23 @@ private static String toString(ToXContent toXContent, ToXContent.Params params, if (toXContent.isFragment()) { builder.endObject(); } - return toString(builder); + return builder.toString(); } catch (IOException e) { try { - XContentBuilder builder = createBuilder(pretty, human); + XContentBuilder builder = createBuilder(mediaType, pretty, human); builder.startObject(); builder.field("error", "error building toString out of XContent: " + e.getMessage()); builder.field("stack_trace", ExceptionsHelper.stackTrace(e)); builder.endObject(); - return toString(builder); + return builder.toString(); } catch (IOException e2) { throw new OpenSearchException("cannot generate error message for deserialization", e); } } } - private static XContentBuilder createBuilder(boolean pretty, boolean human) throws IOException { - XContentBuilder builder = JsonXContent.contentBuilder(); + private static XContentBuilder createBuilder(MediaType mediaType, boolean pretty, boolean human) throws IOException { + XContentBuilder builder = XContentBuilder.builder(mediaType.xContent()); if (pretty) { builder.prettyPrint(); } @@ -870,10 +785,6 @@ public static boolean isNullOrEmpty(@Nullable String s) { return s == null || s.isEmpty(); } - public static String coalesceToEmpty(@Nullable String s) { - return s == null ? "" : s; - } - public static String padStart(String s, int minimumLength, char c) { if (s == null) { throw new NullPointerException("s"); diff --git a/server/src/main/java/org/opensearch/common/breaker/CircuitBreaker.java b/libs/core/src/main/java/org/opensearch/core/common/breaker/CircuitBreaker.java similarity index 76% rename from server/src/main/java/org/opensearch/common/breaker/CircuitBreaker.java rename to libs/core/src/main/java/org/opensearch/core/common/breaker/CircuitBreaker.java index ca6c5aa9698c3..846950ff17c63 100644 --- a/server/src/main/java/org/opensearch/common/breaker/CircuitBreaker.java +++ b/libs/core/src/main/java/org/opensearch/core/common/breaker/CircuitBreaker.java @@ -30,13 +30,15 @@ * GitHub history for details. */ -package org.opensearch.common.breaker; +package org.opensearch.core.common.breaker; import java.util.Locale; /** * Interface for an object that can be incremented, breaking after some * configured limit has been reached. + * + * @opensearch.internal */ public interface CircuitBreaker { @@ -67,14 +69,25 @@ public interface CircuitBreaker { */ String IN_FLIGHT_REQUESTS = "in_flight_requests"; + /** + * The type of breaker + * can be {@link #MEMORY}, {@link #PARENT}, or {@link #NOOP} + * @opensearch.internal + */ enum Type { - // A regular or ChildMemoryCircuitBreaker + /** A regular or ChildMemoryCircuitBreaker */ MEMORY, - // A special parent-type for the hierarchy breaker service + /** A special parent-type for the hierarchy breaker service */ PARENT, - // A breaker where every action is a noop, it never breaks + /** A breaker where every action is a noop, it never breaks */ NOOP; + /** + * Converts string (case-insensitive) to breaker {@link Type} + * @param value "noop", "parent", or "memory" (case-insensitive) + * @return the breaker {@link Type} + * @throws IllegalArgumentException if value is not "noop", "parent", or "memory" + */ public static Type parseValue(String value) { switch (value.toLowerCase(Locale.ROOT)) { case "noop": @@ -89,10 +102,15 @@ public static Type parseValue(String value) { } } + /** + * The breaker durability + * can be {@link #TRANSIENT} or {@link #PERMANENT} + * @opensearch.internal + */ enum Durability { - // The condition that tripped the circuit breaker fixes itself eventually. + /** The condition that tripped the circuit breaker fixes itself eventually. */ TRANSIENT, - // The condition that tripped the circuit breaker requires manual intervention. + /** The condition that tripped the circuit breaker requires manual intervention. */ PERMANENT } @@ -108,11 +126,14 @@ enum Durability { * @param bytes number of bytes to add * @param label string label describing the bytes being added * @return the number of "used" bytes for the circuit breaker + * @throws CircuitBreakingException if the breaker tripped */ double addEstimateBytesAndMaybeBreak(long bytes, String label) throws CircuitBreakingException; /** * Adjust the circuit breaker without tripping + * @param bytes number of bytes to add + * @return the number of "used" bytes for the circuit breaker */ long addWithoutBreaking(long bytes); @@ -142,7 +163,10 @@ enum Durability { String getName(); /** - * @return whether a tripped circuit breaker will reset itself (transient) or requires manual intervention (permanent). + * Returns the {@link Durability} of this breaker + * @return whether a tripped circuit breaker will + * reset itself ({@link Durability#TRANSIENT}) + * or requires manual intervention ({@link Durability#PERMANENT}). */ Durability getDurability(); diff --git a/server/src/main/java/org/opensearch/common/breaker/CircuitBreakingException.java b/libs/core/src/main/java/org/opensearch/core/common/breaker/CircuitBreakingException.java similarity index 86% rename from server/src/main/java/org/opensearch/common/breaker/CircuitBreakingException.java rename to libs/core/src/main/java/org/opensearch/core/common/breaker/CircuitBreakingException.java index 8983ad3b46e60..52a34a103e775 100644 --- a/server/src/main/java/org/opensearch/common/breaker/CircuitBreakingException.java +++ b/libs/core/src/main/java/org/opensearch/core/common/breaker/CircuitBreakingException.java @@ -29,24 +29,29 @@ * GitHub history for details. */ -package org.opensearch.common.breaker; +package org.opensearch.core.common.breaker; import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; /** * Exception thrown when the circuit breaker trips + * + * @opensearch.internal */ public class CircuitBreakingException extends OpenSearchException { + /** The number of bytes wanted */ private final long bytesWanted; + /** The circuit breaker limit */ private final long byteLimit; + /** The {@link CircuitBreaker.Durability} of the circuit breaker */ private final CircuitBreaker.Durability durability; public CircuitBreakingException(StreamInput in) throws IOException { @@ -93,6 +98,7 @@ public CircuitBreaker.Durability getDurability() { return durability; } + /** Always returns {@link RestStatus#TOO_MANY_REQUESTS} */ @Override public RestStatus status() { return RestStatus.TOO_MANY_REQUESTS; diff --git a/libs/core/src/main/java/org/opensearch/core/common/breaker/NoopCircuitBreaker.java b/libs/core/src/main/java/org/opensearch/core/common/breaker/NoopCircuitBreaker.java new file mode 100644 index 0000000000000..17b9fefd27c99 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/breaker/NoopCircuitBreaker.java @@ -0,0 +1,152 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.common.breaker; + +/** + * A {@link CircuitBreaker} that doesn't increment or adjust, and all operations are + * basically noops. + * It never trips, limit is always -1, always returns 0 for all metrics. + * @opensearch.internal + */ +public class NoopCircuitBreaker implements CircuitBreaker { + + /** The limit of this breaker is always -1 */ + public static final int LIMIT = -1; + /** Name of this breaker */ + private final String name; + + /** + * Creates a new NoopCircuitBreaker (that never trip) with the given name + * @param name the name of this breaker + */ + public NoopCircuitBreaker(String name) { + this.name = name; + } + + /** + * This is a noop, a noop breaker never trip + * @param fieldName name of this noop breaker + * @param bytesNeeded bytes needed + */ + @Override + public void circuitBreak(String fieldName, long bytesNeeded) { + // noop + } + + /** + * This is a noop, always return 0 and never throw/trip + * @param bytes number of bytes to add + * @param label string label describing the bytes being added + * @return always return 0 + * @throws CircuitBreakingException never thrown + */ + @Override + public double addEstimateBytesAndMaybeBreak(long bytes, String label) throws CircuitBreakingException { + return 0; + } + + /** + * This is a noop, nothing is added, always return 0 + * @param bytes number of bytes to add (ignored) + * @return always return 0 + */ + @Override + public long addWithoutBreaking(long bytes) { + return 0; + } + + /** + * This is a noop, always return 0 + * @return always return 0 + */ + @Override + public long getUsed() { + return 0; + } + + /** + * A noop breaker have a constant limit of -1 + * @return always return -1 + */ + @Override + public long getLimit() { + return LIMIT; + } + + /** + * A noop breaker have no overhead, always return 0 + * @return always return 0 + */ + @Override + public double getOverhead() { + return 0; + } + + /** + * A noop breaker never trip, always return 0 + * @return always return 0 + */ + @Override + public long getTrippedCount() { + return 0; + } + + /** + * return the name of this breaker + * @return the name of this breaker + */ + @Override + public String getName() { + return this.name; + } + + /** + * A noop breaker {@link Durability} is always {@link Durability#PERMANENT} + * @return always return {@link Durability#PERMANENT } + */ + @Override + public Durability getDurability() { + return Durability.PERMANENT; + } + + /** + * Limit and overhead are constant for a noop breaker. + * this is a noop. + * @param limit the desired limit (ignored) + * @param overhead the desired overhead (ignored) + */ + @Override + public void setLimitAndOverhead(long limit, double overhead) { + // noop + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/common/breaker/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/breaker/package-info.java new file mode 100644 index 0000000000000..f9fb83d2207e1 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/breaker/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Foundation classes for the Circuit Breaker + */ +package org.opensearch.core.common.breaker; diff --git a/server/src/main/java/org/opensearch/common/bytes/AbstractBytesReference.java b/libs/core/src/main/java/org/opensearch/core/common/bytes/AbstractBytesReference.java similarity index 93% rename from server/src/main/java/org/opensearch/common/bytes/AbstractBytesReference.java rename to libs/core/src/main/java/org/opensearch/core/common/bytes/AbstractBytesReference.java index 77ee4df7da2cb..8c1efcd00c24e 100644 --- a/server/src/main/java/org/opensearch/common/bytes/AbstractBytesReference.java +++ b/libs/core/src/main/java/org/opensearch/core/common/bytes/AbstractBytesReference.java @@ -29,21 +29,29 @@ * GitHub history for details. */ -package org.opensearch.common.bytes; +package org.opensearch.core.common.bytes; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefIterator; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.apache.lucene.util.UnicodeUtil; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.EOFException; import java.io.IOException; import java.io.OutputStream; import java.util.function.ToIntBiFunction; +/** + * Base bytesref class + * + * @opensearch.internal + */ public abstract class AbstractBytesReference implements BytesReference { - private Integer hash = null; // we cache the hash of this reference since it can be quite costly to re-calculated it + /** we cache the hash of this reference since it can be quite costly to re-calculated it */ + private Integer hash = null; + private static final int MAX_UTF16_LENGTH = Integer.MAX_VALUE >> 1; @Override public int getInt(int index) { @@ -75,9 +83,19 @@ public void writeTo(OutputStream os) throws IOException { } } + protected int getMaxUTF16Length() { + return MAX_UTF16_LENGTH; + } + @Override public String utf8ToString() { - return toBytesRef().utf8ToString(); + BytesRef bytesRef = toBytesRef(); + final char[] ref = new char[bytesRef.length]; + final int len = UnicodeUtil.UTF8toUTF16(bytesRef, ref); + if (len > getMaxUTF16Length()) { + throw new IllegalArgumentException("UTF16 String size is " + len + ", should be less than " + getMaxUTF16Length()); + } + return new String(ref, 0, len); } @Override diff --git a/server/src/main/java/org/opensearch/common/bytes/BytesArray.java b/libs/core/src/main/java/org/opensearch/core/common/bytes/BytesArray.java similarity index 96% rename from server/src/main/java/org/opensearch/common/bytes/BytesArray.java rename to libs/core/src/main/java/org/opensearch/core/common/bytes/BytesArray.java index 69f715856c696..ae04ddcc19eee 100644 --- a/server/src/main/java/org/opensearch/common/bytes/BytesArray.java +++ b/libs/core/src/main/java/org/opensearch/core/common/bytes/BytesArray.java @@ -30,15 +30,20 @@ * GitHub history for details. */ -package org.opensearch.common.bytes; +package org.opensearch.core.common.bytes; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; +/** + * A bytes array. + * + * @opensearch.internal + */ public final class BytesArray extends AbstractBytesReference { public static final BytesArray EMPTY = new BytesArray(BytesRef.EMPTY_BYTES, 0, 0); diff --git a/server/src/main/java/org/opensearch/common/bytes/BytesReference.java b/libs/core/src/main/java/org/opensearch/core/common/bytes/BytesReference.java similarity index 89% rename from server/src/main/java/org/opensearch/common/bytes/BytesReference.java rename to libs/core/src/main/java/org/opensearch/core/common/bytes/BytesReference.java index 1107eda4b5a81..bb26e4e8a8675 100644 --- a/server/src/main/java/org/opensearch/common/bytes/BytesReference.java +++ b/libs/core/src/main/java/org/opensearch/core/common/bytes/BytesReference.java @@ -30,16 +30,17 @@ * GitHub history for details. */ -package org.opensearch.common.bytes; +package org.opensearch.core.common.bytes; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefIterator; -import org.opensearch.common.io.stream.BytesStream; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.util.ByteArray; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.io.stream.BytesStream; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.util.ByteArray; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -49,7 +50,10 @@ /** * A reference to bytes. + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public interface BytesReference extends Comparable, ToXContentFragment { /** @@ -120,8 +124,13 @@ static BytesReference fromByteBuffers(ByteBuffer[] buffers) { * Returns BytesReference composed of the provided ByteBuffer. */ static BytesReference fromByteBuffer(ByteBuffer buffer) { - assert buffer.hasArray(); - return new BytesArray(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); + if (buffer.hasArray()) { + return new BytesArray(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); + } else { + final byte[] array = new byte[buffer.remaining()]; + buffer.asReadOnlyBuffer().get(array, 0, buffer.remaining()); + return new BytesArray(array); + } } /** diff --git a/server/src/main/java/org/opensearch/common/bytes/CompositeBytesReference.java b/libs/core/src/main/java/org/opensearch/core/common/bytes/CompositeBytesReference.java similarity index 99% rename from server/src/main/java/org/opensearch/common/bytes/CompositeBytesReference.java rename to libs/core/src/main/java/org/opensearch/core/common/bytes/CompositeBytesReference.java index 2a989e33e918f..53915a3da824c 100644 --- a/server/src/main/java/org/opensearch/common/bytes/CompositeBytesReference.java +++ b/libs/core/src/main/java/org/opensearch/core/common/bytes/CompositeBytesReference.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.bytes; +package org.opensearch.core.common.bytes; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; @@ -47,6 +47,8 @@ * into one without copying. * * Note, {@link #toBytesRef()} will materialize all pages in this BytesReference. + * + * @opensearch.internal */ public final class CompositeBytesReference extends AbstractBytesReference { diff --git a/server/src/main/java/org/opensearch/common/bytes/PagedBytesReference.java b/libs/core/src/main/java/org/opensearch/core/common/bytes/PagedBytesReference.java similarity index 93% rename from server/src/main/java/org/opensearch/common/bytes/PagedBytesReference.java rename to libs/core/src/main/java/org/opensearch/core/common/bytes/PagedBytesReference.java index f9a2c6b95c03b..6b2ae821d2841 100644 --- a/server/src/main/java/org/opensearch/common/bytes/PagedBytesReference.java +++ b/libs/core/src/main/java/org/opensearch/core/common/bytes/PagedBytesReference.java @@ -30,22 +30,23 @@ * GitHub history for details. */ -package org.opensearch.common.bytes; +package org.opensearch.core.common.bytes; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefIterator; -import org.opensearch.common.util.ByteArray; -import org.opensearch.common.util.PageCacheRecycler; +import org.opensearch.core.common.util.ByteArray; import java.io.IOException; /** * A page based bytes reference, internally holding the bytes in a paged * data structure. + * + * @opensearch.internal */ public class PagedBytesReference extends AbstractBytesReference { - private static final int PAGE_SIZE = PageCacheRecycler.BYTE_PAGE_SIZE; + public static final int PAGE_SIZE_IN_BYTES = 1 << 14; private final ByteArray byteArray; private final int offset; @@ -93,7 +94,7 @@ public final BytesRefIterator iterator() { // we calculate the initial fragment size here to ensure that if this reference is a slice we are still page aligned // across the entire iteration. The first page is smaller if our offset != 0 then we start in the middle of the page // otherwise we iterate full pages until we reach the last chunk which also might end within a page. - final int initialFragmentSize = offset != 0 ? PAGE_SIZE - (offset % PAGE_SIZE) : PAGE_SIZE; + final int initialFragmentSize = offset != 0 ? PAGE_SIZE_IN_BYTES - (offset % PAGE_SIZE_IN_BYTES) : PAGE_SIZE_IN_BYTES; return new BytesRefIterator() { int position = 0; int nextFragmentSize = Math.min(length, initialFragmentSize); @@ -107,7 +108,7 @@ public BytesRef next() throws IOException { assert materialized == false : "iteration should be page aligned but array got materialized"; position += nextFragmentSize; final int remaining = length - position; - nextFragmentSize = Math.min(remaining, PAGE_SIZE); + nextFragmentSize = Math.min(remaining, PAGE_SIZE_IN_BYTES); return slice; } else { assert nextFragmentSize == 0 : "fragmentSize expected [0] but was: [" + nextFragmentSize + "]"; diff --git a/libs/core/src/main/java/org/opensearch/core/common/bytes/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/bytes/package-info.java new file mode 100644 index 0000000000000..d197e07f3e5fe --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/bytes/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core Bytes module */ +package org.opensearch.core.common.bytes; diff --git a/libs/core/src/main/java/org/opensearch/core/common/io/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/io/package-info.java new file mode 100644 index 0000000000000..7f5318f53dd35 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/io/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core io module */ +package org.opensearch.core.common.io; diff --git a/server/src/main/java/org/opensearch/common/io/stream/ByteBufferStreamInput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/ByteBufferStreamInput.java similarity index 97% rename from server/src/main/java/org/opensearch/common/io/stream/ByteBufferStreamInput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/ByteBufferStreamInput.java index 92b38e67f02c8..fcbab461b14c3 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/ByteBufferStreamInput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/ByteBufferStreamInput.java @@ -29,13 +29,18 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import java.io.EOFException; import java.io.IOException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; +/** + * Byte Buffer Stream Input + * + * @opensearch.internal + */ public class ByteBufferStreamInput extends StreamInput { private final ByteBuffer buffer; diff --git a/server/src/main/java/org/opensearch/common/io/stream/BytesStream.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/BytesStream.java similarity index 88% rename from server/src/main/java/org/opensearch/common/io/stream/BytesStream.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/BytesStream.java index 1a4e34d9e7e0e..70bf9c7710e2a 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/BytesStream.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/BytesStream.java @@ -30,10 +30,15 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; +/** + * Base Bytes Stream. + * + * @opensearch.internal + */ public abstract class BytesStream extends StreamOutput { public abstract BytesReference bytes(); diff --git a/server/src/main/java/org/opensearch/common/io/stream/BytesStreamInput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/BytesStreamInput.java similarity index 87% rename from server/src/main/java/org/opensearch/common/io/stream/BytesStreamInput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/BytesStreamInput.java index e593f3c89b008..a50d1c165ed72 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/BytesStreamInput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/BytesStreamInput.java @@ -6,7 +6,7 @@ * compatible open source license. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.apache.lucene.util.BytesRef; @@ -34,6 +34,8 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * + * @opensearch.internal */ public class BytesStreamInput extends StreamInput { private byte[] bytes; @@ -78,15 +80,19 @@ public void skipBytes(long count) { pos += count; } - // NOTE: AIOOBE not EOF if you read too much @Override - public byte readByte() { + public byte readByte() throws EOFException { + if (eof()) { + throw new EOFException(); + } return bytes[pos++]; } - // NOTE: AIOOBE not EOF if you read too much @Override - public void readBytes(byte[] b, int offset, int len) { + public void readBytes(byte[] b, int offset, int len) throws EOFException { + if (available() < len) { + throw new EOFException(); + } System.arraycopy(bytes, pos, b, offset, len); pos += len; } @@ -109,6 +115,9 @@ protected void ensureCanReadBytes(int length) throws EOFException { @Override public int read() throws IOException { + if (eof()) { + throw new EOFException(); + } return bytes[pos++] & 0xFF; } diff --git a/server/src/main/java/org/opensearch/common/io/stream/DataOutputStreamOutput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/DataOutputStreamOutput.java similarity index 94% rename from server/src/main/java/org/opensearch/common/io/stream/DataOutputStreamOutput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/DataOutputStreamOutput.java index 9fcbc39417257..b0bd6b101dc70 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/DataOutputStreamOutput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/DataOutputStreamOutput.java @@ -30,12 +30,17 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import java.io.Closeable; import java.io.DataOutput; import java.io.IOException; +/** + * Main stream output for data output + * + * @opensearch.internal + */ public class DataOutputStreamOutput extends StreamOutput { private final DataOutput out; diff --git a/server/src/main/java/org/opensearch/common/io/stream/FilterStreamInput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/FilterStreamInput.java similarity index 97% rename from server/src/main/java/org/opensearch/common/io/stream/FilterStreamInput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/FilterStreamInput.java index 7c5ebde4b5eae..a6e49567ac7d5 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/FilterStreamInput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/FilterStreamInput.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.opensearch.Version; @@ -39,6 +39,8 @@ /** * Wraps a {@link StreamInput} and delegates to it. To be used to add functionality to an existing stream by subclassing. + * + * @opensearch.internal */ public abstract class FilterStreamInput extends StreamInput { diff --git a/server/src/main/java/org/opensearch/common/io/stream/InputStreamStreamInput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/InputStreamStreamInput.java similarity index 95% rename from server/src/main/java/org/opensearch/common/io/stream/InputStreamStreamInput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/InputStreamStreamInput.java index f46ecc09f6416..e86ad44fb1dc4 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/InputStreamStreamInput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/InputStreamStreamInput.java @@ -30,14 +30,17 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; - -import org.opensearch.common.io.Streams; +package org.opensearch.core.common.io.stream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +/** + * Main input stream for input data + * + * @opensearch.internal + */ public class InputStreamStreamInput extends StreamInput { private final InputStream is; @@ -75,7 +78,7 @@ public byte readByte() throws IOException { @Override public void readBytes(byte[] b, int offset, int len) throws IOException { if (len < 0) throw new IndexOutOfBoundsException(); - final int read = Streams.readFully(is, b, offset, len); + final int read = is.readNBytes(b, offset, len); if (read != len) { throw new EOFException(); } diff --git a/server/src/main/java/org/opensearch/common/io/stream/NamedWriteable.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteable.java similarity index 95% rename from server/src/main/java/org/opensearch/common/io/stream/NamedWriteable.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteable.java index e61cb729d8463..ae6aa5a9e9a17 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/NamedWriteable.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteable.java @@ -30,12 +30,14 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; /** * A {@link Writeable} object identified by its name. * To be used for arbitrary serializable objects (e.g. queries); when reading them, their name tells * which specific object needs to be created. + * + * @opensearch.internal */ public interface NamedWriteable extends Writeable { diff --git a/server/src/main/java/org/opensearch/common/io/stream/NamedWriteableAwareStreamInput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteableAwareStreamInput.java similarity index 97% rename from server/src/main/java/org/opensearch/common/io/stream/NamedWriteableAwareStreamInput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteableAwareStreamInput.java index e695cbec979e5..298090a18dec6 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/NamedWriteableAwareStreamInput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteableAwareStreamInput.java @@ -30,12 +30,14 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import java.io.IOException; /** * Wraps a {@link StreamInput} and associates it with a {@link NamedWriteableRegistry} + * + * @opensearch.internal */ public class NamedWriteableAwareStreamInput extends FilterStreamInput { diff --git a/server/src/main/java/org/opensearch/common/io/stream/NamedWriteableRegistry.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteableRegistry.java similarity index 91% rename from server/src/main/java/org/opensearch/common/io/stream/NamedWriteableRegistry.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteableRegistry.java index 15de91e551a15..abac76c8b6c27 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/NamedWriteableRegistry.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/NamedWriteableRegistry.java @@ -30,7 +30,9 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; + +import org.opensearch.common.annotation.PublicApi; import java.util.ArrayList; import java.util.Collections; @@ -40,14 +42,22 @@ import java.util.Objects; /** - * A registry for {@link org.opensearch.common.io.stream.Writeable.Reader} readers of {@link NamedWriteable}. + * A registry for {@link Writeable.Reader} readers of {@link NamedWriteable}. * * The registration is keyed by the combination of the category class of {@link NamedWriteable}, and a name unique * to that category. + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public class NamedWriteableRegistry { - /** An entry in the registry, made up of a category class and name, and a reader for that category class. */ + /** + * An entry in the registry, made up of a category class and name, and a reader for that category class. + * + * @opensearch.api + */ + @PublicApi(since = "1.0.0") public static class Entry { /** The superclass of a {@link NamedWriteable} which will be read by {@link #reader}. */ @@ -86,6 +96,7 @@ public NamedWriteableRegistry(List entries) { Map, Map>> registry = new HashMap<>(); Map> readers = null; + @SuppressWarnings("rawtypes") Class currentCategory = null; for (Entry entry : entries) { if (currentCategory != entry.categoryClass) { diff --git a/server/src/main/java/org/opensearch/common/io/stream/NotSerializableExceptionWrapper.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/NotSerializableExceptionWrapper.java similarity index 96% rename from server/src/main/java/org/opensearch/common/io/stream/NotSerializableExceptionWrapper.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/NotSerializableExceptionWrapper.java index e1e0f69aadb09..91d552be0950c 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/NotSerializableExceptionWrapper.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/NotSerializableExceptionWrapper.java @@ -30,11 +30,11 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.rest.RestStatus; +import org.opensearch.OpenSearchException; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; @@ -44,6 +44,8 @@ * This class will preserve the stacktrace as well as the suppressed exceptions of * the throwable it was created with instead of it's own. The stacktrace has no indication * of where this exception was created. + * + * @opensearch.internal */ public final class NotSerializableExceptionWrapper extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/common/io/stream/OutputStreamStreamOutput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/OutputStreamStreamOutput.java similarity index 94% rename from server/src/main/java/org/opensearch/common/io/stream/OutputStreamStreamOutput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/OutputStreamStreamOutput.java index c849ed9e523f9..cf45e0e0b9088 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/OutputStreamStreamOutput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/OutputStreamStreamOutput.java @@ -30,11 +30,16 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import java.io.IOException; import java.io.OutputStream; +/** + * Streaming output data + * + * @opensearch.internal + */ public class OutputStreamStreamOutput extends StreamOutput { private final OutputStream out; diff --git a/server/src/main/java/org/opensearch/common/io/stream/StreamInput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/StreamInput.java similarity index 94% rename from server/src/main/java/org/opensearch/common/io/stream/StreamInput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/StreamInput.java index fc50a4f8b08fe..1499767c5e1a0 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/StreamInput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/StreamInput.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexFormatTooNewException; @@ -41,22 +41,21 @@ import org.apache.lucene.util.BitUtil; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.CharsRef; -import org.joda.time.DateTimeZone; +import org.opensearch.Build; import org.opensearch.OpenSearchException; import org.opensearch.Version; import org.opensearch.common.CharArrays; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.settings.SecureString; -import org.opensearch.common.text.Text; -import org.opensearch.common.time.DateUtils; +import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.script.JodaCompatibleZonedDateTime; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.settings.SecureString; +import org.opensearch.core.common.text.Text; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import java.io.ByteArrayInputStream; import java.io.EOFException; @@ -105,7 +104,10 @@ * everywhere. That being said, this class deals primarily with {@code List}s rather than Arrays. For the most part calls should adapt to * lists, either by storing {@code List}s internally or just converting to and from a {@code List} when calling. This comment is repeated * on {@link StreamInput}. + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public abstract class StreamInput extends InputStream { private Version version = Version.CURRENT; @@ -179,6 +181,10 @@ public BytesRef readBytesRef() throws IOException { return readBytesRef(length); } + public void readFully(byte[] b) throws IOException { + readBytes(b, 0, b.length); + } + public BytesRef readBytesRef(int length) throws IOException { if (length == 0) { return new BytesRef(); @@ -188,10 +194,6 @@ public BytesRef readBytesRef(int length) throws IOException { return new BytesRef(bytes, 0, length); } - public void readFully(byte[] b) throws IOException { - readBytes(b, 0, b.length); - } - public short readShort() throws IOException { return (short) (((readByte() & 0xFF) << 8) | (readByte() & 0xFF)); } @@ -203,16 +205,6 @@ public int readInt() throws IOException { return ((readByte() & 0xFF) << 24) | ((readByte() & 0xFF) << 16) | ((readByte() & 0xFF) << 8) | (readByte() & 0xFF); } - /** - * Reads an optional {@link Integer}. - */ - public Integer readOptionalInt() throws IOException { - if (readBoolean()) { - return readInt(); - } - return null; - } - /** * Reads an int stored in variable-length format. Reads between one and * five bytes. Smaller values take fewer bytes. Negative numbers @@ -247,6 +239,16 @@ public int readVInt() throws IOException { return i | ((b & 0x7F) << 28); } + /** + * Reads an optional {@link Integer}. + */ + public Integer readOptionalInt() throws IOException { + if (readBoolean()) { + return readInt(); + } + return null; + } + /** * Reads eight bytes and returns a long. */ @@ -346,6 +348,10 @@ public BigInteger readBigInteger() throws IOException { return new BigInteger(readString()); } + public MediaType readMediaType() throws IOException { + return MediaTypeRegistry.fromMediaType(readString()); + } + @Nullable public Text readOptionalText() throws IOException { int length = readInt(); @@ -677,25 +683,6 @@ public Map readMap() throws IOException { return (Map) readGenericValue(); } - /** - * Read {@link ImmutableOpenMap} using given key and value readers. - * - * @param keyReader key reader - * @param valueReader value reader - */ - public ImmutableOpenMap readImmutableMap(Writeable.Reader keyReader, Writeable.Reader valueReader) - throws IOException { - final int size = readVInt(); - if (size == 0) { - return ImmutableOpenMap.of(); - } - final ImmutableOpenMap.Builder builder = ImmutableOpenMap.builder(size); - for (int i = 0; i < size; i++) { - builder.put(keyReader.read(this), valueReader.read(this)); - } - return builder.build(); - } - /** * Reads a value of unspecified type. If a collection is read then the collection will be mutable if it contains any entry but might * be immutable if it is empty. @@ -703,6 +690,11 @@ public ImmutableOpenMap readImmutableMap(Writeable.Reader keyRea @Nullable public Object readGenericValue() throws IOException { byte type = readByte(); + Writeable.Reader r = Writeable.WriteableRegistry.getReader(type); + if (r != null) { + return r.read(this); + } + switch (type) { case -1: return null; @@ -732,8 +724,6 @@ public Object readGenericValue() throws IOException { return readByte(); case 12: return readDate(); - case 13: - return readDateTime(); case 14: return readBytesReference(); case 15: @@ -750,8 +740,6 @@ public Object readGenericValue() throws IOException { return readDoubleArray(); case 21: return readBytesRef(); - case 22: - return readGeoPoint(); case 23: return readZonedDateTime(); case 24: @@ -782,7 +770,7 @@ public final Instant readOptionalInstant() throws IOException { return present ? readInstant() : null; } - @SuppressWarnings("unchecked") + @SuppressWarnings({ "rawtypes", "unchecked" }) private List readArrayList() throws IOException { int size = readArraySize(); if (size == 0) { @@ -795,14 +783,6 @@ private List readArrayList() throws IOException { return list; } - private JodaCompatibleZonedDateTime readDateTime() throws IOException { - // we reuse DateTime to communicate with older nodes that don't know about the joda compat layer, but - // here we are on a new node so we always want a compat datetime - final ZoneId zoneId = DateUtils.dateTimeZoneToZoneId(DateTimeZone.forID(readString())); - long millis = readLong(); - return new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(millis), zoneId); - } - private ZonedDateTime readZonedDateTime() throws IOException { final String timeZoneId = readString(); return ZonedDateTime.ofInstant(Instant.ofEpochMilli(readLong()), ZoneId.of(timeZoneId)); @@ -822,6 +802,7 @@ private Object[] readArray() throws IOException { return list8; } + @SuppressWarnings({ "rawtypes", "unchecked" }) private Map readLinkedHashMap() throws IOException { int size9 = readArraySize(); if (size9 == 0) { @@ -834,6 +815,7 @@ private Map readLinkedHashMap() throws IOException { return map9; } + @SuppressWarnings({ "rawtypes", "unchecked" }) private Map readHashMap() throws IOException { int size10 = readArraySize(); if (size10 == 0) { @@ -851,31 +833,7 @@ private Date readDate() throws IOException { } /** - * Reads a {@link GeoPoint} from this stream input - */ - public GeoPoint readGeoPoint() throws IOException { - return new GeoPoint(readDouble(), readDouble()); - } - - /** - * Read a {@linkplain DateTimeZone}. - */ - public DateTimeZone readTimeZone() throws IOException { - return DateTimeZone.forID(readString()); - } - - /** - * Read an optional {@linkplain DateTimeZone}. - */ - public DateTimeZone readOptionalTimeZone() throws IOException { - if (readBoolean()) { - return DateTimeZone.forID(readString()); - } - return null; - } - - /** - * Read a {@linkplain DateTimeZone}. + * Read a {@linkplain ZoneId}. */ public ZoneId readZoneId() throws IOException { return ZoneId.of(readString()); @@ -984,7 +942,7 @@ public byte[] readByteArray() throws IOException { } /** - * Reads an array from the stream using the specified {@link org.opensearch.common.io.stream.Writeable.Reader} to read array elements + * Reads an array from the stream using the specified {@link Writeable.Reader} to read array elements * from the stream. This method can be seen as the reader version of {@link StreamOutput#writeArray(Writeable.Writer, Object[])}. It is * assumed that the stream first contains a variable-length integer representing the size of the array, and then contains that many * elements that can be read from the stream. @@ -1125,6 +1083,24 @@ public T readException() throws IOException { return null; } + /** Reads the OpenSearch Version from the input stream */ + public Version readVersion() throws IOException { + return Version.fromId(readVInt()); + } + + /** Reads the {@link Version} from the input stream */ + public Build readBuild() throws IOException { + // the following is new for opensearch: we write the distribution to support any "forks" + final String distribution = readString(); + // be lenient when reading on the wire, the enumeration values from other versions might be different than what we know + final Build.Type type = Build.Type.fromDisplayName(readString(), false); + String hash = readString(); + String date = readString(); + boolean snapshot = readBoolean(); + final String version = readString(); + return new Build(type, hash, date, snapshot, version, distribution); + } + /** * Get the registry of named writeables if this stream has one, * {@code null} otherwise. diff --git a/server/src/main/java/org/opensearch/common/io/stream/StreamOutput.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/StreamOutput.java similarity index 91% rename from server/src/main/java/org/opensearch/common/io/stream/StreamOutput.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/StreamOutput.java index e53e24171d8b6..94b813246bc7e 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/StreamOutput.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/StreamOutput.java @@ -30,9 +30,8 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexFormatTooNewException; import org.apache.lucene.index.IndexFormatTooOldException; @@ -41,24 +40,20 @@ import org.apache.lucene.util.BitUtil; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; -import org.joda.time.DateTimeZone; -import org.joda.time.ReadableInstant; +import org.opensearch.Build; import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.metadata.Metadata; import org.opensearch.common.CharArrays; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.io.stream.Writeable.Writer; -import org.opensearch.common.settings.SecureString; -import org.opensearch.common.text.Text; +import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.script.JodaCompatibleZonedDateTime; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.Writeable.WriteableRegistry; +import org.opensearch.core.common.io.stream.Writeable.Writer; +import org.opensearch.core.common.settings.SecureString; +import org.opensearch.core.common.text.Text; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; import java.io.EOFException; import java.io.FileNotFoundException; @@ -101,7 +96,10 @@ * everywhere. That being said, this class deals primarily with {@code List}s rather than Arrays. For the most part calls should adapt to * lists, either by storing {@code List}s internally or just converting to and from a {@code List} when calling. This comment is repeated * on {@link StreamInput}. + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public abstract class StreamOutput extends OutputStream { private static final int MAX_NESTED_EXCEPTION_LEVEL = 100; @@ -124,8 +122,8 @@ public void setVersion(Version version) { } /** - * Test if the stream has the specified feature. Features are used when serializing {@link ClusterState.Custom} or - * {@link Metadata.Custom}; see also {@link ClusterState.FeatureAware}. + * Test if the stream has the specified feature. Features are used when serializing {@code ClusterState.Custom} or + * {@code Metadata.Custom}; see also {@code ClusterState.FeatureAware}. * * @param feature the feature to test * @return true if the stream has the specified feature @@ -326,7 +324,7 @@ public void writeOptionalVLong(@Nullable Long l) throws IOException { * Writes a long in a variable-length format without first checking if it is negative. Package private for testing. Use * {@link #writeVLong(long)} instead. */ - void writeVLongNoCheck(long i) throws IOException { + public void writeVLongNoCheck(long i) throws IOException { final byte[] buffer = scratch.get(); int index = 0; while ((i & ~0x7F) != 0) { @@ -495,6 +493,10 @@ public void writeOptionalDouble(@Nullable Double v) throws IOException { } } + public final void writeBigInteger(BigInteger v) throws IOException { + writeString(v.toString()); + } + private static byte ZERO = 0; private static byte ONE = 1; private static byte TWO = 2; @@ -638,28 +640,6 @@ public final void writeMap(final Map map, final Writer keyWriter } } - /** - * Write a {@link ImmutableOpenMap} of {@code K}-type keys to {@code V}-type. - * - * @param keyWriter The key writer - * @param valueWriter The value writer - */ - public final void writeMap(final ImmutableOpenMap map, final Writer keyWriter, final Writer valueWriter) - throws IOException { - writeVInt(map.size()); - for (final ObjectObjectCursor entry : map) { - keyWriter.write(this, entry.key); - valueWriter.write(this, entry.value); - } - } - - /** - * Write a {@link ImmutableOpenMap} of {@code K}-type keys to {@code V}-type. - */ - public final void writeMap(final ImmutableOpenMap map) throws IOException { - writeMap(map, (o, k) -> k.writeTo(o), (o, v) -> v.writeTo(o)); - } - /** * Writes an {@link Instant} to the stream with nanosecond resolution */ @@ -680,10 +660,10 @@ public final void writeOptionalInstant(@Nullable Instant instant) throws IOExcep } } - private static final Map, Writer> WRITERS; + private static final Map, Writer> WRITERS; static { - Map, Writer> writers = new HashMap<>(); + Map, Writer> writers = new HashMap<>(); writers.put(String.class, (o, v) -> { o.writeByte((byte) 0); o.writeString((String) v); @@ -716,6 +696,7 @@ public final void writeOptionalInstant(@Nullable Instant instant) throws IOExcep }); writers.put(List.class, (o, v) -> { o.writeByte((byte) 7); + @SuppressWarnings("rawtypes") final List list = (List) v; o.writeVInt(list.size()); for (Object item : list) { @@ -752,12 +733,6 @@ public final void writeOptionalInstant(@Nullable Instant instant) throws IOExcep o.writeByte((byte) 12); o.writeLong(((Date) v).getTime()); }); - writers.put(ReadableInstant.class, (o, v) -> { - o.writeByte((byte) 13); - final ReadableInstant instant = (ReadableInstant) v; - o.writeString(instant.getZone().getID()); - o.writeLong(instant.getMillis()); - }); writers.put(BytesReference.class, (o, v) -> { o.writeByte((byte) 14); o.writeBytesReference((BytesReference) v); @@ -790,25 +765,12 @@ public final void writeOptionalInstant(@Nullable Instant instant) throws IOExcep o.writeByte((byte) 21); o.writeBytesRef((BytesRef) v); }); - writers.put(GeoPoint.class, (o, v) -> { - o.writeByte((byte) 22); - o.writeGeoPoint((GeoPoint) v); - }); writers.put(ZonedDateTime.class, (o, v) -> { o.writeByte((byte) 23); final ZonedDateTime zonedDateTime = (ZonedDateTime) v; o.writeString(zonedDateTime.getZone().getId()); o.writeLong(zonedDateTime.toInstant().toEpochMilli()); }); - writers.put(JodaCompatibleZonedDateTime.class, (o, v) -> { - // write the joda compatibility datetime as joda datetime - o.writeByte((byte) 13); - final JodaCompatibleZonedDateTime zonedDateTime = (JodaCompatibleZonedDateTime) v; - String zoneId = zonedDateTime.getZonedDateTime().getZone().getId(); - // joda does not understand "Z" for utc, so we must special case - o.writeString(zoneId.equals("Z") ? DateTimeZone.UTC.getID() : zoneId); - o.writeLong(zonedDateTime.toInstant().toEpochMilli()); - }); writers.put(Set.class, (o, v) -> { if (v instanceof LinkedHashSet) { o.writeByte((byte) 24); @@ -826,7 +788,10 @@ public final void writeOptionalInstant(@Nullable Instant instant) throws IOExcep } private static Class getGenericType(Object value) { - if (value instanceof List) { + Class registeredClass = WriteableRegistry.getCustomClassFromInstance(value); + if (registeredClass != null) { + return registeredClass; + } else if (value instanceof List) { return List.class; } else if (value instanceof Object[]) { return Object[].class; @@ -834,8 +799,6 @@ private static Class getGenericType(Object value) { return Map.class; } else if (value instanceof Set) { return Set.class; - } else if (value instanceof ReadableInstant) { - return ReadableInstant.class; } else if (value instanceof BytesReference) { return BytesReference.class; } else { @@ -843,6 +806,23 @@ private static Class getGenericType(Object value) { } } + /** + * Returns the registered writer for the given class type. + */ + @SuppressWarnings("unchecked") + public static > W getWriter(Class type) { + Writer writer = WriteableRegistry.getWriter(type); + if (writer == null) { + // fallback to this local hashmap + // todo: move all writers to the registry + writer = WRITERS.get(type); + } + if (writer == null) { + throw new IllegalArgumentException("can not write type [" + type + "]"); + } + return (W) writer; + } + /** * Notice: when serialization a map, the stream out map with the stream in map maybe have the * different key-value orders, they will maybe have different stream order. @@ -855,12 +835,8 @@ public void writeGenericValue(@Nullable Object value) throws IOException { return; } final Class type = getGenericType(value); - final Writer writer = WRITERS.get(type); - if (writer != null) { - writer.write(this, value); - } else { - throw new IllegalArgumentException("can not write type [" + type + "]"); - } + final Writer writer = getWriter(type); + writer.write(this, value); } public static void checkWriteable(@Nullable Object value) throws IllegalArgumentException { @@ -1120,7 +1096,25 @@ private void writeException(Throwable rootException, Throwable throwable, int ne } } - boolean failOnTooManyNestedExceptions(Throwable throwable) { + /** Writes the OpenSearch {@link Version} to the output stream */ + public void writeVersion(final Version version) throws IOException { + writeVInt(version.id); + } + + /** Writes the OpenSearch {@link Build} informn to the output stream */ + public void writeBuild(final Build build) throws IOException { + // the following is new for opensearch: we write the distribution name to support any "forks" of the code + writeString(build.getDistribution()); + + final Build.Type buildType = build.type(); + writeString(buildType.displayName()); + writeString(build.hash()); + writeString(build.date()); + writeBoolean(build.isSnapshot()); + writeString(build.getQualifiedVersion()); + } + + protected boolean failOnTooManyNestedExceptions(Throwable throwable) { throw new AssertionError("too many nested exceptions", throwable); } @@ -1144,21 +1138,6 @@ public void writeOptionalNamedWriteable(@Nullable NamedWriteable namedWriteable) } } - /** - * Writes the given {@link GeoPoint} to the stream - */ - public void writeGeoPoint(GeoPoint geoPoint) throws IOException { - writeDouble(geoPoint.lat()); - writeDouble(geoPoint.lon()); - } - - /** - * Write a {@linkplain DateTimeZone} to the stream. - */ - public void writeTimeZone(DateTimeZone timeZone) throws IOException { - writeString(timeZone.getID()); - } - /** * Write a {@linkplain ZoneId} to the stream. */ @@ -1166,18 +1145,6 @@ public void writeZoneId(ZoneId timeZone) throws IOException { writeString(timeZone.getId()); } - /** - * Write an optional {@linkplain DateTimeZone} to the stream. - */ - public void writeOptionalTimeZone(@Nullable DateTimeZone timeZone) throws IOException { - if (timeZone == null) { - writeBoolean(false); - } else { - writeBoolean(true); - writeTimeZone(timeZone); - } - } - /** * Write an optional {@linkplain ZoneId} to the stream. */ diff --git a/server/src/main/java/org/opensearch/common/io/stream/VersionedNamedWriteable.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/VersionedNamedWriteable.java similarity index 95% rename from server/src/main/java/org/opensearch/common/io/stream/VersionedNamedWriteable.java rename to libs/core/src/main/java/org/opensearch/core/common/io/stream/VersionedNamedWriteable.java index 6ce224c8e218a..e7124e44692be 100644 --- a/server/src/main/java/org/opensearch/common/io/stream/VersionedNamedWriteable.java +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/VersionedNamedWriteable.java @@ -30,12 +30,14 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.opensearch.Version; /** * A {@link NamedWriteable} that has a minimum version associated with it. + * + * @opensearch.internal */ public interface VersionedNamedWriteable extends NamedWriteable { diff --git a/libs/core/src/main/java/org/opensearch/core/common/io/stream/Writeable.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/Writeable.java new file mode 100644 index 0000000000000..af9df51655414 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/Writeable.java @@ -0,0 +1,175 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.common.io.stream; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Implementers can be written to a {@linkplain StreamOutput} and read from a {@linkplain StreamInput}. This allows them to be "thrown + * across the wire" using OpenSearch's internal protocol. If the implementer also implements equals and hashCode then a copy made by + * serializing and deserializing must be equal and have the same hashCode. It isn't required that such a copy be entirely unchanged. + * + * @opensearch.internal + */ +public interface Writeable { + /** + * A WriteableRegistry registers {@link Writer} methods for writing data types over a + * {@link StreamOutput} channel and {@link Reader} methods for reading data from a + * {@link StreamInput} channel. + * + * @opensearch.internal + */ + class WriteableRegistry { + private static final Map, Writer> WRITER_REGISTRY = new ConcurrentHashMap<>(); + private static final Map, Class> WRITER_CUSTOM_CLASS_MAP = new ConcurrentHashMap<>(); + private static final Map> READER_REGISTRY = new ConcurrentHashMap<>(); + + /** + * registers a streamable writer + * + * @opensearch.internal + */ + public static > void registerWriter(final Class clazz, final W writer) { + if (WRITER_REGISTRY.putIfAbsent(clazz, writer) != null) { + throw new IllegalArgumentException("Streamable writer already registered for type [" + clazz.getName() + "]"); + } + } + + /** + * registers a streamable reader + * + * @opensearch.internal + */ + public static > void registerReader(final byte ordinal, final R reader) { + if (READER_REGISTRY.putIfAbsent(ordinal, reader) != null) { + throw new IllegalArgumentException("Streamable reader already registered for ordinal [" + (int) ordinal + "]"); + } + } + + public static void registerClassAlias(final Class classInstance, final Class classGeneric) { + if (WRITER_CUSTOM_CLASS_MAP.putIfAbsent(classInstance, classGeneric) != null) { + throw new IllegalArgumentException("Streamable custom class already registered [" + classInstance.getClass() + "]"); + } + } + + /** + * Returns the registered writer keyed by the class type + */ + @SuppressWarnings("unchecked") + public static > W getWriter(final Class clazz) { + return (W) WRITER_REGISTRY.get(clazz); + } + + /** + * Returns the ristered reader keyed by the unique ordinal + */ + @SuppressWarnings("unchecked") + public static > R getReader(final byte b) { + return (R) READER_REGISTRY.get(b); + } + + public static Class getCustomClassFromInstance(final Object value) { + if (value == null) { + throw new IllegalArgumentException("Attempting to retrieve a class type from a null value"); + } + // rip through registered classes; return the class iff 'value' is an instance + // we do it this way to cover inheritance and interfaces (e.g., joda DateTime is an instanceof + // a ReadableInstant interface) + for (final Class clazz : WRITER_CUSTOM_CLASS_MAP.values()) { + if (clazz.isInstance(value)) { + return clazz; + } + } + return null; + } + } + + /** + * Write this into the {@linkplain StreamOutput}. + */ + void writeTo(StreamOutput out) throws IOException; + + /** + * Reference to a method that can write some object to a {@link StreamOutput}. + *

    + * By convention this is a method from {@link StreamOutput} itself (e.g., {@code StreamOutput#writeString}). If the value can be + * {@code null}, then the "optional" variant of methods should be used! + *

    + * Most classes should implement {@code Writeable} and the {@code Writeable#writeTo(StreamOutput)} method should use + * {@link StreamOutput} methods directly or this indirectly: + *

    
    +     * public void writeTo(StreamOutput out) throws IOException {
    +     *     out.writeVInt(someValue);
    +     *     out.writeMapOfLists(someMap, StreamOutput::writeString, StreamOutput::writeString);
    +     * }
    +     * 
    + */ + @FunctionalInterface + interface Writer { + + /** + * Write {@code V}-type {@code value} to the {@code out}put stream. + * + * @param out Output to write the {@code value} too + * @param value The value to add + */ + void write(final StreamOutput out, V value) throws IOException; + } + + /** + * Reference to a method that can read some object from a stream. By convention this is a constructor that takes + * {@linkplain StreamInput} as an argument for most classes and a static method for things like enums. Returning null from one of these + * is always wrong - for that we use methods like {@code StreamInput#readOptionalWriteable(Reader)}. + *

    + * As most classes will implement this via a constructor (or a static method in the case of enumerations), it's something that should + * look like: + *

    
    +     * public MyClass(final StreamInput in) throws IOException {
    +     *     this.someValue = in.readVInt();
    +     *     this.someMap = in.readMapOfLists(StreamInput::readString, StreamInput::readString);
    +     * }
    +     * 
    + */ + @FunctionalInterface + interface Reader { + + /** + * Read {@code V}-type value from a stream. + * + * @param in Input to read the value from + */ + V read(final StreamInput in) throws IOException; + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/common/io/stream/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/io/stream/package-info.java new file mode 100644 index 0000000000000..371f7ff7dd7b4 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/io/stream/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Core transport stream classes */ +package org.opensearch.core.common.io.stream; diff --git a/server/src/main/java/org/opensearch/common/logging/LoggerMessageFormat.java b/libs/core/src/main/java/org/opensearch/core/common/logging/LoggerMessageFormat.java similarity index 82% rename from server/src/main/java/org/opensearch/common/logging/LoggerMessageFormat.java rename to libs/core/src/main/java/org/opensearch/core/common/logging/LoggerMessageFormat.java index a81e2a4fc479f..0560fc5c7b7e9 100644 --- a/server/src/main/java/org/opensearch/common/logging/LoggerMessageFormat.java +++ b/libs/core/src/main/java/org/opensearch/core/common/logging/LoggerMessageFormat.java @@ -30,13 +30,19 @@ * GitHub history for details. */ -package org.opensearch.common.logging; +package org.opensearch.core.common.logging; import java.util.HashSet; import java.util.Set; /** * Format string for OpenSearch log messages. + *

    + * This class is almost a copy of {@code org.slf4j.helpers.MessageFormatter}

    + * The original code is licensed under the MIT License and is available at : + * MessageFormatter.java + * + * @opensearch.internal */ public class LoggerMessageFormat { @@ -49,6 +55,17 @@ public static String format(final String messagePattern, final Object... argArra return format(null, messagePattern, argArray); } + /** + * (this is almost a copy of {@code org.slf4j.helpers.MessageFormatter.arrayFormat}) + * + * @param prefix the prefix to prepend to the formatted message (can be null) + * @param messagePattern the message pattern which will be parsed and formatted + * @param argArray an array of arguments to be substituted in place of formatting anchors + * @return null if messagePattern is null

    + * messagePattern if argArray is (null or empty) and prefix is null

    + * prefix + messagePattern if argArray is (null or empty) and prefix is not null

    + * formatted message otherwise (even if prefix is null) + */ public static String format(final String prefix, final String messagePattern, final Object... argArray) { if (messagePattern == null) { return null; @@ -108,6 +125,13 @@ public static String format(final String prefix, final String messagePattern, fi return sbuf.toString(); } + /** + * Checks if (delimterStartIndex - 1) in messagePattern is an escape character. + * @param messagePattern the message pattern + * @param delimiterStartIndex the index of the character to check + * @return true if there is an escape char before the character at delimiterStartIndex.

    + * Always returns false if delimiterStartIndex == 0 (edge case) + */ static boolean isEscapedDelimiter(String messagePattern, int delimiterStartIndex) { if (delimiterStartIndex == 0) { @@ -121,6 +145,13 @@ static boolean isEscapedDelimiter(String messagePattern, int delimiterStartIndex } } + /** + * Checks if (delimterStartIndex - 2) in messagePattern is an escape character. + * @param messagePattern the message pattern + * @param delimiterStartIndex the index of the character to check + * @return true if (delimterStartIndex - 2) in messagePattern is an escape character. + * Always returns false if delimiterStartIndex is less than 2 (edge case) + */ static boolean isDoubleEscaped(String messagePattern, int delimiterStartIndex) { if (delimiterStartIndex >= 2 && messagePattern.charAt(delimiterStartIndex - 2) == ESCAPE_CHAR) { return true; diff --git a/libs/core/src/main/java/org/opensearch/core/common/logging/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/logging/package-info.java new file mode 100644 index 0000000000000..b3094b1e2782c --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/logging/package-info.java @@ -0,0 +1,18 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** core logging classes */ +package org.opensearch.core.common.logging; diff --git a/libs/core/src/main/java/org/opensearch/core/common/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/package-info.java new file mode 100644 index 0000000000000..b5cf352aa5495 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** common core classes that require third party dependencies */ +package org.opensearch.core.common; diff --git a/server/src/main/java/org/opensearch/common/settings/SecureString.java b/libs/core/src/main/java/org/opensearch/core/common/settings/SecureString.java similarity index 97% rename from server/src/main/java/org/opensearch/common/settings/SecureString.java rename to libs/core/src/main/java/org/opensearch/core/common/settings/SecureString.java index 83c6fbb78c976..322300a554284 100644 --- a/server/src/main/java/org/opensearch/common/settings/SecureString.java +++ b/libs/core/src/main/java/org/opensearch/core/common/settings/SecureString.java @@ -30,7 +30,9 @@ * GitHub history for details. */ -package org.opensearch.common.settings; +package org.opensearch.core.common.settings; + +import org.opensearch.common.annotation.PublicApi; import java.io.Closeable; import java.util.Arrays; @@ -38,7 +40,10 @@ /** * A String implementations which allows clearing the underlying char array. + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public final class SecureString implements CharSequence, Closeable { private char[] chars; diff --git a/libs/core/src/main/java/org/opensearch/core/common/settings/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/settings/package-info.java new file mode 100644 index 0000000000000..a2588a4633417 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/settings/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core Settings module */ +package org.opensearch.core.common.settings; diff --git a/server/src/main/java/org/opensearch/common/text/Text.java b/libs/core/src/main/java/org/opensearch/core/common/text/Text.java similarity index 93% rename from server/src/main/java/org/opensearch/common/text/Text.java rename to libs/core/src/main/java/org/opensearch/core/common/text/Text.java index 6756fa8a001f0..ca5402edae59e 100644 --- a/server/src/main/java/org/opensearch/common/text/Text.java +++ b/libs/core/src/main/java/org/opensearch/core/common/text/Text.java @@ -29,13 +29,13 @@ * GitHub history for details. */ -package org.opensearch.common.text; +package org.opensearch.core.common.text; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -43,6 +43,8 @@ /** * Both {@link String} and {@link BytesReference} representation of the text. Starts with one of those, and if * the other is requests, caches the other one in a local reference so no additional conversion will be needed. + * + * @opensearch.internal */ public final class Text implements Comparable, ToXContentFragment { diff --git a/libs/core/src/main/java/org/opensearch/core/common/text/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/text/package-info.java new file mode 100644 index 0000000000000..02abc5835c84f --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/text/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core text module */ +package org.opensearch.core.common.text; diff --git a/server/src/main/java/org/opensearch/common/transport/BoundTransportAddress.java b/libs/core/src/main/java/org/opensearch/core/common/transport/BoundTransportAddress.java similarity index 93% rename from server/src/main/java/org/opensearch/common/transport/BoundTransportAddress.java rename to libs/core/src/main/java/org/opensearch/core/common/transport/BoundTransportAddress.java index a6aae5674615b..8908a172395f2 100644 --- a/server/src/main/java/org/opensearch/common/transport/BoundTransportAddress.java +++ b/libs/core/src/main/java/org/opensearch/core/common/transport/BoundTransportAddress.java @@ -30,12 +30,12 @@ * GitHub history for details. */ -package org.opensearch.common.transport; +package org.opensearch.core.common.transport; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.network.InetAddresses; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; @@ -43,6 +43,8 @@ * A bounded transport address is a tuple of {@link TransportAddress}, one array that represents * the addresses the transport is bound to, and the other is the published one that represents the address clients * should communicate on. + * + * @opensearch.internal */ public class BoundTransportAddress implements Writeable { diff --git a/server/src/main/java/org/opensearch/common/transport/TransportAddress.java b/libs/core/src/main/java/org/opensearch/core/common/transport/TransportAddress.java similarity index 84% rename from server/src/main/java/org/opensearch/common/transport/TransportAddress.java rename to libs/core/src/main/java/org/opensearch/core/common/transport/TransportAddress.java index 3efdfb1f97681..551504ed6f719 100644 --- a/server/src/main/java/org/opensearch/common/transport/TransportAddress.java +++ b/libs/core/src/main/java/org/opensearch/core/common/transport/TransportAddress.java @@ -30,14 +30,14 @@ * GitHub history for details. */ -package org.opensearch.common.transport; +package org.opensearch.core.common.transport; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.network.NetworkAddress; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.net.InetAddress; @@ -46,6 +46,8 @@ /** * A transport address used for IP socket address (wraps {@link java.net.InetSocketAddress}). + * + * @opensearch.internal */ public final class TransportAddress implements Writeable, ToXContentFragment { @@ -69,6 +71,12 @@ public TransportAddress(InetAddress address, int port) { this(new InetSocketAddress(address, port)); } + /** + * Creates a new {@link TransportAddress} from a {@link InetSocketAddress}. + * @param address the address to wrap + * @throws IllegalArgumentException if the address is null or not resolved + * @see InetSocketAddress#getAddress() + */ public TransportAddress(InetSocketAddress address) { if (address == null) { throw new IllegalArgumentException("InetSocketAddress must not be null"); @@ -80,7 +88,9 @@ public TransportAddress(InetSocketAddress address) { } /** - * Read from a stream. + * Creates a new {@link TransportAddress} from a {@link StreamInput}. + * @param in the stream to read from + * @throws IOException if an I/O error occurs */ public TransportAddress(StreamInput in) throws IOException { final int len = in.readByte(); @@ -114,6 +124,8 @@ public String getAddress() { /** * Returns the addresses port + * @return the port number, or 0 if the socket is not bound yet. + * @see InetSocketAddress#getPort() */ public int getPort() { return address.getPort(); diff --git a/libs/core/src/main/java/org/opensearch/core/common/transport/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/transport/package-info.java new file mode 100644 index 0000000000000..21d2abfce958a --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/transport/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Common / Base Transport classes used to implement the OpenSearch transport layer */ +package org.opensearch.core.common.transport; diff --git a/server/src/main/java/org/opensearch/common/unit/ByteSizeUnit.java b/libs/core/src/main/java/org/opensearch/core/common/unit/ByteSizeUnit.java similarity index 94% rename from server/src/main/java/org/opensearch/common/unit/ByteSizeUnit.java rename to libs/core/src/main/java/org/opensearch/core/common/unit/ByteSizeUnit.java index 1552833fe5694..c15db75d06d49 100644 --- a/server/src/main/java/org/opensearch/common/unit/ByteSizeUnit.java +++ b/libs/core/src/main/java/org/opensearch/core/common/unit/ByteSizeUnit.java @@ -30,11 +30,12 @@ * GitHub history for details. */ -package org.opensearch.common.unit; +package org.opensearch.core.common.unit; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; @@ -44,7 +45,17 @@ * A {@code SizeUnit} does not maintain size information, but only * helps organize and use size representations that may be maintained * separately across various contexts. + * + * It use conventional data storage values (base-2) : + *

      + *
    • 1KB = 1024 bytes
    • + *
    • 1MB = 1024KB
    • + *
    • ...
    • + *
    + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public enum ByteSizeUnit implements Writeable { BYTES { @Override diff --git a/server/src/main/java/org/opensearch/common/unit/ByteSizeValue.java b/libs/core/src/main/java/org/opensearch/core/common/unit/ByteSizeValue.java similarity index 86% rename from server/src/main/java/org/opensearch/common/unit/ByteSizeValue.java rename to libs/core/src/main/java/org/opensearch/core/common/unit/ByteSizeValue.java index 4f58abc64e495..1ed6d2d204a99 100644 --- a/server/src/main/java/org/opensearch/common/unit/ByteSizeValue.java +++ b/libs/core/src/main/java/org/opensearch/core/common/unit/ByteSizeValue.java @@ -30,34 +30,29 @@ * GitHub history for details. */ -package org.opensearch.common.unit; +package org.opensearch.core.common.unit; import org.opensearch.OpenSearchParseException; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.logging.DeprecationLogger; -import org.opensearch.common.logging.LogConfigurator; -import org.opensearch.common.network.NetworkService; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Locale; import java.util.Objects; +/** + * A byte size value + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") public class ByteSizeValue implements Writeable, Comparable, ToXContentFragment { - /** - * We have to lazy initialize the deprecation logger as otherwise a static logger here would be constructed before logging is configured - * leading to a runtime failure (see {@link LogConfigurator#checkErrorListener()} ). The premature construction would come from any - * {@link ByteSizeValue} object constructed in, for example, settings in {@link NetworkService}. - */ - static class DeprecationLoggerHolder { - static DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(ByteSizeValue.class); - } - public static final ByteSizeValue ZERO = new ByteSizeValue(0, ByteSizeUnit.BYTES); private final long size; @@ -255,14 +250,14 @@ private static ByteSizeValue parse( return new ByteSizeValue(Long.parseLong(s), unit); } catch (final NumberFormatException e) { try { - final double doubleValue = Double.parseDouble(s); - DeprecationLoggerHolder.deprecationLogger.deprecate( - "fractional_byte_values", - "Fractional bytes values are deprecated. Use non-fractional bytes values instead: [{}] found for setting [{}]", + Double.parseDouble(s); + throw new OpenSearchParseException( + "Failed to parse bytes value [{}]. Fractional bytes values have been " + + "deprecated since Legacy 6.2. Use non-fractional bytes values instead: found for setting [{}]", + e, initialInput, settingName ); - return new ByteSizeValue((long) (doubleValue * unit.toBytes(1))); } catch (final NumberFormatException ignored) { throw new OpenSearchParseException("failed to parse [{}]", e, initialInput); } diff --git a/libs/core/src/main/java/org/opensearch/core/common/unit/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/unit/package-info.java new file mode 100644 index 0000000000000..79b5dcdcba3b6 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/unit/package-info.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Common units of measurement used by the core library. These units of measurement classes exist + * in the core because they depend on core functionality beyond the common library (e.g., serializable). + * + * @opensearch.api + * @opensearch.experimental + */ +package org.opensearch.core.common.unit; diff --git a/server/src/main/java/org/opensearch/common/util/BigArray.java b/libs/core/src/main/java/org/opensearch/core/common/util/BigArray.java similarity index 92% rename from server/src/main/java/org/opensearch/common/util/BigArray.java rename to libs/core/src/main/java/org/opensearch/core/common/util/BigArray.java index c71b3da9ef14c..eafefa38bc061 100644 --- a/server/src/main/java/org/opensearch/common/util/BigArray.java +++ b/libs/core/src/main/java/org/opensearch/core/common/util/BigArray.java @@ -30,12 +30,16 @@ * GitHub history for details. */ -package org.opensearch.common.util; +package org.opensearch.core.common.util; import org.apache.lucene.util.Accountable; import org.opensearch.common.lease.Releasable; -/** Base abstraction of an array. */ +/** + * Base abstraction of an array. + * + * @opensearch.internal + */ public interface BigArray extends Releasable, Accountable { /** Return the length of this array. */ diff --git a/server/src/main/java/org/opensearch/common/util/ByteArray.java b/libs/core/src/main/java/org/opensearch/core/common/util/ByteArray.java similarity index 97% rename from server/src/main/java/org/opensearch/common/util/ByteArray.java rename to libs/core/src/main/java/org/opensearch/core/common/util/ByteArray.java index 0b360148ddc01..e50f24417f8bc 100644 --- a/server/src/main/java/org/opensearch/common/util/ByteArray.java +++ b/libs/core/src/main/java/org/opensearch/core/common/util/ByteArray.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.util; +package org.opensearch.core.common.util; import org.apache.lucene.util.BytesRef; @@ -38,6 +38,8 @@ /** * Abstraction of an array of byte values. + * + * @opensearch.internal */ public interface ByteArray extends BigArray { diff --git a/libs/core/src/main/java/org/opensearch/core/common/util/CollectionUtils.java b/libs/core/src/main/java/org/opensearch/core/common/util/CollectionUtils.java new file mode 100644 index 0000000000000..5335c98182b64 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/util/CollectionUtils.java @@ -0,0 +1,351 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.common.util; + +import org.opensearch.common.collect.Iterators; +import org.opensearch.core.common.Strings; + +import java.nio.file.Path; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.RandomAccess; +import java.util.Set; + +/** + * Collections-related utility methods. + * + * @opensearch.internal + */ +public class CollectionUtils { + + /** + * Checks if the given array contains any elements. + * + * @param array The array to check + * + * @return false if the array contains an element, true if not or the array is null. + */ + public static boolean isEmpty(Object[] array) { + return array == null || array.length == 0; + } + + /** + * Return a rotated view of the given list with the given distance. + *
      + *
    • The distance can be negative, in which case the list is rotated to the left.
    • + *
    • The distance can be larger than the size of the list, in which case the list is rotated multiple times.
    • + *
    • The distance can be zero, in which case the list is not rotated.
    • + *
    • The list can be empty, in which case it remains empty.
    • + *
    + * @param list the list to rotate + * @param distance the distance to rotate (positive rotates right, negative rotates left) + * @return a rotated view of the given list with the given distance + * @see RotatedList + */ + public static List rotate(final List list, int distance) { + if (list.isEmpty()) { + return list; + } + + int d = distance % list.size(); + if (d < 0) { + d += list.size(); + } + + if (d == 0) { + return list; + } + + return new RotatedList<>(list, d); + } + + /** + * In place de-duplicates items in a list + * Noop if the list is empty or has one item. + * + * @throws NullPointerException if the list is `null` or comparator is `null` + * @param array the list to de-duplicate + * @param comparator the comparator to use to compare items + * @param the type of the items in the list + */ + public static void sortAndDedup(final List array, Comparator comparator) { + // base case: one item + if (array.size() <= 1) { + return; + } + array.sort(comparator); + ListIterator deduped = array.listIterator(); + T cmp = deduped.next(); // return the first item and advance + Iterator oldArray = array.iterator(); + oldArray.next(); // advance to the old to the second item (advanced to third below) + + do { + T old = oldArray.next(); // get the next item and advance iter + if (comparator.compare(cmp, old) != 0 && (cmp = deduped.next()) != old) { + deduped.set(old); + } + } while (oldArray.hasNext()); + // in place update + array.subList(deduped.nextIndex(), array.size()).clear(); + } + + /** + * Converts a collection of Integers to an array of ints. + * @param ints The collection of Integers to convert + * @return The array of ints + * @throws NullPointerException if ints is null + */ + public static int[] toArray(Collection ints) { + Objects.requireNonNull(ints); + return ints.stream().mapToInt(s -> s).toArray(); + } + + /** + * Deeply inspects a Map, Iterable, or Object array looking for references back to itself. + * @throws IllegalArgumentException if a self-reference is found + * @param value The object to evaluate looking for self references + * @param messageHint A string to be included in the exception message if the call fails, to provide + * more context to the handler of the exception + */ + public static void ensureNoSelfReferences(Object value, String messageHint) { + Iterable it = convert(value); + if (it != null) { + ensureNoSelfReferences(it, value, Collections.newSetFromMap(new IdentityHashMap<>()), messageHint); + } + } + + /** + * Converts an object to an Iterable, if possible. + * @param value The object to convert + * @return The Iterable, or null if the object cannot be converted + */ + @SuppressWarnings("unchecked") + private static Iterable convert(Object value) { + if (value == null) { + return null; + } + if (value instanceof Map) { + Map map = (Map) value; + return () -> Iterators.concat(map.keySet().iterator(), map.values().iterator()); + } else if ((value instanceof Iterable) && (value instanceof Path == false)) { + return (Iterable) value; + } else if (value instanceof Object[]) { + return Arrays.asList((Object[]) value); + } else { + return null; + } + } + + private static void ensureNoSelfReferences( + final Iterable value, + Object originalReference, + final Set ancestors, + String messageHint + ) { + if (value != null) { + if (ancestors.add(originalReference) == false) { + String suffix = Strings.isNullOrEmpty(messageHint) ? "" : String.format(Locale.ROOT, " (%s)", messageHint); + throw new IllegalArgumentException("Iterable object is self-referencing itself" + suffix); + } + for (Object o : value) { + ensureNoSelfReferences(convert(o), o, ancestors, messageHint); + } + ancestors.remove(originalReference); + } + } + + /** + * Returns an unmodifiable copy of the given map. + * @param map Map to copy + * @return unmodifiable copy of the map + */ + public static Map copyMap(Map map) { + if (map.isEmpty()) { + return Collections.emptyMap(); + } else { + return Collections.unmodifiableMap(new HashMap<>(map)); + } + } + + /** + * A rotated list + * + * @opensearch.internal + */ + private static class RotatedList extends AbstractList implements RandomAccess { + + private final List in; + private final int distance; + + /** + * Creates a rotated list + * @param list The list to rotate + * @param distance The distance to rotate to the right + * @throws IllegalArgumentException if the distance is negative or greater than the size of the list; + * or if the list is not a {@link RandomAccess} list + */ + RotatedList(List list, int distance) { + if (distance < 0 || distance >= list.size()) { + throw new IllegalArgumentException(); + } + if (!(list instanceof RandomAccess)) { + throw new IllegalArgumentException(); + } + this.in = list; + this.distance = distance; + } + + @Override + public T get(int index) { + int idx = distance + index; + if (idx < 0 || idx >= in.size()) { + idx -= in.size(); + } + return in.get(idx); + } + + @Override + public int size() { + return in.size(); + } + } + + /** + * Converts an {@link Iterable} to an {@link ArrayList}. + * @param elements The iterable to convert + * @param the type the elements + * @return an {@link ArrayList} + * @throws NullPointerException if elements is null + */ + @SuppressWarnings("unchecked") + public static ArrayList iterableAsArrayList(Iterable elements) { + if (elements == null) { + throw new NullPointerException("elements"); + } + if (elements instanceof Collection) { + return new ArrayList<>((Collection) elements); + } else { + ArrayList list = new ArrayList<>(); + for (E element : elements) { + list.add(element); + } + return list; + } + } + + @SuppressWarnings("unchecked") + public static ArrayList arrayAsArrayList(E... elements) { + if (elements == null) { + throw new NullPointerException("elements"); + } + return new ArrayList<>(Arrays.asList(elements)); + } + + @SuppressWarnings("unchecked") + public static ArrayList asArrayList(E first, E... other) { + if (other == null) { + throw new NullPointerException("other"); + } + ArrayList list = new ArrayList<>(1 + other.length); + list.add(first); + list.addAll(Arrays.asList(other)); + return list; + } + + @SuppressWarnings("unchecked") + public static ArrayList asArrayList(E first, E second, E... other) { + if (other == null) { + throw new NullPointerException("other"); + } + ArrayList list = new ArrayList<>(1 + 1 + other.length); + list.add(first); + list.add(second); + list.addAll(Arrays.asList(other)); + return list; + } + + public static ArrayList newSingletonArrayList(E element) { + return new ArrayList<>(Collections.singletonList(element)); + } + + public static List> eagerPartition(List list, int size) { + if (list == null) { + throw new NullPointerException("list"); + } + if (size <= 0) { + throw new IllegalArgumentException("size <= 0"); + } + List> result = new ArrayList<>((int) Math.ceil(list.size() / size)); + + List accumulator = new ArrayList<>(size); + int count = 0; + for (E element : list) { + if (count == size) { + result.add(accumulator); + accumulator = new ArrayList<>(size); + count = 0; + } + accumulator.add(element); + count++; + } + if (count > 0) { + result.add(accumulator); + } + + return result; + } + + /** + * Checks if a collection is empty or not. Empty collection mean either it is null or it has no elements in it. + * If collection contains a null element it means it is not empty. + * + * @param collection {@link Collection} + * @return true if collection is null or {@code isEmpty()}, false otherwise + * @param Element + */ + public static boolean isEmpty(final Collection collection) { + return collection == null || collection.isEmpty(); + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/common/util/package-info.java b/libs/core/src/main/java/org/opensearch/core/common/util/package-info.java new file mode 100644 index 0000000000000..57b1bfece1f90 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/common/util/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core util module */ +package org.opensearch.core.common.util; diff --git a/server/src/main/java/org/opensearch/common/compress/Compressor.java b/libs/core/src/main/java/org/opensearch/core/compress/Compressor.java similarity index 75% rename from server/src/main/java/org/opensearch/common/compress/Compressor.java rename to libs/core/src/main/java/org/opensearch/core/compress/Compressor.java index b5116598ea4ce..27d5b5dfdfa15 100644 --- a/server/src/main/java/org/opensearch/common/compress/Compressor.java +++ b/libs/core/src/main/java/org/opensearch/core/compress/Compressor.java @@ -30,14 +30,29 @@ * GitHub history for details. */ -package org.opensearch.common.compress; +package org.opensearch.core.compress; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +/** + * Compressor interface used for compressing {@link org.opensearch.core.xcontent.MediaType} and + * {@code org.opensearch.repositories.blobstore.BlobStoreRepository} implementations. + * + * This is not to be confused with {@link org.apache.lucene.codecs.compressing.Compressor} which is used + * for codec implementations such as {@code org.opensearch.index.codec.customcodecs.Lucene95CustomCodec} + * for compressing {@link org.apache.lucene.document.StoredField}s + * + * @opensearch.api - intended to be extended + * @opensearch.experimental - however, bwc is not guaranteed at this time + */ +@ExperimentalApi +@PublicApi(since = "2.10.0") public interface Compressor { boolean isCompressed(BytesReference bytes); diff --git a/libs/core/src/main/java/org/opensearch/core/compress/CompressorRegistry.java b/libs/core/src/main/java/org/opensearch/core/compress/CompressorRegistry.java new file mode 100644 index 0000000000000..9290254c30d8d --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/compress/CompressorRegistry.java @@ -0,0 +1,115 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.compress; + +import org.opensearch.common.Nullable; +import org.opensearch.common.annotation.InternalApi; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.compress.spi.CompressorProvider; +import org.opensearch.core.xcontent.MediaTypeRegistry; + +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import java.util.ServiceLoader; +import java.util.stream.Collectors; + +/** + * A registry that wraps a static Map singleton which holds a mapping of unique String names (typically the + * compressor header as a string) to registerd {@link Compressor} implementations. + * + * This enables plugins, modules, extensions to register their own compression implementations through SPI + * + * @opensearch.experimental + * @opensearch.internal + */ +@InternalApi +public final class CompressorRegistry { + + // the backing registry map + private static final Map registeredCompressors = ServiceLoader.load( + CompressorProvider.class, + CompressorProvider.class.getClassLoader() + ) + .stream() + .flatMap(p -> p.get().getCompressors().stream()) + .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); + + // no instance: + private CompressorRegistry() {} + + /** + * Returns the default compressor + */ + public static Compressor defaultCompressor() { + return registeredCompressors.get("DEFLATE"); + } + + public static Compressor none() { + return registeredCompressors.get(NoneCompressor.NAME); + } + + public static boolean isCompressed(BytesReference bytes) { + return compressor(bytes) != null; + } + + @Nullable + public static Compressor compressor(final BytesReference bytes) { + for (Compressor compressor : registeredCompressors.values()) { + if (compressor.isCompressed(bytes) == true) { + // bytes should be either detected as compressed or as xcontent, + // if we have bytes that can be either detected as compressed or + // as a xcontent, we have a problem + assert MediaTypeRegistry.xContentType(bytes) == null; + return compressor; + } + } + + if (MediaTypeRegistry.xContentType(bytes) == null) { + throw new NotXContentException("Compressor detection can only be called on some xcontent bytes or compressed xcontent bytes"); + } + + return null; + } + + /** Decompress the provided {@link BytesReference}. */ + public static BytesReference uncompress(BytesReference bytes) throws IOException { + Compressor compressor = compressor(bytes); + if (compressor == null) { + throw new NotCompressedException(); + } + return compressor.uncompress(bytes); + } + + /** + * Uncompress the provided data, data can be detected as compressed using {@link #isCompressed(BytesReference)}. + */ + public static BytesReference uncompressIfNeeded(BytesReference bytes) throws IOException { + Compressor compressor = compressor(Objects.requireNonNull(bytes, "the BytesReference must not be null")); + return compressor == null ? bytes : compressor.uncompress(bytes); + } + + /** Returns a registered compressor by its registered name */ + public static Compressor getCompressor(final String name) { + if (registeredCompressors.containsKey(name)) { + return registeredCompressors.get(name); + } + throw new IllegalArgumentException("No registered compressor found by name [" + name + "]"); + } + + /** + * Returns the registered compressors as an Immutable collection + * + * note: used for testing + */ + public static Map registeredCompressors() { + // no destructive danger as backing map is immutable + return registeredCompressors; + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/compress/NoneCompressor.java b/libs/core/src/main/java/org/opensearch/core/compress/NoneCompressor.java new file mode 100644 index 0000000000000..6e607ed701633 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/compress/NoneCompressor.java @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.compress; + +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.bytes.BytesReference; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * {@link Compressor} no compressor implementation. + * + * @opensearch.api - registered name requires BWC support + * @opensearch.experimental - class methods might change + */ +public class NoneCompressor implements Compressor { + /** + * The name to register the compressor by + * + * @opensearch.api - requires BWC support + */ + @PublicApi(since = "2.10.0") + public static final String NAME = "NONE"; + + @Override + public boolean isCompressed(BytesReference bytes) { + return false; + } + + @Override + public int headerLength() { + return 0; + } + + @Override + public InputStream threadLocalInputStream(InputStream in) throws IOException { + return in; + } + + @Override + public OutputStream threadLocalOutputStream(OutputStream out) throws IOException { + return out; + } + + @Override + public BytesReference uncompress(BytesReference bytesReference) throws IOException { + return bytesReference; + } + + @Override + public BytesReference compress(BytesReference bytesReference) throws IOException { + return bytesReference; + } + +} diff --git a/server/src/main/java/org/opensearch/common/compress/NotCompressedException.java b/libs/core/src/main/java/org/opensearch/core/compress/NotCompressedException.java similarity index 83% rename from server/src/main/java/org/opensearch/common/compress/NotCompressedException.java rename to libs/core/src/main/java/org/opensearch/core/compress/NotCompressedException.java index eedb3c0b9ac98..91d6bc57f1cd6 100644 --- a/server/src/main/java/org/opensearch/common/compress/NotCompressedException.java +++ b/libs/core/src/main/java/org/opensearch/core/compress/NotCompressedException.java @@ -30,11 +30,15 @@ * GitHub history for details. */ -package org.opensearch.common.compress; +package org.opensearch.core.compress; -/** Exception indicating that we were expecting something compressed, which - * was not compressed or corrupted so that the compression format could not - * be detected. */ +/** + * Exception indicating that we were expecting something compressed, which + * was not compressed or corrupted so that the compression format could not + * be detected. + * + * @opensearch.internal + */ public class NotCompressedException extends RuntimeException { public NotCompressedException() { diff --git a/server/src/main/java/org/opensearch/common/compress/NotXContentException.java b/libs/core/src/main/java/org/opensearch/core/compress/NotXContentException.java similarity index 84% rename from server/src/main/java/org/opensearch/common/compress/NotXContentException.java rename to libs/core/src/main/java/org/opensearch/core/compress/NotXContentException.java index e06b6ee96ab72..99337d5a26025 100644 --- a/server/src/main/java/org/opensearch/common/compress/NotXContentException.java +++ b/libs/core/src/main/java/org/opensearch/core/compress/NotXContentException.java @@ -30,12 +30,16 @@ * GitHub history for details. */ -package org.opensearch.common.compress; +package org.opensearch.core.compress; -import org.opensearch.common.xcontent.XContent; +import org.opensearch.core.xcontent.XContent; -/** Exception indicating that we were expecting some {@link XContent} but could - * not detect its type. */ +/** + * Exception indicating that we were expecting some {@link XContent} but could + * not detect its type. + * + * @opensearch.internal + */ public class NotXContentException extends RuntimeException { public NotXContentException(String message) { diff --git a/libs/core/src/main/java/org/opensearch/core/compress/package-info.java b/libs/core/src/main/java/org/opensearch/core/compress/package-info.java new file mode 100644 index 0000000000000..c0365e45702bc --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/compress/package-info.java @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Concrete {@link org.opensearch.core.compress.Compressor} implementations provided by the core library + * + * See {@link org.opensearch.core.compress.NoneCompressor} + */ +package org.opensearch.core.compress; diff --git a/libs/core/src/main/java/org/opensearch/core/compress/spi/CompressorProvider.java b/libs/core/src/main/java/org/opensearch/core/compress/spi/CompressorProvider.java new file mode 100644 index 0000000000000..019e282444d64 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/compress/spi/CompressorProvider.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.compress.spi; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.compress.Compressor; + +import java.util.List; +import java.util.Map; + +/** + * Service Provider Interface for plugins, modules, extensions providing custom + * compression algorithms + * + * see {@link Compressor} for implementing methods + * and {@link org.opensearch.core.compress.CompressorRegistry} for the registration of custom + * Compressors + * + * @opensearch.experimental + * @opensearch.api + */ +@ExperimentalApi +@PublicApi(since = "2.10.0") +public interface CompressorProvider { + /** Extensions that implement their own concrete {@link Compressor}s provide them through this interface method*/ + List> getCompressors(); +} diff --git a/libs/core/src/main/java/org/opensearch/core/compress/spi/DefaultCompressorProvider.java b/libs/core/src/main/java/org/opensearch/core/compress/spi/DefaultCompressorProvider.java new file mode 100644 index 0000000000000..3ca10b564ef68 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/compress/spi/DefaultCompressorProvider.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.compress.spi; + +import org.opensearch.core.compress.Compressor; +import org.opensearch.core.compress.NoneCompressor; + +import java.util.AbstractMap.SimpleEntry; +import java.util.List; +import java.util.Map.Entry; + +/** + * Default {@link Compressor} implementations provided by the + * opensearch core library + * + * @opensearch.internal + */ +public class DefaultCompressorProvider implements CompressorProvider { + /** Returns the default {@link Compressor}s provided by the core library */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public List> getCompressors() { + return List.of(new SimpleEntry(NoneCompressor.NAME, new NoneCompressor())); + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/compress/spi/package-info.java b/libs/core/src/main/java/org/opensearch/core/compress/spi/package-info.java new file mode 100644 index 0000000000000..6e33cc8fb63d3 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/compress/spi/package-info.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * The Service Provider Interface implementation for registering {@link org.opensearch.core.compress.Compressor} + * with the {@link org.opensearch.core.compress.CompressorRegistry} + * + * See {@link org.opensearch.core.compress.spi.DefaultCompressorProvider} as an example of registering the core + * {@link org.opensearch.core.compress.NoneCompressor} + */ +package org.opensearch.core.compress.spi; diff --git a/server/src/main/java/org/opensearch/common/util/concurrent/OpenSearchRejectedExecutionException.java b/libs/core/src/main/java/org/opensearch/core/concurrency/OpenSearchRejectedExecutionException.java similarity index 95% rename from server/src/main/java/org/opensearch/common/util/concurrent/OpenSearchRejectedExecutionException.java rename to libs/core/src/main/java/org/opensearch/core/concurrency/OpenSearchRejectedExecutionException.java index aee5bc63d9dbc..2a1234eb66c4e 100644 --- a/server/src/main/java/org/opensearch/common/util/concurrent/OpenSearchRejectedExecutionException.java +++ b/libs/core/src/main/java/org/opensearch/core/concurrency/OpenSearchRejectedExecutionException.java @@ -30,10 +30,15 @@ * GitHub history for details. */ -package org.opensearch.common.util.concurrent; +package org.opensearch.core.concurrency; import java.util.concurrent.RejectedExecutionException; +/** + * Thrown when an execution is rejected + * + * @opensearch.internal + */ public class OpenSearchRejectedExecutionException extends RejectedExecutionException { private final boolean isExecutorShutdown; diff --git a/libs/core/src/main/java/org/opensearch/core/concurrency/package-info.java b/libs/core/src/main/java/org/opensearch/core/concurrency/package-info.java new file mode 100644 index 0000000000000..331066995ccd5 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/concurrency/package-info.java @@ -0,0 +1,9 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +/** core concurrency package */ +package org.opensearch.core.concurrency; diff --git a/libs/core/src/main/java/org/opensearch/core/index/Index.java b/libs/core/src/main/java/org/opensearch/core/index/Index.java new file mode 100644 index 0000000000000..b5ee482bcd952 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/index/Index.java @@ -0,0 +1,203 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.index; + +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +/** + * A value class representing the basic required properties of an OpenSearch index. + * + * (This class is immutable.) + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") +public class Index implements Writeable, ToXContentObject { + + public static final Index[] EMPTY_ARRAY = new Index[0]; + private static final String INDEX_UUID_KEY = "index_uuid"; + private static final String INDEX_NAME_KEY = "index_name"; + public static final String UNKNOWN_INDEX_NAME = "_unknown_"; + + private static final ObjectParser INDEX_PARSER = new ObjectParser<>("index", Builder::new); + static { + INDEX_PARSER.declareString(Builder::name, new ParseField(INDEX_NAME_KEY)); + INDEX_PARSER.declareString(Builder::uuid, new ParseField(INDEX_UUID_KEY)); + } + + private final String name; + private final String uuid; + + /** + * Creates a new Index instance with name and unique identifier + * + * @param name the name of the index + * @param uuid the unique identifier of the index + * @throws NullPointerException if either name or uuid are null + */ + public Index(String name, String uuid) { + this.name = Objects.requireNonNull(name); + this.uuid = Objects.requireNonNull(uuid); + } + + /** + * Creates a new Index instance from a {@link StreamInput}. + * Reads the name and unique identifier from the stream. + * + * @param in the stream to read from + * @throws IOException if an error occurs while reading from the stream + * @see #writeTo(StreamOutput) + */ + public Index(StreamInput in) throws IOException { + this.name = in.readString(); + this.uuid = in.readString(); + } + + /** + * Gets the name of the index. + * + * @return the name of the index. + */ + public String getName() { + return this.name; + } + + /** + * Gets the unique identifier of the index. + * + * @return the unique identifier of the index. "_na_" if {@link Strings#UNKNOWN_UUID_VALUE}. + */ + public String getUUID() { + return uuid; + } + + /** + * Returns either the name and unique identifier of the index + * or only the name if the uuid is {@link Strings#UNKNOWN_UUID_VALUE}. + * + * If we have a uuid we put it in the toString so it'll show up in logs + * which is useful as more and more things use the uuid rather + * than the name as the lookup key for the index. + * + * @return {@code "[name/uuid]"} or {@code "[name]"} + */ + @Override + public String toString() { + if (Strings.UNKNOWN_UUID_VALUE.equals(uuid)) { + return "[" + name + "]"; + } + return "[" + name + "/" + uuid + "]"; + } + + /** + * Checks if this index is the same as another index by comparing the name and unique identifier. + * If both uuid are {@link Strings#UNKNOWN_UUID_VALUE} then only the name is compared. + * + * @param o the index to compare to + * @return true if the name and unique identifier are the same, false otherwise. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Index index1 = (Index) o; + return uuid.equals(index1.uuid) && name.equals(index1.name); // allow for _na_ uuid + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + uuid.hashCode(); + return result; + } + + /** Writes the name and unique identifier to the {@link StreamOutput} + * + * @param out The stream to write to + */ + @Override + public void writeTo(final StreamOutput out) throws IOException { + out.writeString(name); + out.writeString(uuid); + } + + @Override + public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { + builder.startObject(); + builder.field(INDEX_NAME_KEY, name); + builder.field(INDEX_UUID_KEY, uuid); + return builder.endObject(); + } + + public static Index fromXContent(final XContentParser parser) throws IOException { + return INDEX_PARSER.parse(parser, null).build(); + } + + /** + * Builder for Index objects. Used by ObjectParser instances only. + * + * @opensearch.internal + */ + private static final class Builder { + private String name; + private String uuid; + + public void name(final String name) { + this.name = name; + } + + public void uuid(final String uuid) { + this.uuid = uuid; + } + + public Index build() { + return new Index(name, uuid); + } + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/index/package-info.java b/libs/core/src/main/java/org/opensearch/core/index/package-info.java new file mode 100644 index 0000000000000..31ecb88948c9d --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/index/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core index module */ +package org.opensearch.core.index; diff --git a/libs/core/src/main/java/org/opensearch/core/index/shard/ShardId.java b/libs/core/src/main/java/org/opensearch/core/index/shard/ShardId.java new file mode 100644 index 0000000000000..984434190b486 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/index/shard/ShardId.java @@ -0,0 +1,212 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.index.shard; + +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; + +/** + * Allows for shard level components to be injected with the shard id. + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") +public class ShardId implements Comparable, ToXContentFragment, Writeable { + + private final Index index; + private final int shardId; + private final int hashCode; + + /** + * Constructs a new shard id. + * @param index the index name + * @param shardId the shard id + */ + public ShardId(Index index, int shardId) { + this.index = index; + this.shardId = shardId; + this.hashCode = computeHashCode(); + } + + /** + * Constructs a new shard id with the given index name, index unique identifier, and shard id. + * @param index the index name + * @param indexUUID the index unique identifier + * @param shardId the shard id + */ + public ShardId(String index, String indexUUID, int shardId) { + this(new Index(index, indexUUID), shardId); + } + + /** + * Constructs a new shardId from a stream. + * @param in the stream to read from + * @throws IOException if an error occurs while reading from the stream + * @see #writeTo(StreamOutput) + */ + public ShardId(StreamInput in) throws IOException { + index = new Index(in); + shardId = in.readVInt(); + hashCode = computeHashCode(); + } + + /** + * Writes this shard id to a stream. + * @param out the stream to write to + * @throws IOException if an error occurs while writing to the stream + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + index.writeTo(out); + out.writeVInt(shardId); + } + + /** + * Returns the index of this shard id. + * @return the index of this shard id + */ + public Index getIndex() { + return index; + } + + /** + * Returns the name of the index of this shard id. + * @return the name of the index of this shard id + */ + public String getIndexName() { + return index.getName(); + } + + /** + * Return the shardId of this shard id. + * @return the shardId of this shard id + * @see #getId() + */ + public int id() { + return this.shardId; + } + + /** + * Returns the shard id of this shard id. + * @return the shard id of this shard id + */ + public int getId() { + return id(); + } + + /** + * Returns a string representation of this shard id. + * @return "[indexName][shardId]" + */ + @Override + public String toString() { + return "[" + index.getName() + "][" + shardId + "]"; + } + + /** + * Parse the string representation of this shardId back to an object. + * + * We lose index uuid information here, but since we use toString in + * rest responses, this is the best we can do to reconstruct the object + * on the client side. + * + * @param shardIdString the string representation of the shard id + * (Expect a string of format "[indexName][shardId]" (square brackets included)) + */ + public static ShardId fromString(String shardIdString) { + int splitPosition = shardIdString.indexOf("]["); + if (splitPosition <= 0 || shardIdString.charAt(0) != '[' || shardIdString.charAt(shardIdString.length() - 1) != ']') { + throw new IllegalArgumentException("Unexpected shardId string format, expected [indexName][shardId] but got " + shardIdString); + } + String indexName = shardIdString.substring(1, splitPosition); + int shardId = Integer.parseInt(shardIdString.substring(splitPosition + 2, shardIdString.length() - 1)); + return new ShardId(new Index(indexName, Strings.UNKNOWN_UUID_VALUE), shardId); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ShardId shardId1 = (ShardId) o; + return shardId == shardId1.shardId && index.equals(shardId1.index); + } + + /** Returns the hash code of this shard id. + * + * @return the hash code of this shard id + */ + @Override + public int hashCode() { + return hashCode; + } + + /** Computes the hash code of this shard id. + * + * @return the hash code of this shard id. + */ + private int computeHashCode() { + int result = index != null ? index.hashCode() : 0; + result = 31 * result + shardId; + return result; + } + + /** + * Compares this ShardId with the specified ShardId. + * @param o the ShardId to be compared. + * @return a negative integer, zero, or a positive integer if this ShardId is less than, equal to, or greater than the specified ShardId + */ + @Override + public int compareTo(ShardId o) { + if (o.getId() == shardId) { + int compare = index.getName().compareTo(o.getIndex().getName()); + if (compare != 0) { + return compare; + } + return index.getUUID().compareTo(o.getIndex().getUUID()); + } + return Integer.compare(shardId, o.getId()); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.value(toString()); + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/index/shard/package-info.java b/libs/core/src/main/java/org/opensearch/core/index/shard/package-info.java new file mode 100644 index 0000000000000..267d558a5092c --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/index/shard/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core index/shard module */ +package org.opensearch.core.index.shard; diff --git a/server/src/main/java/org/opensearch/index/snapshots/IndexShardSnapshotException.java b/libs/core/src/main/java/org/opensearch/core/index/snapshots/IndexShardSnapshotException.java similarity index 90% rename from server/src/main/java/org/opensearch/index/snapshots/IndexShardSnapshotException.java rename to libs/core/src/main/java/org/opensearch/core/index/snapshots/IndexShardSnapshotException.java index db7550c964299..24e91ca949551 100644 --- a/server/src/main/java/org/opensearch/index/snapshots/IndexShardSnapshotException.java +++ b/libs/core/src/main/java/org/opensearch/core/index/snapshots/IndexShardSnapshotException.java @@ -30,16 +30,18 @@ * GitHub history for details. */ -package org.opensearch.index.snapshots; +package org.opensearch.core.index.snapshots; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; /** * Generic shard snapshot exception + * + * @opensearch.internal */ public class IndexShardSnapshotException extends OpenSearchException { public IndexShardSnapshotException(ShardId shardId, String msg) { diff --git a/server/src/main/java/org/opensearch/index/snapshots/IndexShardSnapshotFailedException.java b/libs/core/src/main/java/org/opensearch/core/index/snapshots/IndexShardSnapshotFailedException.java similarity index 90% rename from server/src/main/java/org/opensearch/index/snapshots/IndexShardSnapshotFailedException.java rename to libs/core/src/main/java/org/opensearch/core/index/snapshots/IndexShardSnapshotFailedException.java index 236cd668f443d..32d1a49c46f9e 100644 --- a/server/src/main/java/org/opensearch/index/snapshots/IndexShardSnapshotFailedException.java +++ b/libs/core/src/main/java/org/opensearch/core/index/snapshots/IndexShardSnapshotFailedException.java @@ -30,15 +30,17 @@ * GitHub history for details. */ -package org.opensearch.index.snapshots; +package org.opensearch.core.index.snapshots; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; /** * Thrown when snapshot process is failed on a shard level + * + * @opensearch.internal */ public class IndexShardSnapshotFailedException extends IndexShardSnapshotException { public IndexShardSnapshotFailedException(ShardId shardId, String msg) { diff --git a/libs/core/src/main/java/org/opensearch/core/index/snapshots/package-info.java b/libs/core/src/main/java/org/opensearch/core/index/snapshots/package-info.java new file mode 100644 index 0000000000000..b3bb62e40b7d0 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/index/snapshots/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core snapshots module */ +package org.opensearch.core.index.snapshots; diff --git a/libs/core/src/main/java/org/opensearch/core/indices/breaker/AllCircuitBreakerStats.java b/libs/core/src/main/java/org/opensearch/core/indices/breaker/AllCircuitBreakerStats.java new file mode 100644 index 0000000000000..3ce8b4953b9d6 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/indices/breaker/AllCircuitBreakerStats.java @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.indices.breaker; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; + +/** + * Stats class encapsulating all of the different circuit breaker stats + * + * @opensearch.internal + */ +public class AllCircuitBreakerStats implements Writeable, ToXContentFragment { + + /** An array of all the circuit breaker stats */ + private final CircuitBreakerStats[] allStats; + + /** + * Constructs the instance + * + * @param allStats an array of all the circuit breaker stats + */ + public AllCircuitBreakerStats(CircuitBreakerStats[] allStats) { + this.allStats = allStats; + } + + /** + * Constructs the new instance from {@link StreamInput} + * @param in the {@link StreamInput} to read from + * @throws IOException If an error occurs while reading from the StreamInput + * @see #writeTo(StreamOutput) + */ + public AllCircuitBreakerStats(StreamInput in) throws IOException { + allStats = in.readArray(CircuitBreakerStats::new, CircuitBreakerStats[]::new); + } + + /** + * Writes this instance into a {@link StreamOutput} + * @param out the {@link StreamOutput} to write to + * @throws IOException if an error occurs while writing to the StreamOutput + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeArray(allStats); + } + + /** + * Returns inner stats instances for all circuit breakers + * @return inner stats instances for all circuit breakers + */ + public CircuitBreakerStats[] getAllStats() { + return this.allStats; + } + + /** + * Returns the stats for a specific circuit breaker + * @param name the name of the circuit breaker + * @return the {@link CircuitBreakerStats} for the circuit breaker, null if the circuit breaker with such name does not exist + */ + public CircuitBreakerStats getStats(String name) { + for (CircuitBreakerStats stats : allStats) { + if (stats.getName().equals(name)) { + return stats; + } + } + return null; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(Fields.BREAKERS); + for (CircuitBreakerStats stats : allStats) { + if (stats != null) { + stats.toXContent(builder, params); + } + } + builder.endObject(); + return builder; + } + + /** + * Fields used for parsing and toXContent + * + * @opensearch.internal + */ + static final class Fields { + static final String BREAKERS = "breakers"; + } +} diff --git a/server/src/main/java/org/opensearch/indices/breaker/CircuitBreakerService.java b/libs/core/src/main/java/org/opensearch/core/indices/breaker/CircuitBreakerService.java similarity index 91% rename from server/src/main/java/org/opensearch/indices/breaker/CircuitBreakerService.java rename to libs/core/src/main/java/org/opensearch/core/indices/breaker/CircuitBreakerService.java index fd7ce00fde798..ee9c94f432a36 100644 --- a/server/src/main/java/org/opensearch/indices/breaker/CircuitBreakerService.java +++ b/libs/core/src/main/java/org/opensearch/core/indices/breaker/CircuitBreakerService.java @@ -30,16 +30,18 @@ * GitHub history for details. */ -package org.opensearch.indices.breaker; +package org.opensearch.core.indices.breaker; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.component.AbstractLifecycleComponent; +import org.opensearch.common.lifecycle.AbstractLifecycleComponent; +import org.opensearch.core.common.breaker.CircuitBreaker; /** * Interface for Circuit Breaker services, which provide breakers to classes * that load field data. + * + * @opensearch.internal */ public abstract class CircuitBreakerService extends AbstractLifecycleComponent { private static final Logger logger = LogManager.getLogger(CircuitBreakerService.class); diff --git a/libs/core/src/main/java/org/opensearch/core/indices/breaker/CircuitBreakerStats.java b/libs/core/src/main/java/org/opensearch/core/indices/breaker/CircuitBreakerStats.java new file mode 100644 index 0000000000000..9207d3ea77227 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/indices/breaker/CircuitBreakerStats.java @@ -0,0 +1,202 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.indices.breaker; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Locale; + +/** + * Class encapsulating stats about the {@link org.opensearch.core.common.breaker.CircuitBreaker} + * + * @opensearch.internal + */ +public class CircuitBreakerStats implements Writeable, ToXContentObject { + + /** The name of the circuit breaker */ + private final String name; + /** The limit size in byte of the circuit breaker. Field : "limit_size_in_bytes" */ + private final long limit; + /** The estimated size in byte of the breaker. Field : "estimated_size_in_bytes" */ + private final long estimated; + /** The number of times the breaker has been tripped. Field : "tripped" */ + private final long trippedCount; + /** The overhead of the breaker. Field : "overhead" */ + private final double overhead; + + /** + * Constructs new instance + * + * @param name The name of the circuit breaker + * @param limit The limit size in byte of the circuit breaker + * @param estimated The estimated size in byte of the breaker + * @param overhead The overhead of the breaker + * @param trippedCount The number of times the breaker has been tripped + * @see org.opensearch.core.common.breaker.CircuitBreaker + */ + public CircuitBreakerStats(String name, long limit, long estimated, double overhead, long trippedCount) { + this.name = name; + this.limit = limit; + this.estimated = estimated; + this.trippedCount = trippedCount; + this.overhead = overhead; + } + + /** + * Constructs new instance from the {@link StreamInput} + * + * @param in The StreamInput + * @throws IOException if an error occurs while reading from the StreamInput + * @see org.opensearch.core.common.breaker.CircuitBreaker + * @see #writeTo(StreamOutput) + */ + public CircuitBreakerStats(StreamInput in) throws IOException { + this.limit = in.readLong(); + this.estimated = in.readLong(); + this.overhead = in.readDouble(); + this.trippedCount = in.readLong(); + this.name = in.readString(); + } + + /** + * Writes this instance into a {@link StreamOutput} + * + * @param out The StreamOutput + * @throws IOException if an error occurs while writing to the StreamOutput + * @see #CircuitBreakerStats(StreamInput) + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeLong(limit); + out.writeLong(estimated); + out.writeDouble(overhead); + out.writeLong(trippedCount); + out.writeString(name); + } + + /** + * Returns the name of the circuit breaker + * @return The name of the circuit breaker + */ + public String getName() { + return this.name; + } + + /** + * Returns the limit size in byte of the circuit breaker + * @return The limit size in byte of the circuit breaker + */ + public long getLimit() { + return this.limit; + } + + /** + * Returns the estimated size in byte of the breaker + * @return The estimated size in byte of the breaker + */ + public long getEstimated() { + return this.estimated; + } + + /** + * Returns the number of times the breaker has been tripped + * @return The number of times the breaker has been tripped + */ + public long getTrippedCount() { + return this.trippedCount; + } + + /** + * Returns the overhead of the breaker + * @return The overhead of the breaker + */ + public double getOverhead() { + return this.overhead; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(name.toLowerCase(Locale.ROOT)); + builder.field(Fields.LIMIT, limit); + builder.field(Fields.LIMIT_HUMAN, new ByteSizeValue(limit)); + builder.field(Fields.ESTIMATED, estimated); + builder.field(Fields.ESTIMATED_HUMAN, new ByteSizeValue(estimated)); + builder.field(Fields.OVERHEAD, overhead); + builder.field(Fields.TRIPPED_COUNT, trippedCount); + builder.endObject(); + return builder; + } + + /** + * Returns a String representation of this CircuitBreakerStats + * @return "[name,limit=limit/limit_human,estimated=estimated/estimated_human,overhead=overhead,tripped=trippedCount]" + */ + @Override + public String toString() { + return "[" + + this.name + + ",limit=" + + this.limit + + "/" + + new ByteSizeValue(this.limit) + + ",estimated=" + + this.estimated + + "/" + + new ByteSizeValue(this.estimated) + + ",overhead=" + + this.overhead + + ",tripped=" + + this.trippedCount + + "]"; + } + + /** + * Fields used for statistics + * + * @opensearch.internal + */ + static final class Fields { + static final String LIMIT = "limit_size_in_bytes"; + static final String LIMIT_HUMAN = "limit_size"; + static final String ESTIMATED = "estimated_size_in_bytes"; + static final String ESTIMATED_HUMAN = "estimated_size"; + static final String OVERHEAD = "overhead"; + static final String TRIPPED_COUNT = "tripped"; + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/indices/breaker/NoneCircuitBreakerService.java b/libs/core/src/main/java/org/opensearch/core/indices/breaker/NoneCircuitBreakerService.java new file mode 100644 index 0000000000000..49c5a393328b9 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/indices/breaker/NoneCircuitBreakerService.java @@ -0,0 +1,79 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.indices.breaker; + +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.NoopCircuitBreaker; + +/** + * Class that returns a breaker that use the NoopCircuitBreaker and never breaks + * + * @see org.opensearch.core.common.breaker.NoopCircuitBreaker + * @opensearch.internal + */ +public class NoneCircuitBreakerService extends CircuitBreakerService { + + private final CircuitBreaker breaker = new NoopCircuitBreaker(CircuitBreaker.FIELDDATA); + + public NoneCircuitBreakerService() { + super(); + } + + /** + * Returns a breaker that use the NoopCircuitBreaker and never breaks + * + * @param name name of the breaker (ignored) + * @return a NoopCircuitBreaker + */ + @Override + public CircuitBreaker getBreaker(String name) { + return breaker; + } + + @Override + public AllCircuitBreakerStats stats() { + return new AllCircuitBreakerStats(new CircuitBreakerStats[] { stats(CircuitBreaker.FIELDDATA) }); + } + + /** + * Always returns the same stats, a NoopCircuitBreaker never breaks and all operations are noops. + * + * @param name name of the breaker (ignored) + * @return always "fielddata", limit: -1, estimated: -1, overhead: 0, trippedCount: 0 + */ + @Override + public CircuitBreakerStats stats(String name) { + return new CircuitBreakerStats(CircuitBreaker.FIELDDATA, -1, -1, 0, 0); + } + +} diff --git a/libs/core/src/main/java/org/opensearch/core/indices/breaker/package-info.java b/libs/core/src/main/java/org/opensearch/core/indices/breaker/package-info.java new file mode 100644 index 0000000000000..a98f9ab1d9f1e --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/indices/breaker/package-info.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Top Level core circuit breaker implementation + * + * @opensearch.internal + * @opensearch.experimental + */ +package org.opensearch.core.indices.breaker; diff --git a/libs/core/src/main/java/org/opensearch/core/indices/package-info.java b/libs/core/src/main/java/org/opensearch/core/indices/package-info.java new file mode 100644 index 0000000000000..c80edf3d2f01a --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/indices/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Top Level Package for implementations used across indexes + */ +package org.opensearch.core.indices; diff --git a/libs/core/src/main/java/org/opensearch/core/internal/io/Streams.java b/libs/core/src/main/java/org/opensearch/core/internal/io/Streams.java deleted file mode 100644 index 1938e1bbf4dff..0000000000000 --- a/libs/core/src/main/java/org/opensearch/core/internal/io/Streams.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.core.internal.io; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Simple utility methods for file and stream copying. - * All copy methods use a block size of 4096 bytes, - * and close all affected streams when done. - *

    - * Mainly for use within the framework, - * but also useful for application code. - */ -public class Streams { - - private static final ThreadLocal buffer = ThreadLocal.withInitial(() -> new byte[8 * 1024]); - - private Streams() { - - } - - /** - * Copy the contents of the given InputStream to the given OutputStream. Optionally, closes both streams when done. - * - * @param in the stream to copy from - * @param out the stream to copy to - * @param close whether to close both streams after copying - * @param buffer buffer to use for copying - * @return the number of bytes copied - * @throws IOException in case of I/O errors - */ - public static long copy(final InputStream in, final OutputStream out, byte[] buffer, boolean close) throws IOException { - Exception err = null; - try { - long byteCount = 0; - int bytesRead; - while ((bytesRead = in.read(buffer)) != -1) { - out.write(buffer, 0, bytesRead); - byteCount += bytesRead; - } - out.flush(); - return byteCount; - } catch (IOException | RuntimeException e) { - err = e; - throw e; - } finally { - if (close) { - IOUtils.close(err, in, out); - } - } - } - - /** - * @see #copy(InputStream, OutputStream, byte[], boolean) - */ - public static long copy(final InputStream in, final OutputStream out, boolean close) throws IOException { - return copy(in, out, buffer.get(), close); - } - - /** - * @see #copy(InputStream, OutputStream, byte[], boolean) - */ - public static long copy(final InputStream in, final OutputStream out, byte[] buffer) throws IOException { - return copy(in, out, buffer, true); - } - - /** - * @see #copy(InputStream, OutputStream, byte[], boolean) - */ - public static long copy(final InputStream in, final OutputStream out) throws IOException { - return copy(in, out, buffer.get(), true); - } -} diff --git a/libs/core/src/main/java/org/opensearch/core/package-info.java b/libs/core/src/main/java/org/opensearch/core/package-info.java new file mode 100644 index 0000000000000..b64298ad0788b --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Base Core Library Classes */ +package org.opensearch.core; diff --git a/server/src/main/java/org/opensearch/rest/RestStatus.java b/libs/core/src/main/java/org/opensearch/core/rest/RestStatus.java similarity index 97% rename from server/src/main/java/org/opensearch/rest/RestStatus.java rename to libs/core/src/main/java/org/opensearch/core/rest/RestStatus.java index 5f50559ef0f51..8441ce8b1b622 100644 --- a/server/src/main/java/org/opensearch/rest/RestStatus.java +++ b/libs/core/src/main/java/org/opensearch/core/rest/RestStatus.java @@ -30,11 +30,12 @@ * GitHub history for details. */ -package org.opensearch.rest; +package org.opensearch.core.rest; -import org.opensearch.action.ShardOperationFailedException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.action.ShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.HashMap; @@ -42,6 +43,12 @@ import static java.util.Collections.unmodifiableMap; +/** + * OpenSearch REST status + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") public enum RestStatus { /** * The client SHOULD continue with its request. This interim response is used to inform the client that the @@ -426,6 +433,12 @@ public enum RestStatus { * next-hop server. */ EXPECTATION_FAILED(417), + /** + * The request was directed at a server that is not able to produce a response. This can be sent by a server + * that is not configured to produce responses for the combination of scheme and authority that are included + * in the request URI. + */ + MISDIRECTED_REQUEST(421), /** * The 422 (Unprocessable Entity) status code means the server understands the content type of the request * entity (hence a 415(Unsupported Media Type) status code is inappropriate), and the syntax of the request @@ -514,6 +527,15 @@ public int getStatus() { return status; } + /** + * Get category class of a rest status code. + * + * @return Integer representing class category of the concrete rest status code + */ + public int getStatusFamilyCode() { + return status / 100; + } + public static RestStatus readFrom(StreamInput in) throws IOException { return RestStatus.valueOf(in.readString()); } diff --git a/libs/core/src/main/java/org/opensearch/core/rest/package-info.java b/libs/core/src/main/java/org/opensearch/core/rest/package-info.java new file mode 100644 index 0000000000000..8c99d38e5f003 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/rest/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for core REST module */ +package org.opensearch.core.rest; diff --git a/server/src/main/java/org/opensearch/node/ReportingService.java b/libs/core/src/main/java/org/opensearch/core/service/ReportingService.java similarity index 81% rename from server/src/main/java/org/opensearch/node/ReportingService.java rename to libs/core/src/main/java/org/opensearch/core/service/ReportingService.java index 3d81a0f8976a5..3c88169f1e3b0 100644 --- a/server/src/main/java/org/opensearch/node/ReportingService.java +++ b/libs/core/src/main/java/org/opensearch/core/service/ReportingService.java @@ -30,14 +30,24 @@ * GitHub history for details. */ -package org.opensearch.node; +package org.opensearch.core.service; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContent; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContent; +/** + * Node reporting service + * + * @opensearch.internal + */ public interface ReportingService { I info(); + /** + * Information interface. + * + * @opensearch.internal + */ interface Info extends Writeable, ToXContent { } diff --git a/libs/core/src/main/java/org/opensearch/core/service/package-info.java b/libs/core/src/main/java/org/opensearch/core/service/package-info.java new file mode 100644 index 0000000000000..d427c6e5934c9 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/service/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** OpenSearch Core Service Interfaces */ +package org.opensearch.core.service; diff --git a/server/src/main/java/org/opensearch/tasks/TaskCancelledException.java b/libs/core/src/main/java/org/opensearch/core/tasks/TaskCancelledException.java similarity index 92% rename from server/src/main/java/org/opensearch/tasks/TaskCancelledException.java rename to libs/core/src/main/java/org/opensearch/core/tasks/TaskCancelledException.java index f9863a10639e0..6bdc1e42f351a 100644 --- a/server/src/main/java/org/opensearch/tasks/TaskCancelledException.java +++ b/libs/core/src/main/java/org/opensearch/core/tasks/TaskCancelledException.java @@ -29,15 +29,17 @@ * GitHub history for details. */ -package org.opensearch.tasks; +package org.opensearch.core.tasks; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** * A generic exception that can be thrown by a task when it's cancelled by the task manager API + * + * @opensearch.internal */ public class TaskCancelledException extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/tasks/TaskId.java b/libs/core/src/main/java/org/opensearch/core/tasks/TaskId.java similarity index 91% rename from server/src/main/java/org/opensearch/tasks/TaskId.java rename to libs/core/src/main/java/org/opensearch/core/tasks/TaskId.java index e98df346b7041..d34d4acf00e6e 100644 --- a/server/src/main/java/org/opensearch/tasks/TaskId.java +++ b/libs/core/src/main/java/org/opensearch/core/tasks/TaskId.java @@ -30,21 +30,25 @@ * GitHub history for details. */ -package org.opensearch.tasks; +package org.opensearch.core.tasks; import org.opensearch.OpenSearchParseException; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ContextParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; /** * Task id that consists of node id and id of the task on the node + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public final class TaskId implements Writeable { public static final TaskId EMPTY_TASK_ID = new TaskId(); diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/package-info.java b/libs/core/src/main/java/org/opensearch/core/tasks/package-info.java new file mode 100644 index 0000000000000..e421816c6b541 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Core Tasks Foundation classes used across the opensearch code base */ +package org.opensearch.core.tasks; diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceStats.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceStats.java new file mode 100644 index 0000000000000..d65f75581dd1b --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceStats.java @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.tasks.resourcetracker; + +/** + * Different resource stats are defined. + * + * @opensearch.internal + */ +public enum ResourceStats { + CPU("cpu_time_in_nanos"), + MEMORY("memory_in_bytes"); + + private final String statsName; + + ResourceStats(String statsName) { + this.statsName = statsName; + } + + @Override + public String toString() { + return statsName; + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceStatsType.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceStatsType.java new file mode 100644 index 0000000000000..fce8cc65e9bc5 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceStatsType.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.tasks.resourcetracker; + +/** + * Defines the different types of resource stats. + * + * @opensearch.internal + */ +public enum ResourceStatsType { + // resource stats of the worker thread reported directly from runnable. + WORKER_STATS("worker_stats", false); + + private final String statsType; + private final boolean onlyForAnalysis; + + ResourceStatsType(String statsType, boolean onlyForAnalysis) { + this.statsType = statsType; + this.onlyForAnalysis = onlyForAnalysis; + } + + public boolean isOnlyForAnalysis() { + return onlyForAnalysis; + } + + @Override + public String toString() { + return statsType; + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceUsageInfo.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceUsageInfo.java new file mode 100644 index 0000000000000..2cbc3d4b2f5c3 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceUsageInfo.java @@ -0,0 +1,110 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.tasks.resourcetracker; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Collections; +import java.util.EnumMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Thread resource usage information for particular resource stats type. + *

    + * It captures the resource usage information like memory, CPU about a particular execution of thread + * for a specific stats type. + * + * @opensearch.internal + */ +public class ResourceUsageInfo { + private static final Logger logger = LogManager.getLogger(ResourceUsageInfo.class); + private final EnumMap statsInfo = new EnumMap<>(ResourceStats.class); + + public ResourceUsageInfo(ResourceUsageMetric... resourceUsageMetrics) { + for (ResourceUsageMetric resourceUsageMetric : resourceUsageMetrics) { + this.statsInfo.put(resourceUsageMetric.getStats(), new ResourceStatsInfo(resourceUsageMetric.getValue())); + } + } + + public void recordResourceUsageMetrics(ResourceUsageMetric... resourceUsageMetrics) { + for (ResourceUsageMetric resourceUsageMetric : resourceUsageMetrics) { + final ResourceStatsInfo resourceStatsInfo = statsInfo.get(resourceUsageMetric.getStats()); + if (resourceStatsInfo != null) { + updateResourceUsageInfo(resourceStatsInfo, resourceUsageMetric); + } else { + throw new IllegalStateException( + "cannot update [" + + resourceUsageMetric.getStats().toString() + + "] entry as its not present current_stats_info:" + + statsInfo + ); + } + } + } + + private void updateResourceUsageInfo(ResourceStatsInfo resourceStatsInfo, ResourceUsageMetric resourceUsageMetric) { + long currentEndValue; + long newEndValue; + do { + currentEndValue = resourceStatsInfo.endValue.get(); + newEndValue = resourceUsageMetric.getValue(); + if (currentEndValue > newEndValue) { + logger.debug( + "dropping resource usage update as the new value is lower than current value [" + + "resource_stats=[{}], " + + "current_end_value={}, " + + "new_end_value={}]", + resourceUsageMetric.getStats(), + currentEndValue, + newEndValue + ); + return; + } + } while (!resourceStatsInfo.endValue.compareAndSet(currentEndValue, newEndValue)); + logger.debug( + "updated resource usage info [resource_stats=[{}], " + "old_end_value={}, new_end_value={}]", + resourceUsageMetric.getStats(), + currentEndValue, + newEndValue + ); + } + + public Map getStatsInfo() { + return Collections.unmodifiableMap(statsInfo); + } + + @Override + public String toString() { + return statsInfo.toString(); + } + + /** + * Defines resource stats information. + */ + public static class ResourceStatsInfo { + private final long startValue; + private final AtomicLong endValue; + + private ResourceStatsInfo(long startValue) { + this.startValue = startValue; + this.endValue = new AtomicLong(startValue); + } + + public long getTotalValue() { + return endValue.get() - startValue; + } + + @Override + public String toString() { + return String.valueOf(getTotalValue()); + } + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceUsageMetric.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceUsageMetric.java new file mode 100644 index 0000000000000..262dbe20dabda --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ResourceUsageMetric.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.tasks.resourcetracker; + +/** + * Information about resource usage + * + * @opensearch.internal + */ +public class ResourceUsageMetric { + private final ResourceStats stats; + private final long value; + + public ResourceUsageMetric(ResourceStats stats, long value) { + this.stats = stats; + this.value = value; + } + + public ResourceStats getStats() { + return stats; + } + + public long getValue() { + return value; + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskResourceStats.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskResourceStats.java new file mode 100644 index 0000000000000..d0d26550a4742 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskResourceStats.java @@ -0,0 +1,137 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.tasks.resourcetracker; + +import org.opensearch.Version; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Resource information about a currently running task. + *

    + * Writeable TaskResourceStats objects are used to represent resource + * snapshot information about currently running task. + * + * @opensearch.internal + */ +public class TaskResourceStats implements Writeable, ToXContentFragment { + private final Map resourceUsage; + private final TaskThreadUsage threadUsage; + + public static final String THREAD_INFO = "thread_info"; + + public TaskResourceStats(Map resourceUsage, TaskThreadUsage threadUsage) { + this.resourceUsage = Objects.requireNonNull(resourceUsage, "resource usage is required"); + this.threadUsage = Objects.requireNonNull(threadUsage, "thread usage is required"); + } + + /** + * Read from a stream. + */ + public TaskResourceStats(StreamInput in) throws IOException { + resourceUsage = in.readMap(StreamInput::readString, TaskResourceUsage::readFromStream); + if (in.getVersion().onOrAfter(Version.V_2_9_0)) { + threadUsage = TaskThreadUsage.readFromStream(in); + } else { + // Initialize TaskThreadUsage in case it is not found in mixed cluster case + threadUsage = new TaskThreadUsage(0, 0); + } + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeMap(resourceUsage, StreamOutput::writeString, (stream, stats) -> stats.writeTo(stream)); + if (out.getVersion().onOrAfter(Version.V_2_9_0)) { + threadUsage.writeTo(out); + } + } + + public Map getResourceUsageInfo() { + return resourceUsage; + } + + public TaskThreadUsage getThreadUsage() { + return threadUsage; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + for (Map.Entry resourceUsageEntry : resourceUsage.entrySet()) { + builder.startObject(resourceUsageEntry.getKey()); + if (resourceUsageEntry.getValue() != null) { + resourceUsageEntry.getValue().toXContent(builder, params); + } + builder.endObject(); + } + builder.startObject(THREAD_INFO); + threadUsage.toXContent(builder, params); + builder.endObject(); + return builder; + } + + public static TaskResourceStats fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token = parser.currentToken(); + if (token == null) { + token = parser.nextToken(); + } + if (token == XContentParser.Token.START_OBJECT) { + token = parser.nextToken(); + } + final Map resourceStats = new HashMap<>(); + // Initialize TaskThreadUsage in case it is not found in mixed cluster case + TaskThreadUsage threadUsage = new TaskThreadUsage(0, 0); + if (token == XContentParser.Token.FIELD_NAME) { + assert parser.currentToken() == XContentParser.Token.FIELD_NAME : "Expected field name but saw [" + parser.currentToken() + "]"; + do { + // Must point to field name + String fieldName = parser.currentName(); + // And then the value + + if (fieldName.equals(THREAD_INFO)) { + threadUsage = TaskThreadUsage.fromXContent(parser); + } else { + TaskResourceUsage value = TaskResourceUsage.fromXContent(parser); + resourceStats.put(fieldName, value); + } + } while (parser.nextToken() == XContentParser.Token.FIELD_NAME); + } + return new TaskResourceStats(resourceStats, threadUsage); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); + } + + // Implements equals and hashcode for testing + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != TaskResourceStats.class) { + return false; + } + TaskResourceStats other = (TaskResourceStats) obj; + return Objects.equals(resourceUsage, other.resourceUsage); + } + + @Override + public int hashCode() { + return Objects.hash(resourceUsage); + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskResourceUsage.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskResourceUsage.java new file mode 100644 index 0000000000000..7d6cadbef23d7 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskResourceUsage.java @@ -0,0 +1,108 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.tasks.resourcetracker; + +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; + +/** + * Task resource usage information + *

    + * Writeable TaskResourceUsage objects are used to represent resource usage + * information of running tasks. + * + * @opensearch.internal + */ +public class TaskResourceUsage implements Writeable, ToXContentFragment { + private static final ParseField CPU_TIME_IN_NANOS = new ParseField("cpu_time_in_nanos"); + private static final ParseField MEMORY_IN_BYTES = new ParseField("memory_in_bytes"); + + private final long cpuTimeInNanos; + private final long memoryInBytes; + + public TaskResourceUsage(long cpuTimeInNanos, long memoryInBytes) { + this.cpuTimeInNanos = cpuTimeInNanos; + this.memoryInBytes = memoryInBytes; + } + + /** + * Read from a stream. + */ + public static TaskResourceUsage readFromStream(StreamInput in) throws IOException { + return new TaskResourceUsage(in.readVLong(), in.readVLong()); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeVLong(cpuTimeInNanos); + out.writeVLong(memoryInBytes); + } + + public long getCpuTimeInNanos() { + return cpuTimeInNanos; + } + + public long getMemoryInBytes() { + return memoryInBytes; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.field(CPU_TIME_IN_NANOS.getPreferredName(), cpuTimeInNanos); + builder.field(MEMORY_IN_BYTES.getPreferredName(), memoryInBytes); + return builder; + } + + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "task_resource_usage", + a -> new TaskResourceUsage((Long) a[0], (Long) a[1]) + ); + + static { + PARSER.declareLong(constructorArg(), CPU_TIME_IN_NANOS); + PARSER.declareLong(constructorArg(), MEMORY_IN_BYTES); + } + + public static TaskResourceUsage fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); + } + + // Implements equals and hashcode for testing + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != TaskResourceUsage.class) { + return false; + } + TaskResourceUsage other = (TaskResourceUsage) obj; + return Objects.equals(cpuTimeInNanos, other.cpuTimeInNanos) && Objects.equals(memoryInBytes, other.memoryInBytes); + } + + @Override + public int hashCode() { + return Objects.hash(cpuTimeInNanos, memoryInBytes); + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskThreadUsage.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskThreadUsage.java new file mode 100644 index 0000000000000..b593ec96e5996 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/TaskThreadUsage.java @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.tasks.resourcetracker; + +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; + +/** + * Task thread execution information. + * Writeable TaskThreadExecutions objects are used to represent thread related resource usage of running tasks. + * asd + * + * @opensearch.internal + */ +public class TaskThreadUsage implements Writeable, ToXContentFragment { + + private static final String THREAD_EXECUTIONS = "thread_executions"; + private static final String ACTIVE_THREADS = "active_threads"; + private static final ParseField THREAD_EXECUTION_COUNT = new ParseField(THREAD_EXECUTIONS); + private static final ParseField ACTIVE_THREAD_COUNT = new ParseField(ACTIVE_THREADS); + + private final int threadExecutions; + private final int activeThreads; + + public TaskThreadUsage(int threadExecutions, int activeThreads) { + this.threadExecutions = threadExecutions; + this.activeThreads = activeThreads; + } + + /** + * Read from a stream. + */ + public static TaskThreadUsage readFromStream(StreamInput in) throws IOException { + return new TaskThreadUsage(in.readInt(), in.readInt()); + } + + public int getThreadExecutions() { + return threadExecutions; + } + + public int getActiveThreads() { + return activeThreads; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.field(THREAD_EXECUTIONS, threadExecutions); + builder.field(ACTIVE_THREADS, activeThreads); + return builder; + } + + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "task_thread_executions", + a -> new TaskThreadUsage((int) a[0], (int) a[1]) + ); + + static { + PARSER.declareInt(constructorArg(), THREAD_EXECUTION_COUNT); + PARSER.declareInt(constructorArg(), ACTIVE_THREAD_COUNT); + } + + public static TaskThreadUsage fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeInt(threadExecutions); + out.writeInt(activeThreads); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != TaskThreadUsage.class) { + return false; + } + TaskThreadUsage other = (TaskThreadUsage) obj; + return Objects.equals(threadExecutions, other.threadExecutions) && Objects.equals(activeThreads, other.activeThreads); + } + + @Override + public int hashCode() { + return Objects.hash(threadExecutions, activeThreads); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ThreadResourceInfo.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ThreadResourceInfo.java new file mode 100644 index 0000000000000..4b341a94256c4 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/ThreadResourceInfo.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.tasks.resourcetracker; + +/** + * Resource consumption information about a particular execution of thread. + *

    + * It captures the resource usage information about a particular execution of thread + * for a specific stats type like worker_stats or response_stats etc., + * + * @opensearch.internal + */ +public class ThreadResourceInfo { + private final long threadId; + private volatile boolean isActive = true; + private final ResourceStatsType statsType; + private final ResourceUsageInfo resourceUsageInfo; + + public ThreadResourceInfo(long threadId, ResourceStatsType statsType, ResourceUsageMetric... resourceUsageMetrics) { + this.threadId = threadId; + this.statsType = statsType; + this.resourceUsageInfo = new ResourceUsageInfo(resourceUsageMetrics); + } + + /** + * Updates thread's resource consumption information. + */ + public void recordResourceUsageMetrics(ResourceUsageMetric... resourceUsageMetrics) { + resourceUsageInfo.recordResourceUsageMetrics(resourceUsageMetrics); + } + + public void setActive(boolean isActive) { + this.isActive = isActive; + } + + public boolean isActive() { + return isActive; + } + + public ResourceStatsType getStatsType() { + return statsType; + } + + public long getThreadId() { + return threadId; + } + + public ResourceUsageInfo getResourceUsageInfo() { + return resourceUsageInfo; + } + + @Override + public String toString() { + return resourceUsageInfo + ", stats_type=" + statsType + ", is_active=" + isActive + ", threadId=" + threadId; + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/package-info.java b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/package-info.java new file mode 100644 index 0000000000000..b46b685ffaaf0 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/tasks/resourcetracker/package-info.java @@ -0,0 +1,9 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +/** Resource tracking classes for tracking task resource consumption (e.g., memory, cpu) */ +package org.opensearch.core.tasks.resourcetracker; diff --git a/server/src/main/java/org/opensearch/transport/TransportMessage.java b/libs/core/src/main/java/org/opensearch/core/transport/TransportMessage.java similarity index 85% rename from server/src/main/java/org/opensearch/transport/TransportMessage.java rename to libs/core/src/main/java/org/opensearch/core/transport/TransportMessage.java index 0862e25205261..941babda40aa3 100644 --- a/server/src/main/java/org/opensearch/transport/TransportMessage.java +++ b/libs/core/src/main/java/org/opensearch/core/transport/TransportMessage.java @@ -30,12 +30,17 @@ * GitHub history for details. */ -package org.opensearch.transport; +package org.opensearch.core.transport; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.transport.TransportAddress; +/** + * Message over the transport interface + * + * @opensearch.internal + */ public abstract class TransportMessage implements Writeable { private TransportAddress remoteAddress; diff --git a/server/src/main/java/org/opensearch/transport/TransportResponse.java b/libs/core/src/main/java/org/opensearch/core/transport/TransportResponse.java similarity index 86% rename from server/src/main/java/org/opensearch/transport/TransportResponse.java rename to libs/core/src/main/java/org/opensearch/core/transport/TransportResponse.java index 7369f5475926b..038069e93a51b 100644 --- a/server/src/main/java/org/opensearch/transport/TransportResponse.java +++ b/libs/core/src/main/java/org/opensearch/core/transport/TransportResponse.java @@ -30,13 +30,18 @@ * GitHub history for details. */ -package org.opensearch.transport; +package org.opensearch.core.transport; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Response over the transport interface + * + * @opensearch.internal + */ public abstract class TransportResponse extends TransportMessage { /** @@ -53,6 +58,11 @@ public TransportResponse(StreamInput in) throws IOException { super(in); } + /** + * Empty transport response + * + * @opensearch.internal + */ public static class Empty extends TransportResponse { public static final Empty INSTANCE = new Empty(); diff --git a/libs/core/src/main/java/org/opensearch/core/transport/package-info.java b/libs/core/src/main/java/org/opensearch/core/transport/package-info.java new file mode 100644 index 0000000000000..91db839f40305 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/transport/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Core Transport Layer classes used across the OpenSearch core */ +package org.opensearch.core.transport; diff --git a/libs/core/src/main/java/org/opensearch/core/util/BytesRefUtils.java b/libs/core/src/main/java/org/opensearch/core/util/BytesRefUtils.java new file mode 100644 index 0000000000000..30c9f182fcae6 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/util/BytesRefUtils.java @@ -0,0 +1,114 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.util; + +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.BytesRefArray; +import org.apache.lucene.util.BytesRefBuilder; +import org.apache.lucene.util.InPlaceMergeSorter; + +import java.util.Comparator; + +/** + * Utilities for sorting Lucene {@link BytesRefArray} + * + * @opensearch.internal + */ +public class BytesRefUtils { + public static void sort(final BytesRefArray bytes, final int[] indices) { + sort(new BytesRefBuilder(), new BytesRefBuilder(), bytes, indices); + } + + private static void sort( + final BytesRefBuilder scratch, + final BytesRefBuilder scratch1, + final BytesRefArray bytes, + final int[] indices + ) { + + final int numValues = bytes.size(); + assert indices.length >= numValues; + if (numValues > 1) { + new InPlaceMergeSorter() { + final Comparator comparator = Comparator.naturalOrder(); + + @Override + protected int compare(int i, int j) { + return comparator.compare(bytes.get(scratch, indices[i]), bytes.get(scratch1, indices[j])); + } + + @Override + protected void swap(int i, int j) { + int value_i = indices[i]; + indices[i] = indices[j]; + indices[j] = value_i; + } + }.sort(0, numValues); + } + + } + + public static int sortAndDedup(final BytesRefArray bytes, final int[] indices) { + final BytesRefBuilder scratch = new BytesRefBuilder(); + final BytesRefBuilder scratch1 = new BytesRefBuilder(); + final int numValues = bytes.size(); + assert indices.length >= numValues; + if (numValues <= 1) { + return numValues; + } + sort(scratch, scratch1, bytes, indices); + int uniqueCount = 1; + BytesRefBuilder previous = scratch; + BytesRefBuilder current = scratch1; + bytes.get(previous, indices[0]); + for (int i = 1; i < numValues; ++i) { + bytes.get(current, indices[i]); + if (!previous.get().equals(current.get())) { + indices[uniqueCount++] = indices[i]; + } + BytesRefBuilder tmp = previous; + previous = current; + current = tmp; + } + return uniqueCount; + } + + public static long bytesToLong(BytesRef bytes) { + int high = (bytes.bytes[bytes.offset + 0] << 24) | ((bytes.bytes[bytes.offset + 1] & 0xff) << 16) | ((bytes.bytes[bytes.offset + 2] + & 0xff) << 8) | (bytes.bytes[bytes.offset + 3] & 0xff); + int low = (bytes.bytes[bytes.offset + 4] << 24) | ((bytes.bytes[bytes.offset + 5] & 0xff) << 16) | ((bytes.bytes[bytes.offset + 6] + & 0xff) << 8) | (bytes.bytes[bytes.offset + 7] & 0xff); + return (((long) high) << 32) | (low & 0x0ffffffffL); + } + +} diff --git a/server/src/main/java/org/opensearch/common/io/FileSystemUtils.java b/libs/core/src/main/java/org/opensearch/core/util/FileSystemUtils.java similarity index 97% rename from server/src/main/java/org/opensearch/common/io/FileSystemUtils.java rename to libs/core/src/main/java/org/opensearch/core/util/FileSystemUtils.java index e8ba699f61e7a..99f48ed49dd39 100644 --- a/server/src/main/java/org/opensearch/common/io/FileSystemUtils.java +++ b/libs/core/src/main/java/org/opensearch/core/util/FileSystemUtils.java @@ -30,13 +30,12 @@ * GitHub history for details. */ -package org.opensearch.common.io; +package org.opensearch.core.util; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.Constants; -import org.opensearch.core.internal.io.IOUtils; -import org.opensearch.common.Strings; import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.util.io.IOUtils; import java.io.IOException; import java.io.InputStream; @@ -48,6 +47,8 @@ /** * OpenSearch utils to work with {@link java.nio.file.Path} + * + * @opensearch.internal */ public final class FileSystemUtils { @@ -149,7 +150,7 @@ public static InputStream openFileURLStream(URL url) throws IOException { if ("file".equals(protocol) == false && "jar".equals(protocol) == false) { throw new IllegalArgumentException("Invalid protocol [" + protocol + "], must be [file] or [jar]"); } - if (Strings.isEmpty(url.getHost()) == false) { + if (url.getHost().isEmpty() == false) { throw new IllegalArgumentException("URL cannot have host. Found: [" + url.getHost() + ']'); } if (url.getPort() != -1) { diff --git a/libs/core/src/main/java/org/opensearch/core/util/package-info.java b/libs/core/src/main/java/org/opensearch/core/util/package-info.java new file mode 100644 index 0000000000000..deab57b120644 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/util/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Core utility classes that need the Lucene API */ +package org.opensearch.core.util; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/AbstractObjectParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/AbstractObjectParser.java similarity index 98% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/AbstractObjectParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/AbstractObjectParser.java index ee6221a4f94b6..a0e2a54fce91c 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/AbstractObjectParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/AbstractObjectParser.java @@ -30,12 +30,12 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ObjectParser.NamedObjectParser; -import org.opensearch.common.xcontent.ObjectParser.ValueType; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ObjectParser.NamedObjectParser; +import org.opensearch.core.xcontent.ObjectParser.ValueType; import java.io.IOException; import java.util.ArrayList; @@ -46,6 +46,8 @@ /** * Superclass for {@link ObjectParser} and {@link ConstructingObjectParser}. Defines most of the "declare" methods so they can be shared. + * + * @opensearch.internal */ public abstract class AbstractObjectParser { diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/support/AbstractXContentParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/AbstractXContentParser.java similarity index 85% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/support/AbstractXContentParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/AbstractXContentParser.java index 369e8edc82965..4efaacecd0e67 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/support/AbstractXContentParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/AbstractXContentParser.java @@ -30,17 +30,13 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent.support; +package org.opensearch.core.xcontent; import org.opensearch.common.Booleans; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.common.Numbers; import java.io.IOException; -import java.math.BigDecimal; import java.math.BigInteger; import java.nio.CharBuffer; import java.util.ArrayList; @@ -50,6 +46,11 @@ import java.util.Map; import java.util.function.Supplier; +/** + * Base class for parsing serializable content + * + * @opensearch.internal + */ public abstract class AbstractXContentParser implements XContentParser { // Currently this is not a setting that can be changed and is a policy @@ -92,6 +93,18 @@ void ensureNumberConversion(boolean coerce, long result, Class } } + void ensureNumberConversion(boolean coerce, BigInteger result, Class clazz) throws IOException { + if (!coerce) { + double fullVal = doDoubleValue(); + if (result.doubleValue() != fullVal) { + // Need to throw type IllegalArgumentException as current catch + // logic in NumberFieldMapper.parseCreateField relies on this + // for "malformed" value detection + throw new IllegalArgumentException(fullVal + " cannot be converted to " + clazz.getSimpleName() + " without data loss"); + } + } + } + @Override public boolean isBooleanValue() throws IOException { switch (currentToken()) { @@ -166,44 +179,6 @@ public int intValue(boolean coerce) throws IOException { protected abstract int doIntValue() throws IOException; - private static BigInteger LONG_MAX_VALUE_AS_BIGINTEGER = BigInteger.valueOf(Long.MAX_VALUE); - private static BigInteger LONG_MIN_VALUE_AS_BIGINTEGER = BigInteger.valueOf(Long.MIN_VALUE); - // weak bounds on the BigDecimal representation to allow for coercion - private static BigDecimal BIGDECIMAL_GREATER_THAN_LONG_MAX_VALUE = BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.ONE); - private static BigDecimal BIGDECIMAL_LESS_THAN_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE).subtract(BigDecimal.ONE); - - /** Return the long that {@code stringValue} stores or throws an exception if the - * stored value cannot be converted to a long that stores the exact same - * value and {@code coerce} is false. */ - private static long toLong(String stringValue, boolean coerce) { - try { - return Long.parseLong(stringValue); - } catch (NumberFormatException e) { - // we will try again with BigDecimal - } - - final BigInteger bigIntegerValue; - try { - final BigDecimal bigDecimalValue = new BigDecimal(stringValue); - if (bigDecimalValue.compareTo(BIGDECIMAL_GREATER_THAN_LONG_MAX_VALUE) >= 0 - || bigDecimalValue.compareTo(BIGDECIMAL_LESS_THAN_LONG_MIN_VALUE) <= 0) { - throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for a long"); - } - bigIntegerValue = coerce ? bigDecimalValue.toBigInteger() : bigDecimalValue.toBigIntegerExact(); - } catch (ArithmeticException e) { - throw new IllegalArgumentException("Value [" + stringValue + "] has a decimal part"); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("For input string: \"" + stringValue + "\""); - } - - if (bigIntegerValue.compareTo(LONG_MAX_VALUE_AS_BIGINTEGER) > 0 || bigIntegerValue.compareTo(LONG_MIN_VALUE_AS_BIGINTEGER) < 0) { - throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for a long"); - } - - assert bigIntegerValue.longValueExact() <= Long.MAX_VALUE; // asserting that no ArithmeticException is thrown - return bigIntegerValue.longValue(); - } - @Override public long longValue() throws IOException { return longValue(DEFAULT_NUMBER_COERCE_POLICY); @@ -214,7 +189,7 @@ public long longValue(boolean coerce) throws IOException { Token token = currentToken(); if (token == Token.VALUE_STRING) { checkCoerceString(coerce, Long.class); - return toLong(text(), coerce); + return Numbers.toLong(text(), coerce); } long result = doLongValue(); ensureNumberConversion(coerce, result, Long.class); @@ -257,6 +232,25 @@ public double doubleValue(boolean coerce) throws IOException { protected abstract double doDoubleValue() throws IOException; + @Override + public BigInteger bigIntegerValue() throws IOException { + return bigIntegerValue(DEFAULT_NUMBER_COERCE_POLICY); + } + + @Override + public BigInteger bigIntegerValue(boolean coerce) throws IOException { + Token token = currentToken(); + if (token == Token.VALUE_STRING) { + checkCoerceString(coerce, BigInteger.class); + return Numbers.toUnsignedLong(text(), coerce); + } + BigInteger result = doBigIntegerValue(); + ensureNumberConversion(coerce, result, BigInteger.class); + return result; + } + + protected abstract BigInteger doBigIntegerValue() throws IOException; + @Override public final String textOrNull() throws IOException { if (currentToken() == Token.VALUE_NULL) { diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ConstructingObjectParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ConstructingObjectParser.java similarity index 99% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ConstructingObjectParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ConstructingObjectParser.java index 232977b34c62c..33ef0848599f9 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ConstructingObjectParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ConstructingObjectParser.java @@ -30,11 +30,11 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ObjectParser.NamedObjectParser; -import org.opensearch.common.xcontent.ObjectParser.ValueType; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ObjectParser.NamedObjectParser; +import org.opensearch.core.xcontent.ObjectParser.ValueType; import java.io.IOException; import java.util.ArrayList; @@ -85,6 +85,8 @@ *

    * Note: if optional constructor arguments aren't specified then the number of allocations is always the worst case. *

    + * + * @opensearch.internal */ public final class ConstructingObjectParser extends AbstractObjectParser implements diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ContextParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ContextParser.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ContextParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ContextParser.java index 62bcc3a0e8f33..d50dd2e68d890 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ContextParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ContextParser.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import java.io.IOException; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/DeprecationHandler.java b/libs/core/src/main/java/org/opensearch/core/xcontent/DeprecationHandler.java similarity index 99% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/DeprecationHandler.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/DeprecationHandler.java index e30a21c2f9013..570a13ad8e093 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/DeprecationHandler.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/DeprecationHandler.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import java.util.function.Supplier; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ErrorOnUnknown.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ErrorOnUnknown.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ErrorOnUnknown.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ErrorOnUnknown.java index bff2c7a0033bb..a03265ab47114 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ErrorOnUnknown.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ErrorOnUnknown.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import java.util.ServiceLoader; @@ -38,6 +38,8 @@ * Extension point to customize the error message for unknown fields. We expect * OpenSearch to plug a fancy implementation that uses Lucene's spelling * correction infrastructure to suggest corrections. + * + * @opensearch.api */ public interface ErrorOnUnknown { /** diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/InstantiatingObjectParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/InstantiatingObjectParser.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/InstantiatingObjectParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/InstantiatingObjectParser.java index f583fb87816da..0d6e268592275 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/InstantiatingObjectParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/InstantiatingObjectParser.java @@ -30,9 +30,9 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; -import org.opensearch.common.ParseField; +import org.opensearch.core.ParseField; import java.io.IOException; import java.lang.reflect.Constructor; @@ -73,6 +73,8 @@ * PARSER.finalizeFields() * } * } + * + * @opensearch.internal */ public class InstantiatingObjectParser implements @@ -87,6 +89,11 @@ public static Builder builder(String name, Clas return new Builder<>(name, valueClass); } + /** + * Builder for the Instantiating Object Parser + * + * @opensearch.internal + **/ public static class Builder extends AbstractObjectParser { private final ConstructingObjectParser constructingObjectParser; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/support/MapXContentParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/MapXContentParser.java similarity index 96% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/support/MapXContentParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/MapXContentParser.java index b96a3f1db403b..254c340f8836f 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/support/MapXContentParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/MapXContentParser.java @@ -30,12 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent.support; - -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentLocation; -import org.opensearch.common.xcontent.XContentType; +package org.opensearch.core.xcontent; import java.io.IOException; import java.math.BigDecimal; @@ -50,7 +45,7 @@ */ public class MapXContentParser extends AbstractXContentParser { - private XContentType xContentType; + private MediaType mediaType; private TokenIterator iterator; private boolean closed; @@ -58,10 +53,10 @@ public MapXContentParser( NamedXContentRegistry xContentRegistry, DeprecationHandler deprecationHandler, Map map, - XContentType xContentType + MediaType mediaType ) { super(xContentRegistry, deprecationHandler); - this.xContentType = xContentType; + this.mediaType = mediaType; this.iterator = new MapIterator(null, null, map); } @@ -100,8 +95,17 @@ protected double doDoubleValue() throws IOException { } @Override - public XContentType contentType() { - return xContentType; + protected BigInteger doBigIntegerValue() throws IOException { + if (numberValue() instanceof BigInteger) { + return (BigInteger) numberValue(); + } else { + return BigInteger.valueOf(numberValue().longValue()); + } + } + + @Override + public MediaType contentType() { + return mediaType; } @Override diff --git a/libs/core/src/main/java/org/opensearch/core/xcontent/MediaType.java b/libs/core/src/main/java/org/opensearch/core/xcontent/MediaType.java new file mode 100644 index 0000000000000..c58b3e80d98b5 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/MediaType.java @@ -0,0 +1,128 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.xcontent; + +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.io.stream.Writeable; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Locale; + +/** + * Abstracts a Media Type and a format parameter. + * Media types are used as values on Content-Type and Accept headers + * format is an URL parameter, specifies response media type. + * + * @opensearch.api + */ +@PublicApi(since = "2.1.0") +public interface MediaType extends Writeable { + /** + * Returns a type part of a MediaType + * i.e. application for application/json + */ + String type(); + + /** + * Returns a subtype part of a MediaType. + * i.e. json for application/json + */ + String subtype(); + + /** + * Returns a corresponding format for a MediaType. i.e. json for application/json media type + * Can differ from the MediaType's subtype i.e plain/text has a subtype of text but format is txt + */ + String format(); + + /** + * returns a string representation of a media type. + */ + default String typeWithSubtype() { + return type() + "/" + subtype(); + } + + XContent xContent(); + + boolean detectedXContent(final byte[] bytes, int offset, int length); + + boolean detectedXContent(final CharSequence content, final int length); + + default String mediaType() { + return mediaTypeWithoutParameters(); + } + + String mediaTypeWithoutParameters(); + + XContentBuilder contentBuilder() throws IOException; + + XContentBuilder contentBuilder(final OutputStream os) throws IOException; + + /** + * Accepts a format string, which is most of the time is equivalent to {@link MediaType#subtype()} + * and attempts to match the value to an {@link MediaType}. + * The comparisons are done in lower case format. + * This method will return {@code null} if no match is found + */ + static MediaType fromFormat(String mediaType) { + return MediaTypeRegistry.fromFormat(mediaType); + } + + /** + * Attempts to match the given media type with the known {@link MediaType} values. This match is done in a case-insensitive manner. + * The provided media type can optionally has parameters. + * This method is suitable for parsing of the {@code Content-Type} and {@code Accept} HTTP headers. + * This method will return {@code null} if no match is found + */ + static MediaType fromMediaType(String mediaTypeHeaderValue) { + mediaTypeHeaderValue = removeVersionInMediaType(mediaTypeHeaderValue); + return MediaTypeRegistry.fromMediaType(mediaTypeHeaderValue); + } + + /** + * Clients compatible with ES 7.x might start sending media types with versioned media type + * in a form of application/vnd.elasticsearch+json;compatible-with=7. + * This has to be removed in order to be used in 7.x server. + * The same client connecting using that media type will be able to communicate with ES 8 thanks to compatible API. + * @param mediaType - a media type used on Content-Type header, might contain versioned media type. + * + * @return a media type string without + */ + private static String removeVersionInMediaType(String mediaType) { + if (mediaType != null && (mediaType = mediaType.toLowerCase(Locale.ROOT)).contains("vnd.opensearch")) { + return mediaType.replaceAll("vnd.opensearch\\+", "").replaceAll("\\s*;\\s*compatible-with=\\d+", ""); + } + return mediaType; + } +} diff --git a/libs/core/src/main/java/org/opensearch/core/xcontent/MediaTypeRegistry.java b/libs/core/src/main/java/org/opensearch/core/xcontent/MediaTypeRegistry.java new file mode 100644 index 0000000000000..bbb55204712d1 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/MediaTypeRegistry.java @@ -0,0 +1,405 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.core.xcontent; + +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.spi.MediaTypeProvider; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Parses supported internet media types + * + * @opensearch.internal + */ +public final class MediaTypeRegistry { + private static Map formatToMediaType = Map.of(); + private static Map typeWithSubtypeToMediaType = Map.of(); + + // Default mediaType singleton + private static MediaType DEFAULT_MEDIA_TYPE; + public static final int GUESS_HEADER_LENGTH = 20; + + // JSON is a core type, so we create a static instance for implementations that require JSON format (e.g., tests) + // todo we should explore moving the concrete JSON implementation from the xcontent library to core + public static final MediaType JSON; + + static { + List mediaTypes = new ArrayList<>(); + Map amt = new HashMap<>(); + for (MediaTypeProvider provider : ServiceLoader.load(MediaTypeProvider.class, MediaTypeProvider.class.getClassLoader())) { + mediaTypes.addAll(provider.getMediaTypes()); + amt = Stream.of(amt, provider.getAdditionalMediaTypes()) + .flatMap(map -> map.entrySet().stream()) + .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue)); + } + register(mediaTypes.toArray(new MediaType[0]), amt); + JSON = fromMediaType("application/json"); + setDefaultMediaType(JSON); + } + + private static void register(MediaType[] acceptedMediaTypes, Map additionalMediaTypes) { + // ensures the map is not overwritten: + Map typeMap = new HashMap<>(typeWithSubtypeToMediaType); + Map formatMap = new HashMap<>(formatToMediaType); + for (MediaType mediaType : acceptedMediaTypes) { + if (formatMap.containsKey(mediaType.format())) { + throw new IllegalArgumentException("unable to register mediaType: [" + mediaType.format() + "]. Type already exists."); + } + typeMap.put(mediaType.typeWithSubtype(), mediaType); + formatMap.put(mediaType.format(), mediaType); + } + for (Map.Entry entry : additionalMediaTypes.entrySet()) { + String typeWithSubtype = entry.getKey().toLowerCase(Locale.ROOT); + if (typeMap.containsKey(typeWithSubtype)) { + throw new IllegalArgumentException( + "unable to register mediaType: [" + + entry.getKey() + + "]. " + + "Type already exists and is mapped to: [." + + entry.getValue().format() + + "]" + ); + } + + MediaType mediaType = entry.getValue(); + typeMap.put(typeWithSubtype, mediaType); + formatMap.putIfAbsent(mediaType.format(), mediaType); // ignore if the additional type mapping already exists + } + + formatToMediaType = Map.copyOf(formatMap); + typeWithSubtypeToMediaType = Map.copyOf(typeMap); + } + + public static MediaType fromMediaType(String mediaType) { + ParsedMediaType parsedMediaType = parseMediaType(mediaType); + return parsedMediaType != null ? parsedMediaType.getMediaType() : null; + } + + public static MediaType fromFormat(String format) { + if (format == null) { + return null; + } + return formatToMediaType.get(format.toLowerCase(Locale.ROOT)); + } + + /** + * Returns a binary content builder for the provided content type. + */ + public static XContentBuilder contentBuilder(MediaType type) throws IOException { + for (var mediaType : formatToMediaType.values()) { + if (type == mediaType) { + return type.contentBuilder(); + } + } + throw new IllegalArgumentException("No matching content type for " + type); + } + + public static XContentBuilder contentBuilder(MediaType type, OutputStream outputStream) throws IOException { + for (var mediaType : formatToMediaType.values()) { + if (type == mediaType) { + return type.contentBuilder(outputStream); + } + } + throw new IllegalArgumentException("No matching content type for " + type); + } + + /** + * Guesses the content (type) based on the provided char sequence and returns the corresponding {@link XContent} + * + * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. + * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. + * This method is deprecated to prevent usages of it from spreading further without specific reasons. + */ + @Deprecated + public static MediaType xContent(final byte[] data, int offset, int length) { + MediaType type = mediaTypeFromBytes(data, offset, length); + if (type == null) { + throw new XContentParseException("Failed to derive xcontent"); + } + return type; + } + + /** + * Guesses the content type based on the provided bytes and returns the corresponding {@link XContent} + * + * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. + * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. + * This method is deprecated to prevent usages of it from spreading further without specific reasons. + */ + @Deprecated + public static MediaType xContent(byte[] data) { + return xContent(data, 0, data.length); + } + + /** + * Guesses the content (type) based on the provided char sequence and returns the corresponding {@link XContent} + * + * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. + * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. + * This method is deprecated to prevent usages of it from spreading further without specific reasons. + */ + @Deprecated + public static MediaType xContent(CharSequence content) { + MediaType type = xContentType(content); + if (type == null) { + throw new XContentParseException("Failed to derive xcontent"); + } + return type; + } + + /** + * Guesses the content type based on the provided char sequence. + * + * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. + * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. + * This method is deprecated to prevent usages of it from spreading further without specific reasons. + */ + @Deprecated + public static MediaType xContentType(CharSequence content) { + int length = content.length() < GUESS_HEADER_LENGTH ? content.length() : GUESS_HEADER_LENGTH; + if (length == 0) { + return null; + } + for (var mediaType : formatToMediaType.values()) { + if (mediaType.detectedXContent(content, length)) { + return mediaType; + } + } + + // fallback for json + for (int i = 0; i < length; i++) { + char c = content.charAt(i); + if (c == '{') { + return MediaType.fromMediaType("application/json"); + } + if (Character.isWhitespace(c) == false) { + break; + } + } + return null; + } + + /** + * Guesses the content type based on the provided input stream without consuming it. + * + * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. + * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. + * This method is deprecated to prevent usages of it from spreading further without specific reasons. + */ + @Deprecated + public static MediaType xContentType(InputStream si) throws IOException { + /* + * We need to guess the content type. To do this, we look for the first non-whitespace character and then try to guess the content + * type on the GUESS_HEADER_LENGTH bytes that follow. We do this in a way that does not modify the initial read position in the + * underlying input stream. This is why the input stream must support mark/reset and why we repeatedly mark the read position and + * reset. + */ + if (si.markSupported() == false) { + throw new IllegalArgumentException("Cannot guess the xcontent type without mark/reset support on " + si.getClass()); + } + si.mark(Integer.MAX_VALUE); + try { + // scan until we find the first non-whitespace character or the end of the stream + int current; + do { + current = si.read(); + if (current == -1) { + return null; + } + } while (Character.isWhitespace((char) current)); + // now guess the content type off the next GUESS_HEADER_LENGTH bytes including the current byte + final byte[] firstBytes = new byte[GUESS_HEADER_LENGTH]; + firstBytes[0] = (byte) current; + int read = 1; + while (read < GUESS_HEADER_LENGTH) { + final int r = si.read(firstBytes, read, GUESS_HEADER_LENGTH - read); + if (r == -1) { + break; + } + read += r; + } + return mediaTypeFromBytes(firstBytes, 0, read); + } finally { + si.reset(); + } + + } + + /** + * Guesses the content type based on the provided bytes. + * + * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. + * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. + * This method is deprecated to prevent usages of it from spreading further without specific reasons. + */ + @Deprecated + public static MediaType xContentType(BytesReference bytes) { + if (bytes instanceof BytesArray) { + final BytesArray array = (BytesArray) bytes; + return mediaTypeFromBytes(array.array(), array.offset(), array.length()); + } + try { + final InputStream inputStream = bytes.streamInput(); + assert inputStream.markSupported(); + return xContentType(inputStream); + } catch (IOException e) { + assert false : "Should not happen, we're just reading bytes from memory"; + throw new UncheckedIOException(e); + } + } + + /** + * Guesses the content type based on the provided bytes. + * + * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. + * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. + * This method is deprecated to prevent usages of it from spreading further without specific reasons. + */ + @Deprecated + public static MediaType mediaTypeFromBytes(final byte[] data, int offset, int length) { + int totalLength = data.length; + if (totalLength == 0 || length == 0) { + return null; + } else if ((offset + length) > totalLength) { + return null; + } + for (var mediaType : formatToMediaType.values()) { + if (mediaType.detectedXContent(data, offset, length)) { + return mediaType; + } + } + + // a last chance for JSON + int jsonStart = 0; + // JSON may be preceded by UTF-8 BOM + if (length > 3 && data[offset] == (byte) 0xEF && data[offset + 1] == (byte) 0xBB && data[offset + 2] == (byte) 0xBF) { + jsonStart = 3; + } + + for (int i = jsonStart; i < length; i++) { + byte b = data[offset + i]; + if (b == '{') { + return fromMediaType("application/json"); + } + if (Character.isWhitespace(b) == false) { + break; + } + } + + return null; + } + + /** + * parsing media type that follows https://tools.ietf.org/html/rfc7231#section-3.1.1.1 + * @param headerValue a header value from Accept or Content-Type + * @return a parsed media-type + */ + public static ParsedMediaType parseMediaType(String headerValue) { + if (headerValue != null) { + String[] split = headerValue.toLowerCase(Locale.ROOT).split(";"); + + String[] typeSubtype = split[0].trim().split("/"); + if (typeSubtype.length == 2) { + String type = typeSubtype[0]; + String subtype = typeSubtype[1]; + MediaType mediaType = typeWithSubtypeToMediaType.get(type + "/" + subtype); + if (mediaType != null) { + Map parameters = new HashMap<>(); + for (int i = 1; i < split.length; i++) { + // spaces are allowed between parameters, but not between '=' sign + String[] keyValueParam = split[i].trim().split("="); + if (keyValueParam.length != 2 || hasSpaces(keyValueParam[0]) || hasSpaces(keyValueParam[1])) { + return null; + } + parameters.put(keyValueParam[0], keyValueParam[1]); + } + return new ParsedMediaType(mediaType, parameters); + } + } + + } + return null; + } + + private static boolean hasSpaces(String s) { + return s.trim().equals(s) == false; + } + + /** + * A media type object that contains all the information provided on a Content-Type or Accept header + */ + public static class ParsedMediaType { + private final Map parameters; + private final MediaType mediaType; + + public ParsedMediaType(MediaType mediaType, Map parameters) { + this.parameters = parameters; + this.mediaType = mediaType; + } + + public MediaType getMediaType() { + return mediaType; + } + + public Map getParameters() { + return parameters; + } + } + + private static void setDefaultMediaType(final MediaType mediaType) { + if (DEFAULT_MEDIA_TYPE != null) { + throw new RuntimeException( + "unable to reset the default media type from current default [" + DEFAULT_MEDIA_TYPE + "] to [" + mediaType + "]" + ); + } else { + DEFAULT_MEDIA_TYPE = mediaType; + } + } + + public static MediaType getDefaultMediaType() { + return DEFAULT_MEDIA_TYPE; + } +} diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/NamedObjectNotFoundException.java b/libs/core/src/main/java/org/opensearch/core/xcontent/NamedObjectNotFoundException.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/NamedObjectNotFoundException.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/NamedObjectNotFoundException.java index 7afeccb44bdd4..b9e93d886daf5 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/NamedObjectNotFoundException.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/NamedObjectNotFoundException.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; /** * Thrown when {@link NamedXContentRegistry} cannot locate a named object to diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/NamedXContentRegistry.java b/libs/core/src/main/java/org/opensearch/core/xcontent/NamedXContentRegistry.java similarity index 96% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/NamedXContentRegistry.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/NamedXContentRegistry.java index a3195debf588c..9d876825c5196 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/NamedXContentRegistry.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/NamedXContentRegistry.java @@ -30,10 +30,11 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.ParseField; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.ParseField; import java.io.IOException; import java.util.ArrayList; @@ -46,6 +47,12 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; +/** + * Main registry for serializable content (e.g., field mappers, aggregations) + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") public class NamedXContentRegistry { /** * The empty {@link NamedXContentRegistry} for use when you are sure that you aren't going to call @@ -59,6 +66,7 @@ public class NamedXContentRegistry { /** * An entry in the {@linkplain NamedXContentRegistry} containing the name of the object and the parser that can parse it. */ + @PublicApi(since = "1.0.0") public static class Entry { /** The class that this entry can read. */ public final Class categoryClass; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ObjectParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ObjectParser.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ObjectParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ObjectParser.java index 71c2dec930e4a..365b36c755dd2 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ObjectParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ObjectParser.java @@ -29,10 +29,10 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; +import org.opensearch.core.ParseField; import java.io.IOException; import java.lang.reflect.Array; @@ -52,13 +52,13 @@ import java.util.function.Supplier; import static java.util.Objects.requireNonNull; -import static org.opensearch.common.xcontent.XContentParser.Token.START_ARRAY; -import static org.opensearch.common.xcontent.XContentParser.Token.START_OBJECT; -import static org.opensearch.common.xcontent.XContentParser.Token.VALUE_BOOLEAN; -import static org.opensearch.common.xcontent.XContentParser.Token.VALUE_EMBEDDED_OBJECT; -import static org.opensearch.common.xcontent.XContentParser.Token.VALUE_NULL; -import static org.opensearch.common.xcontent.XContentParser.Token.VALUE_NUMBER; -import static org.opensearch.common.xcontent.XContentParser.Token.VALUE_STRING; +import static org.opensearch.core.xcontent.XContentParser.Token.START_ARRAY; +import static org.opensearch.core.xcontent.XContentParser.Token.START_OBJECT; +import static org.opensearch.core.xcontent.XContentParser.Token.VALUE_BOOLEAN; +import static org.opensearch.core.xcontent.XContentParser.Token.VALUE_EMBEDDED_OBJECT; +import static org.opensearch.core.xcontent.XContentParser.Token.VALUE_NULL; +import static org.opensearch.core.xcontent.XContentParser.Token.VALUE_NUMBER; +import static org.opensearch.core.xcontent.XContentParser.Token.VALUE_STRING; /** * A declarative, stateless parser that turns XContent into setter calls. A single parser should be defined for each object being parsed, @@ -82,6 +82,8 @@ * } * It's highly recommended to use the high level declare methods like {@link #declareString(BiConsumer, ParseField)} instead of * {@link #declareField} which can be used to implement exceptional parsing operations not covered by the high level methods. + * + * @opensearch.internal */ public final class ObjectParser extends AbstractObjectParser implements @@ -386,6 +388,11 @@ public Value apply(XContentParser parser, Context context) { } } + /** + * Main parser interface + * + * @opensearch.internal + */ public interface Parser { void parse(XContentParser parser, Value value, Context context) throws IOException; } @@ -683,6 +690,11 @@ public String toString() { } } + /** + * Supported Value Types for Parsable Objects + * + * @opensearch.internal + */ public enum ValueType { STRING(VALUE_STRING), STRING_OR_NULL(VALUE_STRING, VALUE_NULL), diff --git a/server/src/main/java/org/opensearch/common/xcontent/ObjectParserHelper.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ObjectParserHelper.java similarity index 85% rename from server/src/main/java/org/opensearch/common/xcontent/ObjectParserHelper.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ObjectParserHelper.java index c833fd5cedebf..b29ca9dea56c0 100644 --- a/server/src/main/java/org/opensearch/common/xcontent/ObjectParserHelper.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ObjectParserHelper.java @@ -30,13 +30,12 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.ParseField; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ObjectParser.ValueType; -import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.ObjectParser.ValueType; import java.io.IOException; import java.util.function.BiConsumer; @@ -44,6 +43,8 @@ /** * This class provides helpers for {@link ObjectParser} that allow dealing with * classes outside of the xcontent dependencies. + * + * @opensearch.internal */ public final class ObjectParserHelper { @@ -56,7 +57,7 @@ public void declareRawObject( final ParseField field ) { final CheckedFunction bytesParser = p -> { - try (XContentBuilder builder = JsonXContent.contentBuilder()) { + try (XContentBuilder builder = MediaTypeRegistry.JSON.contentBuilder()) { builder.copyCurrentStructure(p); return BytesReference.bytes(builder); } diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ObjectPath.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ObjectPath.java similarity index 98% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ObjectPath.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ObjectPath.java index d18fff26046a3..b1034eb66eb58 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ObjectPath.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ObjectPath.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import java.lang.reflect.Array; import java.util.List; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ParserConstructor.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ParserConstructor.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ParserConstructor.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ParserConstructor.java index f5309eeab92b7..b88cc8bd2b175 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ParserConstructor.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ParserConstructor.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContent.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ToXContent.java similarity index 92% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContent.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ToXContent.java index 1b97cf11f18da..90dd0cbfb9a1a 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContent.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ToXContent.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import org.opensearch.common.Booleans; @@ -41,9 +41,16 @@ * An interface allowing to transfer an object to "XContent" using an {@link XContentBuilder}. * The output may or may not be a value object. Objects implementing {@link ToXContentObject} output a valid value * but those that don't may or may not require emitting a startObject and an endObject. + * + * @opensearch.internal */ public interface ToXContent { + /** + * Base parameters class + * + * @opensearch.internal + */ interface Params { String param(String key); @@ -77,6 +84,11 @@ public Boolean paramAsBoolean(String key, Boolean defaultValue) { }; + /** + * Mapped parameter base class + * + * @opensearch.internal + */ class MapParams implements Params { private final Map params; @@ -110,6 +122,11 @@ public Boolean paramAsBoolean(String key, Boolean defaultValue) { } } + /** + * Delegates mapped parameters to base Params class + * + * @opensearch.internal + */ class DelegatingMapParams extends MapParams { private final Params delegate; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContentFragment.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ToXContentFragment.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContentFragment.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ToXContentFragment.java index d47857c5755a5..bb3b447f54984 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContentFragment.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ToXContentFragment.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; /** * An interface allowing to transfer an object to "XContent" using an diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContentObject.java b/libs/core/src/main/java/org/opensearch/core/xcontent/ToXContentObject.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContentObject.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/ToXContentObject.java index b2c24a8fc4131..d9a0e795cd74d 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/ToXContentObject.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/ToXContentObject.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; /** * An interface allowing to transfer an object to "XContent" using an diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContent.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContent.java similarity index 98% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/XContent.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContent.java index c8a8aa1a0cfa3..dbc0041af42b5 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContent.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContent.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import java.io.IOException; import java.io.InputStream; @@ -46,7 +46,7 @@ public interface XContent { /** * The type this content handles and produces. */ - XContentType type(); + MediaType mediaType(); byte streamSeparator(); diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentBuilder.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentBuilder.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentBuilder.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContentBuilder.java index 7086cdb0fda83..a38bdd049ee88 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentBuilder.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentBuilder.java @@ -30,7 +30,10 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; + +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.common.bytes.BytesReference; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -59,6 +62,7 @@ /** * A utility to build XContent (ie json). */ +@PublicApi(since = "1.0.0") public final class XContentBuilder implements Closeable, Flushable { /** @@ -151,6 +155,19 @@ public static XContentBuilder builder(XContent xContent, Set includes, S DATE_TRANSFORMERS = Collections.unmodifiableMap(dateTransformers); } + /** + * Returns a string representation of the builder (only applicable for text based xcontent). + */ + @Override + public String toString() { + return BytesReference.bytes(this).utf8ToString(); + } + + /** + * The writer interface for the serializable content builder + * + * @opensearch.internal + */ @FunctionalInterface public interface Writer { void write(XContentBuilder builder, Object value) throws IOException; @@ -213,7 +230,7 @@ public XContentBuilder(XContent xContent, OutputStream os, Set includes, this.generator = xContent.createGenerator(bos, includes, excludes); } - public XContentType contentType() { + public MediaType contentType() { return generator.contentType(); } @@ -985,7 +1002,7 @@ public XContentBuilder percentageField(String rawFieldName, String readableField /** * Writes a raw field with the value taken from the bytes in the stream - * @deprecated use {@link #rawField(String, InputStream, XContentType)} to avoid content type auto-detection + * @deprecated use {@link #rawField(String, InputStream, MediaType)} to avoid content type auto-detection */ @Deprecated public XContentBuilder rawField(String name, InputStream value) throws IOException { @@ -996,15 +1013,15 @@ public XContentBuilder rawField(String name, InputStream value) throws IOExcepti /** * Writes a raw field with the value taken from the bytes in the stream */ - public XContentBuilder rawField(String name, InputStream value, XContentType contentType) throws IOException { - generator.writeRawField(name, value, contentType); + public XContentBuilder rawField(String name, InputStream value, MediaType mediaType) throws IOException { + generator.writeRawField(name, value, mediaType); return this; } /** * Writes a value with the source coming directly from the bytes in the stream */ - public XContentBuilder rawValue(InputStream stream, XContentType contentType) throws IOException { + public XContentBuilder rawValue(InputStream stream, MediaType contentType) throws IOException { generator.writeRawValue(stream, contentType); return this; } @@ -1032,11 +1049,11 @@ public XContentGenerator generator() { return this.generator; } - static void ensureNameNotNull(String name) { + public static void ensureNameNotNull(String name) { ensureNotNull(name, "Field name cannot be null"); } - static void ensureNotNull(Object value, String message) { + public static void ensureNotNull(Object value, String message) { if (value == null) { throw new IllegalArgumentException(message); } diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentBuilderExtension.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentBuilderExtension.java similarity index 98% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentBuilderExtension.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContentBuilderExtension.java index 627a0d52974bf..0535da1a584be 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentBuilderExtension.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentBuilderExtension.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import java.util.Map; import java.util.function.Function; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentGenerator.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentGenerator.java similarity index 93% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentGenerator.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContentGenerator.java index b0dacb3826be3..ac0e374f301b7 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentGenerator.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentGenerator.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import java.io.Closeable; import java.io.Flushable; @@ -39,9 +39,14 @@ import java.math.BigDecimal; import java.math.BigInteger; +/** + * Base class to generate serializable content + * + * @opensearch.internal + */ public interface XContentGenerator extends Closeable, Flushable { - XContentType contentType(); + MediaType contentType(); void usePrettyPrint(); @@ -109,7 +114,7 @@ public interface XContentGenerator extends Closeable, Flushable { /** * Writes a raw field with the value taken from the bytes in the stream - * @deprecated use {@link #writeRawField(String, InputStream, XContentType)} to avoid content type auto-detection + * @deprecated use {@link #writeRawField(String, InputStream, MediaType)} to avoid content type auto-detection */ @Deprecated void writeRawField(String name, InputStream value) throws IOException; @@ -117,12 +122,12 @@ public interface XContentGenerator extends Closeable, Flushable { /** * Writes a raw field with the value taken from the bytes in the stream */ - void writeRawField(String name, InputStream value, XContentType xContentType) throws IOException; + void writeRawField(String name, InputStream value, MediaType mediaType) throws IOException; /** * Writes a raw value taken from the bytes in the stream */ - void writeRawValue(InputStream value, XContentType xContentType) throws IOException; + void writeRawValue(InputStream value, MediaType mediaType) throws IOException; void copyCurrentStructure(XContentParser parser) throws IOException; diff --git a/libs/core/src/main/java/org/opensearch/core/xcontent/XContentHelper.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentHelper.java new file mode 100644 index 0000000000000..a99a12273a6f0 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentHelper.java @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.xcontent; + +import org.opensearch.core.common.bytes.BytesReference; + +import java.io.IOException; + +/** + * Core XContent Helper Utilities + * + * @opensearch.internal + */ +public final class XContentHelper { + // no instance + private XContentHelper() {} + + /** + * Returns the bytes that represent the XContent output of the provided {@link ToXContent} object, using the provided + * {@link MediaType}. Wraps the output into a new anonymous object according to the value returned + * by the {@link ToXContent#isFragment()} method returns. + */ + @Deprecated + public static BytesReference toXContent(ToXContent toXContent, MediaType mediaType, boolean humanReadable) throws IOException { + return toXContent(toXContent, mediaType, ToXContent.EMPTY_PARAMS, humanReadable); + } + + /** + * Returns the bytes that represent the XContent output of the provided {@link ToXContent} object, using the provided + * {@link MediaType}. Wraps the output into a new anonymous object according to the value returned + * by the {@link ToXContent#isFragment()} method returns. + */ + public static BytesReference toXContent(ToXContent toXContent, MediaType mediaType, ToXContent.Params params, boolean humanReadable) + throws IOException { + try (XContentBuilder builder = XContentBuilder.builder(mediaType.xContent())) { + builder.humanReadable(humanReadable); + if (toXContent.isFragment()) { + builder.startObject(); + } + toXContent.toXContent(builder, params); + if (toXContent.isFragment()) { + builder.endObject(); + } + return BytesReference.bytes(builder); + } + } +} diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentLocation.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentLocation.java similarity index 96% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentLocation.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContentLocation.java index a6fa6000e6f3d..b17f89993eaf5 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentLocation.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentLocation.java @@ -30,13 +30,15 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; /** * Simple data structure representing the line and column number of a position * in some XContent e.g. JSON. Locations are typically used to communicate the * position of a parsing error to end users and consequently have line and * column numbers starting from 1. + * + * @opensearch.internal */ public final class XContentLocation { diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentParseException.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentParseException.java similarity index 98% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentParseException.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContentParseException.java index a022dd58a2cfc..7eb4843df514f 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentParseException.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentParseException.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import org.opensearch.common.Nullable; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentParser.java similarity index 92% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContentParser.java index 4c924cd8a0bfb..a2f16209a5b7f 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentParser.java @@ -30,30 +30,38 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import org.opensearch.common.CheckedFunction; import java.io.Closeable; import java.io.IOException; +import java.math.BigInteger; import java.nio.CharBuffer; import java.util.List; import java.util.Map; import java.util.function.Supplier; /** - * Interface for pull - parsing {@link XContent} see {@link XContentType} for supported types. + * Interface for pull - parsing {@link XContent} see {@code XContentType} for supported types. * * To obtain an instance of this class use the following pattern: * *
    - *     XContentType xContentType = XContentType.JSON;
    - *     XContentParser parser = xContentType.xContent().createParser(
    + *     MediaType mediaType = MediaTypeRegistry.JSON;
    + *     XContentParser parser = mediaType.xContent().createParser(
      *          NamedXContentRegistry.EMPTY, ParserField."{\"key\" : \"value\"}");
      * 
    + * + * @opensearch.internal */ public interface XContentParser extends Closeable { + /** + * Supported serializable tokens + * + * @opensearch.internal + */ enum Token { START_OBJECT { @Override @@ -129,6 +137,11 @@ public boolean isValue() { public abstract boolean isValue(); } + /** + * Supported numeric types + * + * @opensearch.internal + */ enum NumberType { INT, BIG_INTEGER, @@ -138,7 +151,7 @@ enum NumberType { BIG_DECIMAL } - XContentType contentType(); + MediaType contentType(); Token nextToken() throws IOException; @@ -218,6 +231,8 @@ Map map(Supplier> mapFactory, CheckedFunction Map map(Supplier> mapFactory, CheckedFunctiontrue or false) or one of "false", "true". */ diff --git a/server/src/main/java/org/opensearch/common/xcontent/XContentParserUtils.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentParserUtils.java similarity index 96% rename from server/src/main/java/org/opensearch/common/xcontent/XContentParserUtils.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContentParserUtils.java index 822210ffc1898..13e2f6a695d1b 100644 --- a/server/src/main/java/org/opensearch/common/xcontent/XContentParserUtils.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentParserUtils.java @@ -30,12 +30,12 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; -import org.opensearch.common.ParsingException; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.XContentParser.Token; import java.io.IOException; import java.util.Locale; @@ -44,6 +44,8 @@ /** * A set of static methods to get {@link Token} from {@link XContentParser} * while checking for their types and throw {@link ParsingException} if needed. + * + * @opensearch.internal */ public final class XContentParserUtils { diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentSubParser.java b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentSubParser.java similarity index 94% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentSubParser.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/XContentSubParser.java index 62d7661892e2f..d1cdda4aeb8be 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentSubParser.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/XContentSubParser.java @@ -30,11 +30,12 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent; +package org.opensearch.core.xcontent; import org.opensearch.common.CheckedFunction; import java.io.IOException; +import java.math.BigInteger; import java.nio.CharBuffer; import java.util.List; import java.util.Map; @@ -62,7 +63,7 @@ public XContentSubParser(XContentParser parser) { } @Override - public XContentType contentType() { + public MediaType contentType() { return parser.contentType(); } @@ -219,7 +220,12 @@ public float floatValue(boolean coerce) throws IOException { @Override public double doubleValue(boolean coerce) throws IOException { - return parser.doubleValue(); + return parser.doubleValue(coerce); + } + + @Override + public BigInteger bigIntegerValue(boolean coerce) throws IOException { + return parser.bigIntegerValue(coerce); } @Override @@ -247,6 +253,11 @@ public double doubleValue() throws IOException { return parser.doubleValue(); } + @Override + public BigInteger bigIntegerValue() throws IOException { + return parser.bigIntegerValue(); + } + @Override public boolean isBooleanValue() throws IOException { return parser.isBooleanValue(); diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/support/filtering/FilterPath.java b/libs/core/src/main/java/org/opensearch/core/xcontent/filtering/FilterPath.java similarity index 97% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/support/filtering/FilterPath.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/filtering/FilterPath.java index be7778097b45b..2fa49233a4480 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/support/filtering/FilterPath.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/filtering/FilterPath.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent.support.filtering; +package org.opensearch.core.xcontent.filtering; import org.opensearch.common.Glob; @@ -38,6 +38,11 @@ import java.util.List; import java.util.Set; +/** + * Filters a content path + * + * @opensearch.internal + */ public class FilterPath { static final FilterPath EMPTY = new FilterPath(); diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/support/filtering/FilterPathBasedFilter.java b/libs/core/src/main/java/org/opensearch/core/xcontent/filtering/FilterPathBasedFilter.java similarity index 96% rename from libs/x-content/src/main/java/org/opensearch/common/xcontent/support/filtering/FilterPathBasedFilter.java rename to libs/core/src/main/java/org/opensearch/core/xcontent/filtering/FilterPathBasedFilter.java index 0463caaa93118..fedfa154f33cb 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/support/filtering/FilterPathBasedFilter.java +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/filtering/FilterPathBasedFilter.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent.support.filtering; +package org.opensearch.core.xcontent.filtering; import com.fasterxml.jackson.core.filter.TokenFilter; @@ -38,6 +38,11 @@ import java.util.List; import java.util.Set; +/** + * Concrete filter for a content path + * + * @opensearch.internal + */ public class FilterPathBasedFilter extends TokenFilter { /** diff --git a/libs/core/src/main/java/org/opensearch/core/xcontent/filtering/package-info.java b/libs/core/src/main/java/org/opensearch/core/xcontent/filtering/package-info.java new file mode 100644 index 0000000000000..934cb6499f65a --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/filtering/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Base XContent Filtering Classes */ +package org.opensearch.core.xcontent.filtering; diff --git a/libs/core/src/main/java/org/opensearch/core/xcontent/package-info.java b/libs/core/src/main/java/org/opensearch/core/xcontent/package-info.java new file mode 100644 index 0000000000000..1902e0f175456 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Base XContent Core Classes */ +package org.opensearch.core.xcontent; diff --git a/libs/core/src/main/java/org/opensearch/core/xcontent/spi/MediaTypeProvider.java b/libs/core/src/main/java/org/opensearch/core/xcontent/spi/MediaTypeProvider.java new file mode 100644 index 0000000000000..eeaadc1698df6 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/spi/MediaTypeProvider.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.xcontent.spi; + +import org.opensearch.core.xcontent.MediaType; + +import java.util.List; +import java.util.Map; + +/** + * Service Provider Interface for plugins, modules, extensions providing + * their own Media Types + * + * @opensearch.experimental + * @opensearch.api + */ +public interface MediaTypeProvider { + /** Extensions that implement their own concrete {@link MediaType}s provide them through this interface method */ + List getMediaTypes(); + + /** Extensions that implement additional {@link MediaType} aliases provide them through this interface method */ + Map getAdditionalMediaTypes(); +} diff --git a/libs/core/src/main/java/org/opensearch/core/xcontent/spi/package-info.java b/libs/core/src/main/java/org/opensearch/core/xcontent/spi/package-info.java new file mode 100644 index 0000000000000..67ccd981dafa8 --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/core/xcontent/spi/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Service Provider Interface for extensible media types */ +package org.opensearch.core.xcontent.spi; diff --git a/libs/core/src/main/java/org/opensearch/package-info.java b/libs/core/src/main/java/org/opensearch/package-info.java new file mode 100644 index 0000000000000..894660d5ad0cb --- /dev/null +++ b/libs/core/src/main/java/org/opensearch/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** OpenSearch Core Library */ +package org.opensearch; diff --git a/libs/core/src/main/java11/org/opensearch/common/collect/List.java b/libs/core/src/main/java11/org/opensearch/common/collect/List.java deleted file mode 100644 index 56216d6bbafe2..0000000000000 --- a/libs/core/src/main/java11/org/opensearch/common/collect/List.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -import java.util.Collection; - -public class List { - - /** - * Delegates to the Java9 {@code List.of()} method. - * - * @param the {@code List}'s element type - * @return an empty {@code List} - */ - public static java.util.List of() { - return java.util.List.of(); - } - - /** - * Delegates to the Java9 {@code List.of()} method. - * - * @param the {@code List}'s element type - * @param e1 the single element - * @return a {@code List} containing the specified element - */ - public static java.util.List of(T e1) { - return java.util.List.of(e1); - } - - /** - * Delegates to the Java9 {@code List.of()} method. - * - * @param the {@code List}'s element type - * @param e1 the single element - * @return a {@code List} containing the specified element - */ - public static java.util.List of(T e1, T e2) { - return java.util.List.of(e1, e2); - } - - /** - * Delegates to the Java9 {@code List.of()} method. - * - * @param entries the elements to be contained in the list - * @param the {@code List}'s element type - * @return an unmodifiable list containing the specified elements. - */ - @SafeVarargs - @SuppressWarnings("varargs") - public static java.util.List of(T... entries) { - return java.util.List.of(entries); - } - - /** - * Delegates to the Java9 {@code List.copyOf()} method. - * - * @param the {@code List}'s element type - * @param coll a {@code Collection} from which elements are drawn, must be non-null - * @return a {@code List} containing the elements of the given {@code Collection} - */ - public static java.util.List copyOf(Collection coll) { - return java.util.List.copyOf(coll); - } -} diff --git a/libs/core/src/main/java11/org/opensearch/common/collect/Map.java b/libs/core/src/main/java11/org/opensearch/common/collect/Map.java deleted file mode 100644 index 21de546869390..0000000000000 --- a/libs/core/src/main/java11/org/opensearch/common/collect/Map.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -public class Map { - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of() { - return java.util.Map.of(); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of(K k1, V v1) { - return java.util.Map.of(k1, v1); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2) { - return java.util.Map.of(k1, v1, k2, v2); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3) { - return java.util.Map.of(k1, v1, k2, v2, k3, v3); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { - return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { - return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { - return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) { - return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8 - ) { - return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8, - K k9, - V v9 - ) { - return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9); - } - - /** - * Delegates to the Java9 {@code Map.of()} method. - */ - public static java.util.Map of( - K k1, - V v1, - K k2, - V v2, - K k3, - V v3, - K k4, - V v4, - K k5, - V v5, - K k6, - V v6, - K k7, - V v7, - K k8, - V v8, - K k9, - V v9, - K k10, - V v10 - ) { - return java.util.Map.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10); - } - - /** - * Delegates to the Java9 {@code Map.ofEntries()} method. - */ - @SafeVarargs - @SuppressWarnings("varargs") - public static java.util.Map ofEntries(java.util.Map.Entry... entries) { - return java.util.Map.ofEntries(entries); - } - - /** - * Delegates to the Java9 {@code Map.entry()} method. - */ - public static java.util.Map.Entry entry(K k, V v) { - return java.util.Map.entry(k, v); - } - - /** - * Delegates to the Java10 {@code Map.copyOf()} method. - */ - public static java.util.Map copyOf(java.util.Map map) { - return java.util.Map.copyOf(map); - } - -} diff --git a/libs/core/src/main/java11/org/opensearch/common/collect/Set.java b/libs/core/src/main/java11/org/opensearch/common/collect/Set.java deleted file mode 100644 index 0350023e4e894..0000000000000 --- a/libs/core/src/main/java11/org/opensearch/common/collect/Set.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -import java.util.Collection; - -public class Set { - - /** - * Delegates to the Java9 {@code Set.of()} method. - * - * @param the {@code Set}'s element type - * @return an empty {@code Set} - */ - public static java.util.Set of() { - return java.util.Set.of(); - } - - /** - * Delegates to the Java9 {@code Set.of()} method. - * - * @param the {@code Set}'s element type - * @param e1 the single element - * @return a {@code Set} containing the specified element - */ - public static java.util.Set of(T e1) { - return java.util.Set.of(e1); - } - - /** - * Delegates to the Java9 {@code Set.of()} method. - * - * @param the {@code Set}'s element type - * @param e1 the first element - * @param e2 the second element - * @return a {@code Set} containing the specified element - */ - public static java.util.Set of(T e1, T e2) { - return java.util.Set.of(e1, e2); - } - - /** - * Delegates to the Java9 {@code Set.of()} method. - * - * @param entries the elements to be contained in the set - * @param the {@code Set}'s element type - * @return an unmodifiable set containing the specified elements. - */ - @SafeVarargs - @SuppressWarnings("varargs") - public static java.util.Set of(T... entries) { - return java.util.Set.of(entries); - } - - /** - * Delegates to the Java10 {@code Set.copyOf} method. - * - * @param the {@code Set}'s element type - * @param coll a {@code Collection} from which elements are drawn, must be non-null - * @return a {@code Set} containing the elements of the given {@code Collection} - */ - public static java.util.Set copyOf(Collection coll) { - return java.util.Set.copyOf(coll); - } -} diff --git a/libs/core/src/main/java11/org/opensearch/core/internal/io/Streams.java b/libs/core/src/main/java11/org/opensearch/core/internal/io/Streams.java deleted file mode 100644 index 67765392b1d46..0000000000000 --- a/libs/core/src/main/java11/org/opensearch/core/internal/io/Streams.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.core.internal.io; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Simple utility methods for file and stream copying. - * All copy methods close all affected streams when done. - *

    - * Mainly for use within the framework, - * but also useful for application code. - */ -public abstract class Streams { - - private static final ThreadLocal buffer = ThreadLocal.withInitial(() -> new byte[8 * 1024]); - - /** - * Copy the contents of the given InputStream to the given OutputStream. Optionally, closes both streams when done. - * - * @param in the stream to copy from - * @param out the stream to copy to - * @param close whether to close both streams after copying - * @param buffer buffer to use for copying - * @return the number of bytes copied - * @throws IOException in case of I/O errors - */ - public static long copy(final InputStream in, final OutputStream out, byte[] buffer, boolean close) throws IOException { - Exception err = null; - try { - long byteCount = 0; - int bytesRead; - while ((bytesRead = in.read(buffer)) != -1) { - out.write(buffer, 0, bytesRead); - byteCount += bytesRead; - } - out.flush(); - return byteCount; - } catch (IOException | RuntimeException e) { - err = e; - throw e; - } finally { - if (close) { - IOUtils.close(err, in, out); - } - } - } - - /** - * @see #copy(InputStream, OutputStream, byte[], boolean) - */ - public static long copy(final InputStream in, final OutputStream out, boolean close) throws IOException { - return copy(in, out, buffer.get(), close); - } - - /** - * @see #copy(InputStream, OutputStream, byte[], boolean) - */ - public static long copy(final InputStream in, final OutputStream out, byte[] buffer) throws IOException { - return copy(in, out, buffer, true); - } - - /** - * @see #copy(InputStream, OutputStream, byte[], boolean) - */ - public static long copy(final InputStream in, final OutputStream out) throws IOException { - return copy(in, out, buffer.get(), true); - } -} diff --git a/libs/core/src/main/resources/META-INF/services/org.opensearch.core.compress.spi.CompressorProvider b/libs/core/src/main/resources/META-INF/services/org.opensearch.core.compress.spi.CompressorProvider new file mode 100644 index 0000000000000..181b802952c60 --- /dev/null +++ b/libs/core/src/main/resources/META-INF/services/org.opensearch.core.compress.spi.CompressorProvider @@ -0,0 +1,9 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# + +org.opensearch.core.compress.spi.DefaultCompressorProvider diff --git a/libs/core/src/test/java/org/opensearch/common/collect/ListTests.java b/libs/core/src/test/java/org/opensearch/common/collect/ListTests.java deleted file mode 100644 index 70841a102b783..0000000000000 --- a/libs/core/src/test/java/org/opensearch/common/collect/ListTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -import org.opensearch.test.OpenSearchTestCase; - -import java.util.Arrays; -import java.util.Collection; - -import static org.hamcrest.CoreMatchers.equalTo; - -public class ListTests extends OpenSearchTestCase { - - public void testStringListOfZero() { - final String[] strings = {}; - final java.util.List stringsList = List.of(strings); - assertThat(stringsList.size(), equalTo(strings.length)); - assertTrue(stringsList.containsAll(Arrays.asList(strings))); - expectThrows(UnsupportedOperationException.class, () -> stringsList.add("foo")); - } - - public void testStringListOfOne() { - final String[] strings = { "foo" }; - final java.util.List stringsList = List.of(strings); - assertThat(stringsList.size(), equalTo(strings.length)); - assertTrue(stringsList.containsAll(Arrays.asList(strings))); - expectThrows(UnsupportedOperationException.class, () -> stringsList.add("foo")); - } - - public void testStringListOfTwo() { - final String[] strings = { "foo", "bar" }; - final java.util.List stringsList = List.of(strings); - assertThat(stringsList.size(), equalTo(strings.length)); - assertTrue(stringsList.containsAll(Arrays.asList(strings))); - expectThrows(UnsupportedOperationException.class, () -> stringsList.add("foo")); - } - - public void testStringListOfN() { - final String[] strings = { "foo", "bar", "baz" }; - final java.util.List stringsList = List.of(strings); - assertThat(stringsList.size(), equalTo(strings.length)); - assertTrue(stringsList.containsAll(Arrays.asList(strings))); - expectThrows(UnsupportedOperationException.class, () -> stringsList.add("foo")); - } - - public void testCopyOf() { - final Collection coll = Arrays.asList("foo", "bar", "baz"); - final java.util.List copy = List.copyOf(coll); - assertThat(coll.size(), equalTo(copy.size())); - assertTrue(copy.containsAll(coll)); - expectThrows(UnsupportedOperationException.class, () -> copy.add("foo")); - } -} diff --git a/libs/core/src/test/java/org/opensearch/common/collect/MapTests.java b/libs/core/src/test/java/org/opensearch/common/collect/MapTests.java deleted file mode 100644 index 8d7ffa71df562..0000000000000 --- a/libs/core/src/test/java/org/opensearch/common/collect/MapTests.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -import org.opensearch.test.OpenSearchTestCase; - -import static org.hamcrest.CoreMatchers.equalTo; - -public class MapTests extends OpenSearchTestCase { - - private static final String[] numbers = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine" }; - - public void testMapOfZero() { - final java.util.Map map = Map.of(); - validateMapContents(map, 0); - } - - public void testMapOfOne() { - final java.util.Map map = Map.of(numbers[0], 0); - validateMapContents(map, 1); - } - - public void testMapOfTwo() { - final java.util.Map map = Map.of(numbers[0], 0, numbers[1], 1); - validateMapContents(map, 2); - } - - public void testMapOfThree() { - final java.util.Map map = Map.of(numbers[0], 0, numbers[1], 1, numbers[2], 2); - validateMapContents(map, 3); - } - - public void testMapOfFour() { - final java.util.Map map = Map.of(numbers[0], 0, numbers[1], 1, numbers[2], 2, numbers[3], 3); - validateMapContents(map, 4); - } - - public void testMapOfFive() { - final java.util.Map map = Map.of(numbers[0], 0, numbers[1], 1, numbers[2], 2, numbers[3], 3, numbers[4], 4); - validateMapContents(map, 5); - } - - public void testMapOfSix() { - final java.util.Map map = Map.of( - numbers[0], - 0, - numbers[1], - 1, - numbers[2], - 2, - numbers[3], - 3, - numbers[4], - 4, - numbers[5], - 5 - ); - validateMapContents(map, 6); - } - - public void testMapOfSeven() { - final java.util.Map map = Map.of( - numbers[0], - 0, - numbers[1], - 1, - numbers[2], - 2, - numbers[3], - 3, - numbers[4], - 4, - numbers[5], - 5, - numbers[6], - 6 - ); - validateMapContents(map, 7); - } - - public void testMapOfEight() { - final java.util.Map map = Map.of( - numbers[0], - 0, - numbers[1], - 1, - numbers[2], - 2, - numbers[3], - 3, - numbers[4], - 4, - numbers[5], - 5, - numbers[6], - 6, - numbers[7], - 7 - ); - validateMapContents(map, 8); - } - - public void testMapOfNine() { - final java.util.Map map = Map.of( - numbers[0], - 0, - numbers[1], - 1, - numbers[2], - 2, - numbers[3], - 3, - numbers[4], - 4, - numbers[5], - 5, - numbers[6], - 6, - numbers[7], - 7, - numbers[8], - 8 - ); - validateMapContents(map, 9); - } - - public void testMapOfTen() { - final java.util.Map map = Map.of( - numbers[0], - 0, - numbers[1], - 1, - numbers[2], - 2, - numbers[3], - 3, - numbers[4], - 4, - numbers[5], - 5, - numbers[6], - 6, - numbers[7], - 7, - numbers[8], - 8, - numbers[9], - 9 - ); - validateMapContents(map, 10); - } - - private static void validateMapContents(java.util.Map map, int size) { - assertThat(map.size(), equalTo(size)); - for (int k = 0; k < map.size(); k++) { - assertEquals(Integer.class, map.get(numbers[k]).getClass()); - assertThat(k, equalTo(map.get(numbers[k]))); - } - expectThrows(UnsupportedOperationException.class, () -> map.put("foo", 42)); - } - - public void testOfEntries() { - final java.util.Map map = Map.ofEntries( - Map.entry(numbers[0], 0), - Map.entry(numbers[1], 1), - Map.entry(numbers[2], 2) - ); - validateMapContents(map, 3); - } - - public void testCopyOf() { - final java.util.Map map1 = Map.of("fooK", "fooV", "barK", "barV", "bazK", "bazV"); - final java.util.Map copy = Map.copyOf(map1); - assertThat(map1.size(), equalTo(copy.size())); - for (java.util.Map.Entry entry : map1.entrySet()) { - assertEquals(entry.getValue(), copy.get(entry.getKey())); - } - expectThrows(UnsupportedOperationException.class, () -> copy.put("foo", "bar")); - } -} diff --git a/libs/core/src/test/java/org/opensearch/common/collect/SetTests.java b/libs/core/src/test/java/org/opensearch/common/collect/SetTests.java deleted file mode 100644 index 1b7f29ff0d6f1..0000000000000 --- a/libs/core/src/test/java/org/opensearch/common/collect/SetTests.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common.collect; - -import org.opensearch.test.OpenSearchTestCase; - -import java.util.Arrays; -import java.util.Collection; - -import static org.hamcrest.CoreMatchers.equalTo; - -public class SetTests extends OpenSearchTestCase { - - public void testStringSetOfZero() { - final String[] strings = {}; - final java.util.Set stringsSet = Set.of(strings); - assertThat(stringsSet.size(), equalTo(strings.length)); - assertTrue(stringsSet.containsAll(Arrays.asList(strings))); - expectThrows(UnsupportedOperationException.class, () -> stringsSet.add("foo")); - } - - public void testStringSetOfOne() { - final String[] strings = { "foo" }; - final java.util.Set stringsSet = Set.of(strings); - assertThat(stringsSet.size(), equalTo(strings.length)); - assertTrue(stringsSet.containsAll(Arrays.asList(strings))); - expectThrows(UnsupportedOperationException.class, () -> stringsSet.add("foo")); - } - - public void testStringSetOfTwo() { - final String[] strings = { "foo", "bar" }; - final java.util.Set stringsSet = Set.of(strings); - assertThat(stringsSet.size(), equalTo(strings.length)); - assertTrue(stringsSet.containsAll(Arrays.asList(strings))); - expectThrows(UnsupportedOperationException.class, () -> stringsSet.add("foo")); - } - - public void testStringSetOfN() { - final String[] strings = { "foo", "bar", "baz" }; - final java.util.Set stringsSet = Set.of(strings); - assertThat(stringsSet.size(), equalTo(strings.length)); - assertTrue(stringsSet.containsAll(Arrays.asList(strings))); - expectThrows(UnsupportedOperationException.class, () -> stringsSet.add("foo")); - } - - public void testCopyOf() { - final Collection coll = Arrays.asList("foo", "bar", "baz"); - final java.util.Set copy = Set.copyOf(coll); - assertThat(coll.size(), equalTo(copy.size())); - assertTrue(copy.containsAll(coll)); - expectThrows(UnsupportedOperationException.class, () -> copy.add("foo")); - } -} diff --git a/server/src/test/java/org/opensearch/action/ActionListenerTests.java b/libs/core/src/test/java/org/opensearch/core/action/ActionListenerTests.java similarity index 98% rename from server/src/test/java/org/opensearch/action/ActionListenerTests.java rename to libs/core/src/test/java/org/opensearch/core/action/ActionListenerTests.java index bf64b4ed8a1a5..8d2bef3db68ea 100644 --- a/server/src/test/java/org/opensearch/action/ActionListenerTests.java +++ b/libs/core/src/test/java/org/opensearch/core/action/ActionListenerTests.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.action; +package org.opensearch.core.action; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.common.CheckedConsumer; @@ -266,10 +266,9 @@ public void onFailure(Exception e) { assertThat(assertionError.getCause(), instanceOf(IllegalArgumentException.class)); assertNull(exReference.get()); - assertionError = expectThrows( - AssertionError.class, - () -> ActionListener.completeWith(listener, () -> { throw new IllegalArgumentException(); }) - ); + assertionError = expectThrows(AssertionError.class, () -> ActionListener.completeWith(listener, () -> { + throw new IllegalArgumentException(); + })); assertThat(assertionError.getCause(), instanceOf(IllegalArgumentException.class)); assertThat(exReference.get(), instanceOf(IllegalArgumentException.class)); } diff --git a/server/src/test/java/org/opensearch/action/NotifyOnceListenerTests.java b/libs/core/src/test/java/org/opensearch/core/action/NotifyOnceListenerTests.java similarity index 98% rename from server/src/test/java/org/opensearch/action/NotifyOnceListenerTests.java rename to libs/core/src/test/java/org/opensearch/core/action/NotifyOnceListenerTests.java index 79593f85c2890..948cd752a27c3 100644 --- a/server/src/test/java/org/opensearch/action/NotifyOnceListenerTests.java +++ b/libs/core/src/test/java/org/opensearch/core/action/NotifyOnceListenerTests.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.action; +package org.opensearch.core.action; import org.opensearch.test.OpenSearchTestCase; diff --git a/server/src/test/java/org/opensearch/action/support/DefaultShardOperationFailedExceptionTests.java b/libs/core/src/test/java/org/opensearch/core/action/support/DefaultShardOperationFailedExceptionTests.java similarity index 91% rename from server/src/test/java/org/opensearch/action/support/DefaultShardOperationFailedExceptionTests.java rename to libs/core/src/test/java/org/opensearch/core/action/support/DefaultShardOperationFailedExceptionTests.java index 40a5b05b1dbc6..ad52f2dc12b8f 100644 --- a/server/src/test/java/org/opensearch/action/support/DefaultShardOperationFailedExceptionTests.java +++ b/libs/core/src/test/java/org/opensearch/core/action/support/DefaultShardOperationFailedExceptionTests.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.action.support; +package org.opensearch.core.action.support; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.IndexFormatTooNewException; @@ -39,18 +39,19 @@ import org.apache.lucene.store.LockObtainFailedException; import org.opensearch.OpenSearchException; import org.opensearch.action.support.broadcast.BroadcastShardOperationFailedException; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchTestCase; import java.io.EOFException; @@ -92,7 +93,7 @@ public void testToXContent() throws IOException { assertEquals( "{\"shard\":-1,\"index\":null,\"status\":\"INTERNAL_SERVER_ERROR\"," + "\"reason\":{\"type\":\"exception\",\"reason\":\"foo\"}}", - Strings.toString(exception) + Strings.toString(MediaTypeRegistry.JSON, exception) ); } { @@ -102,7 +103,7 @@ public void testToXContent() throws IOException { assertEquals( "{\"shard\":-1,\"index\":null,\"status\":\"INTERNAL_SERVER_ERROR\",\"reason\":{\"type\":\"exception\"," + "\"reason\":\"foo\",\"caused_by\":{\"type\":\"illegal_argument_exception\",\"reason\":\"bar\"}}}", - Strings.toString(exception) + Strings.toString(MediaTypeRegistry.JSON, exception) ); } { @@ -112,7 +113,7 @@ public void testToXContent() throws IOException { assertEquals( "{\"shard\":2,\"index\":\"test\",\"status\":\"INTERNAL_SERVER_ERROR\"," + "\"reason\":{\"type\":\"illegal_state_exception\",\"reason\":\"bar\"}}", - Strings.toString(exception) + Strings.toString(MediaTypeRegistry.JSON, exception) ); } { @@ -124,7 +125,7 @@ public void testToXContent() throws IOException { assertEquals( "{\"shard\":1,\"index\":\"test\",\"status\":\"BAD_REQUEST\"," + "\"reason\":{\"type\":\"illegal_argument_exception\",\"reason\":\"foo\"}}", - Strings.toString(exception) + Strings.toString(MediaTypeRegistry.JSON, exception) ); } } diff --git a/server/src/test/java/org/opensearch/common/StringsTests.java b/libs/core/src/test/java/org/opensearch/core/common/StringsTests.java similarity index 77% rename from server/src/test/java/org/opensearch/common/StringsTests.java rename to libs/core/src/test/java/org/opensearch/core/common/StringsTests.java index 7d87fc33c12e7..b79bb6fc89f9e 100644 --- a/server/src/test/java/org/opensearch/common/StringsTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/StringsTests.java @@ -6,35 +6,12 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.common; +package org.opensearch.core.common; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.test.OpenSearchTestCase; import java.util.Collections; @@ -42,8 +19,8 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +/** tests for Strings utility class */ public class StringsTests extends OpenSearchTestCase { - public void testIsAllOrWildCardString() { assertThat(Strings.isAllOrWildcard("_all"), is(true)); assertThat(Strings.isAllOrWildcard("*"), is(true)); @@ -76,8 +53,21 @@ public void testCleanTruncate() { * circle around it with a slash through it. As in "no 'o's allowed * here. */ - assertEquals("o", Strings.cleanTruncate("o\uD83D\uDEAB", 1)); - assertEquals("", Strings.cleanTruncate("foo", 0)); + assertEquals("o", org.opensearch.core.common.Strings.cleanTruncate("o\uD83D\uDEAB", 1)); + assertEquals("", org.opensearch.core.common.Strings.cleanTruncate("foo", 0)); + } + + public void testSplitStringToSet() { + assertEquals(Strings.tokenizeByCommaToSet(null), Sets.newHashSet()); + assertEquals(Strings.tokenizeByCommaToSet(""), Sets.newHashSet()); + assertEquals(Strings.tokenizeByCommaToSet("a,b,c"), Sets.newHashSet("a", "b", "c")); + assertEquals(Strings.tokenizeByCommaToSet("a, b, c"), Sets.newHashSet("a", "b", "c")); + assertEquals(Strings.tokenizeByCommaToSet(" a , b, c "), Sets.newHashSet("a", "b", "c")); + assertEquals(Strings.tokenizeByCommaToSet("aa, bb, cc"), Sets.newHashSet("aa", "bb", "cc")); + assertEquals(Strings.tokenizeByCommaToSet(" a "), Sets.newHashSet("a")); + assertEquals(Strings.tokenizeByCommaToSet(" a "), Sets.newHashSet("a")); + assertEquals(Strings.tokenizeByCommaToSet(" aa "), Sets.newHashSet("aa")); + assertEquals(Strings.tokenizeByCommaToSet(" "), Sets.newHashSet()); } public void testToStringToXContent() { @@ -104,7 +94,7 @@ public void testToStringToXContent() { } } - String toString = Strings.toString(toXContent); + String toString = Strings.toString(MediaTypeRegistry.JSON, toXContent); if (error) { assertThat(toString, containsString("\"error\":\"error building toString out of XContent:")); assertThat(toString, containsString("\"stack_trace\":")); @@ -117,24 +107,11 @@ public void testToStringToXContent() { public void testToStringToXContentWithOrWithoutParams() { ToXContent toXContent = (builder, params) -> builder.field("color_from_param", params.param("color", "red")); // Rely on the default value of "color" param when params are not passed - assertThat(Strings.toString(toXContent), containsString("\"color_from_param\":\"red\"")); + assertThat(Strings.toString(MediaTypeRegistry.JSON, toXContent), containsString("\"color_from_param\":\"red\"")); // Pass "color" param explicitly assertThat( - Strings.toString(toXContent, new ToXContent.MapParams(Collections.singletonMap("color", "blue"))), + Strings.toString(MediaTypeRegistry.JSON, toXContent, new ToXContent.MapParams(Collections.singletonMap("color", "blue"))), containsString("\"color_from_param\":\"blue\"") ); } - - public void testSplitStringToSet() { - assertEquals(Strings.tokenizeByCommaToSet(null), Sets.newHashSet()); - assertEquals(Strings.tokenizeByCommaToSet(""), Sets.newHashSet()); - assertEquals(Strings.tokenizeByCommaToSet("a,b,c"), Sets.newHashSet("a", "b", "c")); - assertEquals(Strings.tokenizeByCommaToSet("a, b, c"), Sets.newHashSet("a", "b", "c")); - assertEquals(Strings.tokenizeByCommaToSet(" a , b, c "), Sets.newHashSet("a", "b", "c")); - assertEquals(Strings.tokenizeByCommaToSet("aa, bb, cc"), Sets.newHashSet("aa", "bb", "cc")); - assertEquals(Strings.tokenizeByCommaToSet(" a "), Sets.newHashSet("a")); - assertEquals(Strings.tokenizeByCommaToSet(" a "), Sets.newHashSet("a")); - assertEquals(Strings.tokenizeByCommaToSet(" aa "), Sets.newHashSet("aa")); - assertEquals(Strings.tokenizeByCommaToSet(" "), Sets.newHashSet()); - } } diff --git a/server/src/test/java/org/opensearch/common/io/StreamsTests.java b/libs/core/src/test/java/org/opensearch/core/common/io/StreamsTests.java similarity index 92% rename from server/src/test/java/org/opensearch/common/io/StreamsTests.java rename to libs/core/src/test/java/org/opensearch/core/common/io/StreamsTests.java index f28d1b1762ca2..f2f46daa0fab3 100644 --- a/server/src/test/java/org/opensearch/common/io/StreamsTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/io/StreamsTests.java @@ -30,11 +30,12 @@ * GitHub history for details. */ -package org.opensearch.common.io; +package org.opensearch.core.common.io; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.io.Streams; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.test.OpenSearchTestCase; import java.io.ByteArrayOutputStream; @@ -104,7 +105,7 @@ public void testLimitInputStream() throws IOException { final int limit = randomIntBetween(0, bytes.length); final BytesArray stuffArray = new BytesArray(bytes); final ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length); - final long count = org.opensearch.core.internal.io.Streams.copy(Streams.limitStream(stuffArray.streamInput(), limit), out); + final long count = org.opensearch.common.util.io.Streams.copy(Streams.limitStream(stuffArray.streamInput(), limit), out); assertEquals(limit, count); assertThat(Arrays.equals(out.toByteArray(), Arrays.copyOf(bytes, limit)), equalTo(true)); } diff --git a/server/src/test/java/org/opensearch/common/io/stream/BaseStreamTests.java b/libs/core/src/test/java/org/opensearch/core/common/io/stream/BaseStreamTests.java similarity index 95% rename from server/src/test/java/org/opensearch/common/io/stream/BaseStreamTests.java rename to libs/core/src/test/java/org/opensearch/core/common/io/stream/BaseStreamTests.java index b92e59e43e0db..5da8f58f688a5 100644 --- a/server/src/test/java/org/opensearch/common/io/stream/BaseStreamTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/io/stream/BaseStreamTests.java @@ -30,22 +30,25 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.apache.lucene.util.BytesRef; import org.opensearch.common.CheckedBiConsumer; import org.opensearch.common.CheckedConsumer; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.settings.SecureString; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.settings.SecureString; +import org.opensearch.script.JodaCompatibleZonedDateTime; import org.opensearch.test.OpenSearchTestCase; import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.IOException; import java.time.Instant; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -186,6 +189,7 @@ public void testLinkedHashMap() throws IOException { } BytesStreamOutput out = new BytesStreamOutput(); out.writeGenericValue(write); + @SuppressWarnings("unchecked") LinkedHashMap read = (LinkedHashMap) getStreamInput(out.bytes()).readGenericValue(); assertEquals(size, read.size()); int index = 0; @@ -396,6 +400,18 @@ public void testOptionalInstantSerialization() throws IOException { } } + public void testJodaDateTimeSerialization() throws IOException { + final BytesStreamOutput output = new BytesStreamOutput(); + long millis = randomIntBetween(0, Integer.MAX_VALUE); + JodaCompatibleZonedDateTime time = new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(millis), ZoneOffset.ofHours(-7)); + output.writeGenericValue(time); + + final BytesReference bytesReference = output.bytes(); + final StreamInput input = getStreamInput(bytesReference); + Object inTime = input.readGenericValue(); + assertEquals(time, inTime); + } + static final class WriteableString implements Writeable { final String string; diff --git a/server/src/test/java/org/opensearch/common/io/stream/ByteBufferStreamInputTests.java b/libs/core/src/test/java/org/opensearch/core/common/io/stream/ByteBufferStreamInputTests.java similarity index 87% rename from server/src/test/java/org/opensearch/common/io/stream/ByteBufferStreamInputTests.java rename to libs/core/src/test/java/org/opensearch/core/common/io/stream/ByteBufferStreamInputTests.java index 1061b335d715a..d50160b2b3e2d 100644 --- a/server/src/test/java/org/opensearch/common/io/stream/ByteBufferStreamInputTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/io/stream/ByteBufferStreamInputTests.java @@ -6,10 +6,10 @@ * compatible open source license. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; import java.nio.ByteBuffer; diff --git a/server/src/test/java/org/opensearch/common/io/stream/BytesReferenceStreamInputTests.java b/libs/core/src/test/java/org/opensearch/core/common/io/stream/BytesReferenceStreamInputTests.java similarity index 83% rename from server/src/test/java/org/opensearch/common/io/stream/BytesReferenceStreamInputTests.java rename to libs/core/src/test/java/org/opensearch/core/common/io/stream/BytesReferenceStreamInputTests.java index ed77c3130a32d..7eca07d9b63d5 100644 --- a/server/src/test/java/org/opensearch/common/io/stream/BytesReferenceStreamInputTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/io/stream/BytesReferenceStreamInputTests.java @@ -6,9 +6,9 @@ * compatible open source license. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; diff --git a/server/src/test/java/org/opensearch/common/io/stream/BytesStreamInputTests.java b/libs/core/src/test/java/org/opensearch/core/common/io/stream/BytesStreamInputTests.java similarity index 86% rename from server/src/test/java/org/opensearch/common/io/stream/BytesStreamInputTests.java rename to libs/core/src/test/java/org/opensearch/core/common/io/stream/BytesStreamInputTests.java index c7a47e7580b02..d61eea75538b3 100644 --- a/server/src/test/java/org/opensearch/common/io/stream/BytesStreamInputTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/io/stream/BytesStreamInputTests.java @@ -6,10 +6,10 @@ * compatible open source license. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; diff --git a/server/src/test/java/org/opensearch/common/io/stream/FilterStreamInputTests.java b/libs/core/src/test/java/org/opensearch/core/common/io/stream/FilterStreamInputTests.java similarity index 86% rename from server/src/test/java/org/opensearch/common/io/stream/FilterStreamInputTests.java rename to libs/core/src/test/java/org/opensearch/core/common/io/stream/FilterStreamInputTests.java index 3cf9dc656a8f1..a044586e095e3 100644 --- a/server/src/test/java/org/opensearch/common/io/stream/FilterStreamInputTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/io/stream/FilterStreamInputTests.java @@ -6,10 +6,10 @@ * compatible open source license. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; diff --git a/server/src/test/java/org/opensearch/common/io/stream/InputStreamStreamInputTests.java b/libs/core/src/test/java/org/opensearch/core/common/io/stream/InputStreamStreamInputTests.java similarity index 86% rename from server/src/test/java/org/opensearch/common/io/stream/InputStreamStreamInputTests.java rename to libs/core/src/test/java/org/opensearch/core/common/io/stream/InputStreamStreamInputTests.java index 6a31c21445a04..4bcd697b1c283 100644 --- a/server/src/test/java/org/opensearch/common/io/stream/InputStreamStreamInputTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/io/stream/InputStreamStreamInputTests.java @@ -6,10 +6,10 @@ * compatible open source license. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; diff --git a/server/src/test/java/org/opensearch/common/io/stream/NamedWriteableRegistryTests.java b/libs/core/src/test/java/org/opensearch/core/common/io/stream/NamedWriteableRegistryTests.java similarity index 98% rename from server/src/test/java/org/opensearch/common/io/stream/NamedWriteableRegistryTests.java rename to libs/core/src/test/java/org/opensearch/core/common/io/stream/NamedWriteableRegistryTests.java index e1c0177435005..96f2b93524067 100644 --- a/server/src/test/java/org/opensearch/common/io/stream/NamedWriteableRegistryTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/io/stream/NamedWriteableRegistryTests.java @@ -30,14 +30,14 @@ * GitHub history for details. */ -package org.opensearch.common.io.stream; +package org.opensearch.core.common.io.stream; + +import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; import java.util.Arrays; import java.util.Collections; -import org.opensearch.test.OpenSearchTestCase; - public class NamedWriteableRegistryTests extends OpenSearchTestCase { private static class DummyNamedWriteable implements NamedWriteable { diff --git a/server/src/test/java/org/opensearch/common/unit/ByteSizeUnitTests.java b/libs/core/src/test/java/org/opensearch/core/common/unit/ByteSizeUnitTests.java similarity index 90% rename from server/src/test/java/org/opensearch/common/unit/ByteSizeUnitTests.java rename to libs/core/src/test/java/org/opensearch/core/common/unit/ByteSizeUnitTests.java index 2435fc465a83b..07b9131602ac3 100644 --- a/server/src/test/java/org/opensearch/common/unit/ByteSizeUnitTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/unit/ByteSizeUnitTests.java @@ -30,20 +30,20 @@ * GitHub history for details. */ -package org.opensearch.common.unit; +package org.opensearch.core.common.unit; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; -import static org.opensearch.common.unit.ByteSizeUnit.BYTES; -import static org.opensearch.common.unit.ByteSizeUnit.GB; -import static org.opensearch.common.unit.ByteSizeUnit.KB; -import static org.opensearch.common.unit.ByteSizeUnit.MB; -import static org.opensearch.common.unit.ByteSizeUnit.PB; -import static org.opensearch.common.unit.ByteSizeUnit.TB; +import static org.opensearch.core.common.unit.ByteSizeUnit.BYTES; +import static org.opensearch.core.common.unit.ByteSizeUnit.GB; +import static org.opensearch.core.common.unit.ByteSizeUnit.KB; +import static org.opensearch.core.common.unit.ByteSizeUnit.MB; +import static org.opensearch.core.common.unit.ByteSizeUnit.PB; +import static org.opensearch.core.common.unit.ByteSizeUnit.TB; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; diff --git a/server/src/test/java/org/opensearch/common/unit/ByteSizeValueTests.java b/libs/core/src/test/java/org/opensearch/core/common/unit/ByteSizeValueTests.java similarity index 97% rename from server/src/test/java/org/opensearch/common/unit/ByteSizeValueTests.java rename to libs/core/src/test/java/org/opensearch/core/common/unit/ByteSizeValueTests.java index 7512bf2b32e24..def1694a72ba4 100644 --- a/server/src/test/java/org/opensearch/common/unit/ByteSizeValueTests.java +++ b/libs/core/src/test/java/org/opensearch/core/common/unit/ByteSizeValueTests.java @@ -30,10 +30,10 @@ * GitHub history for details. */ -package org.opensearch.common.unit; +package org.opensearch.core.common.unit; import org.opensearch.OpenSearchParseException; -import org.opensearch.common.io.stream.Writeable.Reader; +import org.opensearch.core.common.io.stream.Writeable.Reader; import org.opensearch.test.AbstractWireSerializingTestCase; import org.hamcrest.MatcherAssert; @@ -336,12 +336,10 @@ public void testParseInvalidNumber() throws IOException { public void testParseFractionalNumber() throws IOException { ByteSizeUnit unit = randomValueOtherThan(ByteSizeUnit.BYTES, () -> randomFrom(ByteSizeUnit.values())); String fractionalValue = "23.5" + unit.getSuffix(); - ByteSizeValue instance = ByteSizeValue.parseBytesSizeValue(fractionalValue, "test"); - assertEquals(fractionalValue, instance.toString()); - assertWarnings( - "Fractional bytes values are deprecated. Use non-fractional bytes values instead: [" - + fractionalValue - + "] found for setting [test]" + // test exception is thrown: fractional byte size values has been deprecated since Legacy 6.2 + OpenSearchParseException e = expectThrows( + OpenSearchParseException.class, + () -> ByteSizeValue.parseBytesSizeValue(fractionalValue, "test") ); } diff --git a/libs/core/src/test/java/org/opensearch/core/internal/io/IOUtilsTests.java b/libs/core/src/test/java/org/opensearch/core/internal/io/IOUtilsTests.java index f1c8642b73044..cd7a40b655579 100644 --- a/libs/core/src/test/java/org/opensearch/core/internal/io/IOUtilsTests.java +++ b/libs/core/src/test/java/org/opensearch/core/internal/io/IOUtilsTests.java @@ -35,12 +35,12 @@ import org.apache.lucene.util.Constants; import org.opensearch.common.CheckedConsumer; import org.opensearch.common.io.PathUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.test.OpenSearchTestCase; import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; -import java.net.URI; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; import java.nio.file.AccessDeniedException; @@ -172,10 +172,8 @@ public void runTestRm(final boolean exception) throws IOException { for (int i = 0; i < numberOfLocations; i++) { if (exception && randomBoolean()) { final Path location = createTempDir(); - final FileSystem fs = new AccessDeniedWhileDeletingFileSystem(location.getFileSystem()).getFileSystem( - URI.create("file:///") - ); - final Path wrapped = new FilterPath(location, fs); + final FilterFileSystemProvider ffsp = new AccessDeniedWhileDeletingFileSystem(location.getFileSystem()); + final Path wrapped = ffsp.wrapPath(location); locations[i] = wrapped.resolve(randomAlphaOfLength(8)); Files.createDirectory(locations[i]); locationsThrowingException.add(locations[i]); @@ -256,8 +254,8 @@ public FileChannel newFileChannel(final Path path, final Set= 0); + assumeTrue("JDK version not supported", Runtime.version().compareTo(Version.parse("11")) >= 0); assumeTrue("Platform possibly not supported", IOUtils.LINUX || IOUtils.MAC_OS_X); assertNotNull(NetUtils.getTcpKeepIdleSocketOptionOrNull()); assertNotNull(NetUtils.getTcpKeepIntervalSocketOptionOrNull()); diff --git a/libs/core/src/test/java/org/opensearch/core/util/BytesRefUtilsTests.java b/libs/core/src/test/java/org/opensearch/core/util/BytesRefUtilsTests.java new file mode 100644 index 0000000000000..421263b883f2a --- /dev/null +++ b/libs/core/src/test/java/org/opensearch/core/util/BytesRefUtilsTests.java @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.core.util; + +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.BytesRefArray; +import org.apache.lucene.util.BytesRefBuilder; +import org.apache.lucene.util.Counter; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.test.OpenSearchTestCase; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +/** + * Tests for the {@link BytesRefUtils} class + */ +public class BytesRefUtilsTests extends OpenSearchTestCase { + public void testSortAndDedupByteRefArray() { + SortedSet set = new TreeSet<>(); + final int numValues = scaledRandomIntBetween(0, 10000); + List tmpList = new ArrayList<>(); + BytesRefArray array = new BytesRefArray(Counter.newCounter()); + for (int i = 0; i < numValues; i++) { + String s = randomRealisticUnicodeOfCodepointLengthBetween(1, 100); + set.add(new BytesRef(s)); + tmpList.add(new BytesRef(s)); + array.append(new BytesRef(s)); + } + if (randomBoolean()) { + Collections.shuffle(tmpList, random()); + for (BytesRef ref : tmpList) { + array.append(ref); + } + } + int[] indices = new int[array.size()]; + for (int i = 0; i < indices.length; i++) { + indices[i] = i; + } + int numUnique = BytesRefUtils.sortAndDedup(array, indices); + assertThat(numUnique, equalTo(set.size())); + Iterator iterator = set.iterator(); + + BytesRefBuilder spare = new BytesRefBuilder(); + for (int i = 0; i < numUnique; i++) { + assertThat(iterator.hasNext(), is(true)); + assertThat(array.get(spare, indices[i]), equalTo(iterator.next())); + } + } + + public void testSortByteRefArray() { + List values = new ArrayList<>(); + final int numValues = scaledRandomIntBetween(0, 10000); + BytesRefArray array = new BytesRefArray(Counter.newCounter()); + for (int i = 0; i < numValues; i++) { + String s = randomRealisticUnicodeOfCodepointLengthBetween(1, 100); + values.add(new BytesRef(s)); + array.append(new BytesRef(s)); + } + if (randomBoolean()) { + Collections.shuffle(values, random()); + } + int[] indices = new int[array.size()]; + for (int i = 0; i < indices.length; i++) { + indices[i] = i; + } + BytesRefUtils.sort(array, indices); + Collections.sort(values); + Iterator iterator = values.iterator(); + + BytesRefBuilder spare = new BytesRefBuilder(); + for (int i = 0; i < values.size(); i++) { + assertThat(iterator.hasNext(), is(true)); + assertThat(array.get(spare, indices[i]), equalTo(iterator.next())); + } + } + + public void testBytesToLong() { + final long value = randomLong(); + final BytesReference buffer = BytesReference.fromByteBuffer(ByteBuffer.allocate(8).putLong(value).flip()); + assertThat(BytesRefUtils.bytesToLong(buffer.toBytesRef()), equalTo(value)); + } +} diff --git a/server/src/test/java/org/opensearch/common/io/FileSystemUtilsTests.java b/libs/core/src/test/java/org/opensearch/core/util/FileSystemUtilsTests.java similarity index 98% rename from server/src/test/java/org/opensearch/common/io/FileSystemUtilsTests.java rename to libs/core/src/test/java/org/opensearch/core/util/FileSystemUtilsTests.java index bb0a1c486a30c..8b29378dfde12 100644 --- a/server/src/test/java/org/opensearch/common/io/FileSystemUtilsTests.java +++ b/libs/core/src/test/java/org/opensearch/core/util/FileSystemUtilsTests.java @@ -30,10 +30,11 @@ * GitHub history for details. */ -package org.opensearch.common.io; +package org.opensearch.core.util; -import org.apache.lucene.util.Constants; import org.apache.lucene.tests.util.LuceneTestCase.SuppressFileSystems; +import org.apache.lucene.util.Constants; +import org.opensearch.common.io.PathUtils; import org.opensearch.test.OpenSearchTestCase; import org.junit.Before; diff --git a/server/src/test/java/org/opensearch/common/xcontent/support/filtering/FilterPathTests.java b/libs/core/src/test/java/org/opensearch/core/xcontent/filtering/FilterPathTests.java similarity index 99% rename from server/src/test/java/org/opensearch/common/xcontent/support/filtering/FilterPathTests.java rename to libs/core/src/test/java/org/opensearch/core/xcontent/filtering/FilterPathTests.java index b96d6904d20aa..0c5a17b70a956 100644 --- a/server/src/test/java/org/opensearch/common/xcontent/support/filtering/FilterPathTests.java +++ b/libs/core/src/test/java/org/opensearch/core/xcontent/filtering/FilterPathTests.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.common.xcontent.support.filtering; +package org.opensearch.core.xcontent.filtering; import org.opensearch.common.util.set.Sets; import org.opensearch.test.OpenSearchTestCase; diff --git a/libs/dissect/build.gradle b/libs/dissect/build.gradle index 0f0b8407e7e6b..dc98d2820ef52 100644 --- a/libs/dissect/build.gradle +++ b/libs/dissect/build.gradle @@ -34,10 +34,9 @@ dependencies { } testImplementation "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" testImplementation "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" - testImplementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + testImplementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}" } tasks.named('forbiddenApisMain').configure { replaceSignatureFiles 'jdk-signatures' } - diff --git a/libs/dissect/src/test/java/org/opensearch/dissect/DissectParserTests.java b/libs/dissect/src/test/java/org/opensearch/dissect/DissectParserTests.java index fcd0dc8e248aa..665fe63e31b1d 100644 --- a/libs/dissect/src/test/java/org/opensearch/dissect/DissectParserTests.java +++ b/libs/dissect/src/test/java/org/opensearch/dissect/DissectParserTests.java @@ -34,10 +34,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; + import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.CoreMatchers; import org.hamcrest.Matchers; -import org.mockito.internal.util.collections.Sets; import java.util.ArrayList; import java.util.Arrays; @@ -45,6 +45,8 @@ import java.util.List; import java.util.Map; +import org.mockito.internal.util.collections.Sets; + import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiAlphanumOfLengthBetween; public class DissectParserTests extends OpenSearchTestCase { diff --git a/libs/dissect/src/test/resources/specification/tests.json b/libs/dissect/src/test/resources/specification/tests.json index 1cb85ce651940..490383ba3fedb 100644 --- a/libs/dissect/src/test/resources/specification/tests.json +++ b/libs/dissect/src/test/resources/specification/tests.json @@ -360,4 +360,4 @@ "append": "" } -] \ No newline at end of file +] diff --git a/libs/geo/build.gradle b/libs/geo/build.gradle index fac5c1b84d2b0..8b81129f43b67 100644 --- a/libs/geo/build.gradle +++ b/libs/geo/build.gradle @@ -42,4 +42,3 @@ tasks.named('forbiddenApisMain').configure { // TODO: Need to decide how we want to handle for forbidden signatures with the changes to core replaceSignatureFiles 'jdk-signatures' } - diff --git a/libs/geo/src/main/java/org/opensearch/geometry/Circle.java b/libs/geo/src/main/java/org/opensearch/geometry/Circle.java index 6f8b0dc6929cc..cf1dce8966e1f 100644 --- a/libs/geo/src/main/java/org/opensearch/geometry/Circle.java +++ b/libs/geo/src/main/java/org/opensearch/geometry/Circle.java @@ -39,12 +39,19 @@ * and optional altitude in meters. */ public class Circle implements Geometry { + + /** Empty circle : x=0, y=0, z=NaN radius=-1 */ public static final Circle EMPTY = new Circle(); + /** Latitude of the center of the circle in degrees */ private final double y; + /** Longitude of the center of the circle in degrees */ private final double x; + /** Altitude of the center of the circle in meters (NaN if irrelevant) */ private final double z; + /** Radius of the circle in meters */ private final double radiusMeters; + /** Create an {@link #EMPTY} circle */ private Circle() { y = 0; x = 0; @@ -52,10 +59,23 @@ private Circle() { radiusMeters = -1; } + /** + * Create a circle with no altitude. + * @param x Longitude of the center of the circle in degrees + * @param y Latitude of the center of the circle in degrees + * @param radiusMeters Radius of the circle in meters + */ public Circle(final double x, final double y, final double radiusMeters) { this(x, y, Double.NaN, radiusMeters); } + /** + * Create a circle with altitude. + * @param x Longitude of the center of the circle in degrees + * @param y Latitude of the center of the circle in degrees + * @param z Altitude of the center of the circle in meters + * @param radiusMeters Radius of the circle in meters + */ public Circle(final double x, final double y, final double z, final double radiusMeters) { this.y = y; this.x = x; @@ -66,39 +86,68 @@ public Circle(final double x, final double y, final double z, final double radiu } } + /** + * @return The type of this geometry (always {@link ShapeType#CIRCLE}) + */ @Override public ShapeType type() { return ShapeType.CIRCLE; } + /** + * @return The y (latitude) of the center of the circle in degrees + */ public double getY() { return y; } + /** + * @return The x (longitude) of the center of the circle in degrees + */ public double getX() { return x; } + /** + * @return The radius of the circle in meters + */ public double getRadiusMeters() { return radiusMeters; } + /** + * @return The altitude of the center of the circle in meters (NaN if irrelevant) + */ public double getZ() { return z; } + /** + * @return The latitude (y) of the center of the circle in degrees + */ public double getLat() { return y; } + /** + * @return The longitude (x) of the center of the circle in degrees + */ public double getLon() { return x; } + /** + * @return The altitude (z) of the center of the circle in meters (NaN if irrelevant) + */ public double getAlt() { return z; } + /** + * Compare this circle to another circle. + * @param o The other circle + * @return True if the two circles are equal in all their properties. False if null or different. + */ @Override public boolean equals(Object o) { if (this == o) return true; @@ -111,6 +160,9 @@ public boolean equals(Object o) { return (Double.compare(circle.z, z) == 0); } + /** + * @return The hashcode of this circle. + */ @Override public int hashCode() { int result; @@ -126,11 +178,22 @@ public int hashCode() { return result; } + /** + * Visit this circle with a {@link GeometryVisitor}. + * @param visitor The visitor + * @param The return type of the visitor + * @param The exception type of the visitor + * @return The result of the visitor + * @throws E The exception thrown by the visitor + */ @Override public T visit(GeometryVisitor visitor) throws E { return visitor.visit(this); } + /** + * @return True if this circle is empty (radius less than 0) + */ @Override public boolean isEmpty() { return radiusMeters < 0; @@ -141,6 +204,9 @@ public String toString() { return WellKnownText.INSTANCE.toWKT(this); } + /** + * @return True if this circle has an altitude. False if NaN. + */ @Override public boolean hasZ() { return Double.isNaN(z) == false; diff --git a/libs/geo/src/main/java/org/opensearch/geometry/GeometryCollection.java b/libs/geo/src/main/java/org/opensearch/geometry/GeometryCollection.java index dfadf9269a097..8aca043017e32 100644 --- a/libs/geo/src/main/java/org/opensearch/geometry/GeometryCollection.java +++ b/libs/geo/src/main/java/org/opensearch/geometry/GeometryCollection.java @@ -88,6 +88,15 @@ public G get(int i) { return shapes.get(i); } + /** + * Returns a {@link List} of All {@link Geometry} present in this collection. + * + * @return a {@link List} of All {@link Geometry} + */ + public List getAll() { + return shapes; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/plugins/ingest-attachment/licenses/bcmail-jdk15on-NOTICE.txt b/libs/grok/.attach_pid85116 similarity index 100% rename from plugins/ingest-attachment/licenses/bcmail-jdk15on-NOTICE.txt rename to libs/grok/.attach_pid85116 diff --git a/libs/grok/build.gradle b/libs/grok/build.gradle index ce23406721fe6..97cf68a7971fa 100644 --- a/libs/grok/build.gradle +++ b/libs/grok/build.gradle @@ -29,9 +29,9 @@ */ dependencies { - api 'org.jruby.joni:joni:2.1.41' + api 'org.jruby.joni:joni:2.2.1' // joni dependencies: - api 'org.jruby.jcodings:jcodings:1.0.44' + api 'org.jruby.jcodings:jcodings:1.0.58' testImplementation(project(":test:framework")) { exclude group: 'org.opensearch', module: 'opensearch-grok' @@ -41,7 +41,3 @@ dependencies { tasks.named('forbiddenApisMain').configure { replaceSignatureFiles 'jdk-signatures' } - -thirdPartyAudit.ignoreMissingClasses( - 'org.jcodings.unicode.UnicodeCodeRange' -) \ No newline at end of file diff --git a/libs/grok/licenses/jcodings-1.0.44.jar.sha1 b/libs/grok/licenses/jcodings-1.0.44.jar.sha1 deleted file mode 100644 index 4449009d3395e..0000000000000 --- a/libs/grok/licenses/jcodings-1.0.44.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a6884b2fd8fd9a56874db05afaa22435043a2e3e \ No newline at end of file diff --git a/libs/grok/licenses/jcodings-1.0.58.jar.sha1 b/libs/grok/licenses/jcodings-1.0.58.jar.sha1 new file mode 100644 index 0000000000000..0202d24704a50 --- /dev/null +++ b/libs/grok/licenses/jcodings-1.0.58.jar.sha1 @@ -0,0 +1 @@ +dce27159dc0382e5f7518d4f3e499fc8396357ed \ No newline at end of file diff --git a/libs/grok/licenses/joni-2.1.41.jar.sha1 b/libs/grok/licenses/joni-2.1.41.jar.sha1 deleted file mode 100644 index 4f0a0a8393dd0..0000000000000 --- a/libs/grok/licenses/joni-2.1.41.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4a35f4eaef792073bc081b756b1f4949879cd41e \ No newline at end of file diff --git a/libs/grok/licenses/joni-2.2.1.jar.sha1 b/libs/grok/licenses/joni-2.2.1.jar.sha1 new file mode 100644 index 0000000000000..d0338540f5512 --- /dev/null +++ b/libs/grok/licenses/joni-2.2.1.jar.sha1 @@ -0,0 +1 @@ +23d2f2eff7fa0cda465d86ec9d8bab53e496d9e6 \ No newline at end of file diff --git a/libs/grok/src/main/java/org/opensearch/grok/Grok.java b/libs/grok/src/main/java/org/opensearch/grok/Grok.java index e80bde02360ed..cd786b74be039 100644 --- a/libs/grok/src/main/java/org/opensearch/grok/Grok.java +++ b/libs/grok/src/main/java/org/opensearch/grok/Grok.java @@ -32,15 +32,6 @@ package org.opensearch.grok; -import org.jcodings.specific.UTF8Encoding; -import org.joni.Matcher; -import org.joni.NameEntry; -import org.joni.Option; -import org.joni.Regex; -import org.joni.Region; -import org.joni.Syntax; -import org.joni.exception.ValueException; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -56,6 +47,15 @@ import java.util.Stack; import java.util.function.Consumer; +import org.jcodings.specific.UTF8Encoding; +import org.joni.Matcher; +import org.joni.NameEntry; +import org.joni.Option; +import org.joni.Regex; +import org.joni.Region; +import org.joni.Syntax; +import org.joni.exception.ValueException; + import static java.util.Collections.unmodifiableList; public final class Grok { @@ -214,8 +214,8 @@ private String groupMatch(String name, Region region, String pattern) { name.getBytes(StandardCharsets.UTF_8).length, region ); - int begin = region.beg[number]; - int end = region.end[number]; + int begin = region.getBeg(number); + int end = region.getEnd(number); return new String(pattern.getBytes(StandardCharsets.UTF_8), begin, end - begin, StandardCharsets.UTF_8); } catch (StringIndexOutOfBoundsException e) { return null; @@ -270,7 +270,12 @@ protected String toRegex(String grokPattern) { grokPart = String.format(Locale.US, "(?<%s>%s)", patternName + "_" + result, pattern); } String start = new String(grokPatternBytes, 0, result, StandardCharsets.UTF_8); - String rest = new String(grokPatternBytes, region.end[0], grokPatternBytes.length - region.end[0], StandardCharsets.UTF_8); + String rest = new String( + grokPatternBytes, + region.getEnd(0), + grokPatternBytes.length - region.getEnd(0), + StandardCharsets.UTF_8 + ); grokPattern = grokPart + rest; res.append(start); } diff --git a/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureConfig.java b/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureConfig.java index 44be133b90fa6..6d1985f2165cd 100644 --- a/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureConfig.java +++ b/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureConfig.java @@ -32,8 +32,6 @@ package org.opensearch.grok; -import org.joni.NameEntry; - import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import java.util.function.DoubleConsumer; @@ -41,6 +39,8 @@ import java.util.function.IntConsumer; import java.util.function.LongConsumer; +import org.joni.NameEntry; + /** * Configuration for a value that {@link Grok} can capture. */ diff --git a/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureExtracter.java b/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureExtracter.java index b551150aac28c..b6d881de4fac1 100644 --- a/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureExtracter.java +++ b/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureExtracter.java @@ -32,13 +32,13 @@ package org.opensearch.grok; -import org.joni.Region; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.joni.Region; + import static java.util.Collections.emptyMap; /** diff --git a/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureType.java b/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureType.java index f5898414827ff..0bd3bb47e55df 100644 --- a/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureType.java +++ b/libs/grok/src/main/java/org/opensearch/grok/GrokCaptureType.java @@ -33,11 +33,12 @@ package org.opensearch.grok; import org.opensearch.grok.GrokCaptureConfig.NativeExtracterMap; -import org.joni.Region; import java.nio.charset.StandardCharsets; import java.util.function.Consumer; +import org.joni.Region; + /** * The type defined for the field in the pattern. */ @@ -105,9 +106,9 @@ protected final GrokCaptureExtracter rawExtracter(int[] backRefs, Consumer= 0) { - int matchOffset = offset + region.beg[number]; - int matchLength = region.end[number] - region.beg[number]; + if (region.getBeg(number) >= 0) { + int matchOffset = offset + region.getBeg(number); + int matchLength = region.getEnd(number) - region.getBeg(number); emit.accept(new String(utf8Bytes, matchOffset, matchLength, StandardCharsets.UTF_8)); return; // Capture only the first value. } diff --git a/libs/grok/src/main/java/org/opensearch/grok/MatcherWatchdog.java b/libs/grok/src/main/java/org/opensearch/grok/MatcherWatchdog.java index 70b4570ee69ad..5c7eaca2a634a 100644 --- a/libs/grok/src/main/java/org/opensearch/grok/MatcherWatchdog.java +++ b/libs/grok/src/main/java/org/opensearch/grok/MatcherWatchdog.java @@ -31,8 +31,6 @@ package org.opensearch.grok; -import org.joni.Matcher; - import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -40,6 +38,8 @@ import java.util.function.BiConsumer; import java.util.function.LongSupplier; +import org.joni.Matcher; + /** * Protects against long running operations that happen between the register and unregister invocations. * Threads that invoke {@link #register(Matcher)}, but take too long to invoke the {@link #unregister(Matcher)} method diff --git a/libs/grok/src/main/resources/patterns/exim b/libs/grok/src/main/resources/patterns/exim index 68c4e5cd7d0d7..e81eace04d32d 100644 --- a/libs/grok/src/main/resources/patterns/exim +++ b/libs/grok/src/main/resources/patterns/exim @@ -10,4 +10,3 @@ EXIM_PROTOCOL (P=%{NOTSPACE:protocol}) EXIM_MSG_SIZE (S=%{NUMBER:exim_msg_size}) EXIM_HEADER_ID (id=%{NOTSPACE:exim_header_id}) EXIM_SUBJECT (T=%{QS:exim_subject}) - diff --git a/libs/grok/src/main/resources/patterns/junos b/libs/grok/src/main/resources/patterns/junos index 4eea59d08ccf9..2da91cc6ce3df 100644 --- a/libs/grok/src/main/resources/patterns/junos +++ b/libs/grok/src/main/resources/patterns/junos @@ -6,4 +6,3 @@ RT_FLOW1 %{RT_FLOW_EVENT:event}: %{GREEDYDATA:close-reason}: %{IP:src-ip}/%{INT: RT_FLOW2 %{RT_FLOW_EVENT:event}: session created %{IP:src-ip}/%{INT:src-port}->%{IP:dst-ip}/%{INT:dst-port} %{DATA:service} %{IP:nat-src-ip}/%{INT:nat-src-port}->%{IP:nat-dst-ip}/%{INT:nat-dst-port} %{DATA:src-nat-rule-name} %{DATA:dst-nat-rule-name} %{INT:protocol-id} %{DATA:policy-name} %{DATA:from-zone} %{DATA:to-zone} %{INT:session-id} .* RT_FLOW3 %{RT_FLOW_EVENT:event}: session denied %{IP:src-ip}/%{INT:src-port}->%{IP:dst-ip}/%{INT:dst-port} %{DATA:service} %{INT:protocol-id}\(\d\) %{DATA:policy-name} %{DATA:from-zone} %{DATA:to-zone} .* - diff --git a/libs/grok/src/main/resources/patterns/postgresql b/libs/grok/src/main/resources/patterns/postgresql index c5b3e90b7250f..6901c6253e926 100644 --- a/libs/grok/src/main/resources/patterns/postgresql +++ b/libs/grok/src/main/resources/patterns/postgresql @@ -1,3 +1,2 @@ # Default postgresql pg_log format pattern POSTGRESQL %{DATESTAMP:timestamp} %{TZ} %{DATA:user_id} %{GREEDYDATA:connection_id} %{POSINT:pid} - diff --git a/libs/grok/src/test/java/org/opensearch/grok/GrokTests.java b/libs/grok/src/test/java/org/opensearch/grok/GrokTests.java index ed48585cc124a..a37689e051c67 100644 --- a/libs/grok/src/test/java/org/opensearch/grok/GrokTests.java +++ b/libs/grok/src/test/java/org/opensearch/grok/GrokTests.java @@ -51,34 +51,34 @@ import java.util.function.IntConsumer; import java.util.function.LongConsumer; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; import static org.opensearch.grok.GrokCaptureType.BOOLEAN; import static org.opensearch.grok.GrokCaptureType.DOUBLE; import static org.opensearch.grok.GrokCaptureType.FLOAT; import static org.opensearch.grok.GrokCaptureType.INTEGER; import static org.opensearch.grok.GrokCaptureType.LONG; import static org.opensearch.grok.GrokCaptureType.STRING; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; public class GrokTests extends OpenSearchTestCase { public void testMatchWithoutCaptures() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "value", logger::warn); - assertThat(grok.captures("value"), equalTo(org.opensearch.common.collect.Map.of())); - assertThat(grok.captures("prefix_value"), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(grok.captures("value"), equalTo(Map.of())); + assertThat(grok.captures("prefix_value"), equalTo(Map.of())); assertThat(grok.captures("no_match"), nullValue()); } public void testCaputuresBytes() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "%{NUMBER:n:int}", logger::warn); byte[] utf8 = "10".getBytes(StandardCharsets.UTF_8); - assertThat(captureBytes(grok, utf8, 0, utf8.length), equalTo(org.opensearch.common.collect.Map.of("n", 10))); - assertThat(captureBytes(grok, utf8, 0, 1), equalTo(org.opensearch.common.collect.Map.of("n", 1))); + assertThat(captureBytes(grok, utf8, 0, utf8.length), equalTo(Map.of("n", 10))); + assertThat(captureBytes(grok, utf8, 0, 1), equalTo(Map.of("n", 1))); utf8 = "10 11 12".getBytes(StandardCharsets.UTF_8); - assertThat(captureBytes(grok, utf8, 0, 2), equalTo(org.opensearch.common.collect.Map.of("n", 10))); - assertThat(captureBytes(grok, utf8, 3, 2), equalTo(org.opensearch.common.collect.Map.of("n", 11))); - assertThat(captureBytes(grok, utf8, 6, 2), equalTo(org.opensearch.common.collect.Map.of("n", 12))); + assertThat(captureBytes(grok, utf8, 0, 2), equalTo(Map.of("n", 10))); + assertThat(captureBytes(grok, utf8, 3, 2), equalTo(Map.of("n", 11))); + assertThat(captureBytes(grok, utf8, 6, 2), equalTo(Map.of("n", 12))); } private Map captureBytes(Grok grok, byte[] utf8, int offset, int length) { @@ -99,15 +99,15 @@ public void testSimpleSyslogLine() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "%{SYSLOGLINE}", logger::warn); assertCaptureConfig( grok, - org.opensearch.common.collect.Map.ofEntries( - org.opensearch.common.collect.Map.entry("facility", STRING), - org.opensearch.common.collect.Map.entry("logsource", STRING), - org.opensearch.common.collect.Map.entry("message", STRING), - org.opensearch.common.collect.Map.entry("pid", STRING), - org.opensearch.common.collect.Map.entry("priority", STRING), - org.opensearch.common.collect.Map.entry("program", STRING), - org.opensearch.common.collect.Map.entry("timestamp", STRING), - org.opensearch.common.collect.Map.entry("timestamp8601", STRING) + Map.ofEntries( + Map.entry("facility", STRING), + Map.entry("logsource", STRING), + Map.entry("message", STRING), + Map.entry("pid", STRING), + Map.entry("priority", STRING), + Map.entry("program", STRING), + Map.entry("timestamp", STRING), + Map.entry("timestamp8601", STRING) ) ); Map matches = grok.captures(line); @@ -134,16 +134,16 @@ public void testSyslog5424Line() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "%{SYSLOG5424LINE}", logger::warn); assertCaptureConfig( grok, - org.opensearch.common.collect.Map.ofEntries( - org.opensearch.common.collect.Map.entry("syslog5424_app", STRING), - org.opensearch.common.collect.Map.entry("syslog5424_host", STRING), - org.opensearch.common.collect.Map.entry("syslog5424_msg", STRING), - org.opensearch.common.collect.Map.entry("syslog5424_msgid", STRING), - org.opensearch.common.collect.Map.entry("syslog5424_pri", STRING), - org.opensearch.common.collect.Map.entry("syslog5424_proc", STRING), - org.opensearch.common.collect.Map.entry("syslog5424_sd", STRING), - org.opensearch.common.collect.Map.entry("syslog5424_ts", STRING), - org.opensearch.common.collect.Map.entry("syslog5424_ver", STRING) + Map.ofEntries( + Map.entry("syslog5424_app", STRING), + Map.entry("syslog5424_host", STRING), + Map.entry("syslog5424_msg", STRING), + Map.entry("syslog5424_msgid", STRING), + Map.entry("syslog5424_pri", STRING), + Map.entry("syslog5424_proc", STRING), + Map.entry("syslog5424_sd", STRING), + Map.entry("syslog5424_ts", STRING), + Map.entry("syslog5424_ver", STRING) ) ); Map matches = grok.captures(line); @@ -161,14 +161,14 @@ public void testSyslog5424Line() { public void testDatePattern() { String line = "fancy 12-12-12 12:12:12"; Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "(?%{DATE_EU} %{TIME})", logger::warn); - assertCaptureConfig(grok, org.opensearch.common.collect.Map.of("timestamp", STRING)); + assertCaptureConfig(grok, Map.of("timestamp", STRING)); Map matches = grok.captures(line); assertEquals("12-12-12 12:12:12", matches.get("timestamp")); } public void testNilCoercedValues() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "test (N/A|%{BASE10NUM:duration:float}ms)", logger::warn); - assertCaptureConfig(grok, org.opensearch.common.collect.Map.of("duration", FLOAT)); + assertCaptureConfig(grok, Map.of("duration", FLOAT)); Map matches = grok.captures("test 28.4ms"); assertEquals(28.4f, matches.get("duration")); matches = grok.captures("test N/A"); @@ -177,7 +177,7 @@ public void testNilCoercedValues() { public void testNilWithNoCoercion() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "test (N/A|%{BASE10NUM:duration}ms)", logger::warn); - assertCaptureConfig(grok, org.opensearch.common.collect.Map.of("duration", STRING)); + assertCaptureConfig(grok, Map.of("duration", STRING)); Map matches = grok.captures("test 28.4ms"); assertEquals("28.4", matches.get("duration")); matches = grok.captures("test N/A"); @@ -194,13 +194,13 @@ public void testUnicodeSyslog() { ); assertCaptureConfig( grok, - org.opensearch.common.collect.Map.ofEntries( - org.opensearch.common.collect.Map.entry("syslog_hostname", STRING), - org.opensearch.common.collect.Map.entry("syslog_message", STRING), - org.opensearch.common.collect.Map.entry("syslog_pid", STRING), - org.opensearch.common.collect.Map.entry("syslog_pri", STRING), - org.opensearch.common.collect.Map.entry("syslog_program", STRING), - org.opensearch.common.collect.Map.entry("syslog_timestamp", STRING) + Map.ofEntries( + Map.entry("syslog_hostname", STRING), + Map.entry("syslog_message", STRING), + Map.entry("syslog_pid", STRING), + Map.entry("syslog_pri", STRING), + Map.entry("syslog_program", STRING), + Map.entry("syslog_timestamp", STRING) ) ); Map matches = grok.captures( @@ -215,21 +215,21 @@ public void testUnicodeSyslog() { public void testNamedFieldsWithWholeTextMatch() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "%{DATE_EU:stimestamp}", logger::warn); - assertCaptureConfig(grok, org.opensearch.common.collect.Map.of("stimestamp", STRING)); + assertCaptureConfig(grok, Map.of("stimestamp", STRING)); Map matches = grok.captures("11/01/01"); assertThat(matches.get("stimestamp"), equalTo("11/01/01")); } public void testWithOniguramaNamedCaptures() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "(?\\w+)", logger::warn); - assertCaptureConfig(grok, org.opensearch.common.collect.Map.of("foo", STRING)); + assertCaptureConfig(grok, Map.of("foo", STRING)); Map matches = grok.captures("hello world"); assertThat(matches.get("foo"), equalTo("hello")); } public void testISO8601() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "^%{TIMESTAMP_ISO8601}$", logger::warn); - assertCaptureConfig(grok, org.opensearch.common.collect.Map.of()); + assertCaptureConfig(grok, Map.of()); List timeMessages = Arrays.asList( "2001-01-01T00:00:00", "1974-03-02T04:09:09", @@ -254,7 +254,7 @@ public void testISO8601() { public void testNotISO8601() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "^%{TIMESTAMP_ISO8601}$", logger::warn); - assertCaptureConfig(grok, org.opensearch.common.collect.Map.of()); + assertCaptureConfig(grok, Map.of()); List timeMessages = Arrays.asList( "2001-13-01T00:00:00", // invalid month "2001-00-01T00:00:00", // invalid month @@ -294,7 +294,7 @@ public void testNoNamedCaptures() { String text = "wowza !!!Tal!!! - Tal"; String pattern = "%{EXCITED_NAME} - %{NAME}"; Grok g = new Grok(bank, pattern, false, logger::warn); - assertCaptureConfig(g, org.opensearch.common.collect.Map.of("EXCITED_NAME_0", STRING, "NAME_21", STRING, "NAME_22", STRING)); + assertCaptureConfig(g, Map.of("EXCITED_NAME_0", STRING, "NAME_21", STRING, "NAME_22", STRING)); assertEquals("(?!!!(?Tal)!!!) - (?Tal)", g.toRegex(pattern)); assertEquals(true, g.match(text)); @@ -400,7 +400,7 @@ public void testMalformedPattern() { public void testBooleanCaptures() { String pattern = "%{WORD:name}=%{WORD:status:boolean}"; Grok g = new Grok(Grok.BUILTIN_PATTERNS, pattern, logger::warn); - assertCaptureConfig(g, org.opensearch.common.collect.Map.of("name", STRING, "status", BOOLEAN)); + assertCaptureConfig(g, Map.of("name", STRING, "status", BOOLEAN)); String text = "active=true"; Map expected = new HashMap<>(); @@ -428,7 +428,7 @@ public void testNumericCaptures() { String pattern = "%{NUMBER:bytes:float} %{NUMBER:id:long} %{NUMBER:rating:double}"; Grok g = new Grok(bank, pattern, logger::warn); - assertCaptureConfig(g, org.opensearch.common.collect.Map.of("bytes", FLOAT, "id", LONG, "rating", DOUBLE)); + assertCaptureConfig(g, Map.of("bytes", FLOAT, "id", LONG, "rating", DOUBLE)); String text = "12009.34 20000000000 4820.092"; Map expected = new HashMap<>(); @@ -476,7 +476,7 @@ public void testNumericCapturesCoercion() { String pattern = "%{NUMBER:bytes:float} %{NUMBER:status} %{NUMBER}"; Grok g = new Grok(bank, pattern, logger::warn); - assertCaptureConfig(g, org.opensearch.common.collect.Map.of("bytes", FLOAT, "status", STRING)); + assertCaptureConfig(g, Map.of("bytes", FLOAT, "status", STRING)); String text = "12009.34 200 9032"; Map expected = new HashMap<>(); @@ -494,8 +494,8 @@ public void testGarbageTypeNameBecomesString() { String pattern = "%{NUMBER:f:not_a_valid_type}"; Grok g = new Grok(bank, pattern, logger::warn); - assertCaptureConfig(g, org.opensearch.common.collect.Map.of("f", STRING)); - assertThat(g.captures("12009.34"), equalTo(org.opensearch.common.collect.Map.of("f", "12009.34"))); + assertCaptureConfig(g, Map.of("f", STRING)); + assertThat(g.captures("12009.34"), equalTo(Map.of("f", "12009.34"))); } public void testApacheLog() { @@ -505,19 +505,19 @@ public void testApacheLog() { Grok grok = new Grok(Grok.BUILTIN_PATTERNS, "%{COMBINEDAPACHELOG}", logger::warn); assertCaptureConfig( grok, - org.opensearch.common.collect.Map.ofEntries( - org.opensearch.common.collect.Map.entry("agent", STRING), - org.opensearch.common.collect.Map.entry("auth", STRING), - org.opensearch.common.collect.Map.entry("bytes", STRING), - org.opensearch.common.collect.Map.entry("clientip", STRING), - org.opensearch.common.collect.Map.entry("httpversion", STRING), - org.opensearch.common.collect.Map.entry("ident", STRING), - org.opensearch.common.collect.Map.entry("rawrequest", STRING), - org.opensearch.common.collect.Map.entry("referrer", STRING), - org.opensearch.common.collect.Map.entry("request", STRING), - org.opensearch.common.collect.Map.entry("response", STRING), - org.opensearch.common.collect.Map.entry("timestamp", STRING), - org.opensearch.common.collect.Map.entry("verb", STRING) + Map.ofEntries( + Map.entry("agent", STRING), + Map.entry("auth", STRING), + Map.entry("bytes", STRING), + Map.entry("clientip", STRING), + Map.entry("httpversion", STRING), + Map.entry("ident", STRING), + Map.entry("rawrequest", STRING), + Map.entry("referrer", STRING), + Map.entry("request", STRING), + Map.entry("response", STRING), + Map.entry("timestamp", STRING), + Map.entry("verb", STRING) ) ); Map matches = grok.captures(logLine); @@ -594,18 +594,18 @@ public void testComplete() { Grok grok = new Grok(bank, pattern, logger::warn); assertCaptureConfig( grok, - org.opensearch.common.collect.Map.ofEntries( - org.opensearch.common.collect.Map.entry("agent", STRING), - org.opensearch.common.collect.Map.entry("auth", STRING), - org.opensearch.common.collect.Map.entry("bytes", INTEGER), - org.opensearch.common.collect.Map.entry("clientip", STRING), - org.opensearch.common.collect.Map.entry("httpversion", STRING), - org.opensearch.common.collect.Map.entry("ident", STRING), - org.opensearch.common.collect.Map.entry("referrer", STRING), - org.opensearch.common.collect.Map.entry("request", STRING), - org.opensearch.common.collect.Map.entry("response", INTEGER), - org.opensearch.common.collect.Map.entry("timestamp", STRING), - org.opensearch.common.collect.Map.entry("verb", STRING) + Map.ofEntries( + Map.entry("agent", STRING), + Map.entry("auth", STRING), + Map.entry("bytes", INTEGER), + Map.entry("clientip", STRING), + Map.entry("httpversion", STRING), + Map.entry("ident", STRING), + Map.entry("referrer", STRING), + Map.entry("request", STRING), + Map.entry("response", INTEGER), + Map.entry("timestamp", STRING), + Map.entry("verb", STRING) ) ); @@ -642,7 +642,7 @@ public void testMultipleNamedCapturesWithSameName() { Map bank = new HashMap<>(); bank.put("SINGLEDIGIT", "[0-9]"); Grok grok = new Grok(bank, "%{SINGLEDIGIT:num}%{SINGLEDIGIT:num}", logger::warn); - assertCaptureConfig(grok, org.opensearch.common.collect.Map.of("num", STRING)); + assertCaptureConfig(grok, Map.of("num", STRING)); Map expected = new HashMap<>(); expected.put("num", "1"); diff --git a/libs/grok/src/test/java/org/opensearch/grok/MatcherWatchdogTests.java b/libs/grok/src/test/java/org/opensearch/grok/MatcherWatchdogTests.java index 0290e200e4382..4a12ccab32f6a 100644 --- a/libs/grok/src/test/java/org/opensearch/grok/MatcherWatchdogTests.java +++ b/libs/grok/src/test/java/org/opensearch/grok/MatcherWatchdogTests.java @@ -31,24 +31,26 @@ package org.opensearch.grok; +import org.opensearch.test.OpenSearchTestCase; + import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.opensearch.test.OpenSearchTestCase; + import org.joni.Matcher; import org.mockito.Mockito; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; public class MatcherWatchdogTests extends OpenSearchTestCase { diff --git a/libs/nio/build.gradle b/libs/nio/build.gradle index cae9f7e6feb26..1bd0c91135330 100644 --- a/libs/nio/build.gradle +++ b/libs/nio/build.gradle @@ -29,11 +29,11 @@ */ import org.opensearch.gradle.info.BuildParams - + apply plugin: 'opensearch.publish' dependencies { - api project(':libs:opensearch-core') + api project(':libs:opensearch-common') testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" testImplementation "junit:junit:${versions.junit}" diff --git a/libs/nio/src/main/java/org/opensearch/nio/SocketChannelContext.java b/libs/nio/src/main/java/org/opensearch/nio/SocketChannelContext.java index 9cf57910a2b96..12a1e80055823 100644 --- a/libs/nio/src/main/java/org/opensearch/nio/SocketChannelContext.java +++ b/libs/nio/src/main/java/org/opensearch/nio/SocketChannelContext.java @@ -33,7 +33,7 @@ package org.opensearch.nio; import org.opensearch.common.concurrent.CompletableContext; -import org.opensearch.core.internal.net.NetUtils; +import org.opensearch.common.util.net.NetUtils; import org.opensearch.nio.utils.ByteBufferUtils; import org.opensearch.nio.utils.ExceptionsHelper; diff --git a/libs/nio/src/test/java/org/opensearch/nio/ChannelFactoryTests.java b/libs/nio/src/test/java/org/opensearch/nio/ChannelFactoryTests.java index ba212de7941e5..0e4ec4a1b3bc5 100644 --- a/libs/nio/src/test/java/org/opensearch/nio/ChannelFactoryTests.java +++ b/libs/nio/src/test/java/org/opensearch/nio/ChannelFactoryTests.java @@ -32,7 +32,7 @@ package org.opensearch.nio; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.test.OpenSearchTestCase; import org.junit.After; import org.junit.Before; diff --git a/libs/nio/src/test/java/org/opensearch/nio/NioSelectorTests.java b/libs/nio/src/test/java/org/opensearch/nio/NioSelectorTests.java index 6af1cb64dac18..2fe5f3cc3f115 100644 --- a/libs/nio/src/test/java/org/opensearch/nio/NioSelectorTests.java +++ b/libs/nio/src/test/java/org/opensearch/nio/NioSelectorTests.java @@ -37,7 +37,6 @@ import org.opensearch.common.util.concurrent.AbstractRunnable; import org.opensearch.test.OpenSearchTestCase; import org.junit.Before; -import org.mockito.ArgumentCaptor; import java.io.IOException; import java.nio.ByteBuffer; @@ -51,13 +50,15 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; +import org.mockito.ArgumentCaptor; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.isNull; -import static org.mockito.Mockito.same; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/libs/nio/src/test/java/org/opensearch/nio/SocketChannelContextTests.java b/libs/nio/src/test/java/org/opensearch/nio/SocketChannelContextTests.java index 801ee7023482c..1e559b597ca89 100644 --- a/libs/nio/src/test/java/org/opensearch/nio/SocketChannelContextTests.java +++ b/libs/nio/src/test/java/org/opensearch/nio/SocketChannelContextTests.java @@ -35,8 +35,6 @@ import org.opensearch.common.SuppressForbidden; import org.opensearch.test.OpenSearchTestCase; import org.junit.Before; -import org.mockito.ArgumentCaptor; -import org.mockito.stubbing.Answer; import java.io.IOException; import java.net.InetSocketAddress; @@ -53,12 +51,15 @@ import java.util.function.Consumer; import java.util.function.IntFunction; +import org.mockito.ArgumentCaptor; +import org.mockito.stubbing.Answer; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.isNull; -import static org.mockito.Mockito.same; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/libs/secure-sm/src/main/java/org/opensearch/secure_sm/SecuredForkJoinWorkerThreadFactory.java b/libs/secure-sm/src/main/java/org/opensearch/secure_sm/SecuredForkJoinWorkerThreadFactory.java new file mode 100644 index 0000000000000..fe239fea8129e --- /dev/null +++ b/libs/secure-sm/src/main/java/org/opensearch/secure_sm/SecuredForkJoinWorkerThreadFactory.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.secure_sm; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Permission; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory; +import java.util.concurrent.ForkJoinWorkerThread; + +public class SecuredForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory { + static AccessControlContext contextWithPermissions(Permission... perms) { + Permissions permissions = new Permissions(); + for (Permission perm : perms) + permissions.add(perm); + return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, permissions) }); + } + + // ACC for access to the factory + private static final AccessControlContext ACC = contextWithPermissions( + new RuntimePermission("getClassLoader"), + new RuntimePermission("setContextClassLoader"), + new RuntimePermission("modifyThreadGroup"), + new RuntimePermission("modifyThread") + ); + + public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { + return AccessController.doPrivileged(new PrivilegedAction<>() { + public ForkJoinWorkerThread run() { + return new ForkJoinWorkerThread(pool) { + + }; + } + }, ACC); + } +} diff --git a/libs/secure-sm/src/test/java/org/opensearch/secure_sm/SecureSMTests.java b/libs/secure-sm/src/test/java/org/opensearch/secure_sm/SecureSMTests.java index 8f184328957a1..026ffb080ee61 100644 --- a/libs/secure-sm/src/test/java/org/opensearch/secure_sm/SecureSMTests.java +++ b/libs/secure-sm/src/test/java/org/opensearch/secure_sm/SecureSMTests.java @@ -32,14 +32,14 @@ package org.opensearch.secure_sm; -import junit.framework.TestCase; - import java.security.Permission; import java.security.Policy; import java.security.ProtectionDomain; import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; +import junit.framework.TestCase; + /** Simple tests for SecureSM */ public class SecureSMTests extends TestCase { static { diff --git a/libs/secure-sm/src/test/java/org/opensearch/secure_sm/ThreadPermissionTests.java b/libs/secure-sm/src/test/java/org/opensearch/secure_sm/ThreadPermissionTests.java index 774e87f08c634..8d821f5bb27aa 100644 --- a/libs/secure-sm/src/test/java/org/opensearch/secure_sm/ThreadPermissionTests.java +++ b/libs/secure-sm/src/test/java/org/opensearch/secure_sm/ThreadPermissionTests.java @@ -32,10 +32,10 @@ package org.opensearch.secure_sm; -import junit.framework.TestCase; - import java.security.AllPermission; +import junit.framework.TestCase; + /** * Simple tests for ThreadPermission */ diff --git a/libs/ssl-config/build.gradle b/libs/ssl-config/build.gradle index 740d5e309350c..3226ec12ff6f7 100644 --- a/libs/ssl-config/build.gradle +++ b/libs/ssl-config/build.gradle @@ -29,11 +29,11 @@ */ import org.opensearch.gradle.info.BuildParams - + apply plugin: "opensearch.publish" dependencies { - api project(':libs:opensearch-core') + api project(':libs:opensearch-common') testImplementation(project(":test:framework")) { exclude group: 'org.opensearch', module: 'opensearch-ssl-config' @@ -61,4 +61,3 @@ tasks.test { jvmArgs += ["--add-opens", "java.base/java.security.cert=ALL-UNNAMED"] } } - diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DefaultJdkTrustConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DefaultJdkTrustConfig.java index 04277e1528a92..859b74b200dc6 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DefaultJdkTrustConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DefaultJdkTrustConfig.java @@ -36,6 +36,7 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; + import java.io.IOException; import java.nio.file.Path; import java.security.GeneralSecurityException; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DiagnosticTrustManager.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DiagnosticTrustManager.java index 75b0b831940d8..a3856038bbca1 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DiagnosticTrustManager.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DiagnosticTrustManager.java @@ -36,6 +36,7 @@ import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; import javax.net.ssl.X509ExtendedTrustManager; + import java.net.Socket; import java.security.GeneralSecurityException; import java.security.cert.CertificateException; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/EmptyKeyConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/EmptyKeyConfig.java index ff5db9a04ff74..b5c7416c792a6 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/EmptyKeyConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/EmptyKeyConfig.java @@ -33,6 +33,7 @@ package org.opensearch.common.ssl; import javax.net.ssl.X509ExtendedKeyManager; + import java.nio.file.Path; import java.util.Collection; import java.util.Collections; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/KeyStoreUtil.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/KeyStoreUtil.java index b2d5732022212..b6b6cdd90af14 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/KeyStoreUtil.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/KeyStoreUtil.java @@ -40,6 +40,7 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; + import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemKeyConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemKeyConfig.java index 038299fd6d161..bfc29a5801b11 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemKeyConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemKeyConfig.java @@ -34,6 +34,7 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.X509ExtendedKeyManager; + import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.NoSuchFileException; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemTrustConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemTrustConfig.java index 236ea6cd6f431..50b5887e7f355 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemTrustConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemTrustConfig.java @@ -34,6 +34,7 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; + import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemUtils.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemUtils.java index 533c8bf429e60..8a3730ee554f9 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemUtils.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemUtils.java @@ -41,6 +41,7 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; + import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslClientAuthenticationMode.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslClientAuthenticationMode.java index e22ae45afd5fb..17d3749567990 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslClientAuthenticationMode.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslClientAuthenticationMode.java @@ -32,6 +32,7 @@ package org.opensearch.common.ssl; import javax.net.ssl.SSLParameters; + import java.util.Collections; import java.util.LinkedHashMap; import java.util.Locale; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfiguration.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfiguration.java index 27c3918ed6da0..23acb0ff269e2 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfiguration.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfiguration.java @@ -35,6 +35,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; + import java.nio.file.Path; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfigurationKeys.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfigurationKeys.java index eb801bd124a50..5d28cbe1005b2 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfigurationKeys.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfigurationKeys.java @@ -33,6 +33,7 @@ package org.opensearch.common.ssl; import javax.net.ssl.TrustManagerFactory; + import java.security.KeyStore; import java.util.Arrays; import java.util.HashSet; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfigurationLoader.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfigurationLoader.java index 6f2670d285e84..0b06a0692197e 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfigurationLoader.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfigurationLoader.java @@ -32,11 +32,10 @@ package org.opensearch.common.ssl; -import org.opensearch.bootstrap.JavaVersion; - import javax.crypto.Cipher; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; + import java.nio.file.Path; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -361,7 +360,6 @@ private List resolveListSetting(String key, Function parser, L private static List loadDefaultCiphers() { final boolean has256BitAES = has256BitAES(); - final boolean useGCM = JavaVersion.current().compareTo(JavaVersion.parse("11")) >= 0; final boolean tlsV13Supported = DEFAULT_PROTOCOLS.contains("TLSv1.3"); List ciphers = new ArrayList<>(); if (tlsV13Supported) { // TLSv1.3 cipher has PFS, AEAD, hardware support @@ -370,19 +368,18 @@ private static List loadDefaultCiphers() { } ciphers.add("TLS_AES_128_GCM_SHA256"); } - if (useGCM) { // PFS, AEAD, hardware support - if (has256BitAES) { - ciphers.addAll( - Arrays.asList( - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" - ) - ); - } else { - ciphers.addAll(Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")); - } + // use GCM: PFS, AEAD, hardware support + if (has256BitAES) { + ciphers.addAll( + Arrays.asList( + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + ) + ); + } else { + ciphers.addAll(Arrays.asList("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")); } // PFS, hardware support @@ -410,13 +407,11 @@ private static List loadDefaultCiphers() { ); } - // AEAD, hardware support - if (useGCM) { - if (has256BitAES) { - ciphers.addAll(Arrays.asList("TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256")); - } else { - ciphers.add("TLS_RSA_WITH_AES_128_GCM_SHA256"); - } + // use GCM: AEAD, hardware support + if (has256BitAES) { + ciphers.addAll(Arrays.asList("TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_GCM_SHA256")); + } else { + ciphers.add("TLS_RSA_WITH_AES_128_GCM_SHA256"); } // hardware support diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslDiagnostics.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslDiagnostics.java index 29de6f6d14031..f0a7d5bff4964 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslDiagnostics.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslDiagnostics.java @@ -35,6 +35,7 @@ import org.opensearch.common.Nullable; import javax.net.ssl.SSLSession; + import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslKeyConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslKeyConfig.java index cb87965e83754..250c407a69bf5 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslKeyConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslKeyConfig.java @@ -33,6 +33,7 @@ package org.opensearch.common.ssl; import javax.net.ssl.X509ExtendedKeyManager; + import java.nio.file.Path; import java.util.Collection; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslTrustConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslTrustConfig.java index 38e51778da225..4dd5890f19311 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslTrustConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslTrustConfig.java @@ -33,6 +33,7 @@ package org.opensearch.common.ssl; import javax.net.ssl.X509ExtendedTrustManager; + import java.nio.file.Path; import java.util.Collection; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/StoreKeyConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/StoreKeyConfig.java index 42246d918192d..b3b7b7dc346a6 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/StoreKeyConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/StoreKeyConfig.java @@ -34,6 +34,7 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.X509ExtendedKeyManager; + import java.nio.file.Path; import java.security.GeneralSecurityException; import java.security.KeyStore; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/StoreTrustConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/StoreTrustConfig.java index d509956368a82..556cb052c4391 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/StoreTrustConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/StoreTrustConfig.java @@ -33,6 +33,7 @@ package org.opensearch.common.ssl; import javax.net.ssl.X509ExtendedTrustManager; + import java.nio.file.Path; import java.security.GeneralSecurityException; import java.security.KeyStore; diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/TrustEverythingConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/TrustEverythingConfig.java index c8b6ab2407e69..c366210133687 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/TrustEverythingConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/TrustEverythingConfig.java @@ -34,6 +34,7 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.X509ExtendedTrustManager; + import java.net.Socket; import java.nio.file.Path; import java.security.cert.X509Certificate; diff --git a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/DefaultJdkTrustConfigTests.java b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/DefaultJdkTrustConfigTests.java index e767787d67d6d..dd325662ce50d 100644 --- a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/DefaultJdkTrustConfigTests.java +++ b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/DefaultJdkTrustConfigTests.java @@ -36,6 +36,7 @@ import org.junit.Assert; import javax.net.ssl.X509ExtendedTrustManager; + import java.security.cert.X509Certificate; import java.util.Locale; import java.util.Optional; diff --git a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemKeyConfigTests.java b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemKeyConfigTests.java index 59d6a36b222ff..688f03a1e51fa 100644 --- a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemKeyConfigTests.java +++ b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemKeyConfigTests.java @@ -36,6 +36,7 @@ import org.hamcrest.Matchers; import javax.net.ssl.X509ExtendedKeyManager; + import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; diff --git a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemTrustConfigTests.java b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemTrustConfigTests.java index 4617727d83857..e664e379d1e97 100644 --- a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemTrustConfigTests.java +++ b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemTrustConfigTests.java @@ -35,6 +35,8 @@ import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.Matchers; +import javax.net.ssl.X509ExtendedTrustManager; + import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; @@ -49,8 +51,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.net.ssl.X509ExtendedTrustManager; - public class PemTrustConfigTests extends OpenSearchTestCase { public void testBuildTrustConfigFromSinglePemFile() throws Exception { diff --git a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslConfigurationLoaderTests.java b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslConfigurationLoaderTests.java index 19f4a85115236..5af7ddc73e680 100644 --- a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslConfigurationLoaderTests.java +++ b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslConfigurationLoaderTests.java @@ -33,12 +33,13 @@ package org.opensearch.common.ssl; import org.opensearch.common.settings.MockSecureSettings; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.test.OpenSearchTestCase; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.TrustManagerFactory; + import java.nio.file.Path; import java.util.Arrays; import java.util.List; diff --git a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslConfigurationTests.java b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslConfigurationTests.java index a629afc7f0d0b..ee907952c52ff 100644 --- a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslConfigurationTests.java +++ b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslConfigurationTests.java @@ -32,17 +32,19 @@ package org.opensearch.common.ssl; -import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.EqualsHashCodeTestUtils; +import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.Matchers; -import org.mockito.Mockito; import javax.net.ssl.SSLContext; + import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.mockito.Mockito; + import static org.opensearch.common.ssl.SslConfigurationLoader.DEFAULT_CIPHERS; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; diff --git a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslDiagnosticsTests.java b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslDiagnosticsTests.java index 38ec0c3b5ff78..c966b4259219f 100644 --- a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslDiagnosticsTests.java +++ b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/SslDiagnosticsTests.java @@ -35,10 +35,10 @@ import org.opensearch.common.Nullable; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.Matchers; -import org.mockito.Mockito; import javax.net.ssl.SSLSession; import javax.security.auth.x500.X500Principal; + import java.io.IOException; import java.nio.file.Path; import java.security.PublicKey; @@ -56,6 +56,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.mockito.Mockito; + public class SslDiagnosticsTests extends OpenSearchTestCase { // Some constants for use in mock certificates diff --git a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/StoreKeyConfigTests.java b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/StoreKeyConfigTests.java index de7420d27c170..7806671d02793 100644 --- a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/StoreKeyConfigTests.java +++ b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/StoreKeyConfigTests.java @@ -37,6 +37,7 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.X509ExtendedKeyManager; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/StoreTrustConfigTests.java b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/StoreTrustConfigTests.java index cbe0418f99ddf..5609f0fa2c877 100644 --- a/libs/ssl-config/src/test/java/org/opensearch/common/ssl/StoreTrustConfigTests.java +++ b/libs/ssl-config/src/test/java/org/opensearch/common/ssl/StoreTrustConfigTests.java @@ -37,6 +37,7 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/libs/telemetry/build.gradle b/libs/telemetry/build.gradle new file mode 100644 index 0000000000000..f8499482a6093 --- /dev/null +++ b/libs/telemetry/build.gradle @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +dependencies { + api project(':libs:opensearch-common') + + testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" + testImplementation "junit:junit:${versions.junit}" + testImplementation "org.hamcrest:hamcrest:${versions.hamcrest}" + testImplementation(project(":test:framework")) { + exclude group: 'org.opensearch', module: 'opensearch-telemetry' + } +} + +tasks.named('forbiddenApisMain').configure { + replaceSignatureFiles 'jdk-signatures' +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/Telemetry.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/Telemetry.java new file mode 100644 index 0000000000000..0f973f50fc640 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/Telemetry.java @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.telemetry.metrics.MetricsTelemetry; +import org.opensearch.telemetry.tracing.TracingTelemetry; + +/** + * Interface defining telemetry + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface Telemetry { + + /** + * Provides tracing telemetry + * @return tracing telemetry instance + */ + TracingTelemetry getTracingTelemetry(); + + /** + * Provides metrics telemetry + * @return metrics telemetry instance + */ + MetricsTelemetry getMetricsTelemetry(); + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/Counter.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/Counter.java new file mode 100644 index 0000000000000..c62288d280e2f --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/Counter.java @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.telemetry.metrics.tags.Tags; + +/** + * Counter adds the value to the existing metric. + * {@opensearch.experimental} + */ +@ExperimentalApi +public interface Counter { + + /** + * add value. + * @param value value to be added. + */ + void add(double value); + + /** + * add value along with the attributes. + * + * @param value value to be added. + * @param tags attributes/dimensions of the metric. + */ + void add(double value, Tags tags); + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/DefaultMetricsRegistry.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/DefaultMetricsRegistry.java new file mode 100644 index 0000000000000..d57def9406b17 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/DefaultMetricsRegistry.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import java.io.IOException; + +/** + * Default implementation for {@link MetricsRegistry} + */ +class DefaultMetricsRegistry implements MetricsRegistry { + private final MetricsTelemetry metricsTelemetry; + + /** + * Constructor + * @param metricsTelemetry metrics telemetry. + */ + public DefaultMetricsRegistry(MetricsTelemetry metricsTelemetry) { + this.metricsTelemetry = metricsTelemetry; + } + + @Override + public Counter createCounter(String name, String description, String unit) { + return metricsTelemetry.createCounter(name, description, unit); + } + + @Override + public Counter createUpDownCounter(String name, String description, String unit) { + return metricsTelemetry.createUpDownCounter(name, description, unit); + } + + @Override + public void close() throws IOException { + metricsTelemetry.close(); + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/MetricsRegistry.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/MetricsRegistry.java new file mode 100644 index 0000000000000..61b3df089928b --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/MetricsRegistry.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import org.opensearch.common.annotation.ExperimentalApi; + +import java.io.Closeable; + +/** + * MetricsRegistry helps in creating the metric instruments. + * @opensearch.experimental + */ +@ExperimentalApi +public interface MetricsRegistry extends Closeable { + + /** + * Creates the counter. + * @param name name of the counter. + * @param description any description about the metric. + * @param unit unit of the metric. + * @return counter. + */ + Counter createCounter(String name, String description, String unit); + + /** + * Creates the upDown counter. + * @param name name of the upDown counter. + * @param description any description about the metric. + * @param unit unit of the metric. + * @return counter. + */ + Counter createUpDownCounter(String name, String description, String unit); +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/MetricsTelemetry.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/MetricsTelemetry.java new file mode 100644 index 0000000000000..2f70c28efb1cd --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/MetricsTelemetry.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import org.opensearch.common.annotation.ExperimentalApi; + +import java.io.Closeable; + +/** + * Interface for metrics telemetry providers + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface MetricsTelemetry extends MetricsRegistry, Closeable { + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/NoopCounter.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/NoopCounter.java new file mode 100644 index 0000000000000..c1daf564dd3bc --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/NoopCounter.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics.noop; + +import org.opensearch.common.annotation.InternalApi; +import org.opensearch.telemetry.metrics.Counter; +import org.opensearch.telemetry.metrics.tags.Tags; + +/** + * No-op {@link Counter} + * {@opensearch.internal} + */ +@InternalApi +public class NoopCounter implements Counter { + + /** + * No-op Counter instance + */ + public final static NoopCounter INSTANCE = new NoopCounter(); + + private NoopCounter() {} + + @Override + public void add(double value) { + + } + + @Override + public void add(double value, Tags tags) { + + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/NoopMetricsRegistry.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/NoopMetricsRegistry.java new file mode 100644 index 0000000000000..640c6842a8960 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/NoopMetricsRegistry.java @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics.noop; + +import org.opensearch.common.annotation.InternalApi; +import org.opensearch.telemetry.metrics.Counter; +import org.opensearch.telemetry.metrics.MetricsRegistry; + +import java.io.IOException; + +/** + *No-op {@link MetricsRegistry} + * {@opensearch.internal} + */ +@InternalApi +public class NoopMetricsRegistry implements MetricsRegistry { + + /** + * No-op Meter instance + */ + public final static NoopMetricsRegistry INSTANCE = new NoopMetricsRegistry(); + + private NoopMetricsRegistry() {} + + @Override + public Counter createCounter(String name, String description, String unit) { + return NoopCounter.INSTANCE; + } + + @Override + public Counter createUpDownCounter(String name, String description, String unit) { + return NoopCounter.INSTANCE; + } + + @Override + public void close() throws IOException { + + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/package-info.java new file mode 100644 index 0000000000000..7c7ed08044993 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/package-info.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains metrics related classes + * {@opensearch.internal} + */ +@InternalApi +package org.opensearch.telemetry.metrics.noop; + +import org.opensearch.common.annotation.InternalApi; diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/package-info.java new file mode 100644 index 0000000000000..dfe17cc1c11ed --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains metrics related classes + */ +package org.opensearch.telemetry.metrics; diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/tags/Tags.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/tags/Tags.java new file mode 100644 index 0000000000000..f2a8764f8021d --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/tags/Tags.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics.tags; + +import org.opensearch.common.annotation.ExperimentalApi; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Class to create tags for a meter. + * + * @opensearch.experimental + */ +@ExperimentalApi +public class Tags { + private final Map tagsMap; + /** + * Empty value. + */ + public final static Tags EMPTY = new Tags(Collections.emptyMap()); + + /** + * Factory method. + * @return tags. + */ + public static Tags create() { + return new Tags(new HashMap<>()); + } + + /** + * Constructor. + */ + private Tags(Map tagsMap) { + this.tagsMap = tagsMap; + } + + /** + * Add String attribute. + * @param key key + * @param value value + * @return Same instance. + */ + public Tags addTag(String key, String value) { + Objects.requireNonNull(value, "value cannot be null"); + tagsMap.put(key, value); + return this; + } + + /** + * Add long attribute. + * @param key key + * @param value value + * @return Same instance. + */ + public Tags addTag(String key, long value) { + tagsMap.put(key, value); + return this; + }; + + /** + * Add double attribute. + * @param key key + * @param value value + * @return Same instance. + */ + public Tags addTag(String key, double value) { + tagsMap.put(key, value); + return this; + }; + + /** + * Add boolean attribute. + * @param key key + * @param value value + * @return Same instance. + */ + public Tags addTag(String key, boolean value) { + tagsMap.put(key, value); + return this; + }; + + /** + * Returns the attribute map. + * @return tags map + */ + public Map getTagsMap() { + return Collections.unmodifiableMap(tagsMap); + } + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/tags/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/tags/package-info.java new file mode 100644 index 0000000000000..70bc9be992b32 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/tags/package-info.java @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains metrics related classes + * @opensearch.experimental + */ +@ExperimentalApi +package org.opensearch.telemetry.metrics.tags; + +import org.opensearch.common.annotation.ExperimentalApi; diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/package-info.java new file mode 100644 index 0000000000000..ad76f5e308bea --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains telemetry related classes + */ +package org.opensearch.telemetry; diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/AbstractSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/AbstractSpan.java new file mode 100644 index 0000000000000..6919995e0ef65 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/AbstractSpan.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.InternalApi; + +/** + * Base span + * + * @opensearch.internal + */ +@InternalApi +public abstract class AbstractSpan implements Span { + + /** + * name of the span + */ + private final String spanName; + /** + * span's parent span + */ + private final Span parentSpan; + + /** + * Base constructor + * @param spanName name of the span + * @param parentSpan span's parent span + */ + protected AbstractSpan(String spanName, Span parentSpan) { + this.spanName = spanName; + this.parentSpan = parentSpan; + } + + @Override + public Span getParentSpan() { + return parentSpan; + } + + @Override + public String getSpanName() { + return spanName; + } + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultScopedSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultScopedSpan.java new file mode 100644 index 0000000000000..dc1a775839adb --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultScopedSpan.java @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.InternalApi; + +import java.util.Objects; + +/** + * Default implementation of Scope + * + * @opensearch.internal + */ +@InternalApi +final class DefaultScopedSpan implements ScopedSpan { + + private final Span span; + + private final SpanScope spanScope; + + /** + * Creates Scope instance for the given span + * + * @param span underlying span + * @param spanScope span scope. + */ + public DefaultScopedSpan(Span span, SpanScope spanScope) { + this.span = Objects.requireNonNull(span); + this.spanScope = Objects.requireNonNull(spanScope); + } + + @Override + public void addAttribute(String key, String value) { + span.addAttribute(key, value); + } + + @Override + public void addAttribute(String key, long value) { + span.addAttribute(key, value); + } + + @Override + public void addAttribute(String key, double value) { + span.addAttribute(key, value); + } + + @Override + public void addAttribute(String key, boolean value) { + span.addAttribute(key, value); + } + + @Override + public void addEvent(String event) { + span.addEvent(event); + } + + @Override + public void setError(Exception exception) { + span.setError(exception); + } + + /** + * Executes the runnable to end the scope + */ + @Override + public void close() { + span.endSpan(); + spanScope.close(); + } + + /** + * Returns span. + * @return the span associated with this scope + */ + Span getSpan() { + return span; + } + + /** + * Returns {@link SpanScope} + * @return spanScope + */ + SpanScope getSpanScope() { + return spanScope; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultSpanScope.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultSpanScope.java new file mode 100644 index 0000000000000..a5d515443b54d --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultSpanScope.java @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.InternalApi; + +import java.util.Objects; + +/** + * Default implementation for {@link SpanScope} + * + * @opensearch.internal + */ +@InternalApi +class DefaultSpanScope implements SpanScope { + private final Span span; + private final SpanScope previousSpanScope; + private static final ThreadLocal spanScopeThreadLocal = new ThreadLocal<>(); + private final TracerContextStorage tracerContextStorage; + + /** + * Constructor + * @param span span + * @param previousSpanScope before attached span scope. + */ + private DefaultSpanScope(Span span, SpanScope previousSpanScope, TracerContextStorage tracerContextStorage) { + this.span = Objects.requireNonNull(span); + this.previousSpanScope = previousSpanScope; + this.tracerContextStorage = tracerContextStorage; + } + + /** + * Creates the SpanScope object. + * @param span span. + * @param tracerContextStorage tracer context storage. + * @return SpanScope spanScope + */ + public static SpanScope create(Span span, TracerContextStorage tracerContextStorage) { + final SpanScope beforeSpanScope = spanScopeThreadLocal.get(); + SpanScope newSpanScope = new DefaultSpanScope(span, beforeSpanScope, tracerContextStorage); + spanScopeThreadLocal.set(newSpanScope); + return newSpanScope; + } + + @Override + public void close() { + detach(); + spanScopeThreadLocal.set(previousSpanScope); + } + + @Override + public SpanScope attach() { + tracerContextStorage.put(TracerContextStorage.CURRENT_SPAN, this.span); + return this; + } + + private void detach() { + if (previousSpanScope != null) { + tracerContextStorage.put(TracerContextStorage.CURRENT_SPAN, previousSpanScope.getSpan()); + } else { + tracerContextStorage.put(TracerContextStorage.CURRENT_SPAN, null); + } + } + + @Override + public Span getSpan() { + return span; + } + + static SpanScope getCurrentSpanScope() { + return spanScopeThreadLocal.get(); + } + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultTracer.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultTracer.java new file mode 100644 index 0000000000000..79b7e4aca6c2f --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultTracer.java @@ -0,0 +1,110 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.InternalApi; + +import java.io.Closeable; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * + * The default tracer implementation. It handles tracing context propagation between spans by maintaining + * current active span in its storage + * + * @opensearch.internal + */ +@InternalApi +class DefaultTracer implements Tracer { + /** + * Current thread name. + */ + static final String THREAD_NAME = "thread.name"; + + private final TracingTelemetry tracingTelemetry; + private final TracerContextStorage tracerContextStorage; + + /** + * Creates DefaultTracer instance + * + * @param tracingTelemetry tracing telemetry instance + * @param tracerContextStorage storage used for storing current span context + */ + public DefaultTracer(TracingTelemetry tracingTelemetry, TracerContextStorage tracerContextStorage) { + this.tracingTelemetry = tracingTelemetry; + this.tracerContextStorage = tracerContextStorage; + } + + @Override + public Span startSpan(SpanCreationContext context) { + Span parentSpan = null; + if (context.getParent() != null) { + parentSpan = context.getParent().getSpan(); + } else { + parentSpan = getCurrentSpanInternal(); + } + Span span = createSpan(context, parentSpan); + setCurrentSpanInContext(span); + addDefaultAttributes(span); + return span; + } + + @Override + public void close() throws IOException { + ((Closeable) tracingTelemetry).close(); + } + + private Span getCurrentSpanInternal() { + return tracerContextStorage.get(TracerContextStorage.CURRENT_SPAN); + } + + @Override + public SpanContext getCurrentSpan() { + final Span currentSpan = tracerContextStorage.get(TracerContextStorage.CURRENT_SPAN); + return (currentSpan == null) ? null : new SpanContext(currentSpan); + } + + @Override + public ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext) { + Span span = startSpan(spanCreationContext); + SpanScope spanScope = withSpanInScope(span); + return new DefaultScopedSpan(span, spanScope); + } + + @Override + public SpanScope withSpanInScope(Span span) { + return DefaultSpanScope.create(span, tracerContextStorage).attach(); + } + + private Span createSpan(SpanCreationContext spanCreationContext, Span parentSpan) { + return tracingTelemetry.createSpan(spanCreationContext, parentSpan); + } + + private void setCurrentSpanInContext(Span span) { + tracerContextStorage.put(TracerContextStorage.CURRENT_SPAN, span); + } + + /** + * Adds default attributes in the span + * @param span the current active span + */ + protected void addDefaultAttributes(Span span) { + span.addAttribute(THREAD_NAME, Thread.currentThread().getName()); + } + + @Override + public Span startSpan(SpanCreationContext spanCreationContext, Map> headers) { + Optional propagatedSpan = tracingTelemetry.getContextPropagator().extractFromHeaders(headers); + return startSpan(spanCreationContext.parent(propagatedSpan.map(SpanContext::new).orElse(null))); + } + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/ScopedSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/ScopedSpan.java new file mode 100644 index 0000000000000..b320bc415de29 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/ScopedSpan.java @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.telemetry.tracing.noop.NoopScopedSpan; + +/** + * An auto-closeable that represents scoped span. + * It provides interface for all the span operations. + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface ScopedSpan extends AutoCloseable { + /** + * No-op Scope implementation + */ + ScopedSpan NO_OP = new NoopScopedSpan(); + + /** + * Adds string attribute to the {@link Span}. + * + * @param key attribute key + * @param value attribute value + */ + void addAttribute(String key, String value); + + /** + * Adds long attribute to the {@link Span}. + * + * @param key attribute key + * @param value attribute value + */ + void addAttribute(String key, long value); + + /** + * Adds double attribute to the {@link Span}. + * + * @param key attribute key + * @param value attribute value + */ + void addAttribute(String key, double value); + + /** + * Adds boolean attribute to the {@link Span}. + * + * @param key attribute key + * @param value attribute value + */ + void addAttribute(String key, boolean value); + + /** + * Adds an event to the {@link Span}. + * + * @param event event name + */ + void addEvent(String event); + + /** + * Records error in the span + * + * @param exception exception to be recorded + */ + void setError(Exception exception); + + /** + * closes the scope + */ + @Override + void close(); +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Span.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Span.java new file mode 100644 index 0000000000000..00b64492c281e --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Span.java @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.ExperimentalApi; + +/** + * An interface that represents a tracing span. + * Spans are created by the Tracer.startSpan method. + * Span must be ended by calling SpanScope.close which internally calls Span's endSpan. + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface Span { + + /** + * Ends the span + */ + void endSpan(); + + /** + * Returns span's parent span + */ + Span getParentSpan(); + + /** + * Returns the name of the {@link Span} + */ + String getSpanName(); + + /** + * Adds string type attribute in the span + * + * @param key of the attribute + * @param value value of the attribute + */ + void addAttribute(String key, String value); + + /** + * Adds long type attribute in the span + * + * @param key of the attribute + * @param value value of the attribute + */ + void addAttribute(String key, Long value); + + /** + * Adds double type attribute in the span + * + * @param key of the attribute + * @param value value of the attribute + */ + void addAttribute(String key, Double value); + + /** + * Adds boolean type attribute in the span + * + * @param key of the attribute + * @param value value of the attribute + */ + void addAttribute(String key, Boolean value); + + /** + * Records error in the span + * + * @param exception exception to be recorded + */ + void setError(Exception exception); + + /** + * Adds an event in the span + * + * @param event name of the event + */ + void addEvent(String event); + + /** + * Returns traceId of the span + * @return span's traceId + */ + String getTraceId(); + + /** + * Returns spanId of the span + * @return span's spanId + */ + String getSpanId(); + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanContext.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanContext.java new file mode 100644 index 0000000000000..f9af611553aff --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanContext.java @@ -0,0 +1,34 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.ExperimentalApi; + +/** + * Wrapped Span will be exposed to the code outside of tracing package for sharing the {@link Span} without having access to + * its properties. + * + * @opensearch.experimental + */ +@ExperimentalApi +public final class SpanContext { + private final Span span; + + /** + * Constructor. + * @param span span to be wrapped. + */ + public SpanContext(Span span) { + this.span = span; + } + + Span getSpan() { + return span; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanCreationContext.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanCreationContext.java new file mode 100644 index 0000000000000..cbbcfe7a85d57 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanCreationContext.java @@ -0,0 +1,122 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.telemetry.tracing.attributes.Attributes; + +/** + * Context for span details. + * + * @opensearch.experimental + */ +@ExperimentalApi +public final class SpanCreationContext { + private String spanName; + private Attributes attributes; + private SpanKind spanKind = SpanKind.INTERNAL; + private SpanContext parent; + + /** + * Constructor. + */ + private SpanCreationContext() {} + + /** + * Sets the span type to server. + * @return spanCreationContext + */ + public static SpanCreationContext server() { + SpanCreationContext spanCreationContext = new SpanCreationContext(); + spanCreationContext.spanKind = SpanKind.SERVER; + return spanCreationContext; + } + + /** + * Sets the span type to client. + * @return spanCreationContext + */ + public static SpanCreationContext client() { + SpanCreationContext spanCreationContext = new SpanCreationContext(); + spanCreationContext.spanKind = SpanKind.CLIENT; + return spanCreationContext; + } + + /** + * Sets the span type to internal. + * @return spanCreationContext + */ + public static SpanCreationContext internal() { + SpanCreationContext spanCreationContext = new SpanCreationContext(); + spanCreationContext.spanKind = SpanKind.INTERNAL; + return spanCreationContext; + } + + /** + * Sets the span name. + * @param spanName span name. + * @return spanCreationContext + */ + public SpanCreationContext name(String spanName) { + this.spanName = spanName; + return this; + } + + /** + * Sets the span attributes. + * @param attributes attributes. + * @return spanCreationContext + */ + public SpanCreationContext attributes(Attributes attributes) { + this.attributes = attributes; + return this; + } + + /** + * Sets the parent for spann + * @param parent parent + * @return spanCreationContext + */ + public SpanCreationContext parent(SpanContext parent) { + this.parent = parent; + return this; + } + + /** + * Returns the span name. + * @return span name + */ + public String getSpanName() { + return spanName; + } + + /** + * Returns the span attributes. + * @return attributes. + */ + public Attributes getAttributes() { + return attributes; + } + + /** + * Returns the span kind. + * @return spankind. + */ + public SpanKind getSpanKind() { + return spanKind; + } + + /** + * Returns the parent span + * @return parent. + */ + public SpanContext getParent() { + return parent; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanKind.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanKind.java new file mode 100644 index 0000000000000..d674bb2c866f2 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanKind.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.PublicApi; + +/** + * Type of Span. + */ +@PublicApi(since = "2.11.0") +public enum SpanKind { + /** + * Span represents the client side code. + */ + CLIENT, + /** + * Span represents the server side code. + */ + SERVER, + + /** + * Span represents the internal calls. This is the default value of a span type. + */ + INTERNAL; +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanReference.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanReference.java new file mode 100644 index 0000000000000..945682c3df390 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanReference.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.InternalApi; + +/** + * Wrapper class to hold reference of Span + * + * @opensearch.internal + */ +@InternalApi +final class SpanReference { + + private Span span; + + /** + * Creates the wrapper with given span + * @param span the span object to wrap + */ + public SpanReference(Span span) { + this.span = span; + } + + /** + * Returns the span object + * @return underlying span + */ + public Span getSpan() { + return span; + } + + /** + * Updates the underlying span + * @param span underlying span + */ + public void setSpan(Span span) { + this.span = span; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanScope.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanScope.java new file mode 100644 index 0000000000000..8bccd5774a340 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanScope.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.telemetry.tracing.noop.NoopSpanScope; + +/** + * An auto-closeable that represents scope of the span. + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface SpanScope extends AutoCloseable { + + /** + * No-op Scope implementation + */ + SpanScope NO_OP = new NoopSpanScope(); + + @Override + void close(); + + /** + * Attaches span to the {@link SpanScope} + * @return spanScope + */ + SpanScope attach(); + + /** + * Returns span attached with the {@link SpanScope} + * @return span. + */ + Span getSpan(); +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Tracer.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Tracer.java new file mode 100644 index 0000000000000..49295f417df9e --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Tracer.java @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.telemetry.tracing.http.HttpTracer; + +import java.io.Closeable; + +/** + * Tracer is the interface used to create a {@link Span} + * It automatically handles the context propagation between threads, tasks, nodes etc. + * + * All methods on the Tracer object are multi-thread safe. + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface Tracer extends HttpTracer, Closeable { + /** + * Starts the {@link Span} with given {@link SpanCreationContext} + * + * @param context span context + * @return span, must be closed. + */ + Span startSpan(SpanCreationContext context); + + /** + * Returns the current span. + * @return current wrapped span. + */ + SpanContext getCurrentSpan(); + + /** + * Start the span and scoped it. This must be used for scenarios where {@link SpanScope} and {@link Span} lifecycles + * are same and ends within the same thread where created. + * @param spanCreationContext span creation context + * @return scope of the span, must be closed with explicit close or with try-with-resource + */ + ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext); + + /** + * Creates the Span Scope for a current thread. It's mandatory to scope the span just after creation so that it will + * automatically manage the attach /detach to the current thread. + * @param span span to be scoped + * @return ScopedSpan + */ + SpanScope withSpanInScope(Span span); + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracerContextStorage.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracerContextStorage.java new file mode 100644 index 0000000000000..958d054948483 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracerContextStorage.java @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.InternalApi; + +/** + * Storage interface used for storing tracing context + * @param key type + * @param value type + * + * @opensearch.internal + */ +@InternalApi +public interface TracerContextStorage { + /** + * Key for storing current span + */ + String CURRENT_SPAN = "current_span"; + + /** + * Fetches value corresponding to key + * @param key of the tracing context + * @return value for key + */ + V get(K key); + + /** + * Puts tracing context value with key + * @param key of the tracing context + * @param value of the tracing context + */ + void put(K key, V value); +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingContextPropagator.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingContextPropagator.java new file mode 100644 index 0000000000000..5fbc5d329e227 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingContextPropagator.java @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.ExperimentalApi; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; + +/** + * Interface defining the tracing related context propagation + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface TracingContextPropagator { + + /** + * Extracts current span from context + * @param props properties + * @return current span + */ + Optional extract(Map props); + + /** + * Extracts current span from HTTP headers. + * + * @param headers request headers to extract the context from + * @return current span + */ + Optional extractFromHeaders(Map> headers); + + /** + * Injects tracing context + * + * @param currentSpan the current active span + * @param setter to add tracing context in map + */ + void inject(Span currentSpan, BiConsumer setter); + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingTelemetry.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingTelemetry.java new file mode 100644 index 0000000000000..f04a505088424 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingTelemetry.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.annotation.ExperimentalApi; + +import java.io.Closeable; + +/** + * Interface for tracing telemetry providers + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface TracingTelemetry extends Closeable { + + /** + * Creates span with provided arguments + * + * @param spanCreationContext span creation context. + * @param parentSpan parent span. + * @return span instance + */ + Span createSpan(SpanCreationContext spanCreationContext, Span parentSpan); + + /** + * provides tracing context propagator + * @return tracing context propagator instance + */ + TracingContextPropagator getContextPropagator(); + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/attributes/Attributes.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/attributes/Attributes.java new file mode 100644 index 0000000000000..6dcc9c5468b38 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/attributes/Attributes.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.attributes; + +import org.opensearch.common.annotation.ExperimentalApi; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Class to create attributes for a span. + * + * @opensearch.experimental + */ +@ExperimentalApi +public class Attributes { + private final Map attributesMap; + /** + * Empty value. + */ + public final static Attributes EMPTY = new Attributes(Collections.emptyMap()); + + /** + * Factory method. + * @return attributes. + */ + public static Attributes create() { + return new Attributes(new HashMap<>()); + } + + /** + * Constructor. + */ + private Attributes(Map attributesMap) { + this.attributesMap = attributesMap; + } + + /** + * Add String attribute. + * @param key key + * @param value value + * @return Same instance. + */ + public Attributes addAttribute(String key, String value) { + Objects.requireNonNull(value, "value cannot be null"); + attributesMap.put(key, value); + return this; + } + + /** + * Add long attribute. + * @param key key + * @param value value + * @return Same instance. + */ + public Attributes addAttribute(String key, long value) { + attributesMap.put(key, value); + return this; + }; + + /** + * Add double attribute. + * @param key key + * @param value value + * @return Same instance. + */ + public Attributes addAttribute(String key, double value) { + attributesMap.put(key, value); + return this; + }; + + /** + * Add boolean attribute. + * @param key key + * @param value value + * @return Same instance. + */ + public Attributes addAttribute(String key, boolean value) { + attributesMap.put(key, value); + return this; + }; + + /** + * Returns the attribute map. + * @return attributes map + */ + public Map getAttributesMap() { + return Collections.unmodifiableMap(attributesMap); + } + +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/attributes/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/attributes/package-info.java new file mode 100644 index 0000000000000..ccd56786f63ef --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/attributes/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains attributes management + */ +package org.opensearch.telemetry.tracing.attributes; diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/HttpTracer.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/HttpTracer.java new file mode 100644 index 0000000000000..b0692f1b62a48 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/HttpTracer.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.http; + +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.telemetry.tracing.Span; +import org.opensearch.telemetry.tracing.SpanCreationContext; + +import java.util.List; +import java.util.Map; + +/** + * HttpTracer helps in creating a {@link Span} which reads the incoming tracing information + * from the HttpRequest header and propagate the span accordingly. + * + * All methods on the Tracer object are multi-thread safe. + * + * @opensearch.experimental + */ +@ExperimentalApi +public interface HttpTracer { + /** + * Start the span with propagating the tracing info from the HttpRequest header. + * + * @param spanCreationContext span name. + * @param header http request header. + * @return span. + */ + Span startSpan(SpanCreationContext spanCreationContext, Map> header); +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/package-info.java new file mode 100644 index 0000000000000..9feb862a4e010 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains No-op implementations + */ +package org.opensearch.telemetry.tracing.http; diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopScopedSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopScopedSpan.java new file mode 100644 index 0000000000000..fc296d3689645 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopScopedSpan.java @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.noop; + +import org.opensearch.common.annotation.InternalApi; +import org.opensearch.telemetry.tracing.ScopedSpan; + +/** + * No-op implementation of SpanScope + * + * @opensearch.internal + */ +@InternalApi +public final class NoopScopedSpan implements ScopedSpan { + + /** + * No-args constructor + */ + public NoopScopedSpan() {} + + @Override + public void addAttribute(String key, String value) { + + } + + @Override + public void addAttribute(String key, long value) { + + } + + @Override + public void addAttribute(String key, double value) { + + } + + @Override + public void addAttribute(String key, boolean value) { + + } + + @Override + public void addEvent(String event) { + + } + + @Override + public void setError(Exception exception) { + + } + + @Override + public void close() { + + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpan.java new file mode 100644 index 0000000000000..f41e11017d155 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpan.java @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.noop; + +import org.opensearch.common.annotation.InternalApi; +import org.opensearch.telemetry.tracing.Span; + +/** + * No-op implementation of {@link org.opensearch.telemetry.tracing.Span} + * + * @opensearch.internal + */ +@InternalApi +public class NoopSpan implements Span { + + /** + * No-op Span instance + */ + public final static NoopSpan INSTANCE = new NoopSpan(); + + private NoopSpan() { + + } + + @Override + public void endSpan() { + + } + + @Override + public Span getParentSpan() { + return null; + } + + @Override + public String getSpanName() { + return "noop-span"; + } + + @Override + public void addAttribute(String key, String value) { + + } + + @Override + public void addAttribute(String key, Long value) { + + } + + @Override + public void addAttribute(String key, Double value) { + + } + + @Override + public void addAttribute(String key, Boolean value) { + + } + + @Override + public void setError(Exception exception) { + + } + + @Override + public void addEvent(String event) { + + } + + @Override + public String getTraceId() { + return "noop-trace-id"; + } + + @Override + public String getSpanId() { + return "noop-span-id"; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpanScope.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpanScope.java new file mode 100644 index 0000000000000..bb04a67657d6e --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpanScope.java @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.noop; + +import org.opensearch.common.annotation.InternalApi; +import org.opensearch.telemetry.tracing.Span; +import org.opensearch.telemetry.tracing.SpanScope; + +/** + * No-op implementation of {@link SpanScope} + * + * @opensearch.internal + */ +@InternalApi +public class NoopSpanScope implements SpanScope { + /** + * Constructor. + */ + public NoopSpanScope() { + + } + + @Override + public void close() { + + } + + @Override + public SpanScope attach() { + return this; + } + + @Override + public Span getSpan() { + return NoopSpan.INSTANCE; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopTracer.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopTracer.java new file mode 100644 index 0000000000000..c073e8d3e766f --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopTracer.java @@ -0,0 +1,66 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.noop; + +import org.opensearch.common.annotation.InternalApi; +import org.opensearch.telemetry.tracing.ScopedSpan; +import org.opensearch.telemetry.tracing.Span; +import org.opensearch.telemetry.tracing.SpanContext; +import org.opensearch.telemetry.tracing.SpanCreationContext; +import org.opensearch.telemetry.tracing.SpanScope; +import org.opensearch.telemetry.tracing.Tracer; + +import java.util.List; +import java.util.Map; + +/** + * No-op implementation of Tracer + * + * @opensearch.internal + */ +@InternalApi +public class NoopTracer implements Tracer { + + /** + * No-op Tracer instance + */ + public static final Tracer INSTANCE = new NoopTracer(); + + private NoopTracer() {} + + @Override + public Span startSpan(SpanCreationContext context) { + return NoopSpan.INSTANCE; + } + + @Override + public SpanContext getCurrentSpan() { + return new SpanContext(NoopSpan.INSTANCE); + } + + @Override + public ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext) { + return ScopedSpan.NO_OP; + } + + @Override + public SpanScope withSpanInScope(Span span) { + return SpanScope.NO_OP; + } + + @Override + public void close() { + + } + + @Override + public Span startSpan(SpanCreationContext spanCreationContext, Map> header) { + return NoopSpan.INSTANCE; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/package-info.java new file mode 100644 index 0000000000000..b9d83e7bc7275 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains No-op implementations + */ +package org.opensearch.telemetry.tracing.noop; diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/package-info.java new file mode 100644 index 0000000000000..66898bd58b753 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains tracing related classes + */ +package org.opensearch.telemetry.tracing; diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/TraceableRunnable.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/TraceableRunnable.java new file mode 100644 index 0000000000000..8a61dd70d6d54 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/TraceableRunnable.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.runnable; + +import org.opensearch.telemetry.tracing.ScopedSpan; +import org.opensearch.telemetry.tracing.SpanCreationContext; +import org.opensearch.telemetry.tracing.Tracer; + +/** + * Wraps the runnable and add instrumentation to trace the {@link Runnable} + */ +public class TraceableRunnable implements Runnable { + private final Runnable runnable; + private final SpanCreationContext spanCreationContext; + private final Tracer tracer; + + /** + * Constructor. + * @param tracer tracer + * @param spanCreationContext spanCreationContext + * @param runnable runnable. + */ + public TraceableRunnable(Tracer tracer, SpanCreationContext spanCreationContext, Runnable runnable) { + this.tracer = tracer; + this.spanCreationContext = spanCreationContext; + this.runnable = runnable; + } + + @Override + public void run() { + try (ScopedSpan spanScope = tracer.startScopedSpan(spanCreationContext)) { + runnable.run(); + } + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/package-info.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/package-info.java new file mode 100644 index 0000000000000..9f696a4ac573e --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Contains tracing related classes + */ +package org.opensearch.telemetry.tracing.runnable; diff --git a/libs/telemetry/src/test/java/org/opensearch/telemetry/metrics/DefaultMetricsRegistryTests.java b/libs/telemetry/src/test/java/org/opensearch/telemetry/metrics/DefaultMetricsRegistryTests.java new file mode 100644 index 0000000000000..6171641db5f07 --- /dev/null +++ b/libs/telemetry/src/test/java/org/opensearch/telemetry/metrics/DefaultMetricsRegistryTests.java @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import org.opensearch.test.OpenSearchTestCase; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultMetricsRegistryTests extends OpenSearchTestCase { + + private MetricsTelemetry metricsTelemetry; + private DefaultMetricsRegistry defaultMeterRegistry; + + @Override + public void setUp() throws Exception { + super.setUp(); + metricsTelemetry = mock(MetricsTelemetry.class); + defaultMeterRegistry = new DefaultMetricsRegistry(metricsTelemetry); + } + + public void testCounter() { + Counter mockCounter = mock(Counter.class); + when(defaultMeterRegistry.createCounter(any(String.class), any(String.class), any(String.class))).thenReturn(mockCounter); + Counter counter = defaultMeterRegistry.createCounter( + "org.opensearch.telemetry.metrics.DefaultMeterRegistryTests.testCounter", + "test counter", + "1" + ); + assertSame(mockCounter, counter); + } + + public void testUpDownCounter() { + Counter mockCounter = mock(Counter.class); + when(defaultMeterRegistry.createUpDownCounter(any(String.class), any(String.class), any(String.class))).thenReturn(mockCounter); + Counter counter = defaultMeterRegistry.createUpDownCounter( + "org.opensearch.telemetry.metrics.DefaultMeterRegistryTests.testUpDownCounter", + "test up-down counter", + "1" + ); + assertSame(mockCounter, counter); + } + +} diff --git a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultScopedSpanTests.java b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultScopedSpanTests.java new file mode 100644 index 0000000000000..1d4871fe1419e --- /dev/null +++ b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultScopedSpanTests.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.test.OpenSearchTestCase; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class DefaultScopedSpanTests extends OpenSearchTestCase { + + @SuppressWarnings("unchecked") + public void testClose() { + Span mockSpan = mock(Span.class); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.close(); + + verify(mockSpan).endSpan(); + verify(mockSpanScope).close(); + } + + public void testAddSpanAttributeString() { + Span mockSpan = mock(Span.class); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addAttribute("key", "value"); + + verify(mockSpan).addAttribute("key", "value"); + } + + public void testAddSpanAttributeLong() { + Span mockSpan = mock(Span.class); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addAttribute("key", 1L); + + verify(mockSpan).addAttribute("key", 1L); + } + + public void testAddSpanAttributeDouble() { + Span mockSpan = mock(Span.class); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addAttribute("key", 1.0); + + verify(mockSpan).addAttribute("key", 1.0); + } + + public void testAddSpanAttributeBoolean() { + Span mockSpan = mock(Span.class); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addAttribute("key", true); + + verify(mockSpan).addAttribute("key", true); + } + + public void testAddEvent() { + Span mockSpan = mock(Span.class); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addEvent("eventName"); + + verify(mockSpan).addEvent("eventName"); + } + + public void testSetError() { + Span mockSpan = mock(Span.class); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + Exception ex = new Exception("error"); + defaultSpanScope.setError(ex); + + verify(mockSpan).setError(ex); + } + +} diff --git a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultTracerTests.java b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultTracerTests.java new file mode 100644 index 0000000000000..0a717e993cb81 --- /dev/null +++ b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultTracerTests.java @@ -0,0 +1,425 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.node.Node; +import org.opensearch.telemetry.tracing.attributes.Attributes; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.telemetry.tracing.MockSpan; +import org.opensearch.test.telemetry.tracing.MockTracingTelemetry; +import org.opensearch.threadpool.ThreadPool; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class DefaultTracerTests extends OpenSearchTestCase { + + private TracingTelemetry mockTracingTelemetry; + private TracerContextStorage mockTracerContextStorage; + private Span mockSpan; + private Span mockParentSpan; + + private SpanScope mockSpanScope; + private ThreadPool threadPool; + private ExecutorService executorService; + private SpanCreationContext spanCreationContext; + + @Override + public void setUp() throws Exception { + super.setUp(); + threadPool = new ThreadPool(Settings.builder().put(Node.NODE_NAME_SETTING.getKey(), "default tracer tests").build()); + executorService = threadPool.executor(ThreadPool.Names.GENERIC); + setupMocks(); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + executorService.shutdown(); + threadPool.shutdownNow(); + } + + public void testCreateSpan() { + DefaultTracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); + + defaultTracer.startSpan(spanCreationContext); + + String spanName = defaultTracer.getCurrentSpan().getSpan().getSpanName(); + assertEquals("span_name", spanName); + } + + @SuppressWarnings("unchecked") + public void testCreateSpanWithAttributesWithMock() { + DefaultTracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); + Attributes attributes = Attributes.create().addAttribute("name", "value"); + SpanCreationContext spanCreationContext = buildSpanCreationContext("span_name", attributes, mockParentSpan); + when(mockTracingTelemetry.createSpan(eq(spanCreationContext), eq(mockParentSpan))).thenReturn(mockSpan); + defaultTracer.startSpan(spanCreationContext); + verify(mockTracingTelemetry).createSpan(eq(spanCreationContext), eq(mockParentSpan)); + } + + @SuppressWarnings("unchecked") + public void testCreateSpanWithAttributesWithParentMock() { + DefaultTracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); + Attributes attributes = Attributes.create().addAttribute("name", "value"); + SpanCreationContext spanCreationContext = buildSpanCreationContext("span_name", attributes, mockParentSpan); + when(mockTracingTelemetry.createSpan(eq(spanCreationContext), eq(mockParentSpan))).thenReturn(mockSpan); + defaultTracer.startSpan(spanCreationContext); + verify(mockTracingTelemetry).createSpan(eq(spanCreationContext), eq(mockParentSpan)); + verify(mockTracerContextStorage, never()).get(TracerContextStorage.CURRENT_SPAN); + } + + public void testCreateSpanWithAttributes() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + DefaultTracer defaultTracer = new DefaultTracer( + tracingTelemetry, + new ThreadContextBasedTracerContextStorage(threadContext, tracingTelemetry) + ); + + SpanCreationContext spanCreationContext = buildSpanCreationContext( + "span_name", + Attributes.create().addAttribute("key1", 1.0).addAttribute("key2", 2l).addAttribute("key3", true).addAttribute("key4", "key4"), + null + ); + + Span span = defaultTracer.startSpan(spanCreationContext); + + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(1.0, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key1")); + assertEquals(2l, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key2")); + assertEquals(true, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key3")); + assertEquals("key4", ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key4")); + span.endSpan(); + } + + public void testCreateSpanWithParent() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + DefaultTracer defaultTracer = new DefaultTracer( + tracingTelemetry, + new ThreadContextBasedTracerContextStorage(new ThreadContext(Settings.EMPTY), tracingTelemetry) + ); + + SpanCreationContext spanCreationContext = buildSpanCreationContext("span_name", null, null); + + Span span = defaultTracer.startSpan(spanCreationContext, null); + + SpanContext parentSpan = defaultTracer.getCurrentSpan(); + + SpanCreationContext spanCreationContext1 = buildSpanCreationContext("span_name_1", Attributes.EMPTY, parentSpan.getSpan()); + + Span span1 = defaultTracer.startSpan(spanCreationContext1); + + assertEquals("span_name_1", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(parentSpan.getSpan(), defaultTracer.getCurrentSpan().getSpan().getParentSpan()); + span1.endSpan(); + span.endSpan(); + } + + @SuppressWarnings("unchecked") + public void testCreateSpanWithContext() { + DefaultTracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); + Attributes attributes = Attributes.create().addAttribute("name", "value"); + SpanCreationContext spanCreationContext = buildSpanCreationContext("span_name", attributes, mockParentSpan); + when(mockTracingTelemetry.createSpan(eq(spanCreationContext), eq(mockParentSpan))).thenReturn(mockSpan); + defaultTracer.startSpan(spanCreationContext); + verify(mockTracingTelemetry).createSpan(eq(spanCreationContext), eq(mockParentSpan)); + } + + public void testCreateSpanWithNullParent() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + DefaultTracer defaultTracer = new DefaultTracer( + tracingTelemetry, + new ThreadContextBasedTracerContextStorage(threadContext, tracingTelemetry) + ); + + SpanCreationContext spanCreationContext = buildSpanCreationContext("span_name", Attributes.EMPTY, null); + + Span span = defaultTracer.startSpan(spanCreationContext); + + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(null, defaultTracer.getCurrentSpan().getSpan().getParentSpan()); + span.endSpan(); + } + + public void testEndSpanByClosingScopedSpan() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + SpanCreationContext spanCreationContext = buildSpanCreationContext("span_name", Attributes.EMPTY, null); + + ScopedSpan scopedSpan = defaultTracer.startScopedSpan(spanCreationContext); + + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(((DefaultScopedSpan) scopedSpan).getSpanScope(), DefaultSpanScope.getCurrentSpanScope()); + scopedSpan.close(); + assertTrue(((MockSpan) ((DefaultScopedSpan) scopedSpan).getSpan()).hasEnded()); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals(null, DefaultSpanScope.getCurrentSpanScope()); + + } + + public void testEndSpanByClosingScopedSpanMultiple() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + SpanCreationContext spanCreationContext = buildSpanCreationContext("span_name", Attributes.EMPTY, null); + SpanCreationContext spanCreationContext1 = buildSpanCreationContext("span_name_1", Attributes.EMPTY, null); + + ScopedSpan scopedSpan = defaultTracer.startScopedSpan(spanCreationContext); + ScopedSpan scopedSpan1 = defaultTracer.startScopedSpan(spanCreationContext1); + + assertEquals("span_name_1", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(((DefaultScopedSpan) scopedSpan1).getSpanScope(), DefaultSpanScope.getCurrentSpanScope()); + + scopedSpan1.close(); + assertTrue(((MockSpan) ((DefaultScopedSpan) scopedSpan1).getSpan()).hasEnded()); + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(((DefaultScopedSpan) scopedSpan).getSpanScope(), DefaultSpanScope.getCurrentSpanScope()); + + scopedSpan.close(); + assertTrue(((MockSpan) ((DefaultScopedSpan) scopedSpan).getSpan()).hasEnded()); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals(null, DefaultSpanScope.getCurrentSpanScope()); + + } + + public void testEndSpanByClosingSpanScope() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + SpanCreationContext spanCreationContext = buildSpanCreationContext("span_name", Attributes.EMPTY, null); + Span span = defaultTracer.startSpan(spanCreationContext); + SpanScope spanScope = defaultTracer.withSpanInScope(span); + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(spanScope, DefaultSpanScope.getCurrentSpanScope()); + + span.endSpan(); + spanScope.close(); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertTrue(((MockSpan) span).hasEnded()); + + } + + public void testEndSpanByClosingSpanScopeMultiple() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + Span span = defaultTracer.startSpan(buildSpanCreationContext("span_name", Attributes.EMPTY, null)); + Span span1 = defaultTracer.startSpan(buildSpanCreationContext("span_name_1", Attributes.EMPTY, null)); + SpanScope spanScope = defaultTracer.withSpanInScope(span); + SpanScope spanScope1 = defaultTracer.withSpanInScope(span1); + assertEquals("span_name_1", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(spanScope1, DefaultSpanScope.getCurrentSpanScope()); + + span1.endSpan(); + spanScope1.close(); + + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(spanScope, DefaultSpanScope.getCurrentSpanScope()); + assertTrue(((MockSpan) span1).hasEnded()); + assertFalse(((MockSpan) span).hasEnded()); + span.endSpan(); + spanScope.close(); + + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals(null, DefaultSpanScope.getCurrentSpanScope()); + assertTrue(((MockSpan) span).hasEnded()); + assertTrue(((MockSpan) span1).hasEnded()); + + } + + /** + * 1. CreateSpan in ThreadA (NotScopedSpan) + * 2. create Async task and pass the span + * 3. Scope.close + * 4. verify the current_span is still the same on async thread as the 2 + * 5. verify the main thread has current span as null. + */ + public void testSpanAcrossThreads() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + + CompletableFuture asyncTask = CompletableFuture.runAsync(() -> { + // create a span + Span span = defaultTracer.startSpan(buildSpanCreationContext("span_name_t_1", Attributes.EMPTY, null)); + SpanScope spanScope = defaultTracer.withSpanInScope(span); + + CompletableFuture asyncTask1 = CompletableFuture.runAsync(() -> { + Span spanT2 = defaultTracer.startSpan(buildSpanCreationContext("span_name_t_2", Attributes.EMPTY, null)); + SpanScope spanScopeT2 = defaultTracer.withSpanInScope(spanT2); + assertEquals(spanT2, defaultTracer.getCurrentSpan().getSpan()); + + spanScopeT2.close(); + spanT2.endSpan(); + assertEquals(null, defaultTracer.getCurrentSpan()); + }, executorService); + asyncTask1.join(); + spanScope.close(); + span.endSpan(); + assertEquals(null, defaultTracer.getCurrentSpan()); + }, executorService); + asyncTask.join(); + } + + public void testSpanCloseOnThread2() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + final Span span = defaultTracer.startSpan(buildSpanCreationContext("span_name_t1", Attributes.EMPTY, null)); + try (SpanScope spanScope = defaultTracer.withSpanInScope(span)) { + CompletableFuture asyncTask = CompletableFuture.runAsync(() -> async(new ActionListener() { + @Override + public void onResponse(Boolean response) { + try (SpanScope s = defaultTracer.withSpanInScope(span)) { + assertEquals(span, defaultTracer.getCurrentSpan().getSpan()); + } finally { + span.endSpan(); + } + } + + @Override + public void onFailure(Exception e) { + + } + }), executorService); + assertEquals(span, defaultTracer.getCurrentSpan().getSpan()); + asyncTask.join(); + } + assertEquals(null, defaultTracer.getCurrentSpan()); + } + + private void async(ActionListener actionListener) { + actionListener.onResponse(true); + } + + /** + * 1. CreateSpan in ThreadA (NotScopedSpan) + * 2. create Async task and pass the span + * 3. Inside Async task start a new span. + * 4. Scope.close + * 5. Parent Scope.close + * 6. verify the current_span is still the same on async thread as the 2 + * 7. verify the main thread has current span as null. + */ + public void testSpanAcrossThreadsMultipleSpans() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + + CompletableFuture asyncTask = CompletableFuture.runAsync(() -> { + // create a parent span + Span parentSpan = defaultTracer.startSpan(buildSpanCreationContext("p_span_name", Attributes.EMPTY, null)); + SpanScope parentSpanScope = defaultTracer.withSpanInScope(parentSpan); + // create a span + Span span = defaultTracer.startSpan(buildSpanCreationContext("span_name_t_1", Attributes.EMPTY, null)); + SpanScope spanScope = defaultTracer.withSpanInScope(span); + + CompletableFuture asyncTask1 = CompletableFuture.runAsync(() -> { + Span spanT2 = defaultTracer.startSpan(buildSpanCreationContext("span_name_t_2", Attributes.EMPTY, null)); + SpanScope spanScopeT2 = defaultTracer.withSpanInScope(spanT2); + Span spanT21 = defaultTracer.startSpan(buildSpanCreationContext("span_name_t_2", Attributes.EMPTY, null)); + SpanScope spanScopeT21 = defaultTracer.withSpanInScope(spanT21); + assertEquals(spanT21, defaultTracer.getCurrentSpan().getSpan()); + spanScopeT21.close(); + spanT21.endSpan(); + + spanScopeT2.close(); + spanT2.endSpan(); + + assertEquals(null, defaultTracer.getCurrentSpan()); + }, executorService); + + asyncTask1.join(); + + spanScope.close(); + span.endSpan(); + parentSpanScope.close(); + parentSpan.endSpan(); + assertEquals(null, defaultTracer.getCurrentSpan()); + }, executorService); + asyncTask.join(); + } + + public void testClose() throws IOException { + Tracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); + + defaultTracer.close(); + + verify(mockTracingTelemetry).close(); + } + + @SuppressWarnings("unchecked") + private void setupMocks() { + mockTracingTelemetry = mock(TracingTelemetry.class); + mockSpan = mock(Span.class); + mockParentSpan = mock(Span.class); + mockSpanScope = mock(SpanScope.class); + mockTracerContextStorage = mock(TracerContextStorage.class); + when(mockSpan.getSpanName()).thenReturn("span_name"); + when(mockSpan.getSpanId()).thenReturn("span_id"); + when(mockSpan.getTraceId()).thenReturn("trace_id"); + when(mockSpan.getParentSpan()).thenReturn(mockParentSpan); + when(mockParentSpan.getSpanId()).thenReturn("parent_span_id"); + when(mockParentSpan.getTraceId()).thenReturn("trace_id"); + spanCreationContext = buildSpanCreationContext("span_name", Attributes.EMPTY, mockParentSpan); + when(mockTracerContextStorage.get(TracerContextStorage.CURRENT_SPAN)).thenReturn(mockSpan, mockParentSpan); + when(mockTracingTelemetry.createSpan(eq(spanCreationContext), eq(mockParentSpan))).thenReturn(mockSpan); + } + + private SpanCreationContext buildSpanCreationContext(String spanName, Attributes attributes, Span parentSpan) { + SpanCreationContext spanCreationContext = SpanCreationContext.internal().name(spanName).attributes(attributes); + if (parentSpan != null) { + spanCreationContext.parent(new SpanContext(parentSpan)); + } + return spanCreationContext; + } +} diff --git a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/TraceableRunnableTests.java b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/TraceableRunnableTests.java new file mode 100644 index 0000000000000..4c4f762653d57 --- /dev/null +++ b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/TraceableRunnableTests.java @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.telemetry.tracing.attributes.Attributes; +import org.opensearch.telemetry.tracing.runnable.TraceableRunnable; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.telemetry.tracing.MockSpan; +import org.opensearch.test.telemetry.tracing.MockTracingTelemetry; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class TraceableRunnableTests extends OpenSearchTestCase { + + private final ThreadContextBasedTracerContextStorage contextStorage = new ThreadContextBasedTracerContextStorage( + new ThreadContext(Settings.EMPTY), + new MockTracingTelemetry() + ); + + public void testRunnableWithNullParent() throws Exception { + String spanName = "testRunnable"; + final DefaultTracer defaultTracer = new DefaultTracer(new MockTracingTelemetry(), contextStorage); + final AtomicBoolean isRunnableCompleted = new AtomicBoolean(false); + final AtomicReference spanNameCaptured = new AtomicReference<>(); + final AtomicReference attributeValue = new AtomicReference<>(); + TraceableRunnable traceableRunnable = new TraceableRunnable( + defaultTracer, + SpanCreationContext.internal().name(spanName).attributes(Attributes.create().addAttribute("name", "value")), + () -> { + spanNameCaptured.set(defaultTracer.getCurrentSpan().getSpan().getSpanName()); + attributeValue.set((String) ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("name")); + isRunnableCompleted.set(true); + } + ); + traceableRunnable.run(); + assertTrue(isRunnableCompleted.get()); + assertEquals(spanName, spanNameCaptured.get()); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals("value", attributeValue.get()); + } + + public void testRunnableWithParent() throws Exception { + String spanName = "testRunnable"; + String parentSpanName = "parentSpan"; + DefaultTracer defaultTracer = new DefaultTracer(new MockTracingTelemetry(), contextStorage); + ScopedSpan scopedSpan = defaultTracer.startScopedSpan( + SpanCreationContext.internal().name(parentSpanName).attributes(Attributes.EMPTY) + ); + SpanContext parentSpanContext = defaultTracer.getCurrentSpan(); + AtomicReference currentSpan = new AtomicReference<>(); + final AtomicBoolean isRunnableCompleted = new AtomicBoolean(false); + TraceableRunnable traceableRunnable = new TraceableRunnable( + defaultTracer, + SpanCreationContext.internal() + .name(spanName) + .attributes(Attributes.create().addAttribute("name", "value")) + .parent(parentSpanContext), + () -> { + isRunnableCompleted.set(true); + currentSpan.set(defaultTracer.getCurrentSpan()); + } + ); + traceableRunnable.run(); + assertTrue(isRunnableCompleted.get()); + assertEquals(spanName, currentSpan.get().getSpan().getSpanName()); + assertEquals(((DefaultScopedSpan) scopedSpan).getSpan(), currentSpan.get().getSpan().getParentSpan()); + assertEquals(((DefaultScopedSpan) scopedSpan).getSpan(), defaultTracer.getCurrentSpan().getSpan()); + scopedSpan.close(); + } +} diff --git a/libs/x-content/build.gradle b/libs/x-content/build.gradle index 3d0465783d7a0..dab07c5af336a 100644 --- a/libs/x-content/build.gradle +++ b/libs/x-content/build.gradle @@ -32,6 +32,7 @@ apply plugin: 'opensearch.build' apply plugin: 'opensearch.publish' dependencies { + api project(':libs:opensearch-common') api project(':libs:opensearch-core') api "org.yaml:snakeyaml:${versions.snakeyaml}" @@ -47,6 +48,7 @@ dependencies { testImplementation(project(":test:framework")) { exclude group: 'org.opensearch', module: 'opensearch-x-content' } + testImplementation(project(":libs:opensearch-core")) } diff --git a/libs/x-content/licenses/jackson-core-2.13.2.jar.sha1 b/libs/x-content/licenses/jackson-core-2.13.2.jar.sha1 deleted file mode 100644 index eb8a8bc45f041..0000000000000 --- a/libs/x-content/licenses/jackson-core-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0a6a0e0620d51833feffc67bccb51937b2345763 \ No newline at end of file diff --git a/libs/x-content/licenses/jackson-core-2.15.2.jar.sha1 b/libs/x-content/licenses/jackson-core-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..ec6781b968eed --- /dev/null +++ b/libs/x-content/licenses/jackson-core-2.15.2.jar.sha1 @@ -0,0 +1 @@ +a6fe1836469a69b3ff66037c324d75fc66ef137c \ No newline at end of file diff --git a/libs/x-content/licenses/jackson-dataformat-cbor-2.13.2.jar.sha1 b/libs/x-content/licenses/jackson-dataformat-cbor-2.13.2.jar.sha1 deleted file mode 100644 index 3a4f0e1b17565..0000000000000 --- a/libs/x-content/licenses/jackson-dataformat-cbor-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4fc77e1ec6922fc48bf1181e4b38f600dac222ff \ No newline at end of file diff --git a/libs/x-content/licenses/jackson-dataformat-cbor-2.15.2.jar.sha1 b/libs/x-content/licenses/jackson-dataformat-cbor-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..0022265a84b68 --- /dev/null +++ b/libs/x-content/licenses/jackson-dataformat-cbor-2.15.2.jar.sha1 @@ -0,0 +1 @@ +baafc85c70765594add14bd93f3efd68e1945b76 \ No newline at end of file diff --git a/libs/x-content/licenses/jackson-dataformat-smile-2.13.2.jar.sha1 b/libs/x-content/licenses/jackson-dataformat-smile-2.13.2.jar.sha1 deleted file mode 100644 index 86a53f72de66e..0000000000000 --- a/libs/x-content/licenses/jackson-dataformat-smile-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -984bb22f310ebbedc967d206e672f8acf766a98e \ No newline at end of file diff --git a/libs/x-content/licenses/jackson-dataformat-smile-2.15.2.jar.sha1 b/libs/x-content/licenses/jackson-dataformat-smile-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..2b8caad846fec --- /dev/null +++ b/libs/x-content/licenses/jackson-dataformat-smile-2.15.2.jar.sha1 @@ -0,0 +1 @@ +16d1dd22f7d641459ed056399d4f7df0220f1176 \ No newline at end of file diff --git a/libs/x-content/licenses/jackson-dataformat-yaml-2.13.2.jar.sha1 b/libs/x-content/licenses/jackson-dataformat-yaml-2.13.2.jar.sha1 deleted file mode 100644 index 1cba175acf2ae..0000000000000 --- a/libs/x-content/licenses/jackson-dataformat-yaml-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -5601496b5b6e43d947aeeffbffadb2b18961c731 \ No newline at end of file diff --git a/libs/x-content/licenses/jackson-dataformat-yaml-2.15.2.jar.sha1 b/libs/x-content/licenses/jackson-dataformat-yaml-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..4ad7255e2318f --- /dev/null +++ b/libs/x-content/licenses/jackson-dataformat-yaml-2.15.2.jar.sha1 @@ -0,0 +1 @@ +58194ff9f51915ad6bf6b6f24818232d7566418a \ No newline at end of file diff --git a/libs/x-content/licenses/snakeyaml-1.26.jar.sha1 b/libs/x-content/licenses/snakeyaml-1.26.jar.sha1 deleted file mode 100644 index fde3aba8edad0..0000000000000 --- a/libs/x-content/licenses/snakeyaml-1.26.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a78a8747147d2c5807683e76ec2b633e95c14fe9 \ No newline at end of file diff --git a/libs/x-content/licenses/snakeyaml-2.1.jar.sha1 b/libs/x-content/licenses/snakeyaml-2.1.jar.sha1 new file mode 100644 index 0000000000000..5586b210a9736 --- /dev/null +++ b/libs/x-content/licenses/snakeyaml-2.1.jar.sha1 @@ -0,0 +1 @@ +c79f47315517560b5bd6a62376ee385e48105437 \ No newline at end of file diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentFactory.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentFactory.java index 63b1ef6af752d..9f423bc9abad3 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentFactory.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentFactory.java @@ -32,29 +32,26 @@ package org.opensearch.common.xcontent; -import com.fasterxml.jackson.dataformat.cbor.CBORConstants; -import com.fasterxml.jackson.dataformat.smile.SmileConstants; -import org.opensearch.common.xcontent.cbor.CborXContent; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.common.xcontent.smile.SmileXContent; import org.opensearch.common.xcontent.yaml.YamlXContent; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; /** - * A one stop to use {@link org.opensearch.common.xcontent.XContent} and {@link XContentBuilder}. + * A one stop to use {@link XContent} and {@link XContentBuilder}. */ public class XContentFactory { - static final int GUESS_HEADER_LENGTH = 20; - /** * Returns a content builder using JSON format ({@link org.opensearch.common.xcontent.XContentType#JSON}. */ public static XContentBuilder jsonBuilder() throws IOException { - return contentBuilder(XContentType.JSON); + return MediaTypeRegistry.contentBuilder(XContentType.JSON); } /** @@ -68,7 +65,7 @@ public static XContentBuilder jsonBuilder(OutputStream os) throws IOException { * Returns a content builder using SMILE format ({@link org.opensearch.common.xcontent.XContentType#SMILE}. */ public static XContentBuilder smileBuilder() throws IOException { - return contentBuilder(XContentType.SMILE); + return MediaTypeRegistry.contentBuilder(XContentType.SMILE); } /** @@ -82,7 +79,7 @@ public static XContentBuilder smileBuilder(OutputStream os) throws IOException { * Returns a content builder using YAML format ({@link org.opensearch.common.xcontent.XContentType#YAML}. */ public static XContentBuilder yamlBuilder() throws IOException { - return contentBuilder(XContentType.YAML); + return MediaTypeRegistry.contentBuilder(XContentType.YAML); } /** @@ -96,261 +93,6 @@ public static XContentBuilder yamlBuilder(OutputStream os) throws IOException { * Returns a content builder using CBOR format ({@link org.opensearch.common.xcontent.XContentType#CBOR}. */ public static XContentBuilder cborBuilder() throws IOException { - return contentBuilder(XContentType.CBOR); - } - - /** - * Constructs a new cbor builder that will output the result into the provided output stream. - */ - public static XContentBuilder cborBuilder(OutputStream os) throws IOException { - return new XContentBuilder(CborXContent.cborXContent, os); - } - - /** - * Constructs a xcontent builder that will output the result into the provided output stream. - */ - public static XContentBuilder contentBuilder(XContentType type, OutputStream outputStream) throws IOException { - if (type == XContentType.JSON) { - return jsonBuilder(outputStream); - } else if (type == XContentType.SMILE) { - return smileBuilder(outputStream); - } else if (type == XContentType.YAML) { - return yamlBuilder(outputStream); - } else if (type == XContentType.CBOR) { - return cborBuilder(outputStream); - } - throw new IllegalArgumentException("No matching content type for " + type); - } - - /** - * Returns a binary content builder for the provided content type. - */ - public static XContentBuilder contentBuilder(XContentType type) throws IOException { - if (type == XContentType.JSON) { - return JsonXContent.contentBuilder(); - } else if (type == XContentType.SMILE) { - return SmileXContent.contentBuilder(); - } else if (type == XContentType.YAML) { - return YamlXContent.contentBuilder(); - } else if (type == XContentType.CBOR) { - return CborXContent.contentBuilder(); - } - throw new IllegalArgumentException("No matching content type for " + type); - } - - /** - * Returns the {@link org.opensearch.common.xcontent.XContent} for the provided content type. - */ - public static XContent xContent(XContentType type) { - if (type == null) { - throw new IllegalArgumentException("Cannot get xcontent for unknown type"); - } - return type.xContent(); - } - - /** - * Guesses the content type based on the provided char sequence. - * - * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. - * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. - * This method is deprecated to prevent usages of it from spreading further without specific reasons. - */ - @Deprecated - public static XContentType xContentType(CharSequence content) { - int length = content.length() < GUESS_HEADER_LENGTH ? content.length() : GUESS_HEADER_LENGTH; - if (length == 0) { - return null; - } - char first = content.charAt(0); - if (first == '{') { - return XContentType.JSON; - } - // Should we throw a failure here? Smile idea is to use it in bytes.... - if (length > 2 - && first == SmileConstants.HEADER_BYTE_1 - && content.charAt(1) == SmileConstants.HEADER_BYTE_2 - && content.charAt(2) == SmileConstants.HEADER_BYTE_3) { - return XContentType.SMILE; - } - if (length > 2 && first == '-' && content.charAt(1) == '-' && content.charAt(2) == '-') { - return XContentType.YAML; - } - - // CBOR is not supported - - for (int i = 0; i < length; i++) { - char c = content.charAt(i); - if (c == '{') { - return XContentType.JSON; - } - if (Character.isWhitespace(c) == false) { - break; - } - } - return null; - } - - /** - * Guesses the content (type) based on the provided char sequence and returns the corresponding {@link XContent} - * - * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. - * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. - * This method is deprecated to prevent usages of it from spreading further without specific reasons. - */ - @Deprecated - public static XContent xContent(CharSequence content) { - XContentType type = xContentType(content); - if (type == null) { - throw new XContentParseException("Failed to derive xcontent"); - } - return xContent(type); - } - - /** - * Guesses the content type based on the provided bytes and returns the corresponding {@link XContent} - * - * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. - * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. - * This method is deprecated to prevent usages of it from spreading further without specific reasons. - */ - @Deprecated - public static XContent xContent(byte[] data) { - return xContent(data, 0, data.length); - } - - /** - * Guesses the content type based on the provided bytes and returns the corresponding {@link XContent} - * - * @deprecated guessing the content type should not be needed ideally. We should rather know the content type upfront or read it - * from headers. Till we fixed the REST layer to read the Content-Type header, that should be the only place where guessing is needed. - */ - @Deprecated - public static XContent xContent(byte[] data, int offset, int length) { - XContentType type = xContentType(data, offset, length); - if (type == null) { - throw new XContentParseException("Failed to derive xcontent"); - } - return xContent(type); - } - - /** - * Guesses the content type based on the provided input stream without consuming it. - * - * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. - * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. - * This method is deprecated to prevent usages of it from spreading further without specific reasons. - */ - @Deprecated - public static XContentType xContentType(InputStream si) throws IOException { - /* - * We need to guess the content type. To do this, we look for the first non-whitespace character and then try to guess the content - * type on the GUESS_HEADER_LENGTH bytes that follow. We do this in a way that does not modify the initial read position in the - * underlying input stream. This is why the input stream must support mark/reset and why we repeatedly mark the read position and - * reset. - */ - if (si.markSupported() == false) { - throw new IllegalArgumentException("Cannot guess the xcontent type without mark/reset support on " + si.getClass()); - } - si.mark(Integer.MAX_VALUE); - try { - // scan until we find the first non-whitespace character or the end of the stream - int current; - do { - current = si.read(); - if (current == -1) { - return null; - } - } while (Character.isWhitespace((char) current)); - // now guess the content type off the next GUESS_HEADER_LENGTH bytes including the current byte - final byte[] firstBytes = new byte[GUESS_HEADER_LENGTH]; - firstBytes[0] = (byte) current; - int read = 1; - while (read < GUESS_HEADER_LENGTH) { - final int r = si.read(firstBytes, read, GUESS_HEADER_LENGTH - read); - if (r == -1) { - break; - } - read += r; - } - return xContentType(firstBytes, 0, read); - } finally { - si.reset(); - } - - } - - /** - * Guesses the content type based on the provided bytes. - * - * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. - * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. - * This method is deprecated to prevent usages of it from spreading further without specific reasons. - */ - @Deprecated - public static XContentType xContentType(byte[] bytes) { - return xContentType(bytes, 0, bytes.length); - } - - /** - * Guesses the content type based on the provided bytes. - * - * @deprecated the content type should not be guessed except for few cases where we effectively don't know the content type. - * The REST layer should move to reading the Content-Type header instead. There are other places where auto-detection may be needed. - * This method is deprecated to prevent usages of it from spreading further without specific reasons. - */ - @Deprecated - public static XContentType xContentType(byte[] bytes, int offset, int length) { - int totalLength = bytes.length; - if (totalLength == 0 || length == 0) { - return null; - } else if ((offset + length) > totalLength) { - return null; - } - byte first = bytes[offset]; - if (first == '{') { - return XContentType.JSON; - } - if (length > 2 - && first == SmileConstants.HEADER_BYTE_1 - && bytes[offset + 1] == SmileConstants.HEADER_BYTE_2 - && bytes[offset + 2] == SmileConstants.HEADER_BYTE_3) { - return XContentType.SMILE; - } - if (length > 2 && first == '-' && bytes[offset + 1] == '-' && bytes[offset + 2] == '-') { - return XContentType.YAML; - } - // CBOR logic similar to CBORFactory#hasCBORFormat - if (first == CBORConstants.BYTE_OBJECT_INDEFINITE && length > 1) { - return XContentType.CBOR; - } - if (CBORConstants.hasMajorType(CBORConstants.MAJOR_TYPE_TAG, first) && length > 2) { - // Actually, specific "self-describe tag" is a very good indicator - if (first == (byte) 0xD9 && bytes[offset + 1] == (byte) 0xD9 && bytes[offset + 2] == (byte) 0xF7) { - return XContentType.CBOR; - } - } - // for small objects, some encoders just encode as major type object, we can safely - // say its CBOR since it doesn't contradict SMILE or JSON, and its a last resort - if (CBORConstants.hasMajorType(CBORConstants.MAJOR_TYPE_OBJECT, first)) { - return XContentType.CBOR; - } - - int jsonStart = 0; - // JSON may be preceded by UTF-8 BOM - if (length > 3 && first == (byte) 0xEF && bytes[offset + 1] == (byte) 0xBB && bytes[offset + 2] == (byte) 0xBF) { - jsonStart = 3; - } - - // a last chance for JSON - for (int i = jsonStart; i < length; i++) { - byte b = bytes[offset + i]; - if (b == '{') { - return XContentType.JSON; - } - if (Character.isWhitespace(b) == false) { - break; - } - } - return null; + return MediaTypeRegistry.contentBuilder(XContentType.CBOR); } } diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentType.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentType.java index b0986d603ef23..9d1581a3a1517 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentType.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentType.java @@ -32,18 +32,25 @@ package org.opensearch.common.xcontent; +import com.fasterxml.jackson.dataformat.cbor.CBORConstants; +import com.fasterxml.jackson.dataformat.smile.SmileConstants; + import org.opensearch.common.xcontent.cbor.CborXContent; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.common.xcontent.smile.SmileXContent; import org.opensearch.common.xcontent.yaml.YamlXContent; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; -import java.util.Locale; -import java.util.Objects; +import java.io.IOException; +import java.io.OutputStream; /** - * The content type of {@link org.opensearch.common.xcontent.XContent}. + * The content type of {@link XContent}. */ -public enum XContentType { +public enum XContentType implements MediaType { /** * A JSON based content type. @@ -60,7 +67,7 @@ public String mediaType() { } @Override - public String shortName() { + public String subtype() { return "json"; } @@ -68,6 +75,26 @@ public String shortName() { public XContent xContent() { return JsonXContent.jsonXContent; } + + @Override + public boolean detectedXContent(final byte[] bytes, int offset, int length) { + return bytes[offset] == '{'; + } + + @Override + public boolean detectedXContent(final CharSequence content, final int length) { + return content.charAt(0) == '{'; + } + + @Override + public XContentBuilder contentBuilder() throws IOException { + return JsonXContent.contentBuilder(); + } + + @Override + public XContentBuilder contentBuilder(final OutputStream os) throws IOException { + return new XContentBuilder(JsonXContent.jsonXContent, os); + } }, /** * The jackson based smile binary format. Fast and compact binary format. @@ -79,7 +106,7 @@ public String mediaTypeWithoutParameters() { } @Override - public String shortName() { + public String subtype() { return "smile"; } @@ -87,6 +114,32 @@ public String shortName() { public XContent xContent() { return SmileXContent.smileXContent; } + + @Override + public boolean detectedXContent(final byte[] bytes, int offset, int length) { + return length > 2 + && bytes[offset] == SmileConstants.HEADER_BYTE_1 + && bytes[offset + 1] == SmileConstants.HEADER_BYTE_2 + && bytes[offset + 2] == SmileConstants.HEADER_BYTE_3; + } + + @Override + public boolean detectedXContent(final CharSequence content, final int length) { + return length > 2 + && content.charAt(0) == SmileConstants.HEADER_BYTE_1 + && content.charAt(1) == SmileConstants.HEADER_BYTE_2 + && content.charAt(2) == SmileConstants.HEADER_BYTE_3; + } + + @Override + public XContentBuilder contentBuilder() throws IOException { + return SmileXContent.contentBuilder(); + } + + @Override + public XContentBuilder contentBuilder(final OutputStream os) throws IOException { + return new XContentBuilder(SmileXContent.smileXContent, os); + } }, /** * A YAML based content type. @@ -98,7 +151,7 @@ public String mediaTypeWithoutParameters() { } @Override - public String shortName() { + public String subtype() { return "yaml"; } @@ -106,6 +159,26 @@ public String shortName() { public XContent xContent() { return YamlXContent.yamlXContent; } + + @Override + public boolean detectedXContent(final byte[] bytes, int offset, int length) { + return length > 2 && bytes[offset] == '-' && bytes[offset + 1] == '-' && bytes[offset + 2] == '-'; + } + + @Override + public boolean detectedXContent(final CharSequence content, final int length) { + return length > 2 && content.charAt(0) == '-' && content.charAt(1) == '-' && content.charAt(2) == '-'; + } + + @Override + public XContentBuilder contentBuilder() throws IOException { + return YamlXContent.contentBuilder(); + } + + @Override + public XContentBuilder contentBuilder(final OutputStream os) throws IOException { + return new XContentBuilder(YamlXContent.yamlXContent, os); + } }, /** * A CBOR based content type. @@ -117,7 +190,7 @@ public String mediaTypeWithoutParameters() { } @Override - public String shortName() { + public String subtype() { return "cbor"; } @@ -125,74 +198,42 @@ public String shortName() { public XContent xContent() { return CborXContent.cborXContent; } - }; - /** - * Accepts either a format string, which is equivalent to {@link XContentType#shortName()} or a media type that optionally has - * parameters and attempts to match the value to an {@link XContentType}. The comparisons are done in lower case format and this method - * also supports a wildcard accept for {@code application/*}. This method can be used to parse the {@code Accept} HTTP header or a - * format query string parameter. This method will return {@code null} if no match is found - */ - public static XContentType fromMediaTypeOrFormat(String mediaType) { - if (mediaType == null) { - return null; - } - - mediaType = removeVersionInMediaType(mediaType); - for (XContentType type : values()) { - if (isSameMediaTypeOrFormatAs(mediaType, type)) { - return type; + @Override + public boolean detectedXContent(final byte[] bytes, int offset, int length) { + // CBOR logic similar to CBORFactory#hasCBORFormat + if (bytes[offset] == CBORConstants.BYTE_OBJECT_INDEFINITE && length > 1) { + return true; } + if (CBORConstants.hasMajorType(CBORConstants.MAJOR_TYPE_TAG, bytes[offset]) && length > 2) { + // Actually, specific "self-describe tag" is a very good indicator + if (bytes[offset] == (byte) 0xD9 && bytes[offset + 1] == (byte) 0xD9 && bytes[offset + 2] == (byte) 0xF7) { + return true; + } + } + // for small objects, some encoders just encode as major type object, we can safely + // say its CBOR since it doesn't contradict SMILE or JSON, and its a last resort + if (CBORConstants.hasMajorType(CBORConstants.MAJOR_TYPE_OBJECT, bytes[offset])) { + return true; + } + return false; } - final String lowercaseMediaType = mediaType.toLowerCase(Locale.ROOT); - if (lowercaseMediaType.startsWith("application/*")) { - return JSON; - } - - return null; - } - /** - * Clients compatible with ES 7.x might start sending media types with versioned media type - * in a form of application/vnd.opensearch+json;compatible-with=7. - * This has to be removed in order to be used in 7.x server. - * The same client connecting using that media type will be able to communicate with ES 8 thanks to compatible API. - * @param mediaType - a media type used on Content-Type header, might contain versioned media type. - * - * @return a media type string without - */ - private static String removeVersionInMediaType(String mediaType) { - if (mediaType.contains("vnd.opensearch")) { - return mediaType.replaceAll("vnd.opensearch\\+", "").replaceAll("\\s*;\\s*compatible-with=\\d+", ""); + @Override + public boolean detectedXContent(final CharSequence content, final int length) { + return false; } - return mediaType; - } - /** - * Attempts to match the given media type with the known {@link XContentType} values. This match is done in a case-insensitive manner. - * The provided media type should not include any parameters. This method is suitable for parsing part of the {@code Content-Type} - * HTTP header. This method will return {@code null} if no match is found - */ - public static XContentType fromMediaType(String mediaType) { - final String lowercaseMediaType = Objects.requireNonNull(mediaType, "mediaType cannot be null").toLowerCase(Locale.ROOT); - for (XContentType type : values()) { - if (type.mediaTypeWithoutParameters().equals(lowercaseMediaType)) { - return type; - } - } - // we also support newline delimited JSON: http://specs.okfnlabs.org/ndjson/ - if (lowercaseMediaType.toLowerCase(Locale.ROOT).equals("application/x-ndjson")) { - return XContentType.JSON; + @Override + public XContentBuilder contentBuilder() throws IOException { + return CborXContent.contentBuilder(); } - return null; - } - - private static boolean isSameMediaTypeOrFormatAs(String stringType, XContentType type) { - return type.mediaTypeWithoutParameters().equalsIgnoreCase(stringType) - || stringType.toLowerCase(Locale.ROOT).startsWith(type.mediaTypeWithoutParameters().toLowerCase(Locale.ROOT) + ";") - || type.shortName().equalsIgnoreCase(stringType); - } + @Override + public XContentBuilder contentBuilder(final OutputStream os) throws IOException { + return new XContentBuilder(CborXContent.cborXContent, os); + } + }; private int index; @@ -204,14 +245,18 @@ public int index() { return index; } - public String mediaType() { - return mediaTypeWithoutParameters(); + @Override + public String type() { + return "application"; } - public abstract String shortName(); - - public abstract XContent xContent(); - - public abstract String mediaTypeWithoutParameters(); + @Override + public String format() { + return subtype(); + } + @Override + public void writeTo(StreamOutput output) throws IOException { + output.writeString(this.mediaType()); + } } diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentUtils.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentUtils.java index 6bb6c9ad09932..d0f2937807584 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentUtils.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/XContentUtils.java @@ -32,6 +32,8 @@ package org.opensearch.common.xcontent; +import org.opensearch.core.xcontent.XContentParser; + import java.io.IOException; public final class XContentUtils { diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContent.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContent.java index 674b6566bdec0..cf6123dba57cd 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContent.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContent.java @@ -35,15 +35,20 @@ import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.StreamReadConstraints; +import com.fasterxml.jackson.core.StreamReadFeature; +import com.fasterxml.jackson.core.StreamWriteFeature; import com.fasterxml.jackson.dataformat.cbor.CBORFactory; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentGenerator; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; + import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentGenerator; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.InputStream; @@ -55,6 +60,11 @@ * A CBOR based content implementation using Jackson. */ public class CborXContent implements XContent { + public static final int DEFAULT_MAX_STRING_LEN = Integer.parseInt( + System.getProperty("opensearch.xcontent.string.length.max", Integer.toString(Integer.MAX_VALUE) /* no limit */) + ); + + public static final boolean USE_FAST_DOUBLE_WRITER = Boolean.getBoolean("opensearch.xcontent.use_fast_double_writer"); public static XContentBuilder contentBuilder() throws IOException { return XContentBuilder.builder(cborXContent); @@ -69,13 +79,16 @@ public static XContentBuilder contentBuilder() throws IOException { // Do not automatically close unclosed objects/arrays in com.fasterxml.jackson.dataformat.cbor.CBORGenerator#close() method cborFactory.configure(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT, false); cborFactory.configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION, true); + cborFactory.setStreamReadConstraints(StreamReadConstraints.builder().maxStringLength(DEFAULT_MAX_STRING_LEN).build()); + cborFactory.configure(StreamReadFeature.USE_FAST_DOUBLE_PARSER.mappedFeature(), true); + cborFactory.configure(StreamWriteFeature.USE_FAST_DOUBLE_WRITER.mappedFeature(), USE_FAST_DOUBLE_WRITER); cborXContent = new CborXContent(); } private CborXContent() {} @Override - public XContentType type() { + public MediaType mediaType() { return XContentType.CBOR; } diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContentGenerator.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContentGenerator.java index 2dba887c664b6..1c13ebd3981a9 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContentGenerator.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContentGenerator.java @@ -33,6 +33,7 @@ package org.opensearch.common.xcontent.cbor; import com.fasterxml.jackson.core.JsonGenerator; + import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContentGenerator; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContentParser.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContentParser.java index d36273a46df89..4abc9650c4bf6 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContentParser.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/cbor/CborXContentParser.java @@ -33,10 +33,11 @@ package org.opensearch.common.xcontent.cbor; import com.fasterxml.jackson.core.JsonParser; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; + import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContentParser; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.NamedXContentRegistry; public class CborXContentParser extends JsonXContentParser { diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContent.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContent.java index a7872904b1126..3cd8af551ba81 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContent.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContent.java @@ -36,13 +36,18 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentGenerator; -import org.opensearch.common.xcontent.XContentParser; +import com.fasterxml.jackson.core.StreamReadConstraints; +import com.fasterxml.jackson.core.StreamReadFeature; +import com.fasterxml.jackson.core.StreamWriteFeature; + import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentGenerator; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.InputStream; @@ -54,6 +59,11 @@ * A JSON based content implementation using Jackson. */ public class JsonXContent implements XContent { + public static final int DEFAULT_MAX_STRING_LEN = Integer.parseInt( + System.getProperty("opensearch.xcontent.string.length.max", Integer.toString(Integer.MAX_VALUE) /* no limit */) + ); + + public static final boolean USE_FAST_DOUBLE_WRITER = Boolean.getBoolean("opensearch.xcontent.use_fast_double_writer"); public static XContentBuilder contentBuilder() throws IOException { return XContentBuilder.builder(jsonXContent); @@ -71,13 +81,16 @@ public static XContentBuilder contentBuilder() throws IOException { // Do not automatically close unclosed objects/arrays in com.fasterxml.jackson.core.json.UTF8JsonGenerator#close() method jsonFactory.configure(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT, false); jsonFactory.configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION, true); + jsonFactory.setStreamReadConstraints(StreamReadConstraints.builder().maxStringLength(DEFAULT_MAX_STRING_LEN).build()); + jsonFactory.configure(StreamReadFeature.USE_FAST_DOUBLE_PARSER.mappedFeature(), true); + jsonFactory.configure(StreamWriteFeature.USE_FAST_DOUBLE_WRITER.mappedFeature(), USE_FAST_DOUBLE_WRITER); jsonXContent = new JsonXContent(); } private JsonXContent() {} @Override - public XContentType type() { + public MediaType mediaType() { return XContentType.JSON; } diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContentGenerator.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContentGenerator.java index b972f52c57201..3f8493d7a4f14 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContentGenerator.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContentGenerator.java @@ -41,15 +41,17 @@ import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.core.util.JsonGeneratorDelegate; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentGenerator; -import org.opensearch.common.xcontent.XContentParser; + +import org.opensearch.common.util.io.Streams; import org.opensearch.common.xcontent.XContentType; -import org.opensearch.common.xcontent.support.filtering.FilterPathBasedFilter; -import org.opensearch.core.internal.io.Streams; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentGenerator; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.filtering.FilterPathBasedFilter; import java.io.BufferedInputStream; import java.io.IOException; @@ -338,19 +340,22 @@ public void writeRawField(String name, InputStream content) throws IOException { // needed for the XContentFactory.xContentType call content = new BufferedInputStream(content); } - XContentType contentType = XContentFactory.xContentType(content); + MediaType contentType = MediaTypeRegistry.xContentType(content); if (contentType == null) { throw new IllegalArgumentException("Can't write raw bytes whose xcontent-type can't be guessed"); } writeRawField(name, content, contentType); } + /** + * Writes a raw field with the value taken from the bytes in the stream + */ @Override - public void writeRawField(String name, InputStream content, XContentType contentType) throws IOException { - if (mayWriteRawData(contentType) == false) { + public void writeRawField(String name, InputStream content, MediaType mediaType) throws IOException { + if (mayWriteRawData(mediaType) == false) { // EMPTY is safe here because we never call namedObject when writing raw data try ( - XContentParser parser = XContentFactory.xContent(contentType) + XContentParser parser = mediaType.xContent() // It's okay to pass the throwing deprecation handler // because we should not be writing raw fields when // generating JSON @@ -368,10 +373,13 @@ public void writeRawField(String name, InputStream content, XContentType content } } + /** + * Writes the raw value to the stream + */ @Override - public void writeRawValue(InputStream stream, XContentType xContentType) throws IOException { - if (mayWriteRawData(xContentType) == false) { - copyRawValue(stream, xContentType.xContent()); + public void writeRawValue(InputStream stream, MediaType mediaType) throws IOException { + if (mayWriteRawData(mediaType) == false) { + copyRawValue(stream, mediaType.xContent()); } else { if (generator.getOutputContext().getCurrentName() != null) { // If we've just started a field we'll need to add the separator @@ -383,6 +391,12 @@ public void writeRawValue(InputStream stream, XContentType xContentType) throws } } + /** + * possibly copy the whole structure to correctly filter + * + * @deprecated use {@link #mayWriteRawData(MediaType)} instead + */ + @Deprecated private boolean mayWriteRawData(XContentType contentType) { // When the current generator is filtered (ie filter != null) // or the content is in a different format than the current generator, @@ -391,6 +405,17 @@ private boolean mayWriteRawData(XContentType contentType) { return supportsRawWrites() && isFiltered() == false && contentType == contentType() && prettyPrint == false; } + /** + * possibly copy the whole structure to correctly filter + */ + private boolean mayWriteRawData(MediaType contentType) { + // When the current generator is filtered (ie filter != null) + // or the content is in a different format than the current generator, + // we need to copy the whole structure so that it will be correctly + // filtered or converted + return supportsRawWrites() && isFiltered() == false && contentType == contentType() && prettyPrint == false; + } + /** Whether this generator supports writing raw data directly */ protected boolean supportsRawWrites() { return true; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContentParser.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContentParser.java index 8639e50214d85..ed830c6b7bfa3 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContentParser.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/json/JsonXContentParser.java @@ -35,14 +35,16 @@ import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentLocation; + +import org.opensearch.common.util.io.IOUtils; import org.opensearch.common.xcontent.XContentType; -import org.opensearch.common.xcontent.support.AbstractXContentParser; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.core.xcontent.AbstractXContentParser; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentLocation; import java.io.IOException; +import java.math.BigInteger; import java.nio.CharBuffer; public class JsonXContentParser extends AbstractXContentParser { @@ -188,6 +190,15 @@ public double doDoubleValue() throws IOException { return parser.getDoubleValue(); } + @Override + public BigInteger doBigIntegerValue() throws IOException { + if (parser.getCurrentToken() == JsonToken.VALUE_NUMBER_FLOAT) { + return parser.getDecimalValue().toBigInteger(); + } else { + return parser.getBigIntegerValue(); + } + } + @Override public byte[] binaryValue() throws IOException { return parser.getBinaryValue(); diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContent.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContent.java index 82925c5f84f9c..b28192d0ab44f 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContent.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContent.java @@ -35,15 +35,20 @@ import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.StreamReadConstraints; +import com.fasterxml.jackson.core.StreamReadFeature; +import com.fasterxml.jackson.core.StreamWriteFeature; import com.fasterxml.jackson.dataformat.smile.SmileFactory; import com.fasterxml.jackson.dataformat.smile.SmileGenerator; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentGenerator; -import org.opensearch.common.xcontent.XContentParser; + import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentGenerator; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.InputStream; @@ -55,6 +60,11 @@ * A Smile based content implementation using Jackson. */ public class SmileXContent implements XContent { + public static final int DEFAULT_MAX_STRING_LEN = Integer.parseInt( + System.getProperty("opensearch.xcontent.string.length.max", Integer.toString(Integer.MAX_VALUE) /* no limit */) + ); + + public static final boolean USE_FAST_DOUBLE_WRITER = Boolean.getBoolean("opensearch.xcontent.use_fast_double_writer"); public static XContentBuilder contentBuilder() throws IOException { return XContentBuilder.builder(smileXContent); @@ -71,13 +81,16 @@ public static XContentBuilder contentBuilder() throws IOException { // Do not automatically close unclosed objects/arrays in com.fasterxml.jackson.dataformat.smile.SmileGenerator#close() method smileFactory.configure(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT, false); smileFactory.configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION, true); + smileFactory.setStreamReadConstraints(StreamReadConstraints.builder().maxStringLength(DEFAULT_MAX_STRING_LEN).build()); + smileFactory.configure(StreamReadFeature.USE_FAST_DOUBLE_PARSER.mappedFeature(), true); + smileFactory.configure(StreamWriteFeature.USE_FAST_DOUBLE_WRITER.mappedFeature(), USE_FAST_DOUBLE_WRITER); smileXContent = new SmileXContent(); } private SmileXContent() {} @Override - public XContentType type() { + public MediaType mediaType() { return XContentType.SMILE; } diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContentGenerator.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContentGenerator.java index b7ba56bdb01ea..0315e9a77272f 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContentGenerator.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContentGenerator.java @@ -33,6 +33,7 @@ package org.opensearch.common.xcontent.smile; import com.fasterxml.jackson.core.JsonGenerator; + import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContentGenerator; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContentParser.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContentParser.java index 84c4d1a2112cd..ade265798b5a4 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContentParser.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/smile/SmileXContentParser.java @@ -33,10 +33,11 @@ package org.opensearch.common.xcontent.smile; import com.fasterxml.jackson.core.JsonParser; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; + import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContentParser; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.NamedXContentRegistry; public class SmileXContentParser extends JsonXContentParser { diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/spi/XContentProvider.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/spi/XContentProvider.java new file mode 100644 index 0000000000000..af5ab67507b81 --- /dev/null +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/spi/XContentProvider.java @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.xcontent.spi; + +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.spi.MediaTypeProvider; + +import java.util.List; +import java.util.Map; + +/** + * Media Type implementations provided by xcontent library + * + * @opensearch.internal + */ +public class XContentProvider implements MediaTypeProvider { + /** Returns the concrete {@link MediaType} provided by the xcontent library */ + @Override + public List getMediaTypes() { + return List.of(XContentType.values()); + } + + /** Returns the additional {@link MediaType} aliases provided by the xcontent library */ + @Override + public Map getAdditionalMediaTypes() { + return Map.of("application/*", XContentType.JSON, "application/x-ndjson", XContentType.JSON); + } +} diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/spi/package-info.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/spi/package-info.java new file mode 100644 index 0000000000000..c265021f12763 --- /dev/null +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/spi/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** SPI implementation for the xcontent library */ +package org.opensearch.common.xcontent.spi; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContent.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContent.java index 8ebdeca71c0ce..84e19b226cd36 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContent.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContent.java @@ -34,14 +34,19 @@ import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.StreamReadConstraints; +import com.fasterxml.jackson.core.StreamReadFeature; +import com.fasterxml.jackson.core.StreamWriteFeature; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentGenerator; -import org.opensearch.common.xcontent.XContentParser; + import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentGenerator; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.InputStream; @@ -53,6 +58,11 @@ * A YAML based content implementation using Jackson. */ public class YamlXContent implements XContent { + public static final int DEFAULT_MAX_STRING_LEN = Integer.parseInt( + System.getProperty("opensearch.xcontent.string.length.max", Integer.toString(Integer.MAX_VALUE) /* no limit */) + ); + + public static final boolean USE_FAST_DOUBLE_WRITER = Boolean.getBoolean("opensearch.xcontent.use_fast_double_writer"); public static XContentBuilder contentBuilder() throws IOException { return XContentBuilder.builder(yamlXContent); @@ -64,13 +74,16 @@ public static XContentBuilder contentBuilder() throws IOException { static { yamlFactory = new YAMLFactory(); yamlFactory.configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION, true); + yamlFactory.setStreamReadConstraints(StreamReadConstraints.builder().maxStringLength(DEFAULT_MAX_STRING_LEN).build()); + yamlFactory.configure(StreamReadFeature.USE_FAST_DOUBLE_PARSER.mappedFeature(), true); + yamlFactory.configure(StreamWriteFeature.USE_FAST_DOUBLE_WRITER.mappedFeature(), USE_FAST_DOUBLE_WRITER); yamlXContent = new YamlXContent(); } private YamlXContent() {} @Override - public XContentType type() { + public MediaType mediaType() { return XContentType.YAML; } diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContentGenerator.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContentGenerator.java index 5dd85afdb9c01..ddc9f5365c22b 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContentGenerator.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContentGenerator.java @@ -33,6 +33,7 @@ package org.opensearch.common.xcontent.yaml; import com.fasterxml.jackson.core.JsonGenerator; + import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContentGenerator; diff --git a/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContentParser.java b/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContentParser.java index cc523dc6f978d..5fabe58cca919 100644 --- a/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContentParser.java +++ b/libs/x-content/src/main/java/org/opensearch/common/xcontent/yaml/YamlXContentParser.java @@ -33,10 +33,11 @@ package org.opensearch.common.xcontent.yaml; import com.fasterxml.jackson.core.JsonParser; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; + import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContentParser; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.NamedXContentRegistry; public class YamlXContentParser extends JsonXContentParser { diff --git a/libs/x-content/src/main/resources/META-INF/services/org.opensearch.core.xcontent.spi.MediaTypeProvider b/libs/x-content/src/main/resources/META-INF/services/org.opensearch.core.xcontent.spi.MediaTypeProvider new file mode 100644 index 0000000000000..ce3fab93087dd --- /dev/null +++ b/libs/x-content/src/main/resources/META-INF/services/org.opensearch.core.xcontent.spi.MediaTypeProvider @@ -0,0 +1,9 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# + +org.opensearch.common.xcontent.spi.XContentProvider diff --git a/libs/x-content/src/test/java/org/opensearch/common/ParseFieldTests.java b/libs/x-content/src/test/java/org/opensearch/common/ParseFieldTests.java index d0d3c1236241b..a5cb14cdc8393 100644 --- a/libs/x-content/src/test/java/org/opensearch/common/ParseFieldTests.java +++ b/libs/x-content/src/test/java/org/opensearch/common/ParseFieldTests.java @@ -32,6 +32,7 @@ package org.opensearch.common; import org.opensearch.common.xcontent.LoggingDeprecationHandler; +import org.opensearch.core.ParseField; import org.opensearch.test.OpenSearchTestCase; import static org.hamcrest.CoreMatchers.is; diff --git a/libs/x-content/src/test/java/org/opensearch/common/xcontent/ConstructingObjectParserTests.java b/libs/x-content/src/test/java/org/opensearch/common/xcontent/ConstructingObjectParserTests.java index d555f3c60bd75..d27289d3b1359 100644 --- a/libs/x-content/src/test/java/org/opensearch/common/xcontent/ConstructingObjectParserTests.java +++ b/libs/x-content/src/test/java/org/opensearch/common/xcontent/ConstructingObjectParserTests.java @@ -34,9 +34,17 @@ import org.opensearch.common.CheckedFunction; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; import org.opensearch.common.xcontent.ObjectParserTests.NamedObject; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.Matcher; @@ -46,8 +54,8 @@ import java.util.List; import static java.util.Collections.unmodifiableList; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; diff --git a/libs/x-content/src/test/java/org/opensearch/common/xcontent/InstantiatingObjectParserTests.java b/libs/x-content/src/test/java/org/opensearch/common/xcontent/InstantiatingObjectParserTests.java index 3c34453a636d1..4e509951462b2 100644 --- a/libs/x-content/src/test/java/org/opensearch/common/xcontent/InstantiatingObjectParserTests.java +++ b/libs/x-content/src/test/java/org/opensearch/common/xcontent/InstantiatingObjectParserTests.java @@ -32,14 +32,17 @@ package org.opensearch.common.xcontent; -import org.opensearch.common.ParseField; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.InstantiatingObjectParser; +import org.opensearch.core.xcontent.ParserConstructor; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; diff --git a/libs/x-content/src/test/java/org/opensearch/common/xcontent/MapXContentParserTests.java b/libs/x-content/src/test/java/org/opensearch/common/xcontent/MapXContentParserTests.java index 380af6ff2648f..ed6d38289ef6a 100644 --- a/libs/x-content/src/test/java/org/opensearch/common/xcontent/MapXContentParserTests.java +++ b/libs/x-content/src/test/java/org/opensearch/common/xcontent/MapXContentParserTests.java @@ -33,8 +33,10 @@ package org.opensearch.common.xcontent; import org.opensearch.common.CheckedConsumer; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.support.MapXContentParser; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MapXContentParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/libs/x-content/src/test/java/org/opensearch/common/xcontent/MediaTypeParserTests.java b/libs/x-content/src/test/java/org/opensearch/common/xcontent/MediaTypeParserTests.java new file mode 100644 index 0000000000000..64d36f0a8b78f --- /dev/null +++ b/libs/x-content/src/test/java/org/opensearch/common/xcontent/MediaTypeParserTests.java @@ -0,0 +1,82 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.common.xcontent; + +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.test.OpenSearchTestCase; + +import java.util.Collections; +import java.util.Map; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +public class MediaTypeParserTests extends OpenSearchTestCase { + + public void testJsonWithParameters() throws Exception { + String mediaType = "application/json"; + assertThat(MediaTypeRegistry.parseMediaType(mediaType).getParameters(), equalTo(Collections.emptyMap())); + assertThat(MediaTypeRegistry.parseMediaType(mediaType + ";").getParameters(), equalTo(Collections.emptyMap())); + assertThat(MediaTypeRegistry.parseMediaType(mediaType + "; charset=UTF-8").getParameters(), equalTo(Map.of("charset", "utf-8"))); + assertThat( + MediaTypeRegistry.parseMediaType(mediaType + "; custom=123;charset=UTF-8").getParameters(), + equalTo(Map.of("charset", "utf-8", "custom", "123")) + ); + } + + public void testWhiteSpaceInTypeSubtype() { + String mediaType = " application/json "; + assertThat(MediaTypeRegistry.parseMediaType(mediaType).getMediaType(), equalTo(XContentType.JSON)); + + assertThat( + MediaTypeRegistry.parseMediaType(mediaType + "; custom=123; charset=UTF-8").getParameters(), + equalTo(Map.of("charset", "utf-8", "custom", "123")) + ); + assertThat( + MediaTypeRegistry.parseMediaType(mediaType + "; custom=123;\n charset=UTF-8").getParameters(), + equalTo(Map.of("charset", "utf-8", "custom", "123")) + ); + + mediaType = " application / json "; + assertThat(MediaTypeRegistry.parseMediaType(mediaType), is(nullValue())); + } + + public void testInvalidParameters() { + String mediaType = "application/json"; + assertThat(MediaTypeRegistry.parseMediaType(mediaType + "; keyvalueNoEqualsSign"), is(nullValue())); + + assertThat(MediaTypeRegistry.parseMediaType(mediaType + "; key = value"), is(nullValue())); + assertThat(MediaTypeRegistry.parseMediaType(mediaType + "; key="), is(nullValue())); + } +} diff --git a/libs/x-content/src/test/java/org/opensearch/common/xcontent/ObjectParserTests.java b/libs/x-content/src/test/java/org/opensearch/common/xcontent/ObjectParserTests.java index 7ac11e05f741c..5f87a17007fb5 100644 --- a/libs/x-content/src/test/java/org/opensearch/common/xcontent/ObjectParserTests.java +++ b/libs/x-content/src/test/java/org/opensearch/common/xcontent/ObjectParserTests.java @@ -32,11 +32,19 @@ package org.opensearch.common.xcontent; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ObjectParser.NamedObjectParser; -import org.opensearch.common.xcontent.ObjectParser.ValueType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedObjectNotFoundException; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ObjectParser.NamedObjectParser; +import org.opensearch.core.xcontent.ObjectParser.ValueType; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import org.opensearch.test.OpenSearchTestCase; import java.io.ByteArrayOutputStream; @@ -387,7 +395,7 @@ public void testAllVariants() throws IOException { double expectedNullableDouble; int expectedNullableInt; - XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent()); + XContentBuilder builder = MediaTypeRegistry.JSON.contentBuilder(); builder.startObject(); builder.field("int_field", randomBoolean() ? "1" : 1); if (randomBoolean()) { @@ -441,7 +449,7 @@ public void testAllVariants() throws IOException { } builder.field("string_or_null", nullValue ? null : "5"); builder.endObject(); - XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder)); + XContentParser parser = createParser(JsonXContent.jsonXContent, builder.toString()); class TestStruct { int int_field; int nullableIntField; @@ -639,7 +647,7 @@ public void testParseNamedObjectsInOrderNotSupported() throws IOException { } public void testIgnoreUnknownFields() throws IOException { - XContentBuilder b = XContentBuilder.builder(XContentType.JSON.xContent()); + XContentBuilder b = MediaTypeRegistry.JSON.contentBuilder(); b.startObject(); { b.field("test", "foo"); @@ -661,7 +669,7 @@ class TestStruct { } public void testIgnoreUnknownObjects() throws IOException { - XContentBuilder b = XContentBuilder.builder(XContentType.JSON.xContent()); + XContentBuilder b = MediaTypeRegistry.JSON.contentBuilder(); b.startObject(); { b.field("test", "foo"); @@ -687,7 +695,7 @@ class TestStruct { } public void testIgnoreUnknownArrays() throws IOException { - XContentBuilder b = XContentBuilder.builder(XContentType.JSON.xContent()); + XContentBuilder b = MediaTypeRegistry.JSON.contentBuilder(); b.startObject(); { b.field("test", "foo"); diff --git a/libs/x-content/src/test/java/org/opensearch/common/xcontent/ObjectPathTests.java b/libs/x-content/src/test/java/org/opensearch/common/xcontent/ObjectPathTests.java index f6731aa128da5..a400c02092a63 100644 --- a/libs/x-content/src/test/java/org/opensearch/common/xcontent/ObjectPathTests.java +++ b/libs/x-content/src/test/java/org/opensearch/common/xcontent/ObjectPathTests.java @@ -32,6 +32,7 @@ package org.opensearch.common.xcontent; +import org.opensearch.core.xcontent.ObjectPath; import org.opensearch.test.OpenSearchTestCase; import java.util.ArrayList; diff --git a/libs/x-content/src/test/java/org/opensearch/common/xcontent/SimpleStruct.java b/libs/x-content/src/test/java/org/opensearch/common/xcontent/SimpleStruct.java index acc6dc4c8aa3c..1d2a66ea1f78f 100644 --- a/libs/x-content/src/test/java/org/opensearch/common/xcontent/SimpleStruct.java +++ b/libs/x-content/src/test/java/org/opensearch/common/xcontent/SimpleStruct.java @@ -32,13 +32,17 @@ package org.opensearch.common.xcontent; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * Simple structure with 3 fields: int, double and String. @@ -101,6 +105,6 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(XContentType.JSON, this); } } diff --git a/libs/x-content/src/test/java/org/opensearch/common/xcontent/XContentParserTests.java b/libs/x-content/src/test/java/org/opensearch/common/xcontent/XContentParserTests.java index 33dc7f4ba25b5..44a4fe4143d05 100644 --- a/libs/x-content/src/test/java/org/opensearch/common/xcontent/XContentParserTests.java +++ b/libs/x-content/src/test/java/org/opensearch/common/xcontent/XContentParserTests.java @@ -33,10 +33,17 @@ package org.opensearch.common.xcontent; import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.dataformat.yaml.JacksonYAMLParseException; + import org.opensearch.common.CheckedSupplier; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.common.xcontent.cbor.CborXContent; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.common.xcontent.smile.SmileXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentSubParser; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; @@ -46,6 +53,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; @@ -53,6 +61,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasLength; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.in; import static org.hamcrest.Matchers.instanceOf; @@ -60,6 +69,72 @@ import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage; public class XContentParserTests extends OpenSearchTestCase { + private static final Map> GENERATORS = Map.of( + XContentType.JSON, + () -> randomAlphaOfLengthBetween(1, JsonXContent.DEFAULT_MAX_STRING_LEN / 10), /* limit to ~200Mb */ + XContentType.CBOR, + () -> randomAlphaOfLengthBetween(1, CborXContent.DEFAULT_MAX_STRING_LEN / 10), /* limit to ~200Mb */ + XContentType.SMILE, + () -> randomAlphaOfLengthBetween(1, SmileXContent.DEFAULT_MAX_STRING_LEN / 10), /* limit to ~200Mb */ + /* YAML parser limitation */ + XContentType.YAML, + () -> randomAlphaOfLengthBetween(1, 3140000) + ); + + public void testStringOffLimit() throws IOException { + final String field = randomAlphaOfLengthBetween(1, 5); + final String value = randomRealisticUnicodeOfCodepointLength(3145730); + + try (XContentBuilder builder = XContentBuilder.builder(XContentType.YAML.xContent())) { + builder.startObject(); + if (randomBoolean()) { + builder.field(field, value); + } else { + builder.field(field).value(value); + } + builder.endObject(); + + try (XContentParser parser = createParser(XContentType.YAML.xContent(), BytesReference.bytes(builder))) { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals(field, parser.currentName()); + assertEquals(XContentParser.Token.VALUE_STRING, parser.nextToken()); + assertThrows(JacksonYAMLParseException.class, () -> parser.nextToken()); + } + } + } + + public void testString() throws IOException { + final XContentType xContentType = randomFrom(XContentType.values()); + + final String field = randomAlphaOfLengthBetween(1, 5); + final String value = GENERATORS.get(xContentType).get(); + + try (XContentBuilder builder = XContentBuilder.builder(xContentType.xContent())) { + builder.startObject(); + if (randomBoolean()) { + builder.field(field, value); + } else { + builder.field(field).value(value); + } + builder.endObject(); + + final String text; + try (XContentParser parser = createParser(xContentType.xContent(), BytesReference.bytes(builder))) { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); + assertEquals(field, parser.currentName()); + assertEquals(XContentParser.Token.VALUE_STRING, parser.nextToken()); + + text = parser.text(); + + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + } + + assertThat(text, hasLength(value.length())); + } + } public void testFloat() throws IOException { final XContentType xContentType = randomFrom(XContentType.values()); @@ -271,7 +346,7 @@ public void testReadBooleans() throws IOException { public void testEmptyList() throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder().startObject().startArray("some_array").endArray().endObject(); - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { + try (XContentParser parser = createParser(JsonXContent.jsonXContent, builder.toString())) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); assertEquals("some_array", parser.currentName()); @@ -293,7 +368,7 @@ public void testSimpleList() throws IOException { .endArray() .endObject(); - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { + try (XContentParser parser = createParser(JsonXContent.jsonXContent, builder.toString())) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); assertEquals("some_array", parser.currentName()); @@ -321,7 +396,7 @@ public void testNestedList() throws IOException { .endArray() .endObject(); - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { + try (XContentParser parser = createParser(JsonXContent.jsonXContent, builder.toString())) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); assertEquals("some_array", parser.currentName()); @@ -345,7 +420,7 @@ public void testNestedMapInList() throws IOException { .endArray() .endObject(); - try (XContentParser parser = createParser(JsonXContent.jsonXContent, Strings.toString(builder))) { + try (XContentParser parser = createParser(JsonXContent.jsonXContent, builder.toString())) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); assertEquals("some_array", parser.currentName()); @@ -421,7 +496,7 @@ public void testSubParserObject() throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); int numberOfTokens; numberOfTokens = generateRandomObjectForMarking(builder); - String content = Strings.toString(builder); + String content = builder.toString(); try (XContentParser parser = createParser(JsonXContent.jsonXContent, content)) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); @@ -469,7 +544,7 @@ public void testSubParserArray() throws IOException { builder.endArray(); builder.endObject(); - String content = Strings.toString(builder); + String content = builder.toString(); try (XContentParser parser = createParser(JsonXContent.jsonXContent, content)) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); @@ -502,7 +577,7 @@ public void testSubParserArray() throws IOException { public void testCreateSubParserAtAWrongPlace() throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); generateRandomObjectForMarking(builder); - String content = Strings.toString(builder); + String content = builder.toString(); try (XContentParser parser = createParser(JsonXContent.jsonXContent, content)) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); @@ -516,7 +591,7 @@ public void testCreateSubParserAtAWrongPlace() throws IOException { public void testCreateRootSubParser() throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); int numberOfTokens = generateRandomObjectForMarking(builder); - String content = Strings.toString(builder); + String content = builder.toString(); try (XContentParser parser = createParser(JsonXContent.jsonXContent, content)) { assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/spi/MatrixStatsNamedXContentProvider.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/spi/MatrixStatsNamedXContentProvider.java index fc63bf1fa537c..302c724324159 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/spi/MatrixStatsNamedXContentProvider.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/spi/MatrixStatsNamedXContentProvider.java @@ -32,9 +32,9 @@ package org.opensearch.search.aggregations.matrix.spi; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ContextParser; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.plugins.spi.NamedXContentProvider; import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.matrix.stats.MatrixStatsAggregationBuilder; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/InternalMatrixStats.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/InternalMatrixStats.java index 42c6d106a414e..3099ba3c6aa19 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/InternalMatrixStats.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/InternalMatrixStats.java @@ -31,9 +31,9 @@ package org.opensearch.search.aggregations.matrix.stats; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.InternalAggregation; import java.io.IOException; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregationBuilder.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregationBuilder.java index 2f73780fcb4af..f757968bd86f7 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregationBuilder.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregationBuilder.java @@ -31,10 +31,10 @@ package org.opensearch.search.aggregations.matrix.stats; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.MultiValueMode; import org.opensearch.search.aggregations.AggregationBuilder; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregator.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregator.java index 142e7ca3dbc15..7e48d659e0bbc 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregator.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregator.java @@ -43,9 +43,9 @@ import org.opensearch.search.aggregations.LeafBucketCollector; import org.opensearch.search.aggregations.LeafBucketCollectorBase; import org.opensearch.search.aggregations.metrics.MetricsAggregator; +import org.opensearch.search.aggregations.support.ArrayValuesSource; import org.opensearch.search.aggregations.support.ValuesSource; import org.opensearch.search.internal.SearchContext; -import org.opensearch.search.aggregations.support.ArrayValuesSource; import java.io.IOException; import java.util.Map; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregatorFactory.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregatorFactory.java index f7ab0db3c9607..24f74f3859157 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregatorFactory.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregatorFactory.java @@ -89,4 +89,9 @@ protected Aggregator doCreateInternal( } return new MatrixStatsAggregator(name, typedValuesSources, searchContext, parent, multiValueMode, metadata); } + + @Override + protected boolean supportsConcurrentSegmentSearch() { + return true; + } } diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsParser.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsParser.java index 9ee1851a02a1c..a62033d93e640 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsParser.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsParser.java @@ -31,13 +31,13 @@ package org.opensearch.search.aggregations.matrix.stats; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.MultiValueMode; -import org.opensearch.search.aggregations.support.ValueType; -import org.opensearch.search.aggregations.support.ValuesSourceType; import org.opensearch.search.aggregations.support.ArrayValuesSourceAggregationBuilder; import org.opensearch.search.aggregations.support.ArrayValuesSourceParser; +import org.opensearch.search.aggregations.support.ValueType; +import org.opensearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Map; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsResults.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsResults.java index 87eb58ab3427a..6e04efbf6689c 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsResults.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsResults.java @@ -32,9 +32,9 @@ package org.opensearch.search.aggregations.matrix.stats; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; import java.util.Collections; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/ParsedMatrixStats.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/ParsedMatrixStats.java index c3049992b6c0e..868d070bb6065 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/ParsedMatrixStats.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/ParsedMatrixStats.java @@ -32,10 +32,10 @@ package org.opensearch.search.aggregations.matrix.stats; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.aggregations.ParsedAggregation; import java.io.IOException; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/RunningStats.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/RunningStats.java index cad0011602f67..de67cc2930652 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/RunningStats.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/matrix/stats/RunningStats.java @@ -32,9 +32,9 @@ package org.opensearch.search.aggregations.matrix.stats; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; import java.util.ArrayList; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/support/ArrayValuesSourceAggregationBuilder.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/support/ArrayValuesSourceAggregationBuilder.java index bea838994fa2c..102188deb91b5 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/support/ArrayValuesSourceAggregationBuilder.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/support/ArrayValuesSourceAggregationBuilder.java @@ -31,10 +31,10 @@ package org.opensearch.search.aggregations.support; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.aggregations.AbstractAggregationBuilder; import org.opensearch.search.aggregations.AggregationInitializationException; diff --git a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/support/ArrayValuesSourceParser.java b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/support/ArrayValuesSourceParser.java index 0c7da3bdec298..855e1ca8e92ec 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/support/ArrayValuesSourceParser.java +++ b/modules/aggs-matrix-stats/src/main/java/org/opensearch/search/aggregations/support/ArrayValuesSourceParser.java @@ -32,9 +32,9 @@ package org.opensearch.search.aggregations.support; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.script.Script; import org.opensearch.search.aggregations.AggregationBuilder.CommonFields; import org.opensearch.search.aggregations.Aggregator; diff --git a/modules/aggs-matrix-stats/src/test/java/org/opensearch/search/aggregations/matrix/stats/InternalMatrixStatsTests.java b/modules/aggs-matrix-stats/src/test/java/org/opensearch/search/aggregations/matrix/stats/InternalMatrixStatsTests.java index e523c77e0786f..3e129bdb8a93c 100644 --- a/modules/aggs-matrix-stats/src/test/java/org/opensearch/search/aggregations/matrix/stats/InternalMatrixStatsTests.java +++ b/modules/aggs-matrix-stats/src/test/java/org/opensearch/search/aggregations/matrix/stats/InternalMatrixStatsTests.java @@ -31,13 +31,13 @@ package org.opensearch.search.aggregations.matrix.stats; -import org.opensearch.common.ParseField; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.MockBigArrays; import org.opensearch.common.util.MockPageCacheRecycler; -import org.opensearch.common.xcontent.ContextParser; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.ParseField; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.plugins.SearchPlugin; import org.opensearch.script.ScriptService; import org.opensearch.search.aggregations.Aggregation; diff --git a/modules/aggs-matrix-stats/src/test/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregatorTests.java b/modules/aggs-matrix-stats/src/test/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregatorTests.java index da3228829816b..863e4e0e14c0a 100644 --- a/modules/aggs-matrix-stats/src/test/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregatorTests.java +++ b/modules/aggs-matrix-stats/src/test/java/org/opensearch/search/aggregations/matrix/stats/MatrixStatsAggregatorTests.java @@ -36,10 +36,10 @@ import org.apache.lucene.document.SortedNumericDocValuesField; import org.apache.lucene.document.StringField; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.NumericUtils; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.mapper.NumberFieldMapper; diff --git a/modules/aggs-matrix-stats/src/yamlRestTest/java/org/opensearch/search/aggregations/matrix/MatrixStatsClientYamlTestSuiteIT.java b/modules/aggs-matrix-stats/src/yamlRestTest/java/org/opensearch/search/aggregations/matrix/MatrixStatsClientYamlTestSuiteIT.java index 4aea2a210c2b4..b0d9681339652 100644 --- a/modules/aggs-matrix-stats/src/yamlRestTest/java/org/opensearch/search/aggregations/matrix/MatrixStatsClientYamlTestSuiteIT.java +++ b/modules/aggs-matrix-stats/src/yamlRestTest/java/org/opensearch/search/aggregations/matrix/MatrixStatsClientYamlTestSuiteIT.java @@ -33,6 +33,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/modules/aggs-matrix-stats/src/yamlRestTest/resources/rest-api-spec/test/stats/10_basic.yml b/modules/aggs-matrix-stats/src/yamlRestTest/resources/rest-api-spec/test/stats/10_basic.yml index 2416d2b2b3141..b983a3960c64e 100644 --- a/modules/aggs-matrix-stats/src/yamlRestTest/resources/rest-api-spec/test/stats/10_basic.yml +++ b/modules/aggs-matrix-stats/src/yamlRestTest/resources/rest-api-spec/test/stats/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: aggs-matrix-stats } } + - contains: { nodes.$cluster_manager.modules: { name: aggs-matrix-stats } } diff --git a/modules/analysis-common/src/internalClusterTest/java/org/opensearch/analysis/common/QueryStringWithAnalyzersIT.java b/modules/analysis-common/src/internalClusterTest/java/org/opensearch/analysis/common/QueryStringWithAnalyzersIT.java index 8c2f83bf83d85..7b1db7d648ebd 100644 --- a/modules/analysis-common/src/internalClusterTest/java/org/opensearch/analysis/common/QueryStringWithAnalyzersIT.java +++ b/modules/analysis-common/src/internalClusterTest/java/org/opensearch/analysis/common/QueryStringWithAnalyzersIT.java @@ -32,20 +32,42 @@ package org.opensearch.analysis.common; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.Operator; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.Arrays; import java.util.Collection; import static org.opensearch.index.query.QueryBuilders.queryStringQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; -public class QueryStringWithAnalyzersIT extends OpenSearchIntegTestCase { +public class QueryStringWithAnalyzersIT extends ParameterizedOpenSearchIntegTestCase { + + public QueryStringWithAnalyzersIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Arrays.asList(CommonAnalysisPlugin.class); diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/ASCIIFoldingTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/ASCIIFoldingTokenFilterFactory.java index b2eca8a601294..dc187de86ee19 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/ASCIIFoldingTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/ASCIIFoldingTokenFilterFactory.java @@ -34,8 +34,8 @@ import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter; -import org.opensearch.common.ParseField; import org.opensearch.common.settings.Settings; +import org.opensearch.core.ParseField; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/BrazilianStemTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/BrazilianStemTokenFilterFactory.java index 83e3cff1ddeda..8fbcb3fcd8215 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/BrazilianStemTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/BrazilianStemTokenFilterFactory.java @@ -32,10 +32,10 @@ package org.opensearch.analysis.common; +import org.apache.lucene.analysis.CharArraySet; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.br.BrazilianStemFilter; import org.apache.lucene.analysis.miscellaneous.SetKeywordMarkerFilter; -import org.apache.lucene.analysis.CharArraySet; import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/CommonAnalysisPlugin.java index c69917ed52be8..ec01f71da58e4 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/CommonAnalysisPlugin.java @@ -89,6 +89,7 @@ import org.apache.lucene.analysis.lt.LithuanianAnalyzer; import org.apache.lucene.analysis.lv.LatvianAnalyzer; import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter; +import org.apache.lucene.analysis.miscellaneous.DelimitedTermFrequencyTokenFilter; import org.apache.lucene.analysis.miscellaneous.DisableGraphAttribute; import org.apache.lucene.analysis.miscellaneous.KeywordRepeatFilter; import org.apache.lucene.analysis.miscellaneous.LengthFilter; @@ -123,16 +124,16 @@ import org.apache.lucene.analysis.tr.ApostropheFilter; import org.apache.lucene.analysis.tr.TurkishAnalyzer; import org.apache.lucene.analysis.util.ElisionFilter; -import org.apache.lucene.util.SetOnce; import org.opensearch.LegacyESVersion; import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.SetOnce; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.index.IndexSettings; @@ -154,8 +155,6 @@ import org.opensearch.script.ScriptService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; -import org.tartarus.snowball.ext.DutchStemmer; -import org.tartarus.snowball.ext.FrenchStemmer; import java.util.ArrayList; import java.util.Collection; @@ -165,6 +164,9 @@ import java.util.TreeMap; import java.util.function.Supplier; +import org.tartarus.snowball.ext.DutchStemmer; +import org.tartarus.snowball.ext.FrenchStemmer; + import static org.opensearch.plugins.AnalysisPlugin.requiresAnalysisSettings; public class CommonAnalysisPlugin extends Plugin implements AnalysisPlugin, ScriptPlugin { @@ -264,6 +266,7 @@ public Map> getTokenFilters() { ); filters.put("decimal_digit", DecimalDigitFilterFactory::new); filters.put("delimited_payload", DelimitedPayloadTokenFilterFactory::new); + filters.put("delimited_term_freq", DelimitedTermFrequencyTokenFilterFactory::new); filters.put("dictionary_decompounder", requiresAnalysisSettings(DictionaryCompoundWordTokenFilterFactory::new)); filters.put("dutch_stem", DutchStemTokenFilterFactory::new); filters.put("edge_ngram", EdgeNGramTokenFilterFactory::new); @@ -482,6 +485,13 @@ public List getPreConfiguredTokenFilters() { ) ) ); + filters.add( + PreConfiguredTokenFilter.singleton( + "delimited_term_freq", + false, + input -> new DelimitedTermFrequencyTokenFilter(input, DelimitedTermFrequencyTokenFilterFactory.DEFAULT_DELIMITER) + ) + ); filters.add(PreConfiguredTokenFilter.singleton("dutch_stem", false, input -> new SnowballFilter(input, new DutchStemmer()))); filters.add(PreConfiguredTokenFilter.singleton("edge_ngram", false, false, input -> new EdgeNGramTokenFilter(input, 1))); filters.add(PreConfiguredTokenFilter.openSearchVersion("edgeNGram", false, false, (reader, version) -> { diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/DelimitedTermFrequencyTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/DelimitedTermFrequencyTokenFilterFactory.java new file mode 100644 index 0000000000000..8929a7c54ef4c --- /dev/null +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/DelimitedTermFrequencyTokenFilterFactory.java @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.analysis.common; + +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.miscellaneous.DelimitedTermFrequencyTokenFilter; +import org.opensearch.common.settings.Settings; +import org.opensearch.env.Environment; +import org.opensearch.index.IndexSettings; +import org.opensearch.index.analysis.AbstractTokenFilterFactory; + +public class DelimitedTermFrequencyTokenFilterFactory extends AbstractTokenFilterFactory { + public static final char DEFAULT_DELIMITER = '|'; + private static final String DELIMITER = "delimiter"; + private final char delimiter; + + DelimitedTermFrequencyTokenFilterFactory(IndexSettings indexSettings, Environment environment, String name, Settings settings) { + super(indexSettings, name, settings); + delimiter = parseDelimiter(settings); + } + + @Override + public TokenStream create(TokenStream tokenStream) { + return new DelimitedTermFrequencyTokenFilter(tokenStream, delimiter); + } + + private static char parseDelimiter(Settings settings) { + String delimiter = settings.get(DELIMITER); + if (delimiter == null) { + return DEFAULT_DELIMITER; + } else if (delimiter.length() == 1) { + return delimiter.charAt(0); + } + + throw new IllegalArgumentException( + "Setting [" + DELIMITER + "] must be a single, non-null character. [" + delimiter + "] was provided." + ); + } +} diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/DutchStemTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/DutchStemTokenFilterFactory.java index 296740fb161ad..66e5cac331990 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/DutchStemTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/DutchStemTokenFilterFactory.java @@ -41,6 +41,7 @@ import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; import org.opensearch.index.analysis.Analysis; + import org.tartarus.snowball.ext.DutchStemmer; public class DutchStemTokenFilterFactory extends AbstractTokenFilterFactory { diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/EnglishPluralStemFilter.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/EnglishPluralStemFilter.java new file mode 100644 index 0000000000000..c30318a31527b --- /dev/null +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/EnglishPluralStemFilter.java @@ -0,0 +1,182 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.analysis.common; + +import org.apache.lucene.analysis.TokenFilter; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.en.EnglishMinimalStemFilter; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.KeywordAttribute; + +import java.io.IOException; + +public final class EnglishPluralStemFilter extends TokenFilter { + private final EnglishPluralStemmer stemmer = new EnglishPluralStemmer(); + private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class); + private final KeywordAttribute keywordAttr = addAttribute(KeywordAttribute.class); + + public EnglishPluralStemFilter(TokenStream input) { + super(input); + } + + @Override + public boolean incrementToken() throws IOException { + if (input.incrementToken()) { + if (!keywordAttr.isKeyword()) { + final int newlen = stemmer.stem(termAtt.buffer(), termAtt.length()); + termAtt.setLength(newlen); + } + return true; + } else { + return false; + } + } + + /** + * Plural stemmer for English based on the {@link EnglishMinimalStemFilter} + *

    + * This stemmer removes plurals but beyond EnglishMinimalStemFilter adds + * four new suffix rules to remove dangling e characters: + *

      + *
    • xes - "boxes" becomes "box"
    • + *
    • sses - "dresses" becomes "dress"
    • + *
    • shes - "dishes" becomes "dish"
    • + *
    • tches - "watches" becomes "watch"
    • + *
    + * See https://github.com/elastic/elasticsearch/issues/42892 + *

    + * In addition the s stemmer logic is amended so that + *

      + *
    • ees->ee so that bees matches bee
    • + *
    • ies->y only on longer words to that ties matches tie
    • + *
    • oes->o rule so that tomatoes matches tomato but retains e for some words eg shoes to shoe
    • + *
    + */ + public static class EnglishPluralStemmer { + + // Words ending in oes that retain the e when stemmed + public static final char[][] oesExceptions = { "shoes".toCharArray(), "canoes".toCharArray(), "oboes".toCharArray() }; + // Words ending in ches that retain the e when stemmed + public static final char[][] chesExceptions = { + "cliches".toCharArray(), + "avalanches".toCharArray(), + "mustaches".toCharArray(), + "moustaches".toCharArray(), + "quiches".toCharArray(), + "headaches".toCharArray(), + "heartaches".toCharArray(), + "porsches".toCharArray(), + "tranches".toCharArray(), + "caches".toCharArray() }; + + @SuppressWarnings("fallthrough") + public int stem(char s[], int len) { + if (len < 3 || s[len - 1] != 's') return len; + + switch (s[len - 2]) { + case 'u': + case 's': + return len; + case 'e': + // Modified ies->y logic from original s-stemmer - only work on strings > 4 + // so spies -> spy still but pies->pie. + // The original code also special-cased aies and eies for no good reason as far as I can tell. + // ( no words of consequence - eg http://www.thefreedictionary.com/words-that-end-in-aies ) + if (len > 4 && s[len - 3] == 'i') { + s[len - 3] = 'y'; + return len - 2; + } + + // Suffix rules to remove any dangling "e" + if (len > 3) { + // xes (but >1 prefix so we can stem "boxes->box" but keep "axes->axe") + if (len > 4 && s[len - 3] == 'x') { + return len - 2; + } + // oes + if (len > 3 && s[len - 3] == 'o') { + if (isException(s, len, oesExceptions)) { + // Only remove the S + return len - 1; + } + // Remove the es + return len - 2; + } + if (len > 4) { + // shes/sses + if (s[len - 4] == 's' && (s[len - 3] == 'h' || s[len - 3] == 's')) { + return len - 2; + } + + // ches + if (len > 4) { + if (s[len - 4] == 'c' && s[len - 3] == 'h') { + if (isException(s, len, chesExceptions)) { + // Only remove the S + return len - 1; + } + // Remove the es + return len - 2; + + } + } + } + } + + default: + return len - 1; + } + } + + private boolean isException(char[] s, int len, char[][] exceptionsList) { + for (char[] oesRule : exceptionsList) { + int rulePos = oesRule.length - 1; + int sPos = len - 1; + boolean matched = true; + while (rulePos >= 0 && sPos >= 0) { + if (oesRule[rulePos] != s[sPos]) { + matched = false; + break; + } + rulePos--; + sPos--; + } + if (matched) { + return true; + } + } + return false; + } + } + +} diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/FingerprintAnalyzerProvider.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/FingerprintAnalyzerProvider.java index f5c90dbd23b1e..ae315ff0a9cfe 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/FingerprintAnalyzerProvider.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/FingerprintAnalyzerProvider.java @@ -34,8 +34,8 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.CharArraySet; -import org.opensearch.common.ParseField; import org.opensearch.common.settings.Settings; +import org.opensearch.core.ParseField; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractIndexAnalyzerProvider; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/FrenchStemTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/FrenchStemTokenFilterFactory.java index 15872d8b288c0..3aa546a79cde6 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/FrenchStemTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/FrenchStemTokenFilterFactory.java @@ -41,6 +41,7 @@ import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; import org.opensearch.index.analysis.Analysis; + import org.tartarus.snowball.ext.FrenchStemmer; public class FrenchStemTokenFilterFactory extends AbstractTokenFilterFactory { diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/HyphenationCompoundWordTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/HyphenationCompoundWordTokenFilterFactory.java index 875c5261f8387..8d29a347caeb8 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/HyphenationCompoundWordTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/HyphenationCompoundWordTokenFilterFactory.java @@ -32,18 +32,21 @@ package org.opensearch.analysis.common; +import org.apache.logging.log4j.LogManager; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.compound.HyphenationCompoundWordTokenFilter; import org.apache.lucene.analysis.compound.hyphenation.HyphenationTree; import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; -import org.xml.sax.InputSource; +import org.opensearch.index.analysis.Analysis; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import org.xml.sax.InputSource; + /** * Uses the {@link org.apache.lucene.analysis.compound.HyphenationCompoundWordTokenFilter} to decompound tokens based on hyphenation rules. * @@ -61,13 +64,15 @@ public class HyphenationCompoundWordTokenFilterFactory extends AbstractCompoundW throw new IllegalArgumentException("hyphenation_patterns_path is a required setting."); } - Path hyphenationPatternsFile = env.configFile().resolve(hyphenationPatternsPath); + Path hyphenationPatternsFile = Analysis.resolveAnalyzerPath(env, hyphenationPatternsPath); try { InputStream in = Files.newInputStream(hyphenationPatternsFile); hyphenationTree = HyphenationCompoundWordTokenFilter.getHyphenationTree(new InputSource(in)); } catch (Exception e) { - throw new IllegalArgumentException("Exception while reading hyphenation_patterns_path.", e); + LogManager.getLogger(HyphenationCompoundWordTokenFilterFactory.class) + .error("Exception while reading hyphenation_patterns_path ", e); + throw new IllegalArgumentException("Exception while reading hyphenation_patterns_path."); } } diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/MappingCharFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/MappingCharFilterFactory.java index 7200b69135a30..d6d9f8975f2fc 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/MappingCharFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/MappingCharFilterFactory.java @@ -39,6 +39,7 @@ import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractCharFilterFactory; import org.opensearch.index.analysis.Analysis; +import org.opensearch.index.analysis.MappingRule; import org.opensearch.index.analysis.NormalizingCharFilterFactory; import java.io.Reader; @@ -53,13 +54,13 @@ public class MappingCharFilterFactory extends AbstractCharFilterFactory implemen MappingCharFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) { super(indexSettings, name); - List rules = Analysis.getWordList(env, settings, "mappings"); + List> rules = Analysis.parseWordList(env, settings, "mappings", this::parse); if (rules == null) { throw new IllegalArgumentException("mapping requires either `mappings` or `mappings_path` to be configured"); } NormalizeCharMap.Builder normMapBuilder = new NormalizeCharMap.Builder(); - parseRules(rules, normMapBuilder); + rules.forEach(rule -> normMapBuilder.add(rule.getLeft(), rule.getRight())); normMap = normMapBuilder.build(); } @@ -71,18 +72,13 @@ public Reader create(Reader tokenStream) { // source => target private static Pattern rulePattern = Pattern.compile("(.*)\\s*=>\\s*(.*)\\s*$"); - /** - * parses a list of MappingCharFilter style rules into a normalize char map - */ - private void parseRules(List rules, NormalizeCharMap.Builder map) { - for (String rule : rules) { - Matcher m = rulePattern.matcher(rule); - if (!m.find()) throw new RuntimeException("Invalid Mapping Rule : [" + rule + "]"); - String lhs = parseString(m.group(1).trim()); - String rhs = parseString(m.group(2).trim()); - if (lhs == null || rhs == null) throw new RuntimeException("Invalid Mapping Rule : [" + rule + "]. Illegal mapping."); - map.add(lhs, rhs); - } + private MappingRule parse(String rule) { + Matcher m = rulePattern.matcher(rule); + if (!m.find()) throw new RuntimeException("Invalid mapping rule : [" + rule + "]"); + String lhs = parseString(m.group(1).trim()); + String rhs = parseString(m.group(2).trim()); + if (lhs == null || rhs == null) throw new RuntimeException("Invalid mapping rule: [" + rule + "]. Illegal mapping."); + return new MappingRule<>(lhs, rhs); } char[] out = new char[256]; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/MultiplexerTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/MultiplexerTokenFilterFactory.java index 27203efa6a230..fde47a53733e4 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/MultiplexerTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/MultiplexerTokenFilterFactory.java @@ -37,8 +37,8 @@ import org.apache.lucene.analysis.miscellaneous.ConditionalTokenFilter; import org.apache.lucene.analysis.miscellaneous.RemoveDuplicatesTokenFilter; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/OpenSearchSolrSynonymParser.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/OpenSearchSolrSynonymParser.java index e50c37f03cd2e..e9d5d8cb25faf 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/OpenSearchSolrSynonymParser.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/OpenSearchSolrSynonymParser.java @@ -32,8 +32,8 @@ package org.opensearch.analysis.common; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.synonym.SolrSynonymParser; import org.apache.lucene.util.CharsRef; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/OpenSearchWordnetSynonymParser.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/OpenSearchWordnetSynonymParser.java index c7ae6c08411b9..9ecc38fde7e60 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/OpenSearchWordnetSynonymParser.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/OpenSearchWordnetSynonymParser.java @@ -32,8 +32,8 @@ package org.opensearch.analysis.common; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.synonym.WordnetSynonymParser; import org.apache.lucene.util.CharsRef; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/PatternReplaceCharFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/PatternReplaceCharFilterFactory.java index 3c6cdbf5c5a69..d6fca8ac67d55 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/PatternReplaceCharFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/PatternReplaceCharFilterFactory.java @@ -32,9 +32,9 @@ package org.opensearch.analysis.common; import org.apache.lucene.analysis.pattern.PatternReplaceCharFilter; -import org.opensearch.common.Strings; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractCharFilterFactory; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/SnowballTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/SnowballTokenFilterFactory.java index 591d1947c1eb8..759febb81728b 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/SnowballTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/SnowballTokenFilterFactory.java @@ -33,8 +33,8 @@ import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.snowball.SnowballFilter; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactory.java index 89f0766542296..bdd6e01261443 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactory.java @@ -40,24 +40,31 @@ import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; import org.opensearch.index.analysis.Analysis; +import org.opensearch.index.analysis.MappingRule; import java.io.IOException; +import java.util.ArrayList; import java.util.List; public class StemmerOverrideTokenFilterFactory extends AbstractTokenFilterFactory { + private static final String MAPPING_SEPARATOR = "=>"; private final StemmerOverrideMap overrideMap; StemmerOverrideTokenFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) throws IOException { super(indexSettings, name, settings); - List rules = Analysis.getWordList(env, settings, "rules"); + List, String>> rules = Analysis.parseWordList(env, settings, "rules", this::parse); if (rules == null) { throw new IllegalArgumentException("stemmer override filter requires either `rules` or `rules_path` to be configured"); } StemmerOverrideFilter.Builder builder = new StemmerOverrideFilter.Builder(false); - parseRules(rules, builder, "=>"); + for (MappingRule, String> rule : rules) { + for (String key : rule.getLeft()) { + builder.add(key, rule.getRight()); + } + } overrideMap = builder.build(); } @@ -67,27 +74,26 @@ public TokenStream create(TokenStream tokenStream) { return new StemmerOverrideFilter(tokenStream, overrideMap); } - static void parseRules(List rules, StemmerOverrideFilter.Builder builder, String mappingSep) { - for (String rule : rules) { - String[] sides = rule.split(mappingSep, -1); - if (sides.length != 2) { - throw new RuntimeException("Invalid Keyword override Rule:" + rule); - } + private MappingRule, String> parse(String rule) { + String[] sides = rule.split(MAPPING_SEPARATOR, -1); + if (sides.length != 2) { + throw new RuntimeException("Invalid keyword override rule: " + rule); + } - String[] keys = sides[0].split(",", -1); - String override = sides[1].trim(); - if (override.isEmpty() || override.indexOf(',') != -1) { - throw new RuntimeException("Invalid Keyword override Rule:" + rule); - } + String[] keys = sides[0].split(",", -1); + String override = sides[1].trim(); + if (override.isEmpty() || override.indexOf(',') != -1) { + throw new RuntimeException("Invalid keyword override rule: " + rule); + } - for (String key : keys) { - String trimmedKey = key.trim(); - if (trimmedKey.isEmpty()) { - throw new RuntimeException("Invalid Keyword override Rule:" + rule); - } - builder.add(trimmedKey, override); + List trimmedKeys = new ArrayList<>(); + for (String key : keys) { + String trimmedKey = key.trim(); + if (trimmedKey.isEmpty()) { + throw new RuntimeException("Invalid keyword override rule: " + rule); } + trimmedKeys.add(trimmedKey); } + return new MappingRule<>(trimmedKeys, override); } - } diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerTokenFilterFactory.java index 5d96f01265cf6..5506626e40da0 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/StemmerTokenFilterFactory.java @@ -67,11 +67,14 @@ import org.apache.lucene.analysis.ru.RussianLightStemFilter; import org.apache.lucene.analysis.snowball.SnowballFilter; import org.apache.lucene.analysis.sv.SwedishLightStemFilter; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; + +import java.io.IOException; + import org.tartarus.snowball.ext.ArmenianStemmer; import org.tartarus.snowball.ext.BasqueStemmer; import org.tartarus.snowball.ext.CatalanStemmer; @@ -97,8 +100,6 @@ import org.tartarus.snowball.ext.SwedishStemmer; import org.tartarus.snowball.ext.TurkishStemmer; -import java.io.IOException; - public class StemmerTokenFilterFactory extends AbstractTokenFilterFactory { private static final TokenStream EMPTY_TOKEN_STREAM = new EmptyTokenStream(); @@ -154,6 +155,8 @@ public TokenStream create(TokenStream tokenStream) { return new SnowballFilter(tokenStream, new EnglishStemmer()); } else if ("minimal_english".equalsIgnoreCase(language) || "minimalEnglish".equalsIgnoreCase(language)) { return new EnglishMinimalStemFilter(tokenStream); + } else if ("plural_english".equalsIgnoreCase(language) || "pluralEnglish".equalsIgnoreCase(language)) { + return new EnglishPluralStemFilter(tokenStream); } else if ("possessive_english".equalsIgnoreCase(language) || "possessiveEnglish".equalsIgnoreCase(language)) { return new EnglishPossessiveFilter(tokenStream); diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/SynonymTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/SynonymTokenFilterFactory.java index dc6b5b2dd8b7b..01a65e87d7466 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/SynonymTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/SynonymTokenFilterFactory.java @@ -32,6 +32,7 @@ package org.opensearch.analysis.common; +import org.apache.logging.log4j.LogManager; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.synonym.SynonymFilter; @@ -155,14 +156,15 @@ SynonymMap buildSynonyms(Analyzer analyzer, Reader rules) { } return parser.build(); } catch (Exception e) { - throw new IllegalArgumentException("failed to build synonyms", e); + LogManager.getLogger(SynonymTokenFilterFactory.class).error("Failed to build synonyms: ", e); + throw new IllegalArgumentException("Failed to build synonyms"); } } Reader getRulesFromSettings(Environment env) { Reader rulesReader; if (settings.getAsList("synonyms", null) != null) { - List rulesList = Analysis.getWordList(env, settings, "synonyms"); + List rulesList = Analysis.parseWordList(env, settings, "synonyms", s -> s); StringBuilder sb = new StringBuilder(); for (String line : rulesList) { sb.append(line).append(System.lineSeparator()); diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/UAX29URLEmailTokenizerFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/UAX29URLEmailTokenizerFactory.java index 8d6e0ec0815b4..8d9eb4902daae 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/UAX29URLEmailTokenizerFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/UAX29URLEmailTokenizerFactory.java @@ -33,8 +33,8 @@ package org.opensearch.analysis.common; import org.apache.lucene.analysis.Tokenizer; -import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.email.UAX29URLEmailTokenizer; +import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactory.java index 31d52d030cb71..15f4cf8cba0e0 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactory.java @@ -41,11 +41,13 @@ import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; import org.opensearch.index.analysis.Analysis; +import org.opensearch.index.analysis.MappingRule; import org.opensearch.index.analysis.TokenFilterFactory; import java.util.List; import java.util.Set; +import static org.opensearch.analysis.common.WordDelimiterTokenFilterFactory.parseTypes; import static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.CATENATE_ALL; import static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.CATENATE_NUMBERS; import static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.CATENATE_WORDS; @@ -56,7 +58,6 @@ import static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.SPLIT_ON_CASE_CHANGE; import static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.SPLIT_ON_NUMERICS; import static org.apache.lucene.analysis.miscellaneous.WordDelimiterGraphFilter.STEM_ENGLISH_POSSESSIVE; -import static org.opensearch.analysis.common.WordDelimiterTokenFilterFactory.parseTypes; public class WordDelimiterGraphTokenFilterFactory extends AbstractTokenFilterFactory { private final byte[] charTypeTable; @@ -73,7 +74,12 @@ public WordDelimiterGraphTokenFilterFactory(IndexSettings indexSettings, Environ // . => DIGIT // \u002C => DIGIT // \u200D => ALPHANUM - List charTypeTableValues = Analysis.getWordList(env, settings, "type_table"); + List> charTypeTableValues = Analysis.parseWordList( + env, + settings, + "type_table", + WordDelimiterTokenFilterFactory::parse + ); if (charTypeTableValues == null) { this.charTypeTable = WordDelimiterIterator.DEFAULT_WORD_DELIM_TABLE; } else { diff --git a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterTokenFilterFactory.java index d40acfa05dd21..96e50206fb53d 100644 --- a/modules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/opensearch/analysis/common/WordDelimiterTokenFilterFactory.java @@ -41,6 +41,7 @@ import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AbstractTokenFilterFactory; import org.opensearch.index.analysis.Analysis; +import org.opensearch.index.analysis.MappingRule; import org.opensearch.index.analysis.TokenFilterFactory; import java.util.Collection; @@ -76,7 +77,12 @@ public WordDelimiterTokenFilterFactory(IndexSettings indexSettings, Environment // . => DIGIT // \u002C => DIGIT // \u200D => ALPHANUM - List charTypeTableValues = Analysis.getWordList(env, settings, "type_table"); + List> charTypeTableValues = Analysis.parseWordList( + env, + settings, + "type_table", + WordDelimiterTokenFilterFactory::parse + ); if (charTypeTableValues == null) { this.charTypeTable = WordDelimiterIterator.DEFAULT_WORD_DELIM_TABLE; } else { @@ -127,19 +133,23 @@ public int getFlag(int flag, Settings settings, String key, boolean defaultValue // source => type private static Pattern typePattern = Pattern.compile("(.*)\\s*=>\\s*(.*)\\s*$"); + static MappingRule parse(String rule) { + Matcher m = typePattern.matcher(rule); + if (!m.find()) throw new RuntimeException("Invalid mapping rule: [" + rule + "]"); + String lhs = parseString(m.group(1).trim()); + Byte rhs = parseType(m.group(2).trim()); + if (lhs.length() != 1) throw new RuntimeException("Invalid mapping rule: [" + rule + "]. Only a single character is allowed."); + if (rhs == null) throw new RuntimeException("Invalid mapping rule: [" + rule + "]. Illegal type."); + return new MappingRule<>(lhs.charAt(0), rhs); + } + /** * parses a list of MappingCharFilter style rules into a custom byte[] type table */ - static byte[] parseTypes(Collection rules) { + static byte[] parseTypes(Collection> rules) { SortedMap typeMap = new TreeMap<>(); - for (String rule : rules) { - Matcher m = typePattern.matcher(rule); - if (!m.find()) throw new RuntimeException("Invalid Mapping Rule : [" + rule + "]"); - String lhs = parseString(m.group(1).trim()); - Byte rhs = parseType(m.group(2).trim()); - if (lhs.length() != 1) throw new RuntimeException("Invalid Mapping Rule : [" + rule + "]. Only a single character is allowed."); - if (rhs == null) throw new RuntimeException("Invalid Mapping Rule : [" + rule + "]. Illegal type."); - typeMap.put(lhs.charAt(0), rhs); + for (MappingRule rule : rules) { + typeMap.put(rule.getLeft(), rule.getRight()); } // ensure the table is always at least as big as DEFAULT_WORD_DELIM_TABLE for performance diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/BaseWordDelimiterTokenFilterFactoryTestCase.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/BaseWordDelimiterTokenFilterFactoryTestCase.java index 9d54776755766..d28155272a9db 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/BaseWordDelimiterTokenFilterFactoryTestCase.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/BaseWordDelimiterTokenFilterFactoryTestCase.java @@ -195,4 +195,24 @@ public void testStemEnglishPossessive() throws IOException { tokenizer.setReader(new StringReader(source)); assertTokenStreamContents(tokenFilter.create(tokenizer), expected); } + + private void createTokenFilterFactoryWithTypeTable(String[] rules) throws IOException { + OpenSearchTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings( + Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) + .put("index.analysis.filter.my_word_delimiter.type", type) + .putList("index.analysis.filter.my_word_delimiter.type_table", rules) + .put("index.analysis.filter.my_word_delimiter.catenate_words", "true") + .put("index.analysis.filter.my_word_delimiter.generate_word_parts", "true") + .build(), + new CommonAnalysisPlugin() + ); + analysis.tokenFilter.get("my_word_delimiter"); + } + + public void testTypeTableParsingError() { + String[] rules = { "# This is a comment", "# => ALPHANUM", "$ => DIGIT", "\\u200D => ALPHANUM", "abc => ALPHA" }; + RuntimeException ex = expectThrows(RuntimeException.class, () -> createTokenFilterFactoryWithTypeTable(rules)); + assertEquals("Line [5]: Invalid mapping rule: [abc => ALPHA]. Only a single character is allowed.", ex.getMessage()); + } } diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CharGroupTokenizerFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CharGroupTokenizerFactoryTests.java index 54ab1bc154641..459f463c3ee28 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CharGroupTokenizerFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CharGroupTokenizerFactoryTests.java @@ -33,13 +33,14 @@ package org.opensearch.analysis.common; import com.carrotsearch.randomizedtesting.generators.RandomStrings; + import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.util.CharTokenizer; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexSettings; -import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import java.io.IOException; import java.io.Reader; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CommonAnalysisFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CommonAnalysisFactoryTests.java index 4cf0d1de28717..e0e99cdc31672 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CommonAnalysisFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CommonAnalysisFactoryTests.java @@ -145,6 +145,7 @@ protected Map> getTokenFilters() { filters.put("cjkwidth", CJKWidthFilterFactory.class); filters.put("cjkbigram", CJKBigramFilterFactory.class); filters.put("delimitedpayload", DelimitedPayloadTokenFilterFactory.class); + filters.put("delimitedtermfrequency", DelimitedTermFrequencyTokenFilterFactory.class); filters.put("keepword", KeepWordFilterFactory.class); filters.put("type", KeepTypesFilterFactory.class); filters.put("classic", ClassicFilterFactory.class); @@ -202,6 +203,7 @@ protected Map> getPreConfiguredTokenFilters() { filters.put("decimal_digit", null); filters.put("delimited_payload_filter", org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilterFactory.class); filters.put("delimited_payload", org.apache.lucene.analysis.payloads.DelimitedPayloadTokenFilterFactory.class); + filters.put("delimited_term_freq", org.apache.lucene.analysis.miscellaneous.DelimitedTermFrequencyTokenFilterFactory.class); filters.put("dutch_stem", SnowballPorterFilterFactory.class); filters.put("edge_ngram", null); filters.put("edgeNGram", null); diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CompoundAnalysisTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CompoundAnalysisTests.java index e5ce7c818f72b..1955b7eaa0e45 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CompoundAnalysisTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/CompoundAnalysisTests.java @@ -47,8 +47,8 @@ import org.opensearch.indices.analysis.AnalysisModule; import org.opensearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.opensearch.plugins.AnalysisPlugin; -import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.MatcherAssert; import java.io.IOException; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/ConcatenateGraphTokenFilterFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/ConcatenateGraphTokenFilterFactoryTests.java index 509010e209088..a1cbfd527b21e 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/ConcatenateGraphTokenFilterFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/ConcatenateGraphTokenFilterFactoryTests.java @@ -8,12 +8,12 @@ package org.opensearch.analysis.common; -import org.apache.lucene.tests.analysis.CannedTokenStream; -import org.apache.lucene.tests.analysis.Token; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.core.WhitespaceTokenizer; import org.apache.lucene.analysis.miscellaneous.ConcatenateGraphFilter; +import org.apache.lucene.tests.analysis.CannedTokenStream; +import org.apache.lucene.tests.analysis.Token; import org.apache.lucene.util.automaton.TooComplexToDeterminizeException; import org.opensearch.LegacyESVersion; import org.opensearch.cluster.metadata.IndexMetadata; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/DelimitedTermFrequencyTokenFilterFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/DelimitedTermFrequencyTokenFilterFactoryTests.java new file mode 100644 index 0000000000000..bb8698e535a62 --- /dev/null +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/DelimitedTermFrequencyTokenFilterFactoryTests.java @@ -0,0 +1,89 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.analysis.common; + +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.Tokenizer; +import org.apache.lucene.analysis.core.WhitespaceTokenizer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.TermFrequencyAttribute; +import org.opensearch.common.settings.Settings; +import org.opensearch.env.Environment; +import org.opensearch.index.analysis.AnalysisTestsHelper; +import org.opensearch.index.analysis.TokenFilterFactory; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.OpenSearchTokenStreamTestCase; + +import java.io.StringReader; + +public class DelimitedTermFrequencyTokenFilterFactoryTests extends OpenSearchTokenStreamTestCase { + + public void testDefault() throws Exception { + OpenSearchTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings( + Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) + .put("index.analysis.filter.my_delimited_term_freq.type", "delimited_term_freq") + .build(), + new CommonAnalysisPlugin() + ); + doTest(analysis, "cat|4 dog|5"); + } + + public void testDelimiter() throws Exception { + OpenSearchTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings( + Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) + .put("index.analysis.filter.my_delimited_term_freq.type", "delimited_term_freq") + .put("index.analysis.filter.my_delimited_term_freq.delimiter", ":") + .build(), + new CommonAnalysisPlugin() + ); + doTest(analysis, "cat:4 dog:5"); + } + + public void testDelimiterLongerThanOneCharThrows() { + IllegalArgumentException ex = expectThrows( + IllegalArgumentException.class, + () -> AnalysisTestsHelper.createTestAnalysisFromSettings( + Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) + .put("index.analysis.filter.my_delimited_term_freq.type", "delimited_term_freq") + .put("index.analysis.filter.my_delimited_term_freq.delimiter", "^^") + .build(), + new CommonAnalysisPlugin() + ) + ); + + assertEquals("Setting [delimiter] must be a single, non-null character. [^^] was provided.", ex.getMessage()); + } + + private void doTest(OpenSearchTestCase.TestAnalysis analysis, String source) throws Exception { + TokenFilterFactory tokenFilter = analysis.tokenFilter.get("my_delimited_term_freq"); + Tokenizer tokenizer = new WhitespaceTokenizer(); + tokenizer.setReader(new StringReader(source)); + + TokenStream stream = tokenFilter.create(tokenizer); + + CharTermAttribute termAtt = stream.getAttribute(CharTermAttribute.class); + TermFrequencyAttribute tfAtt = stream.getAttribute(TermFrequencyAttribute.class); + stream.reset(); + assertTermEquals("cat", stream, termAtt, tfAtt, 4); + assertTermEquals("dog", stream, termAtt, tfAtt, 5); + assertFalse(stream.incrementToken()); + stream.end(); + stream.close(); + } + + void assertTermEquals(String expected, TokenStream stream, CharTermAttribute termAtt, TermFrequencyAttribute tfAtt, int expectedTf) + throws Exception { + assertTrue(stream.incrementToken()); + assertEquals(expected, termAtt.toString()); + assertEquals(expectedTf, tfAtt.getTermFrequency()); + } +} diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/DisableGraphQueryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/DisableGraphQueryTests.java index 35915af8f263d..0d1448c9b276f 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/DisableGraphQueryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/DisableGraphQueryTests.java @@ -33,13 +33,13 @@ package org.opensearch.analysis.common; import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.MultiPhraseQuery; +import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.SynonymQuery; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.PhraseQuery; -import org.apache.lucene.search.MultiPhraseQuery; import org.opensearch.common.settings.Settings; import org.opensearch.index.IndexService; import org.opensearch.index.query.MatchPhraseQueryBuilder; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/EdgeNGramTokenizerTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/EdgeNGramTokenizerTests.java index 07ac0d69428af..a8aafc402baed 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/EdgeNGramTokenizerTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/EdgeNGramTokenizerTests.java @@ -37,15 +37,15 @@ import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.Index; import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; -import org.opensearch.index.Index; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.IndexAnalyzers; import org.opensearch.index.analysis.NamedAnalyzer; import org.opensearch.indices.analysis.AnalysisModule; -import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.VersionUtils; import java.io.IOException; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/FlattenGraphTokenFilterFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/FlattenGraphTokenFilterFactoryTests.java index 7d9555923e4ef..5e8365409a725 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/FlattenGraphTokenFilterFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/FlattenGraphTokenFilterFactoryTests.java @@ -32,14 +32,14 @@ package org.opensearch.analysis.common; +import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.tests.analysis.CannedTokenStream; import org.apache.lucene.tests.analysis.Token; -import org.apache.lucene.analysis.TokenStream; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexSettings; -import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import java.io.IOException; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/HighlighterWithAnalyzersTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/HighlighterWithAnalyzersTests.java index 57c959a4f0b65..0ffb7f2114f28 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/HighlighterWithAnalyzersTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/HighlighterWithAnalyzersTests.java @@ -32,16 +32,19 @@ package org.opensearch.analysis.common; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.Operator; import org.opensearch.plugins.Plugin; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.Arrays; @@ -50,19 +53,40 @@ import static org.opensearch.client.Requests.searchRequest; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.boolQuery; +import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.matchPhrasePrefixQuery; import static org.opensearch.index.query.QueryBuilders.matchPhraseQuery; import static org.opensearch.index.query.QueryBuilders.matchQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.builder.SearchSourceBuilder.highlight; import static org.opensearch.search.builder.SearchSourceBuilder.searchSource; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHighlight; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.startsWith; -public class HighlighterWithAnalyzersTests extends OpenSearchIntegTestCase { +public class HighlighterWithAnalyzersTests extends ParameterizedOpenSearchIntegTestCase { + + public HighlighterWithAnalyzersTests(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Arrays.asList(CommonAnalysisPlugin.class); @@ -270,9 +294,13 @@ public void testPhrasePrefix() throws IOException { refresh(); logger.info("--> highlighting and searching on field0"); - SearchSourceBuilder source = searchSource().query(matchPhrasePrefixQuery("field0", "bro")) - .highlighter(highlight().field("field0").order("score").preTags("").postTags("")); + SearchSourceBuilder source = searchSource().query(matchAllQuery()); SearchResponse searchResponse = client().search(searchRequest("first_test_index").source(source)).actionGet(); + assertHitCount(searchResponse, 2); + + source = searchSource().query(matchPhrasePrefixQuery("field0", "bro")) + .highlighter(highlight().field("field0").order("score").preTags("").postTags("")); + searchResponse = client().search(searchRequest("first_test_index").source(source)).actionGet(); assertHighlight(searchResponse, 0, "field0", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); @@ -415,6 +443,7 @@ public void testPhrasePrefix() throws IOException { public static XContentBuilder type1TermVectorMapping() throws IOException { return XContentFactory.jsonBuilder() .startObject() + .startObject("_doc") .startObject("properties") .startObject("field1") .field("type", "text") @@ -425,6 +454,7 @@ public static XContentBuilder type1TermVectorMapping() throws IOException { .field("term_vector", "with_positions_offsets") .endObject() .endObject() + .endObject() .endObject(); } } diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/MappingCharFilterFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/MappingCharFilterFactoryTests.java new file mode 100644 index 0000000000000..2ef9f4e91655f --- /dev/null +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/MappingCharFilterFactoryTests.java @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.analysis.common; + +import org.apache.lucene.analysis.CharFilter; +import org.opensearch.common.settings.Settings; +import org.opensearch.env.Environment; +import org.opensearch.index.analysis.AnalysisTestsHelper; +import org.opensearch.index.analysis.CharFilterFactory; +import org.opensearch.test.OpenSearchTestCase; + +import java.io.IOException; +import java.io.StringReader; +import java.util.Arrays; + +public class MappingCharFilterFactoryTests extends OpenSearchTestCase { + public static CharFilterFactory create(String... rules) throws IOException { + OpenSearchTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings( + Settings.builder() + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) + .put("index.analysis.analyzer.my_analyzer.tokenizer", "standard") + .put("index.analysis.analyzer.my_analyzer.char_filter", "my_mappings_char_filter") + .put("index.analysis.char_filter.my_mappings_char_filter.type", "mapping") + .putList("index.analysis.char_filter.my_mappings_char_filter.mappings", rules) + .build(), + new CommonAnalysisPlugin() + ); + + return analysis.charFilter.get("my_mappings_char_filter"); + } + + public void testRulesOk() throws IOException { + MappingCharFilterFactory mappingCharFilterFactory = (MappingCharFilterFactory) create( + "# This is a comment", + "# => _hashtag_", + ":) => _happy_", + ":( => _sad_" + ); + CharFilter inputReader = (CharFilter) mappingCharFilterFactory.create(new StringReader("I'm so :), I'm so :( #confused")); + char[] tempBuff = new char[14]; + StringBuilder output = new StringBuilder(); + while (true) { + int length = inputReader.read(tempBuff); + if (length == -1) break; + output.append(tempBuff, 0, length); + } + assertEquals("I'm so _happy_, I'm so _sad_ _hashtag_confused", output.toString()); + } + + public void testRuleError() { + for (String rule : Arrays.asList( + "", // empty + "a", // no arrow + "a:>b" // invalid delimiter + )) { + RuntimeException ex = expectThrows(RuntimeException.class, () -> create(rule)); + assertEquals("Line [1]: Invalid mapping rule : [" + rule + "]", ex.getMessage()); + } + } + + public void testRulePartError() { + RuntimeException ex = expectThrows( + RuntimeException.class, + () -> create("# This is a comment", "# => _hashtag_", ":) => _happy_", "a:b") + ); + assertEquals("Line [4]: Invalid mapping rule : [a:b]", ex.getMessage()); + } +} diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/MultiplexerTokenFilterTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/MultiplexerTokenFilterTests.java index 167f61464da1b..1d3858f581c3a 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/MultiplexerTokenFilterTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/MultiplexerTokenFilterTests.java @@ -41,8 +41,8 @@ import org.opensearch.index.analysis.IndexAnalyzers; import org.opensearch.index.analysis.NamedAnalyzer; import org.opensearch.indices.analysis.AnalysisModule; -import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import java.io.IOException; import java.util.Collections; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/NGramTokenizerFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/NGramTokenizerFactoryTests.java index 49ccc742a355c..21b65059cf688 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/NGramTokenizerFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/NGramTokenizerFactoryTests.java @@ -32,27 +32,27 @@ package org.opensearch.analysis.common; -import org.apache.lucene.tests.analysis.MockTokenizer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter; import org.apache.lucene.analysis.reverse.ReverseStringFilter; +import org.apache.lucene.tests.analysis.MockTokenizer; import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.Settings.Builder; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexSettings; -import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.VersionUtils; import java.io.IOException; import java.io.StringReader; import java.util.Arrays; -import static com.carrotsearch.randomizedtesting.RandomizedTest.scaledRandomIntBetween; import static org.hamcrest.Matchers.instanceOf; +import static com.carrotsearch.randomizedtesting.RandomizedTest.scaledRandomIntBetween; public class NGramTokenizerFactoryTests extends OpenSearchTokenStreamTestCase { public void testParseTokenChars() { diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PathHierarchyTokenizerFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PathHierarchyTokenizerFactoryTests.java index 73c104a5b7200..1fe7c582449ec 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PathHierarchyTokenizerFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PathHierarchyTokenizerFactoryTests.java @@ -36,9 +36,9 @@ import org.apache.lucene.analysis.Tokenizer; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; -import org.opensearch.test.OpenSearchTokenStreamTestCase; +import org.opensearch.core.index.Index; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import java.io.IOException; import java.io.StringReader; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PatternCaptureTokenFilterTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PatternCaptureTokenFilterTests.java index 5cd18a5b01f18..4d882354b1709 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PatternCaptureTokenFilterTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PatternCaptureTokenFilterTests.java @@ -39,8 +39,8 @@ import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.IndexAnalyzers; import org.opensearch.index.analysis.NamedAnalyzer; -import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import static org.opensearch.test.OpenSearchTestCase.createTestAnalysis; import static org.hamcrest.Matchers.containsString; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PredicateTokenScriptFilterTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PredicateTokenScriptFilterTests.java index c16f4f37846ec..2856eef885385 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PredicateTokenScriptFilterTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/PredicateTokenScriptFilterTests.java @@ -44,8 +44,8 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptContext; import org.opensearch.script.ScriptService; -import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import java.io.IOException; import java.util.Collections; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/ScriptedConditionTokenFilterTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/ScriptedConditionTokenFilterTests.java index 9212fcad2850d..202ce7233d3e8 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/ScriptedConditionTokenFilterTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/ScriptedConditionTokenFilterTests.java @@ -44,8 +44,8 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptContext; import org.opensearch.script.ScriptService; -import org.opensearch.test.OpenSearchTokenStreamTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTokenStreamTestCase; import java.util.Collections; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactoryTests.java index 96e05efa97768..9e3345aa30dca 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/StemmerOverrideTokenFilterFactoryTests.java @@ -46,7 +46,6 @@ import java.io.IOException; import java.io.StringReader; import java.util.Arrays; -import java.util.Locale; public class StemmerOverrideTokenFilterFactoryTests extends OpenSearchTokenStreamTestCase { @Rule @@ -76,11 +75,8 @@ public void testRuleError() { "=>a", // no keys "a,=>b" // empty key )) { - expectThrows( - RuntimeException.class, - String.format(Locale.ROOT, "Should fail for invalid rule: '%s'", rule), - () -> create(rule) - ); + RuntimeException ex = expectThrows(RuntimeException.class, () -> create(rule)); + assertEquals("Line [1]: Invalid keyword override rule: " + rule, ex.getMessage()); } } @@ -90,4 +86,9 @@ public void testRulesOk() throws IOException { tokenizer.setReader(new StringReader("a b c")); assertTokenStreamContents(tokenFilterFactory.create(tokenizer), new String[] { "1", "2", "2" }); } + + public void testRulePartError() { + RuntimeException ex = expectThrows(RuntimeException.class, () -> create("a => 1", "b,c => 2", "# This is a comment", "=>a=>b")); + assertEquals("Line [4]: Invalid keyword override rule: =>a=>b", ex.getMessage()); + } } diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/StemmerTokenFilterFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/StemmerTokenFilterFactoryTests.java index fca64f4915cbf..45147ff813797 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/StemmerTokenFilterFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/StemmerTokenFilterFactoryTests.java @@ -50,9 +50,9 @@ import java.io.IOException; import java.io.StringReader; -import static com.carrotsearch.randomizedtesting.RandomizedTest.scaledRandomIntBetween; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_VERSION_CREATED; import static org.hamcrest.Matchers.instanceOf; +import static com.carrotsearch.randomizedtesting.RandomizedTest.scaledRandomIntBetween; public class StemmerTokenFilterFactoryTests extends OpenSearchTokenStreamTestCase { @@ -111,6 +111,83 @@ public void testPorter2FilterFactory() throws IOException { } } + public void testEnglishPluralFilter() throws IOException { + int iters = scaledRandomIntBetween(20, 100); + for (int i = 0; i < iters; i++) { + + Version v = VersionUtils.randomVersion(random()); + Settings settings = Settings.builder() + .put("index.analysis.filter.my_plurals.type", "stemmer") + .put("index.analysis.filter.my_plurals.language", "plural_english") + .put("index.analysis.analyzer.my_plurals.tokenizer", "whitespace") + .put("index.analysis.analyzer.my_plurals.filter", "my_plurals") + .put(SETTING_VERSION_CREATED, v) + .put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toString()) + .build(); + + OpenSearchTestCase.TestAnalysis analysis = AnalysisTestsHelper.createTestAnalysisFromSettings(settings, PLUGIN); + TokenFilterFactory tokenFilter = analysis.tokenFilter.get("my_plurals"); + assertThat(tokenFilter, instanceOf(StemmerTokenFilterFactory.class)); + Tokenizer tokenizer = new WhitespaceTokenizer(); + tokenizer.setReader(new StringReader("dresses")); + TokenStream create = tokenFilter.create(tokenizer); + IndexAnalyzers indexAnalyzers = analysis.indexAnalyzers; + NamedAnalyzer analyzer = indexAnalyzers.get("my_plurals"); + assertThat(create, instanceOf(EnglishPluralStemFilter.class)); + + // Check old EnglishMinimalStemmer ("S" stemmer) logic + assertAnalyzesTo(analyzer, "phones", new String[] { "phone" }); + assertAnalyzesTo(analyzer, "horses", new String[] { "horse" }); + assertAnalyzesTo(analyzer, "cameras", new String[] { "camera" }); + + // The orginal s stemmer gives up on stemming oes words because English has no fixed rule for the stem + // (see https://howtospell.co.uk/making-O-words-plural ) + // This stemmer removes the es but retains e for a small number of exceptions + assertAnalyzesTo(analyzer, "mosquitoes", new String[] { "mosquito" }); + assertAnalyzesTo(analyzer, "heroes", new String[] { "hero" }); + // oes exceptions that retain the e. + assertAnalyzesTo(analyzer, "shoes", new String[] { "shoe" }); + assertAnalyzesTo(analyzer, "horseshoes", new String[] { "horseshoe" }); + assertAnalyzesTo(analyzer, "canoes", new String[] { "canoe" }); + assertAnalyzesTo(analyzer, "oboes", new String[] { "oboe" }); + + // Check improved EnglishPluralStemFilter logic + // sses + assertAnalyzesTo(analyzer, "dresses", new String[] { "dress" }); + assertAnalyzesTo(analyzer, "possess", new String[] { "possess" }); + assertAnalyzesTo(analyzer, "possesses", new String[] { "possess" }); + // xes + assertAnalyzesTo(analyzer, "boxes", new String[] { "box" }); + assertAnalyzesTo(analyzer, "axes", new String[] { "axe" }); + // shes + assertAnalyzesTo(analyzer, "dishes", new String[] { "dish" }); + assertAnalyzesTo(analyzer, "washes", new String[] { "wash" }); + // ees + assertAnalyzesTo(analyzer, "employees", new String[] { "employee" }); + assertAnalyzesTo(analyzer, "bees", new String[] { "bee" }); + // tch + assertAnalyzesTo(analyzer, "watches", new String[] { "watch" }); + assertAnalyzesTo(analyzer, "itches", new String[] { "itch" }); + // ies->y but only for length >4 + assertAnalyzesTo(analyzer, "spies", new String[] { "spy" }); + assertAnalyzesTo(analyzer, "ties", new String[] { "tie" }); + assertAnalyzesTo(analyzer, "lies", new String[] { "lie" }); + assertAnalyzesTo(analyzer, "pies", new String[] { "pie" }); + assertAnalyzesTo(analyzer, "dies", new String[] { "die" }); + + assertAnalyzesTo(analyzer, "lunches", new String[] { "lunch" }); + assertAnalyzesTo(analyzer, "avalanches", new String[] { "avalanche" }); + assertAnalyzesTo(analyzer, "headaches", new String[] { "headache" }); + assertAnalyzesTo(analyzer, "caches", new String[] { "cache" }); + assertAnalyzesTo(analyzer, "beaches", new String[] { "beach" }); + assertAnalyzesTo(analyzer, "britches", new String[] { "britch" }); + assertAnalyzesTo(analyzer, "cockroaches", new String[] { "cockroach" }); + assertAnalyzesTo(analyzer, "cliches", new String[] { "cliche" }); + assertAnalyzesTo(analyzer, "quiches", new String[] { "quiche" }); + + } + } + public void testMultipleLanguagesThrowsException() throws IOException { Version v = VersionUtils.randomVersion(random()); Settings settings = Settings.builder() diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/SynonymsAnalysisTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/SynonymsAnalysisTests.java index 8094e24b9adc8..d6285c64b09f5 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/SynonymsAnalysisTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/SynonymsAnalysisTests.java @@ -33,10 +33,10 @@ package org.opensearch.analysis.common; import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.core.KeywordTokenizer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase; import org.opensearch.LegacyESVersion; import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; @@ -47,8 +47,8 @@ import org.opensearch.index.analysis.PreConfiguredTokenFilter; import org.opensearch.index.analysis.TokenFilterFactory; import org.opensearch.index.analysis.TokenizerFactory; -import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.VersionUtils; import org.hamcrest.MatcherAssert; @@ -117,7 +117,7 @@ public void testSynonymWordDeleteByAnalyzer() throws IOException { fail("fail! due to synonym word deleted by analyzer"); } catch (Exception e) { assertThat(e, instanceOf(IllegalArgumentException.class)); - assertThat(e.getMessage(), startsWith("failed to build synonyms")); + assertThat(e.getMessage(), startsWith("Failed to build synonyms")); } } @@ -138,7 +138,7 @@ public void testExpandSynonymWordDeleteByAnalyzer() throws IOException { fail("fail! due to synonym word deleted by analyzer"); } catch (Exception e) { assertThat(e, instanceOf(IllegalArgumentException.class)); - assertThat(e.getMessage(), startsWith("failed to build synonyms")); + assertThat(e.getMessage(), startsWith("Failed to build synonyms")); } } @@ -244,10 +244,9 @@ public void testShingleFilters() { .build(); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("index", settings); - expectThrows( - IllegalArgumentException.class, - () -> { indexAnalyzers = createTestAnalysis(idxSettings, settings, new CommonAnalysisPlugin()).indexAnalyzers; } - ); + expectThrows(IllegalArgumentException.class, () -> { + indexAnalyzers = createTestAnalysis(idxSettings, settings, new CommonAnalysisPlugin()).indexAnalyzers; + }); } @@ -308,7 +307,9 @@ public void testPreconfiguredTokenFilters() throws IOException { IllegalArgumentException e = expectThrows( IllegalArgumentException.class, "Expected exception for factory " + tf.getName(), - () -> { tf.get(idxSettings, null, tf.getName(), settings).getSynonymFilter(); } + () -> { + tf.get(idxSettings, null, tf.getName(), settings).getSynonymFilter(); + } ); assertEquals(tf.getName(), "Token filter [" + tf.getName() + "] cannot be used to parse synonyms", e.getMessage()); } else { diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/UniqueTokenFilterTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/UniqueTokenFilterTests.java index a321fd4a5879c..ba8de4db07396 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/UniqueTokenFilterTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/UniqueTokenFilterTests.java @@ -33,10 +33,10 @@ package org.opensearch.analysis.common; import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.tests.analysis.MockTokenizer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.tests.analysis.MockTokenizer; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/WhitespaceTokenizerFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/WhitespaceTokenizerFactoryTests.java index c0034c0e8ef20..0d4db04141e45 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/WhitespaceTokenizerFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/WhitespaceTokenizerFactoryTests.java @@ -38,10 +38,10 @@ import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexSettings; -import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; import java.io.Reader; diff --git a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactoryTests.java b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactoryTests.java index 6129971a69e18..784b92e4a9324 100644 --- a/modules/analysis-common/src/test/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactoryTests.java +++ b/modules/analysis-common/src/test/java/org/opensearch/analysis/common/WordDelimiterGraphTokenFilterFactoryTests.java @@ -45,8 +45,8 @@ import org.opensearch.index.analysis.NamedAnalyzer; import org.opensearch.index.analysis.TokenFilterFactory; import org.opensearch.indices.analysis.AnalysisModule; -import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.IndexSettingsModule; +import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.VersionUtils; import java.io.IOException; diff --git a/modules/analysis-common/src/test/resources/org/opensearch/analysis/common/cjk_analysis.json b/modules/analysis-common/src/test/resources/org/opensearch/analysis/common/cjk_analysis.json index 89a1281473cd7..c69b889c914a4 100644 --- a/modules/analysis-common/src/test/resources/org/opensearch/analysis/common/cjk_analysis.json +++ b/modules/analysis-common/src/test/resources/org/opensearch/analysis/common/cjk_analysis.json @@ -34,4 +34,4 @@ } } } -} \ No newline at end of file +} diff --git a/modules/analysis-common/src/test/resources/org/opensearch/analysis/common/pattern_capture.json b/modules/analysis-common/src/test/resources/org/opensearch/analysis/common/pattern_capture.json index d82fb987e6ed2..5057a1e6d7f9e 100644 --- a/modules/analysis-common/src/test/resources/org/opensearch/analysis/common/pattern_capture.json +++ b/modules/analysis-common/src/test/resources/org/opensearch/analysis/common/pattern_capture.json @@ -43,4 +43,4 @@ } } } -} \ No newline at end of file +} diff --git a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/10_basic.yml b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/10_basic.yml index ca6cd2e953be4..c42438797496c 100644 --- a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/10_basic.yml +++ b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/10_basic.yml @@ -5,10 +5,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: analysis-common } } + - contains: { nodes.$cluster_manager.modules: { name: analysis-common } } diff --git a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/40_token_filters.yml b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/40_token_filters.yml index 40c82ff185661..802c79c780689 100644 --- a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/40_token_filters.yml +++ b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/40_token_filters.yml @@ -127,6 +127,69 @@ - match: { tokens.2.token: brown } - match: { tokens.3.token: fox } + - do: + indices.analyze: + body: + text: 'text1 #text2' + tokenizer: whitespace + filter: + - type: word_delimiter + split_on_numerics: false + type_table: + - "\\u0023 => ALPHANUM" + - length: { tokens: 2 } + - match: { tokens.0.token: text1 } + - match: { tokens.0.start_offset: 0 } + - match: { tokens.0.end_offset: 5 } + - match: { tokens.0.position: 0 } + - match: { tokens.1.token: "#text2" } + - match: { tokens.1.start_offset: 6 } + - match: { tokens.1.end_offset: 12 } + - match: { tokens.1.position: 1 } + + - do: + indices.analyze: + body: + text: 'text1 #text2' + tokenizer: whitespace + filter: + - type: word_delimiter + split_on_numerics: false + type_table: + - "# This is a comment" + - "# => ALPHANUM" + - length: { tokens: 2 } + - match: { tokens.0.token: text1 } + - match: { tokens.0.start_offset: 0 } + - match: { tokens.0.end_offset: 5 } + - match: { tokens.0.position: 0 } + - match: { tokens.1.token: "#text2" } + - match: { tokens.1.start_offset: 6 } + - match: { tokens.1.end_offset: 12 } + - match: { tokens.1.position: 1 } + + - do: + indices.analyze: + body: + text: 'text1 #text2' + tokenizer: whitespace + filter: + - type: word_delimiter + split_on_numerics: false + type_table: + - "# This is a comment" + - "# => ALPHANUM" + - "@ => ALPHANUM" + - length: { tokens: 2 } + - match: { tokens.0.token: text1 } + - match: { tokens.0.start_offset: 0 } + - match: { tokens.0.end_offset: 5 } + - match: { tokens.0.position: 0 } + - match: { tokens.1.token: "#text2" } + - match: { tokens.1.start_offset: 6 } + - match: { tokens.1.end_offset: 12 } + - match: { tokens.1.position: 1 } + --- "word_delimiter_graph": - do: @@ -231,6 +294,69 @@ - match: { detail.tokenfilters.0.tokens.5.end_offset: 19 } - match: { detail.tokenfilters.0.tokens.5.position: 5 } + - do: + indices.analyze: + body: + text: 'text1 #text2' + tokenizer: whitespace + filter: + - type: word_delimiter_graph + split_on_numerics: false + type_table: + - "\\u0023 => ALPHANUM" + - length: { tokens: 2 } + - match: { tokens.0.token: text1 } + - match: { tokens.0.start_offset: 0 } + - match: { tokens.0.end_offset: 5 } + - match: { tokens.0.position: 0 } + - match: { tokens.1.token: "#text2" } + - match: { tokens.1.start_offset: 6 } + - match: { tokens.1.end_offset: 12 } + - match: { tokens.1.position: 1 } + + - do: + indices.analyze: + body: + text: 'text1 #text2' + tokenizer: whitespace + filter: + - type: word_delimiter_graph + split_on_numerics: false + type_table: + - "# This is a comment" + - "# => ALPHANUM" + - length: { tokens: 2 } + - match: { tokens.0.token: text1 } + - match: { tokens.0.start_offset: 0 } + - match: { tokens.0.end_offset: 5 } + - match: { tokens.0.position: 0 } + - match: { tokens.1.token: "#text2" } + - match: { tokens.1.start_offset: 6 } + - match: { tokens.1.end_offset: 12 } + - match: { tokens.1.position: 1 } + + - do: + indices.analyze: + body: + text: 'text1 #text2' + tokenizer: whitespace + filter: + - type: word_delimiter_graph + split_on_numerics: false + type_table: + - "# This is a comment" + - "# => ALPHANUM" + - "@ => ALPHANUM" + - length: { tokens: 2 } + - match: { tokens.0.token: text1 } + - match: { tokens.0.start_offset: 0 } + - match: { tokens.0.end_offset: 5 } + - match: { tokens.0.position: 0 } + - match: { tokens.1.token: "#text2" } + - match: { tokens.1.start_offset: 6 } + - match: { tokens.1.end_offset: 12 } + - match: { tokens.1.position: 1 } + --- "unique": - do: @@ -1198,6 +1324,46 @@ - match: { tokens.0.token: foo } --- +"delimited_term_freq": + - skip: + version: " - 2.9.99" + reason: "delimited_term_freq token filter was added in v2.10.0" + - do: + indices.create: + index: test + body: + settings: + analysis: + filter: + my_delimited_term_freq: + type: delimited_term_freq + delimiter: ^ + - do: + indices.analyze: + index: test + body: + text: foo^3 + tokenizer: keyword + filter: [my_delimited_term_freq] + attributes: termFrequency + explain: true + - length: { detail.tokenfilters: 1 } + - match: { detail.tokenfilters.0.tokens.0.token: foo } + - match: { detail.tokenfilters.0.tokens.0.termFrequency: 3 } + + # Test pre-configured token filter too: + - do: + indices.analyze: + body: + text: foo|100 + tokenizer: keyword + filter: [delimited_term_freq] + attributes: termFrequency + explain: true + - length: { detail.tokenfilters: 1 } + - match: { detail.tokenfilters.0.tokens.0.token: foo } + - match: { detail.tokenfilters.0.tokens.0.termFrequency: 100 } +--- "keep_filter": - do: indices.create: diff --git a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/50_char_filters.yml b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/50_char_filters.yml index 67e68428c07c7..5e266c10cba8f 100644 --- a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/50_char_filters.yml +++ b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/50_char_filters.yml @@ -57,3 +57,22 @@ - match: { detail.tokenizer.tokens.0.start_offset: 0 } - match: { detail.tokenizer.tokens.0.end_offset: 15 } - match: { detail.tokenizer.tokens.0.position: 0 } +--- +"mapping_with_hashtag": + - do: + indices.analyze: + body: + text: 'test #test @test' + tokenizer: standard + filter: + - lowercase + char_filter: + - type: mapping + mappings: + - "# This is a comment" + - "# => _hashsign_" + - "@ => _atsign_" + - length: { tokens: 3 } + - match: { tokens.0.token: test } + - match: { tokens.1.token: _hashsign_test } + - match: { tokens.2.token: _atsign_test } diff --git a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/60_analysis_scripting.yml b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/60_analysis_scripting.yml index 2015fe31fccb5..1637c8736134f 100644 --- a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/60_analysis_scripting.yml +++ b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/analysis-common/60_analysis_scripting.yml @@ -68,4 +68,3 @@ - match: { tokens.1.token: "f" } - match: { tokens.2.token: "g" } - match: { tokens.3.token: "h" } - diff --git a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/cluster.stats/10_analysis_stats.yml b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/cluster.stats/10_analysis_stats.yml index a19a1f2721910..5468da5216bb4 100644 --- a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/cluster.stats/10_analysis_stats.yml +++ b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/cluster.stats/10_analysis_stats.yml @@ -119,4 +119,3 @@ - match: { indices.analysis.built_in_analyzers.2.name: spanish } - match: { indices.analysis.built_in_analyzers.2.count: 2 } - match: { indices.analysis.built_in_analyzers.2.index_count: 2 } - diff --git a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/indices.analyze/10_synonyms.yml b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/indices.analyze/10_synonyms.yml index f0f8765ab5130..42d1c23001300 100644 --- a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/indices.analyze/10_synonyms.yml +++ b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/indices.analyze/10_synonyms.yml @@ -76,4 +76,3 @@ - match: { tokens.5.token: dude } - match: { tokens.5.position: 4 } - match: { tokens.5.positionLength: null } - diff --git a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/search.query/60_synonym_graph.yml b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/search.query/60_synonym_graph.yml index ae039e453be6c..4388de3eef30a 100644 --- a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/search.query/60_synonym_graph.yml +++ b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/search.query/60_synonym_graph.yml @@ -229,4 +229,3 @@ setup: query: bar baz analyzer: lower_graph_syns - match: { hits.total: 1 } - diff --git a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/search.query/70_intervals.yml b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/search.query/70_intervals.yml index 35a611d13f359..9ad68e960421c 100644 --- a/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/search.query/70_intervals.yml +++ b/modules/analysis-common/src/yamlRestTest/resources/rest-api-spec/test/search.query/70_intervals.yml @@ -56,4 +56,3 @@ setup: use_field: text_en max_gaps: 1 - match: { hits.total.value: 1 } - diff --git a/modules/build.gradle b/modules/build.gradle index b493c4a577cec..126bf0c8870ac 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -32,7 +32,7 @@ configure(subprojects.findAll { it.parent.path == project.path }) { group = 'org.opensearch.plugin' // for modules which publish client jars apply plugin: 'opensearch.testclusters' apply plugin: 'opensearch.opensearchplugin' - + opensearchplugin { // for local OpenSearch plugins, the name of the plugin is the same as the directory name project.name diff --git a/modules/geo/build.gradle b/modules/geo/build.gradle index d78e83ec7c4c6..7ab6f80b65ca2 100644 --- a/modules/geo/build.gradle +++ b/modules/geo/build.gradle @@ -28,18 +28,26 @@ * under the License. */ apply plugin: 'opensearch.yaml-rest-test' +apply plugin: 'opensearch.internal-cluster-test' opensearchplugin { - description 'Placeholder plugin for geospatial features in OpenSearch. only registers geo_shape field mapper for now' - classname 'org.opensearch.geo.GeoPlugin' + description 'Plugin for geospatial features in OpenSearch. Registering the geo_shape and aggregations on GeoShape and GeoPoint' + classname 'org.opensearch.geo.GeoModulePlugin' } restResources { restApi { - includeCore '_common', 'indices', 'index', 'search' + includeCore '_common', 'indices', 'index', 'search', 'bulk' } } + artifacts { restTests(project.file('src/yamlRestTest/resources/rest-api-spec/test')) } -test.enabled = false +/** + * These compiler arguments needs to be removed, as there are raw types being used in the GeoGrid and GeoTile aggregations. + */ +tasks.withType(JavaCompile).configureEach { + options.compilerArgs -= '-Xlint:rawtypes' + options.compilerArgs -= '-Xlint:unchecked' +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/GeoModulePluginIntegTestCase.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/GeoModulePluginIntegTestCase.java new file mode 100644 index 0000000000000..c38b29502e282 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/GeoModulePluginIntegTestCase.java @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.geometry.utils.StandardValidator; +import org.opensearch.geometry.utils.WellKnownText; +import org.opensearch.index.mapper.GeoShapeFieldMapper; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import org.opensearch.test.TestGeoShapeFieldMapperPlugin; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; + +/** + * This is the base class for all the Geo related integration tests. Use this class to add the features and settings + * for the test cluster on which integration tests are running. + */ +public abstract class GeoModulePluginIntegTestCase extends ParameterizedOpenSearchIntegTestCase { + + protected static final double GEOHASH_TOLERANCE = 1E-5D; + + protected static final WellKnownText WKT = new WellKnownText(true, new StandardValidator(true)); + + public GeoModulePluginIntegTestCase(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + + /** + * Returns a collection of plugins that should be loaded on each node for doing the integration tests. As this + * geo plugin is not getting packaged in a zip, we need to load it before the tests run. + * + * @return List of {@link Plugin} + */ + @Override + protected Collection> nodePlugins() { + return Collections.singletonList(GeoModulePlugin.class); + } + + /** + * This was added as a backdoor to Mock the implementation of {@link GeoShapeFieldMapper} which was coming from + * {@link GeoModulePlugin}. Mock implementation is {@link TestGeoShapeFieldMapperPlugin}. Now we are using the + * {@link GeoModulePlugin} in our integration tests we need to override this functionality to avoid multiple mapper + * error. + * + * @return boolean + */ + @Override + protected boolean addMockGeoShapeFieldMapper() { + return false; + } +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/MissingValueIT.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/MissingValueIT.java new file mode 100644 index 0000000000000..7344903fd5220 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/MissingValueIT.java @@ -0,0 +1,161 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.settings.Settings; +import org.opensearch.geo.GeoModulePluginIntegTestCase; +import org.opensearch.geo.search.aggregations.common.GeoBoundsHelper; +import org.opensearch.geo.search.aggregations.metrics.GeoBounds; +import org.opensearch.geo.tests.common.AggregationBuilders; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.geo.tests.common.RandomGeoGeometryGenerator; +import org.opensearch.geometry.Geometry; +import org.opensearch.geometry.utils.WellKnownText; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.hamcrest.MatcherAssert; +import org.junit.Before; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.closeTo; + +/** + * Tests to validate if user specified a missingValue in the input while doing the aggregation + */ +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class MissingValueIT extends GeoModulePluginIntegTestCase { + + private static final String INDEX_NAME = "idx"; + private static final String GEO_SHAPE_FIELD_NAME = "myshape"; + private static final String GEO_SHAPE_FIELD_TYPE = "type=geo_shape"; + private static final String AGGREGATION_NAME = "bounds"; + private static final String NON_EXISTENT_FIELD = "non_existing_field"; + private static final WellKnownText WKT = WellKnownText.INSTANCE; + private static Geometry indexedGeometry; + private static GeoPoint indexedGeoPoint; + private GeoPoint bottomRight; + private GeoPoint topLeft; + + public MissingValueIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @Override + protected void setupSuiteScopeCluster() throws Exception { + assertAcked( + prepareCreate(INDEX_NAME).setMapping( + "date", + "type=date", + "location", + "type=geo_point", + "str", + "type=keyword", + GEO_SHAPE_FIELD_NAME, + GEO_SHAPE_FIELD_TYPE + ).get() + ); + indexedGeometry = RandomGeoGeometryGenerator.randomGeometry(random()); + indexedGeoPoint = RandomGeoGenerator.randomPoint(random()); + assert indexedGeometry != null; + indexRandom( + true, + client().prepareIndex(INDEX_NAME).setId("1").setSource(), + client().prepareIndex(INDEX_NAME) + .setId("2") + .setSource( + "str", + "foo", + "long", + 3L, + "double", + 5.5, + "date", + "2015-05-07", + "location", + indexedGeoPoint.toString(), + GEO_SHAPE_FIELD_NAME, + WKT.toWKT(indexedGeometry) + ) + ); + } + + @Before + public void runBeforeEachTest() { + bottomRight = new GeoPoint(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY); + topLeft = new GeoPoint(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + } + + public void testUnmappedGeoBounds() { + final GeoPoint missingGeoPoint = RandomGeoGenerator.randomPoint(random()); + GeoBoundsHelper.updateBoundsBottomRight(missingGeoPoint, bottomRight); + GeoBoundsHelper.updateBoundsTopLeft(missingGeoPoint, topLeft); + SearchResponse response = client().prepareSearch(INDEX_NAME) + .addAggregation( + AggregationBuilders.geoBounds(AGGREGATION_NAME) + .field(NON_EXISTENT_FIELD) + .wrapLongitude(false) + .missing(missingGeoPoint.toString()) + ) + .get(); + assertSearchResponse(response); + validateResult(response.getAggregations().get(AGGREGATION_NAME)); + } + + public void testGeoBounds() { + GeoBoundsHelper.updateBoundsForGeoPoint(indexedGeoPoint, topLeft, bottomRight); + final GeoPoint missingGeoPoint = RandomGeoGenerator.randomPoint(random()); + GeoBoundsHelper.updateBoundsForGeoPoint(missingGeoPoint, topLeft, bottomRight); + SearchResponse response = client().prepareSearch(INDEX_NAME) + .addAggregation( + AggregationBuilders.geoBounds(AGGREGATION_NAME).field("location").wrapLongitude(false).missing(missingGeoPoint.toString()) + ) + .get(); + assertSearchResponse(response); + validateResult(response.getAggregations().get(AGGREGATION_NAME)); + } + + public void testGeoBoundsWithMissingShape() { + // create GeoBounds for the indexed Field + GeoBoundsHelper.updateBoundsForGeometry(indexedGeometry, topLeft, bottomRight); + final Geometry missingGeometry = RandomGeoGeometryGenerator.randomGeometry(random()); + assert missingGeometry != null; + GeoBoundsHelper.updateBoundsForGeometry(missingGeometry, topLeft, bottomRight); + final SearchResponse response = client().prepareSearch(INDEX_NAME) + .addAggregation( + AggregationBuilders.geoBounds(AGGREGATION_NAME) + .wrapLongitude(false) + .field(GEO_SHAPE_FIELD_NAME) + .missing(WKT.toWKT(missingGeometry)) + ) + .get(); + assertSearchResponse(response); + validateResult(response.getAggregations().get(AGGREGATION_NAME)); + } + + public void testUnmappedGeoBoundsOnGeoShape() { + // We cannot useGeometry other than Point as for GeoBoundsAggregation as the Default Value for the + // CoreValueSourceType is GeoPoint hence we need to use Point here. + final Geometry missingGeometry = RandomGeoGeometryGenerator.randomPoint(random()); + final SearchResponse response = client().prepareSearch(INDEX_NAME) + .addAggregation(AggregationBuilders.geoBounds(AGGREGATION_NAME).field(NON_EXISTENT_FIELD).missing(WKT.toWKT(missingGeometry))) + .get(); + GeoBoundsHelper.updateBoundsForGeometry(missingGeometry, topLeft, bottomRight); + assertSearchResponse(response); + validateResult(response.getAggregations().get(AGGREGATION_NAME)); + } + + private void validateResult(final GeoBounds bounds) { + MatcherAssert.assertThat(bounds.bottomRight().lat(), closeTo(bottomRight.lat(), GEOHASH_TOLERANCE)); + MatcherAssert.assertThat(bounds.bottomRight().lon(), closeTo(bottomRight.lon(), GEOHASH_TOLERANCE)); + MatcherAssert.assertThat(bounds.topLeft().lat(), closeTo(topLeft.lat(), GEOHASH_TOLERANCE)); + MatcherAssert.assertThat(bounds.topLeft().lon(), closeTo(topLeft.lon(), GEOHASH_TOLERANCE)); + } +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/AbstractGeoBucketAggregationIntegTest.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/AbstractGeoBucketAggregationIntegTest.java new file mode 100644 index 0000000000000..86d8ad2968e7f --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/AbstractGeoBucketAggregationIntegTest.java @@ -0,0 +1,275 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket; + +import org.apache.lucene.geo.GeoEncodingUtils; +import org.opensearch.Version; +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.geo.GeoShapeDocValue; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.geo.GeoModulePluginIntegTestCase; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.geo.tests.common.RandomGeoGeometryGenerator; +import org.opensearch.geometry.Geometry; +import org.opensearch.geometry.Rectangle; +import org.opensearch.test.VersionUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +/** + * This is the base class for all the Bucket Aggregation related integration tests. Use this class to add common + * methods which can be used across different bucket aggregations. If there is any common code that can be used + * across other integration test too then this is not the class. Use {@link GeoModulePluginIntegTestCase} + */ +public abstract class AbstractGeoBucketAggregationIntegTest extends GeoModulePluginIntegTestCase { + + protected static final int MAX_PRECISION_FOR_GEO_SHAPES_AGG_TESTING = 2; + + protected static final int MIN_PRECISION_WITHOUT_BB_AGGS = 2; + + protected static final int NUM_DOCS = 100; + + protected static final String GEO_SHAPE_INDEX_NAME = "geoshape_index"; + + protected static Rectangle boundingRectangleForGeoShapesAgg; + + protected static Map expectedDocsCountForGeoShapes; + + protected static Map expectedDocCountsForSingleGeoPoint; + + protected static Map multiValuedExpectedDocCountsGeoPoint; + + protected static final String GEO_SHAPE_FIELD_NAME = "location_geo_shape"; + + protected static final String GEO_POINT_FIELD_NAME = "location"; + + protected static final String KEYWORD_FIELD_NAME = "city"; + + protected static String smallestGeoHash = null; + + protected final Version version = VersionUtils.randomIndexCompatibleVersion(random()); + + public AbstractGeoBucketAggregationIntegTest(Settings dynamicSettings) { + super(dynamicSettings); + } + + @Override + protected boolean forbidPrivateIndexSettings() { + return false; + } + + /** + * Prepares a GeoShape index for testing the GeoShape bucket aggregations. Different bucket aggregations can use + * different techniques for creating buckets. Override the method + * {@link AbstractGeoBucketAggregationIntegTest#generateBucketsForGeometry} in the test class for creating the + * buckets which will then be used for verifications. + * + * @param random {@link Random} + * @throws Exception thrown during index creation. + */ + protected void prepareGeoShapeIndexForAggregations(final Random random) throws Exception { + expectedDocsCountForGeoShapes = new HashMap<>(); + final Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, version).build(); + final List geoshapes = new ArrayList<>(); + assertAcked(prepareCreate(GEO_SHAPE_INDEX_NAME).setSettings(settings).setMapping(GEO_SHAPE_FIELD_NAME, "type" + "=geo_shape")); + boolean isShapeIntersectingBB = false; + for (int i = 0; i < NUM_DOCS;) { + final Geometry geometry = RandomGeoGeometryGenerator.randomGeometry(random); + final GeoShapeDocValue geometryDocValue = GeoShapeDocValue.createGeometryDocValue(geometry); + // make sure that there is 1 shape is intersecting with the bounding box + if (!isShapeIntersectingBB) { + isShapeIntersectingBB = geometryDocValue.isIntersectingRectangle(boundingRectangleForGeoShapesAgg); + if (!isShapeIntersectingBB && i == NUM_DOCS - 1) { + continue; + } + } + + i++; + final Set values = generateBucketsForGeometry(geometry, geometryDocValue); + geoshapes.add(indexGeoShape(GEO_SHAPE_INDEX_NAME, geometry)); + for (final String hash : values) { + expectedDocsCountForGeoShapes.put(hash, expectedDocsCountForGeoShapes.getOrDefault(hash, 0) + 1); + } + } + indexRandom(true, geoshapes); + ensureGreen(GEO_SHAPE_INDEX_NAME); + } + + /** + * Returns a set of buckets for the shape at different precision level. Override this method for different bucket + * aggregations. + * + * @param geometry {@link Geometry} + * @param geoShapeDocValue {@link GeoShapeDocValue} + * @return A {@link Set} of {@link String} which represents the buckets. + */ + protected abstract Set generateBucketsForGeometry(final Geometry geometry, final GeoShapeDocValue geoShapeDocValue); + + /** + * Prepares a GeoPoint index for testing the GeoPoint bucket aggregations. Different bucket aggregations can use + * different techniques for creating buckets. Override the method + * {@link AbstractGeoBucketAggregationIntegTest#generateBucketsForGeoPoint} in the test class for creating the + * buckets which will then be used for verifications. + * + * @param random {@link Random} + * @throws Exception thrown during index creation. + */ + protected void prepareSingleValueGeoPointIndex(final Random random) throws Exception { + expectedDocCountsForSingleGeoPoint = new HashMap<>(); + createIndex("idx_unmapped"); + final Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_VERSION_CREATED, version) + .put("index.number_of_shards", 4) + .put("index.number_of_replicas", 0) + .build(); + assertAcked( + prepareCreate("idx").setSettings(settings) + .setMapping(GEO_POINT_FIELD_NAME, "type=geo_point", KEYWORD_FIELD_NAME, "type=keyword") + ); + final List cities = new ArrayList<>(); + for (int i = 0; i < NUM_DOCS; i++) { + // generate random point + final GeoPoint geoPoint = RandomGeoGenerator.randomPoint(random); + cities.add(indexGeoPoint("idx", geoPoint.toString(), geoPoint.getLat() + ", " + geoPoint.getLon())); + final Set buckets = generateBucketsForGeoPoint(geoPoint); + for (final String bucket : buckets) { + expectedDocCountsForSingleGeoPoint.put(bucket, expectedDocCountsForSingleGeoPoint.getOrDefault(bucket, 0) + 1); + } + } + indexRandom(true, cities); + ensureGreen("idx_unmapped", "idx"); + } + + protected void prepareMultiValuedGeoPointIndex(final Random random) throws Exception { + multiValuedExpectedDocCountsGeoPoint = new HashMap<>(); + final Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, version).build(); + final List cities = new ArrayList<>(); + assertAcked( + prepareCreate("multi_valued_idx").setSettings(settings) + .setMapping(GEO_POINT_FIELD_NAME, "type=geo_point", KEYWORD_FIELD_NAME, "type=keyword") + ); + for (int i = 0; i < NUM_DOCS; i++) { + final int numPoints = random.nextInt(4); + final List points = new ArrayList<>(); + final Set buckets = new HashSet<>(); + for (int j = 0; j < numPoints; ++j) { + // generate random point + final GeoPoint geoPoint = RandomGeoGenerator.randomPoint(random); + points.add(geoPoint.getLat() + "," + geoPoint.getLon()); + buckets.addAll(generateBucketsForGeoPoint(geoPoint)); + } + cities.add(indexGeoPoints("multi_valued_idx", Integer.toString(i), points)); + for (final String bucket : buckets) { + multiValuedExpectedDocCountsGeoPoint.put(bucket, multiValuedExpectedDocCountsGeoPoint.getOrDefault(bucket, 0) + 1); + } + } + indexRandom(true, cities); + ensureGreen("multi_valued_idx"); + } + + /** + * Returns a set of buckets for the GeoPoint at different precision level. Override this method for different bucket + * aggregations. + * + * @param geoPoint {@link GeoPoint} + * @return A {@link Set} of {@link String} which represents the buckets. + */ + protected abstract Set generateBucketsForGeoPoint(final GeoPoint geoPoint); + + /** + * Indexes a GeoShape in the provided index. + * @param index {@link String} index name + * @param geometry {@link Geometry} the Geometry to be indexed + * @return {@link IndexRequestBuilder} + * @throws Exception thrown during creation of {@link IndexRequestBuilder} + */ + protected IndexRequestBuilder indexGeoShape(final String index, final Geometry geometry) throws Exception { + XContentBuilder source = jsonBuilder().startObject(); + source = source.field(GEO_SHAPE_FIELD_NAME, WKT.toWKT(geometry)); + source = source.endObject(); + return client().prepareIndex(index).setSource(source); + } + + /** + * Indexes a {@link List} of {@link GeoPoint}s in the provided Index name. + * @param index {@link String} index name + * @param name {@link String} value for the string field in index + * @param latLon {@link List} of {@link String} representing the String representation of GeoPoint + * @return {@link IndexRequestBuilder} + * @throws Exception thrown during indexing. + */ + protected IndexRequestBuilder indexGeoPoints(final String index, final String name, final List latLon) throws Exception { + XContentBuilder source = jsonBuilder().startObject().field(KEYWORD_FIELD_NAME, name); + if (latLon != null) { + source = source.field(GEO_POINT_FIELD_NAME, latLon); + } + source = source.endObject(); + return client().prepareIndex(index).setSource(source); + } + + /** + * Indexes a {@link GeoPoint} in the provided Index name. + * @param index {@link String} index name + * @param name {@link String} value for the string field in index + * @param latLon {@link String} representing the String representation of GeoPoint + * @return {@link IndexRequestBuilder} + * @throws Exception thrown during indexing. + */ + protected IndexRequestBuilder indexGeoPoint(final String index, final String name, final String latLon) throws Exception { + return indexGeoPoints(index, name, List.of(latLon)); + } + + /** + * Generates a Bounding Box of a fixed radius that can be used for shapes aggregations to reduce the size of + * aggregation results. + * @param random {@link Random} + * @return {@link Rectangle} + */ + protected Rectangle getGridAggregationBoundingBox(final Random random) { + final double radius = getRadiusOfBoundingBox(); + assertTrue("The radius of Bounding Box is less than or equal to 0", radius > 0); + return RandomGeoGeometryGenerator.randomRectangle(random, radius); + } + + /** + * Returns a radius for the Bounding box. Test classes can override this method to change the radius of BBox for + * the test cases. If we increase this value, it will lead to creation of a lot of buckets that can lead of + * IndexOutOfBoundsExceptions. + * @return double + */ + protected double getRadiusOfBoundingBox() { + return 5.0; + } + + /** + * Encode and Decode the {@link GeoPoint} to get a {@link GeoPoint} which has the exact precision which is being + * stored. + * @param geoPoint {@link GeoPoint} + * @return {@link GeoPoint} + */ + protected GeoPoint toStoragePrecision(final GeoPoint geoPoint) { + return new GeoPoint( + GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(geoPoint.getLat())), + GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(geoPoint.getLon())) + ); + } + +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridIT.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridIT.java new file mode 100644 index 0000000000000..4048bb62f8818 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridIT.java @@ -0,0 +1,327 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.bucket; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.geo.GeoShapeDocValue; +import org.opensearch.common.settings.Settings; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGrid; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.common.GeoBoundsHelper; +import org.opensearch.geo.tests.common.AggregationBuilders; +import org.opensearch.geometry.Geometry; +import org.opensearch.geometry.Rectangle; +import org.opensearch.geometry.utils.Geohash; +import org.opensearch.index.query.GeoBoundingBoxQueryBuilder; +import org.opensearch.search.aggregations.InternalAggregation; +import org.opensearch.search.aggregations.bucket.filter.Filter; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import static org.opensearch.geometry.utils.Geohash.PRECISION; +import static org.opensearch.geometry.utils.Geohash.stringEncode; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; + +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class GeoHashGridIT extends AbstractGeoBucketAggregationIntegTest { + + private static final String AGG_NAME = "geohashgrid"; + + public GeoHashGridIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @Override + public void setupSuiteScopeCluster() throws Exception { + Random random = random(); + // Creating a BB for limiting the number buckets generated during aggregation + boundingRectangleForGeoShapesAgg = getGridAggregationBoundingBox(random); + expectedDocCountsForSingleGeoPoint = new HashMap<>(); + prepareSingleValueGeoPointIndex(random); + prepareMultiValuedGeoPointIndex(random); + prepareGeoShapeIndexForAggregations(random); + } + + public void testSimple() { + for (int precision = 1; precision <= PRECISION; precision++) { + SearchResponse response = client().prepareSearch("idx") + .addAggregation(AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).precision(precision)) + .get(); + + assertSearchResponse(response); + + GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + List buckets = geoGrid.getBuckets(); + Object[] propertiesKeys = (Object[]) ((InternalAggregation) geoGrid).getProperty("_key"); + Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) geoGrid).getProperty("_count"); + for (int i = 0; i < buckets.size(); i++) { + GeoGrid.Bucket cell = buckets.get(i); + String geohash = cell.getKeyAsString(); + + long bucketCount = cell.getDocCount(); + int expectedBucketCount = expectedDocCountsForSingleGeoPoint.get(geohash); + assertNotSame(bucketCount, 0); + assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); + GeoPoint geoPoint = (GeoPoint) propertiesKeys[i]; + assertThat(stringEncode(geoPoint.lon(), geoPoint.lat(), precision), equalTo(geohash)); + assertThat((long) propertiesDocCounts[i], equalTo(bucketCount)); + } + } + } + + public void testGeoShapes() { + final GeoBoundingBox boundingBox = new GeoBoundingBox( + new GeoPoint(boundingRectangleForGeoShapesAgg.getMaxLat(), boundingRectangleForGeoShapesAgg.getMinLon()), + new GeoPoint(boundingRectangleForGeoShapesAgg.getMinLat(), boundingRectangleForGeoShapesAgg.getMaxLon()) + ); + for (int precision = 1; precision <= MAX_PRECISION_FOR_GEO_SHAPES_AGG_TESTING; precision++) { + GeoGridAggregationBuilder builder = AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_SHAPE_FIELD_NAME).precision(precision); + // This makes sure that for only higher precision we are providing the GeoBounding Box. This also ensures + // that we are able to test both bounded and unbounded aggregations + if (precision > MIN_PRECISION_WITHOUT_BB_AGGS) { + builder.setGeoBoundingBox(boundingBox); + } + final SearchResponse response = client().prepareSearch(GEO_SHAPE_INDEX_NAME).addAggregation(builder).get(); + final GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + final List buckets = geoGrid.getBuckets(); + final Object[] propertiesKeys = (Object[]) ((InternalAggregation) geoGrid).getProperty("_key"); + final Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) geoGrid).getProperty("_count"); + for (int i = 0; i < buckets.size(); i++) { + final GeoGrid.Bucket cell = buckets.get(i); + final String geohash = cell.getKeyAsString(); + + final long bucketCount = cell.getDocCount(); + final int expectedBucketCount = expectedDocsCountForGeoShapes.get(geohash); + assertNotSame(bucketCount, 0); + assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); + final GeoPoint geoPoint = (GeoPoint) propertiesKeys[i]; + assertThat(stringEncode(geoPoint.lon(), geoPoint.lat(), precision), equalTo(geohash)); + assertThat((long) propertiesDocCounts[i], equalTo(bucketCount)); + } + } + } + + public void testMultivalued() { + for (int precision = 1; precision <= PRECISION; precision++) { + SearchResponse response = client().prepareSearch("multi_valued_idx") + .addAggregation(AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).precision(precision)) + .get(); + assertSearchResponse(response); + GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { + String geohash = cell.getKeyAsString(); + + long bucketCount = cell.getDocCount(); + int expectedBucketCount = multiValuedExpectedDocCountsGeoPoint.get(geohash); + assertNotSame(bucketCount, 0); + assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); + } + } + } + + public void testFiltered() { + GeoBoundingBoxQueryBuilder bbox = new GeoBoundingBoxQueryBuilder(GEO_POINT_FIELD_NAME); + bbox.setCorners(smallestGeoHash).queryName("bbox"); + for (int precision = 1; precision <= PRECISION; precision++) { + SearchResponse response = client().prepareSearch("idx") + .addAggregation( + org.opensearch.search.aggregations.AggregationBuilders.filter("filtered", bbox) + .subAggregation(AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).precision(precision)) + ) + .get(); + + assertSearchResponse(response); + + Filter filter = response.getAggregations().get("filtered"); + + GeoGrid geoGrid = filter.getAggregations().get(AGG_NAME); + for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { + String geohash = cell.getKeyAsString(); + long bucketCount = cell.getDocCount(); + int expectedBucketCount = expectedDocCountsForSingleGeoPoint.get(geohash); + assertNotSame(bucketCount, 0); + assertTrue("Buckets must be filtered", geohash.startsWith(smallestGeoHash)); + assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); + + } + } + } + + public void testUnmapped() { + for (int precision = 1; precision <= PRECISION; precision++) { + SearchResponse response = client().prepareSearch("idx_unmapped") + .addAggregation(AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).precision(precision)) + .get(); + + assertSearchResponse(response); + + GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + assertThat(geoGrid.getBuckets().size(), equalTo(0)); + } + + } + + public void testPartiallyUnmapped() { + for (int precision = 1; precision <= PRECISION; precision++) { + SearchResponse response = client().prepareSearch("idx", "idx_unmapped") + .addAggregation(AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).precision(precision)) + .get(); + + assertSearchResponse(response); + + GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { + String geohash = cell.getKeyAsString(); + + long bucketCount = cell.getDocCount(); + int expectedBucketCount = expectedDocCountsForSingleGeoPoint.get(geohash); + assertNotSame(bucketCount, 0); + assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); + } + } + } + + public void testTopMatch() { + for (int precision = 1; precision <= PRECISION; precision++) { + SearchResponse response = client().prepareSearch("idx") + .addAggregation( + AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).size(1).shardSize(100).precision(precision) + ) + .get(); + + assertSearchResponse(response); + + GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + // Check we only have one bucket with the best match for that resolution + assertThat(geoGrid.getBuckets().size(), equalTo(1)); + for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { + String geohash = cell.getKeyAsString(); + long bucketCount = cell.getDocCount(); + int expectedBucketCount = 0; + for (var cursor : expectedDocCountsForSingleGeoPoint.entrySet()) { + if (cursor.getKey().length() == precision) { + expectedBucketCount = Math.max(expectedBucketCount, cursor.getValue()); + } + } + assertNotSame(bucketCount, 0); + assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); + } + } + } + + public void testSizeIsZero() { + final int size = 0; + final int shardSize = 10000; + IllegalArgumentException exception = expectThrows( + IllegalArgumentException.class, + () -> client().prepareSearch("idx") + .addAggregation(AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).size(size).shardSize(shardSize)) + .get() + ); + assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [" + AGG_NAME + "]")); + } + + public void testShardSizeIsZero() { + final int size = 100; + final int shardSize = 0; + IllegalArgumentException exception = expectThrows( + IllegalArgumentException.class, + () -> client().prepareSearch("idx") + .addAggregation(AggregationBuilders.geohashGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).size(size).shardSize(shardSize)) + .get() + ); + assertThat(exception.getMessage(), containsString("[shardSize] must be greater than 0. Found [0] in [" + AGG_NAME + "]")); + } + + @Override + protected Set generateBucketsForGeometry(final Geometry geometry, final GeoShapeDocValue geometryDocValue) { + final GeoPoint topLeft = new GeoPoint(); + final GeoPoint bottomRight = new GeoPoint(); + assert geometry != null; + GeoBoundsHelper.updateBoundsForGeometry(geometry, topLeft, bottomRight); + final Set geoHashes = new HashSet<>(); + final boolean isIntersectingWithBoundingRectangle = geometryDocValue.isIntersectingRectangle(boundingRectangleForGeoShapesAgg); + for (int precision = MAX_PRECISION_FOR_GEO_SHAPES_AGG_TESTING; precision > 0; precision--) { + if (precision > MIN_PRECISION_WITHOUT_BB_AGGS && isIntersectingWithBoundingRectangle == false) { + continue; + } + final GeoPoint topRight = new GeoPoint(topLeft.getLat(), bottomRight.getLon()); + String currentGeoHash = Geohash.stringEncode(topLeft.getLon(), topLeft.getLat(), precision); + String startingRowGeoHash = currentGeoHash; + String endGeoHashForCurrentRow = Geohash.stringEncode(topRight.getLon(), topRight.getLat(), precision); + String terminatingGeoHash = Geohash.stringEncode(bottomRight.getLon(), bottomRight.getLat(), precision); + while (true) { + final Rectangle currentRectangle = Geohash.toBoundingBox(currentGeoHash); + if (geometryDocValue.isIntersectingRectangle(currentRectangle)) { + geoHashes.add(currentGeoHash); + } + assert currentGeoHash != null; + if (currentGeoHash.equals(terminatingGeoHash)) { + break; + } + if (currentGeoHash.equals(endGeoHashForCurrentRow)) { + // move in south direction + currentGeoHash = Geohash.getNeighbor(startingRowGeoHash, precision, 0, -1); + startingRowGeoHash = currentGeoHash; + endGeoHashForCurrentRow = Geohash.getNeighbor(endGeoHashForCurrentRow, precision, 0, -1); + } else { + // move in East direction + currentGeoHash = Geohash.getNeighbor(currentGeoHash, precision, 1, 0); + } + } + } + return geoHashes; + } + + @Override + protected Set generateBucketsForGeoPoint(final GeoPoint geoPoint) { + Set buckets = new HashSet<>(); + for (int precision = PRECISION; precision > 0; precision--) { + final String hash = Geohash.stringEncode(geoPoint.getLon(), geoPoint.getLat(), precision); + if ((smallestGeoHash == null) || (hash.length() < smallestGeoHash.length())) { + smallestGeoHash = hash; + } + buckets.add(hash); + } + return buckets; + } + +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridIT.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridIT.java new file mode 100644 index 0000000000000..2a5772d417530 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridIT.java @@ -0,0 +1,174 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.geo.GeoShapeDocValue; +import org.opensearch.common.settings.Settings; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGrid; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.common.GeoBoundsHelper; +import org.opensearch.geo.tests.common.AggregationBuilders; +import org.opensearch.geometry.Geometry; +import org.opensearch.search.aggregations.InternalAggregation; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.hamcrest.MatcherAssert; + +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.equalTo; + +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class GeoTileGridIT extends AbstractGeoBucketAggregationIntegTest { + + private static final int GEOPOINT_MAX_PRECISION = 17; + + private static final String AGG_NAME = "geotilegrid"; + + public GeoTileGridIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @Override + public void setupSuiteScopeCluster() throws Exception { + final Random random = random(); + // Creating a BB for limiting the number buckets generated during aggregation + boundingRectangleForGeoShapesAgg = getGridAggregationBoundingBox(random); + prepareSingleValueGeoPointIndex(random); + prepareMultiValuedGeoPointIndex(random); + prepareGeoShapeIndexForAggregations(random); + ensureSearchable(); + } + + public void testGeoShapes() { + final GeoBoundingBox boundingBox = new GeoBoundingBox( + new GeoPoint(boundingRectangleForGeoShapesAgg.getMaxLat(), boundingRectangleForGeoShapesAgg.getMinLon()), + new GeoPoint(boundingRectangleForGeoShapesAgg.getMinLat(), boundingRectangleForGeoShapesAgg.getMaxLon()) + ); + for (int precision = 1; precision <= MAX_PRECISION_FOR_GEO_SHAPES_AGG_TESTING; precision++) { + final GeoGridAggregationBuilder builder = AggregationBuilders.geotileGrid(AGG_NAME) + .field(GEO_SHAPE_FIELD_NAME) + .precision(precision); + // This makes sure that for only higher precision we are providing the GeoBounding Box. This also ensures + // that we are able to test both bounded and unbounded aggregations + if (precision > MIN_PRECISION_WITHOUT_BB_AGGS) { + builder.setGeoBoundingBox(boundingBox); + } + final SearchResponse response = client().prepareSearch(GEO_SHAPE_INDEX_NAME).addAggregation(builder).get(); + final GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + final List buckets = geoGrid.getBuckets(); + final Object[] propertiesKeys = (Object[]) ((InternalAggregation) geoGrid).getProperty("_key"); + final Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) geoGrid).getProperty("_count"); + for (int i = 0; i < buckets.size(); i++) { + final GeoGrid.Bucket cell = buckets.get(i); + final String geoTile = cell.getKeyAsString(); + + final long bucketCount = cell.getDocCount(); + final int expectedBucketCount = expectedDocsCountForGeoShapes.get(geoTile); + assertNotSame(bucketCount, 0); + assertEquals("Geotile " + geoTile + " has wrong doc count ", expectedBucketCount, bucketCount); + final GeoPoint geoPoint = (GeoPoint) propertiesKeys[i]; + MatcherAssert.assertThat(GeoTileUtils.stringEncode(geoPoint.lon(), geoPoint.lat(), precision), equalTo(geoTile)); + MatcherAssert.assertThat((long) propertiesDocCounts[i], equalTo(bucketCount)); + } + } + } + + public void testSimpleGeoPointsAggregation() { + for (int precision = 1; precision <= GEOPOINT_MAX_PRECISION; precision++) { + SearchResponse response = client().prepareSearch("idx") + .addAggregation(AggregationBuilders.geotileGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).precision(precision)) + .get(); + + assertSearchResponse(response); + + GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + List buckets = geoGrid.getBuckets(); + Object[] propertiesKeys = (Object[]) ((InternalAggregation) geoGrid).getProperty("_key"); + Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) geoGrid).getProperty("_count"); + for (int i = 0; i < buckets.size(); i++) { + GeoGrid.Bucket cell = buckets.get(i); + String geoTile = cell.getKeyAsString(); + + long bucketCount = cell.getDocCount(); + int expectedBucketCount = expectedDocCountsForSingleGeoPoint.get(geoTile); + assertNotSame(bucketCount, 0); + assertEquals("GeoTile " + geoTile + " has wrong doc count ", expectedBucketCount, bucketCount); + GeoPoint geoPoint = (GeoPoint) propertiesKeys[i]; + assertThat(GeoTileUtils.stringEncode(geoPoint.lon(), geoPoint.lat(), precision), equalTo(geoTile)); + assertThat((long) propertiesDocCounts[i], equalTo(bucketCount)); + } + } + } + + public void testMultivaluedGeoPointsAggregation() throws Exception { + for (int precision = 1; precision <= GEOPOINT_MAX_PRECISION; precision++) { + SearchResponse response = client().prepareSearch("multi_valued_idx") + .addAggregation(AggregationBuilders.geotileGrid(AGG_NAME).field(GEO_POINT_FIELD_NAME).precision(precision)) + .get(); + + assertSearchResponse(response); + + GeoGrid geoGrid = response.getAggregations().get(AGG_NAME); + for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { + String geohash = cell.getKeyAsString(); + + long bucketCount = cell.getDocCount(); + int expectedBucketCount = multiValuedExpectedDocCountsGeoPoint.get(geohash); + assertNotSame(bucketCount, 0); + assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); + } + } + } + + /** + * Returns a set of buckets for the shape at different precision level. Override this method for different bucket + * aggregations. + * + * @param geometry {@link Geometry} + * @param geoShapeDocValue {@link GeoShapeDocValue} + * @return A {@link Set} of {@link String} which represents the buckets. + */ + @Override + protected Set generateBucketsForGeometry(final Geometry geometry, final GeoShapeDocValue geoShapeDocValue) { + final GeoPoint topLeft = new GeoPoint(); + final GeoPoint bottomRight = new GeoPoint(); + assert geometry != null; + GeoBoundsHelper.updateBoundsForGeometry(geometry, topLeft, bottomRight); + final Set geoTiles = new HashSet<>(); + final boolean isIntersectingWithBoundingRectangle = geoShapeDocValue.isIntersectingRectangle(boundingRectangleForGeoShapesAgg); + for (int precision = MAX_PRECISION_FOR_GEO_SHAPES_AGG_TESTING; precision > 0; precision--) { + if (precision > MIN_PRECISION_WITHOUT_BB_AGGS && isIntersectingWithBoundingRectangle == false) { + continue; + } + geoTiles.addAll( + GeoTileUtils.encodeShape(geoShapeDocValue, precision).stream().map(GeoTileUtils::stringEncode).collect(Collectors.toSet()) + ); + } + return geoTiles; + } + + protected Set generateBucketsForGeoPoint(final GeoPoint geoPoint) { + Set buckets = new HashSet<>(); + for (int precision = GEOPOINT_MAX_PRECISION; precision > 0; precision--) { + final GeoPoint precisedGeoPoint = this.toStoragePrecision(geoPoint); + final String tile = GeoTileUtils.stringEncode(precisedGeoPoint.getLon(), precisedGeoPoint.getLat(), precision); + buckets.add(tile); + } + return buckets; + } +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/ShardReduceIT.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/ShardReduceIT.java new file mode 100644 index 0000000000000..85541c60f133c --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/ShardReduceIT.java @@ -0,0 +1,112 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket; + +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.geo.GeoModulePluginIntegTestCase; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGrid; +import org.opensearch.geo.tests.common.AggregationBuilders; +import org.opensearch.geometry.utils.Geohash; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; +import org.opensearch.search.aggregations.bucket.histogram.Histogram; +import org.opensearch.test.OpenSearchIntegTestCase; + +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.equalTo; + +/** + * Tests making sure that the reduce is propagated to all aggregations in the hierarchy when executing on a single shard + * These tests are based on the date histogram in combination of min_doc_count=0. In order for the date histogram to + * compute empty buckets, its {@code reduce()} method must be called. So by adding the date histogram under other buckets, + * we can make sure that the reduce is properly propagated by checking that empty buckets were created. + */ +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class ShardReduceIT extends GeoModulePluginIntegTestCase { + + public ShardReduceIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + private IndexRequestBuilder indexDoc(String date, int value) throws Exception { + return client().prepareIndex("idx") + .setSource( + jsonBuilder().startObject() + .field("value", value) + .field("ip", "10.0.0." + value) + .field("location", Geohash.stringEncode(5, 52, Geohash.PRECISION)) + .field("date", date) + .field("term-l", 1) + .field("term-d", 1.5) + .field("term-s", "term") + .startObject("nested") + .field("date", date) + .endObject() + .endObject() + ); + } + + @Override + public void setupSuiteScopeCluster() throws Exception { + assertAcked( + prepareCreate("idx").setMapping( + "nested", + "type=nested", + "ip", + "type=ip", + "location", + "type=geo_point", + "term-s", + "type=keyword" + ) + ); + + indexRandom(true, indexDoc("2014-01-01", 1), indexDoc("2014-01-02", 2), indexDoc("2014-01-04", 3)); + ensureSearchable(); + } + + public void testGeoHashGrid() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .setQuery(QueryBuilders.matchAllQuery()) + .addAggregation( + AggregationBuilders.geohashGrid("grid") + .field("location") + .subAggregation(dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0)) + ) + .get(); + + assertSearchResponse(response); + + GeoGrid grid = response.getAggregations().get("grid"); + Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); + assertThat(histo.getBuckets().size(), equalTo(4)); + } + + public void testGeoTileGrid() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .setQuery(QueryBuilders.matchAllQuery()) + .addAggregation( + AggregationBuilders.geotileGrid("grid") + .field("location") + .subAggregation(dateHistogram("histo").field("date").fixedInterval(DateHistogramInterval.DAY).minDocCount(0)) + ) + .get(); + + assertSearchResponse(response); + + GeoGrid grid = response.getAggregations().get("grid"); + Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); + assertThat(histo.getBuckets().size(), equalTo(4)); + } +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/common/GeoBoundsHelper.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/common/GeoBoundsHelper.java new file mode 100644 index 0000000000000..297aa311d5b75 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/common/GeoBoundsHelper.java @@ -0,0 +1,187 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.common; + +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.geometry.Geometry; +import org.opensearch.geometry.GeometryCollection; +import org.opensearch.geometry.Line; +import org.opensearch.geometry.MultiLine; +import org.opensearch.geometry.MultiPoint; +import org.opensearch.geometry.MultiPolygon; +import org.opensearch.geometry.Point; +import org.opensearch.geometry.Polygon; +import org.opensearch.geometry.Rectangle; +import org.opensearch.geometry.ShapeType; +import org.junit.Assert; + +import java.util.Locale; + +/** + * A helper class for finding the geo bounds for a shape or a point. + */ +public final class GeoBoundsHelper { + + /** + * Updates the GeoBounds for the input GeoPoint in topLeft and bottomRight GeoPoints. + * + * @param geoPoint {@link GeoPoint} + * @param topLeft {@link GeoPoint} + * @param bottomRight {@link GeoPoint} + */ + public static void updateBoundsForGeoPoint(final GeoPoint geoPoint, final GeoPoint topLeft, final GeoPoint bottomRight) { + updateBoundsBottomRight(geoPoint, bottomRight); + updateBoundsTopLeft(geoPoint, topLeft); + } + + /** + * Find the bottom right for a point and put it in the currentBounds param. + * + * @param geoPoint {@link GeoPoint} + * @param currentBound {@link GeoPoint} + */ + public static void updateBoundsBottomRight(final GeoPoint geoPoint, final GeoPoint currentBound) { + if (geoPoint.lat() < currentBound.lat()) { + currentBound.resetLat(geoPoint.lat()); + } + if (geoPoint.lon() > currentBound.lon()) { + currentBound.resetLon(geoPoint.lon()); + } + } + + /** + * Find the top left for a point and put it in the currentBounds param. + * + * @param geoPoint {@link GeoPoint} + * @param currentBound {@link GeoPoint} + */ + public static void updateBoundsTopLeft(final GeoPoint geoPoint, final GeoPoint currentBound) { + if (geoPoint.lat() > currentBound.lat()) { + currentBound.resetLat(geoPoint.lat()); + } + if (geoPoint.lon() < currentBound.lon()) { + currentBound.resetLon(geoPoint.lon()); + } + } + + /** + * Find the bounds for an input shape. + * + * @param geometry {@link Geometry} + * @param geoShapeTopLeft {@link GeoPoint} + * @param geoShapeBottomRight {@link GeoPoint} + */ + public static void updateBoundsForGeometry( + final Geometry geometry, + final GeoPoint geoShapeTopLeft, + final GeoPoint geoShapeBottomRight + ) { + final ShapeType shapeType = geometry.type(); + switch (shapeType) { + case POINT: + updateBoundsTopLeft((Point) geometry, geoShapeTopLeft); + updateBoundsBottomRight((Point) geometry, geoShapeBottomRight); + return; + case MULTIPOINT: + ((MultiPoint) geometry).getAll().forEach(p -> updateBoundsTopLeft(p, geoShapeTopLeft)); + ((MultiPoint) geometry).getAll().forEach(p -> updateBoundsBottomRight(p, geoShapeBottomRight)); + return; + case POLYGON: + updateBoundsTopLeft((Polygon) geometry, geoShapeTopLeft); + updateBoundsBottomRight((Polygon) geometry, geoShapeBottomRight); + return; + case LINESTRING: + updateBoundsTopLeft((Line) geometry, geoShapeTopLeft); + updateBoundsBottomRight((Line) geometry, geoShapeBottomRight); + return; + case MULTIPOLYGON: + ((MultiPolygon) geometry).getAll().forEach(p -> updateBoundsTopLeft(p, geoShapeTopLeft)); + ((MultiPolygon) geometry).getAll().forEach(p -> updateBoundsBottomRight(p, geoShapeBottomRight)); + return; + case GEOMETRYCOLLECTION: + ((GeometryCollection) geometry).getAll() + .forEach(geo -> updateBoundsForGeometry(geo, geoShapeTopLeft, geoShapeBottomRight)); + return; + case MULTILINESTRING: + ((MultiLine) geometry).getAll().forEach(line -> updateBoundsTopLeft(line, geoShapeTopLeft)); + ((MultiLine) geometry).getAll().forEach(line -> updateBoundsBottomRight(line, geoShapeBottomRight)); + return; + case ENVELOPE: + updateBoundsTopLeft((Rectangle) geometry, geoShapeTopLeft); + updateBoundsBottomRight((Rectangle) geometry, geoShapeBottomRight); + return; + default: + Assert.fail(String.format(Locale.ROOT, "The shape type %s is not supported", shapeType)); + } + } + + private static void updateBoundsTopLeft(final Point p, final GeoPoint currentBound) { + final GeoPoint geoPoint = new GeoPoint(p.getLat(), p.getLon()); + updateBoundsTopLeft(geoPoint, currentBound); + } + + private static void updateBoundsTopLeft(final Polygon polygon, final GeoPoint currentBound) { + for (int i = 0; i < polygon.getPolygon().length(); i++) { + double lat = polygon.getPolygon().getLats()[i]; + double lon = polygon.getPolygon().getLons()[i]; + final GeoPoint geoPoint = new GeoPoint(lat, lon); + updateBoundsTopLeft(geoPoint, currentBound); + } + } + + private static void updateBoundsTopLeft(final Line line, final GeoPoint currentBound) { + for (int i = 0; i < line.length(); i++) { + double lat = line.getLats()[i]; + double lon = line.getLons()[i]; + final GeoPoint geoPoint = new GeoPoint(lat, lon); + updateBoundsTopLeft(geoPoint, currentBound); + } + } + + private static void updateBoundsTopLeft(final Rectangle rectangle, final GeoPoint currentBound) { + if (rectangle.getMaxLat() > currentBound.lat()) { + currentBound.resetLat(rectangle.getMaxLat()); + } + if (rectangle.getMinLon() < currentBound.lon()) { + currentBound.resetLon(rectangle.getMinLon()); + } + } + + private static void updateBoundsBottomRight(final Point p, final GeoPoint currentBound) { + final GeoPoint geoPoint = new GeoPoint(p.getLat(), p.getLon()); + updateBoundsBottomRight(geoPoint, currentBound); + } + + private static void updateBoundsBottomRight(final Polygon polygon, final GeoPoint currentBound) { + for (int i = 0; i < polygon.getPolygon().length(); i++) { + double lat = polygon.getPolygon().getLats()[i]; + double lon = polygon.getPolygon().getLons()[i]; + final GeoPoint geoPoint = new GeoPoint(lat, lon); + updateBoundsBottomRight(geoPoint, currentBound); + } + } + + private static void updateBoundsBottomRight(final Line line, final GeoPoint currentBound) { + for (int i = 0; i < line.length(); i++) { + double lat = line.getLats()[i]; + double lon = line.getLons()[i]; + final GeoPoint geoPoint = new GeoPoint(lat, lon); + updateBoundsBottomRight(geoPoint, currentBound); + } + } + + private static void updateBoundsBottomRight(final Rectangle rectangle, final GeoPoint currentBound) { + if (rectangle.getMinLat() < currentBound.lat()) { + currentBound.resetLat(rectangle.getMinLat()); + } + if (rectangle.getMaxLon() > currentBound.lon()) { + currentBound.resetLon(rectangle.getMaxLon()); + } + } +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorModulePluginTestCase.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorModulePluginTestCase.java new file mode 100644 index 0000000000000..711744b944ce3 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoAggregatorModulePluginTestCase.java @@ -0,0 +1,294 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.document.DocumentField; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.geo.GeoModulePluginIntegTestCase; +import org.opensearch.geo.search.aggregations.common.GeoBoundsHelper; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.geo.tests.common.RandomGeoGeometryGenerator; +import org.opensearch.geometry.Geometry; +import org.opensearch.geometry.utils.Geohash; +import org.opensearch.search.SearchHit; +import org.opensearch.search.sort.SortBuilders; +import org.opensearch.search.sort.SortOrder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.equalTo; + +/** + * This is base class for all Geo Aggregations Integration Tests. This class is similar to what we have in the server + * folder of the OpenSearch repo. As part of moving the Geo based aggregation into separate module and plugin we need + * to copy the code as we cannot depend on this class. + * GitHub issue + */ +public abstract class AbstractGeoAggregatorModulePluginTestCase extends GeoModulePluginIntegTestCase { + + protected static final String SINGLE_VALUED_FIELD_NAME = "geo_value"; + protected static final String MULTI_VALUED_FIELD_NAME = "geo_values"; + protected static final String GEO_SHAPE_FIELD_NAME = "shape"; + protected static final String NUMBER_FIELD_NAME = "l_values"; + protected static final String UNMAPPED_IDX_NAME = "idx_unmapped"; + protected static final String IDX_NAME = "idx"; + protected static final String EMPTY_IDX_NAME = "empty_idx"; + protected static final String DATELINE_IDX_NAME = "dateline_idx"; + protected static final String HIGH_CARD_IDX_NAME = "high_card_idx"; + protected static final String IDX_ZERO_NAME = "idx_zero"; + + protected static int numDocs; + protected static int numUniqueGeoPoints; + protected static GeoPoint[] singleValues, multiValues; + protected static Geometry[] geoShapesValues; + protected static GeoPoint singleTopLeft, singleBottomRight, multiTopLeft, multiBottomRight, singleCentroid, multiCentroid, + unmappedCentroid, geoShapeTopLeft, geoShapeBottomRight; + protected static Map expectedDocCountsForGeoHash = null; + protected static Map expectedCentroidsForGeoHash = null; + + public AbstractGeoAggregatorModulePluginTestCase(Settings dynamicSettings) { + super(dynamicSettings); + } + + @Override + public void setupSuiteScopeCluster() throws Exception { + createIndex(UNMAPPED_IDX_NAME); + assertAcked( + prepareCreate(IDX_NAME).setMapping( + SINGLE_VALUED_FIELD_NAME, + "type=geo_point", + MULTI_VALUED_FIELD_NAME, + "type=geo_point", + NUMBER_FIELD_NAME, + "type=long", + "tag", + "type=keyword", + GEO_SHAPE_FIELD_NAME, + "type=geo_shape" + ) + ); + + singleTopLeft = new GeoPoint(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + singleBottomRight = new GeoPoint(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY); + multiTopLeft = new GeoPoint(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + multiBottomRight = new GeoPoint(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY); + geoShapeTopLeft = new GeoPoint(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + geoShapeBottomRight = new GeoPoint(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY); + singleCentroid = new GeoPoint(0, 0); + multiCentroid = new GeoPoint(0, 0); + unmappedCentroid = new GeoPoint(0, 0); + + numDocs = randomIntBetween(6, 20); + numUniqueGeoPoints = randomIntBetween(1, numDocs); + expectedDocCountsForGeoHash = new HashMap<>(numDocs * 2); + expectedCentroidsForGeoHash = new HashMap<>(numDocs * 2); + + singleValues = new GeoPoint[numUniqueGeoPoints]; + for (int i = 0; i < singleValues.length; i++) { + singleValues[i] = RandomGeoGenerator.randomPoint(random()); + GeoBoundsHelper.updateBoundsForGeoPoint(singleValues[i], singleTopLeft, singleBottomRight); + } + + multiValues = new GeoPoint[numUniqueGeoPoints]; + for (int i = 0; i < multiValues.length; i++) { + multiValues[i] = RandomGeoGenerator.randomPoint(random()); + GeoBoundsHelper.updateBoundsForGeoPoint(multiValues[i], multiTopLeft, multiBottomRight); + } + + geoShapesValues = new Geometry[numDocs]; + IntStream.range(0, numDocs).forEach(iterator -> { + geoShapesValues[iterator] = RandomGeoGeometryGenerator.randomGeometry(random()); + GeoBoundsHelper.updateBoundsForGeometry(geoShapesValues[iterator], geoShapeTopLeft, geoShapeBottomRight); + }); + + List builders = new ArrayList<>(); + + GeoPoint singleVal; + final GeoPoint[] multiVal = new GeoPoint[2]; + double newMVLat, newMVLon; + for (int i = 0; i < numDocs; i++) { + singleVal = singleValues[i % numUniqueGeoPoints]; + multiVal[0] = multiValues[i % numUniqueGeoPoints]; + multiVal[1] = multiValues[(i + 1) % numUniqueGeoPoints]; + builders.add( + client().prepareIndex(IDX_NAME) + .setSource( + jsonBuilder().startObject() + .array(SINGLE_VALUED_FIELD_NAME, singleVal.lon(), singleVal.lat()) + .startArray(MULTI_VALUED_FIELD_NAME) + .startArray() + .value(multiVal[0].lon()) + .value(multiVal[0].lat()) + .endArray() + .startArray() + .value(multiVal[1].lon()) + .value(multiVal[1].lat()) + .endArray() + .endArray() + .field(NUMBER_FIELD_NAME, i) + .field("tag", "tag" + i) + .field(GEO_SHAPE_FIELD_NAME, WKT.toWKT(geoShapesValues[i])) + .endObject() + ) + ); + singleCentroid = singleCentroid.reset( + singleCentroid.lat() + (singleVal.lat() - singleCentroid.lat()) / (i + 1), + singleCentroid.lon() + (singleVal.lon() - singleCentroid.lon()) / (i + 1) + ); + newMVLat = (multiVal[0].lat() + multiVal[1].lat()) / 2d; + newMVLon = (multiVal[0].lon() + multiVal[1].lon()) / 2d; + multiCentroid = multiCentroid.reset( + multiCentroid.lat() + (newMVLat - multiCentroid.lat()) / (i + 1), + multiCentroid.lon() + (newMVLon - multiCentroid.lon()) / (i + 1) + ); + } + + assertAcked( + prepareCreate(EMPTY_IDX_NAME).setMapping(SINGLE_VALUED_FIELD_NAME, "type=geo_point", GEO_SHAPE_FIELD_NAME, "type=geo_shape") + ); + + assertAcked( + prepareCreate(DATELINE_IDX_NAME).setMapping( + SINGLE_VALUED_FIELD_NAME, + "type=geo_point", + MULTI_VALUED_FIELD_NAME, + "type=geo_point", + NUMBER_FIELD_NAME, + "type=long", + "tag", + "type=keyword" + ) + ); + + GeoPoint[] geoValues = new GeoPoint[5]; + geoValues[0] = new GeoPoint(38, 178); + geoValues[1] = new GeoPoint(12, -179); + geoValues[2] = new GeoPoint(-24, 170); + geoValues[3] = new GeoPoint(32, -175); + geoValues[4] = new GeoPoint(-11, 178); + + for (int i = 0; i < 5; i++) { + builders.add( + client().prepareIndex(DATELINE_IDX_NAME) + .setSource( + jsonBuilder().startObject() + .array(SINGLE_VALUED_FIELD_NAME, geoValues[i].lon(), geoValues[i].lat()) + .field(NUMBER_FIELD_NAME, i) + .field("tag", "tag" + i) + .endObject() + ) + ); + } + assertAcked( + prepareCreate(HIGH_CARD_IDX_NAME).setSettings(Settings.builder().put("number_of_shards", 2)) + .setMapping( + SINGLE_VALUED_FIELD_NAME, + "type=geo_point", + MULTI_VALUED_FIELD_NAME, + "type=geo_point", + NUMBER_FIELD_NAME, + "type=long,store=true", + "tag", + "type=keyword" + ) + ); + + for (int i = 0; i < 2000; i++) { + singleVal = singleValues[i % numUniqueGeoPoints]; + builders.add( + client().prepareIndex(HIGH_CARD_IDX_NAME) + .setSource( + jsonBuilder().startObject() + .array(SINGLE_VALUED_FIELD_NAME, singleVal.lon(), singleVal.lat()) + .startArray(MULTI_VALUED_FIELD_NAME) + .startArray() + .value(multiValues[i % numUniqueGeoPoints].lon()) + .value(multiValues[i % numUniqueGeoPoints].lat()) + .endArray() + .startArray() + .value(multiValues[(i + 1) % numUniqueGeoPoints].lon()) + .value(multiValues[(i + 1) % numUniqueGeoPoints].lat()) + .endArray() + .endArray() + .field(NUMBER_FIELD_NAME, i) + .field("tag", "tag" + i) + .endObject() + ) + ); + updateGeohashBucketsCentroid(singleVal); + } + + builders.add( + client().prepareIndex(IDX_ZERO_NAME) + .setSource(jsonBuilder().startObject().array(SINGLE_VALUED_FIELD_NAME, 0.0, 1.0).endObject()) + ); + assertAcked(prepareCreate(IDX_ZERO_NAME).setMapping(SINGLE_VALUED_FIELD_NAME, "type=geo_point")); + + indexRandom(true, builders); + ensureSearchable(); + + // Added to debug a test failure where the terms aggregation seems to be reporting two documents with the same + // value for NUMBER_FIELD_NAME. This will check that after random indexing each document only has 1 value for + // NUMBER_FIELD_NAME and it is the correct value. Following this initial change its seems that this call was getting + // more that 2000 hits (actual value was 2059) so now it will also check to ensure all hits have the correct index and type. + SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) + .addStoredField(NUMBER_FIELD_NAME) + .addSort(SortBuilders.fieldSort(NUMBER_FIELD_NAME).order(SortOrder.ASC)) + .setSize(5000) + .get(); + assertSearchResponse(response); + long totalHits = response.getHits().getTotalHits().value; + XContentBuilder builder = XContentFactory.jsonBuilder(); + response.toXContent(builder, ToXContent.EMPTY_PARAMS); + logger.info("Full high_card_idx Response Content:\n{ {} }", builder.toString()); + for (int i = 0; i < totalHits; i++) { + SearchHit searchHit = response.getHits().getAt(i); + assertThat("Hit " + i + " with id: " + searchHit.getId(), searchHit.getIndex(), equalTo("high_card_idx")); + DocumentField hitField = searchHit.field(NUMBER_FIELD_NAME); + + assertThat("Hit " + i + " has wrong number of values", hitField.getValues().size(), equalTo(1)); + Long value = hitField.getValue(); + assertThat("Hit " + i + " has wrong value", value.intValue(), equalTo(i)); + } + assertThat(totalHits, equalTo(2000L)); + } + + private void updateGeohashBucketsCentroid(final GeoPoint location) { + String hash = Geohash.stringEncode(location.lon(), location.lat(), Geohash.PRECISION); + for (int precision = Geohash.PRECISION; precision > 0; --precision) { + final String h = hash.substring(0, precision); + expectedDocCountsForGeoHash.put(h, expectedDocCountsForGeoHash.getOrDefault(h, 0) + 1); + expectedCentroidsForGeoHash.put(h, updateHashCentroid(h, location)); + } + } + + private GeoPoint updateHashCentroid(String hash, final GeoPoint location) { + GeoPoint centroid = expectedCentroidsForGeoHash.getOrDefault(hash, null); + if (centroid == null) { + return new GeoPoint(location.lat(), location.lon()); + } + final int docCount = expectedDocCountsForGeoHash.get(hash); + final double newLon = centroid.lon() + (location.lon() - centroid.lon()) / docCount; + final double newLat = centroid.lat() + (location.lat() - centroid.lat()) / docCount; + return centroid.reset(newLat, newLon); + } +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsITTestCase.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsITTestCase.java new file mode 100644 index 0000000000000..1c28df6bc4ea2 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsITTestCase.java @@ -0,0 +1,316 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.util.BigArray; +import org.opensearch.search.aggregations.InternalAggregation; +import org.opensearch.search.aggregations.bucket.global.Global; +import org.opensearch.search.aggregations.bucket.terms.Terms; +import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.hamcrest.MatcherAssert; + +import java.util.List; + +import static org.opensearch.geo.tests.common.AggregationBuilders.geoBounds; +import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.aggregations.AggregationBuilders.global; +import static org.opensearch.search.aggregations.AggregationBuilders.terms; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.sameInstance; + +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class GeoBoundsITTestCase extends AbstractGeoAggregatorModulePluginTestCase { + private static final String aggName = "geoBounds"; + + public GeoBoundsITTestCase(Settings dynamicSettings) { + super(dynamicSettings); + } + + public void testSingleValuedField() throws Exception { + SearchResponse response = client().prepareSearch(IDX_NAME) + .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) + .get(); + + assertSearchResponse(response); + + GeoBounds geoBounds = response.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + GeoPoint topLeft = geoBounds.topLeft(); + GeoPoint bottomRight = geoBounds.bottomRight(); + assertThat(topLeft.lat(), closeTo(singleTopLeft.lat(), GEOHASH_TOLERANCE)); + assertThat(topLeft.lon(), closeTo(singleTopLeft.lon(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lat(), closeTo(singleBottomRight.lat(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lon(), closeTo(singleBottomRight.lon(), GEOHASH_TOLERANCE)); + } + + public void testSingleValuedField_getProperty() throws Exception { + SearchResponse searchResponse = client().prepareSearch(IDX_NAME) + .setQuery(matchAllQuery()) + .addAggregation(global("global").subAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false))) + .get(); + + assertSearchResponse(searchResponse); + + Global global = searchResponse.getAggregations().get("global"); + assertThat(global, notNullValue()); + assertThat(global.getName(), equalTo("global")); + assertThat(global.getDocCount(), equalTo((long) numDocs)); + assertThat(global.getAggregations(), notNullValue()); + assertThat(global.getAggregations().asMap().size(), equalTo(1)); + + GeoBounds geobounds = global.getAggregations().get(aggName); + assertThat(geobounds, notNullValue()); + assertThat(geobounds.getName(), equalTo(aggName)); + assertThat((GeoBounds) ((InternalAggregation) global).getProperty(aggName), sameInstance(geobounds)); + GeoPoint topLeft = geobounds.topLeft(); + GeoPoint bottomRight = geobounds.bottomRight(); + assertThat(topLeft.lat(), closeTo(singleTopLeft.lat(), GEOHASH_TOLERANCE)); + assertThat(topLeft.lon(), closeTo(singleTopLeft.lon(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lat(), closeTo(singleBottomRight.lat(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lon(), closeTo(singleBottomRight.lon(), GEOHASH_TOLERANCE)); + assertThat((double) ((InternalAggregation) global).getProperty(aggName + ".top"), closeTo(singleTopLeft.lat(), GEOHASH_TOLERANCE)); + assertThat((double) ((InternalAggregation) global).getProperty(aggName + ".left"), closeTo(singleTopLeft.lon(), GEOHASH_TOLERANCE)); + assertThat( + (double) ((InternalAggregation) global).getProperty(aggName + ".bottom"), + closeTo(singleBottomRight.lat(), GEOHASH_TOLERANCE) + ); + assertThat( + (double) ((InternalAggregation) global).getProperty(aggName + ".right"), + closeTo(singleBottomRight.lon(), GEOHASH_TOLERANCE) + ); + } + + public void testMultiValuedField() throws Exception { + SearchResponse response = client().prepareSearch(IDX_NAME) + .addAggregation(geoBounds(aggName).field(MULTI_VALUED_FIELD_NAME).wrapLongitude(false)) + .get(); + + assertSearchResponse(response); + + GeoBounds geoBounds = response.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + GeoPoint topLeft = geoBounds.topLeft(); + GeoPoint bottomRight = geoBounds.bottomRight(); + assertThat(topLeft.lat(), closeTo(multiTopLeft.lat(), GEOHASH_TOLERANCE)); + assertThat(topLeft.lon(), closeTo(multiTopLeft.lon(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lat(), closeTo(multiBottomRight.lat(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lon(), closeTo(multiBottomRight.lon(), GEOHASH_TOLERANCE)); + } + + public void testUnmapped() throws Exception { + SearchResponse response = client().prepareSearch(UNMAPPED_IDX_NAME) + .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) + .get(); + + assertSearchResponse(response); + + GeoBounds geoBounds = response.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + GeoPoint topLeft = geoBounds.topLeft(); + GeoPoint bottomRight = geoBounds.bottomRight(); + assertThat(topLeft, equalTo(null)); + assertThat(bottomRight, equalTo(null)); + } + + public void testPartiallyUnmapped() throws Exception { + SearchResponse response = client().prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME) + .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) + .get(); + + assertSearchResponse(response); + + GeoBounds geoBounds = response.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + GeoPoint topLeft = geoBounds.topLeft(); + GeoPoint bottomRight = geoBounds.bottomRight(); + assertThat(topLeft.lat(), closeTo(singleTopLeft.lat(), GEOHASH_TOLERANCE)); + assertThat(topLeft.lon(), closeTo(singleTopLeft.lon(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lat(), closeTo(singleBottomRight.lat(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lon(), closeTo(singleBottomRight.lon(), GEOHASH_TOLERANCE)); + } + + public void testEmptyAggregation() throws Exception { + SearchResponse searchResponse = client().prepareSearch(EMPTY_IDX_NAME) + .setQuery(matchAllQuery()) + .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) + .get(); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); + GeoBounds geoBounds = searchResponse.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + GeoPoint topLeft = geoBounds.topLeft(); + GeoPoint bottomRight = geoBounds.bottomRight(); + assertThat(topLeft, equalTo(null)); + assertThat(bottomRight, equalTo(null)); + } + + public void testSingleValuedFieldNearDateLine() throws Exception { + SearchResponse response = client().prepareSearch(DATELINE_IDX_NAME) + .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) + .get(); + + assertSearchResponse(response); + + GeoPoint geoValuesTopLeft = new GeoPoint(38, -179); + GeoPoint geoValuesBottomRight = new GeoPoint(-24, 178); + + GeoBounds geoBounds = response.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + GeoPoint topLeft = geoBounds.topLeft(); + GeoPoint bottomRight = geoBounds.bottomRight(); + assertThat(topLeft.lat(), closeTo(geoValuesTopLeft.lat(), GEOHASH_TOLERANCE)); + assertThat(topLeft.lon(), closeTo(geoValuesTopLeft.lon(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lat(), closeTo(geoValuesBottomRight.lat(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lon(), closeTo(geoValuesBottomRight.lon(), GEOHASH_TOLERANCE)); + } + + public void testSingleValuedFieldNearDateLineWrapLongitude() throws Exception { + + GeoPoint geoValuesTopLeft = new GeoPoint(38, 170); + GeoPoint geoValuesBottomRight = new GeoPoint(-24, -175); + SearchResponse response = client().prepareSearch(DATELINE_IDX_NAME) + .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(true)) + .get(); + + assertSearchResponse(response); + + GeoBounds geoBounds = response.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + GeoPoint topLeft = geoBounds.topLeft(); + GeoPoint bottomRight = geoBounds.bottomRight(); + assertThat(topLeft.lat(), closeTo(geoValuesTopLeft.lat(), GEOHASH_TOLERANCE)); + assertThat(topLeft.lon(), closeTo(geoValuesTopLeft.lon(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lat(), closeTo(geoValuesBottomRight.lat(), GEOHASH_TOLERANCE)); + assertThat(bottomRight.lon(), closeTo(geoValuesBottomRight.lon(), GEOHASH_TOLERANCE)); + } + + /** + * This test forces the {@link GeoBoundsAggregator} to resize the {@link BigArray}s it uses to ensure they are + * resized correctly + */ + public void testSingleValuedFieldAsSubAggToHighCardTermsAgg() { + SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) + .addAggregation( + terms("terms").field(NUMBER_FIELD_NAME) + .subAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) + ) + .get(); + + assertSearchResponse(response); + + Terms terms = response.getAggregations().get("terms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("terms")); + List buckets = terms.getBuckets(); + assertThat(buckets.size(), equalTo(10)); + for (int i = 0; i < 10; i++) { + Bucket bucket = buckets.get(i); + assertThat(bucket, notNullValue()); + assertThat("InternalBucket " + bucket.getKey() + " has wrong number of documents", bucket.getDocCount(), equalTo(1L)); + GeoBounds geoBounds = bucket.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + assertThat(geoBounds.topLeft().getLat(), allOf(greaterThanOrEqualTo(-90.0), lessThanOrEqualTo(90.0))); + assertThat(geoBounds.topLeft().getLon(), allOf(greaterThanOrEqualTo(-180.0), lessThanOrEqualTo(180.0))); + assertThat(geoBounds.bottomRight().getLat(), allOf(greaterThanOrEqualTo(-90.0), lessThanOrEqualTo(90.0))); + assertThat(geoBounds.bottomRight().getLon(), allOf(greaterThanOrEqualTo(-180.0), lessThanOrEqualTo(180.0))); + } + } + + public void testSingleValuedFieldWithZeroLon() throws Exception { + SearchResponse response = client().prepareSearch(IDX_ZERO_NAME) + .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) + .get(); + + assertSearchResponse(response); + + GeoBounds geoBounds = response.getAggregations().get(aggName); + assertThat(geoBounds, notNullValue()); + assertThat(geoBounds.getName(), equalTo(aggName)); + GeoPoint topLeft = geoBounds.topLeft(); + GeoPoint bottomRight = geoBounds.bottomRight(); + assertThat(topLeft.lat(), closeTo(1.0, GEOHASH_TOLERANCE)); + assertThat(topLeft.lon(), closeTo(0.0, GEOHASH_TOLERANCE)); + assertThat(bottomRight.lat(), closeTo(1.0, GEOHASH_TOLERANCE)); + assertThat(bottomRight.lon(), closeTo(0.0, GEOHASH_TOLERANCE)); + } + + public void testGeoShapeValuedField() { + final SearchResponse response = client().prepareSearch(IDX_NAME) + .addAggregation(geoBounds(aggName).field(GEO_SHAPE_FIELD_NAME).wrapLongitude(false)) + .get(); + assertSearchResponse(response); + final GeoBounds geoBounds = response.getAggregations().get(aggName); + MatcherAssert.assertThat(geoBounds, notNullValue()); + MatcherAssert.assertThat(geoBounds.getName(), equalTo(aggName)); + final GeoPoint topLeft = geoBounds.topLeft(); + final GeoPoint bottomRight = geoBounds.bottomRight(); + MatcherAssert.assertThat(topLeft.lat(), closeTo(geoShapeTopLeft.lat(), GEOHASH_TOLERANCE)); + MatcherAssert.assertThat(topLeft.lon(), closeTo(geoShapeTopLeft.lon(), GEOHASH_TOLERANCE)); + MatcherAssert.assertThat(bottomRight.lat(), closeTo(geoShapeBottomRight.lat(), GEOHASH_TOLERANCE)); + MatcherAssert.assertThat(bottomRight.lon(), closeTo(geoShapeBottomRight.lon(), GEOHASH_TOLERANCE)); + } + + public void testEmptyAggregationOnGeoShapes() { + final SearchResponse searchResponse = client().prepareSearch(EMPTY_IDX_NAME) + .setQuery(matchAllQuery()) + .addAggregation(geoBounds(aggName).field(GEO_SHAPE_FIELD_NAME).wrapLongitude(false)) + .get(); + + MatcherAssert.assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); + final GeoBounds geoBounds = searchResponse.getAggregations().get(aggName); + MatcherAssert.assertThat(geoBounds, notNullValue()); + MatcherAssert.assertThat(geoBounds.getName(), equalTo(aggName)); + final GeoPoint topLeft = geoBounds.topLeft(); + final GeoPoint bottomRight = geoBounds.bottomRight(); + MatcherAssert.assertThat(topLeft, equalTo(null)); + MatcherAssert.assertThat(bottomRight, equalTo(null)); + } +} diff --git a/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoCentroidITTestCase.java b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoCentroidITTestCase.java new file mode 100644 index 0000000000000..2dc8a91600419 --- /dev/null +++ b/modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/metrics/GeoCentroidITTestCase.java @@ -0,0 +1,89 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.settings.Settings; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGrid; +import org.opensearch.geo.tests.common.AggregationBuilders; +import org.opensearch.search.aggregations.metrics.GeoCentroid; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.List; + +import static org.opensearch.search.aggregations.AggregationBuilders.geoCentroid; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; + +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class GeoCentroidITTestCase extends AbstractGeoAggregatorModulePluginTestCase { + private static final String aggName = "geoCentroid"; + + public GeoCentroidITTestCase(Settings dynamicSettings) { + super(dynamicSettings); + } + + public void testSingleValueFieldAsSubAggToGeohashGrid() throws Exception { + SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) + .addAggregation( + AggregationBuilders.geohashGrid("geoGrid") + .field(SINGLE_VALUED_FIELD_NAME) + .subAggregation(geoCentroid(aggName).field(SINGLE_VALUED_FIELD_NAME)) + ) + .get(); + assertSearchResponse(response); + + GeoGrid grid = response.getAggregations().get("geoGrid"); + assertThat(grid, notNullValue()); + assertThat(grid.getName(), equalTo("geoGrid")); + List buckets = grid.getBuckets(); + for (GeoGrid.Bucket cell : buckets) { + String geohash = cell.getKeyAsString(); + GeoPoint expectedCentroid = expectedCentroidsForGeoHash.get(geohash); + GeoCentroid centroidAgg = cell.getAggregations().get(aggName); + assertThat( + "Geohash " + geohash + " has wrong centroid latitude ", + expectedCentroid.lat(), + closeTo(centroidAgg.centroid().lat(), GEOHASH_TOLERANCE) + ); + assertThat( + "Geohash " + geohash + " has wrong centroid longitude", + expectedCentroid.lon(), + closeTo(centroidAgg.centroid().lon(), GEOHASH_TOLERANCE) + ); + } + } +} diff --git a/modules/geo/src/main/java/org/opensearch/geo/GeoModulePlugin.java b/modules/geo/src/main/java/org/opensearch/geo/GeoModulePlugin.java new file mode 100644 index 0000000000000..efee09d01d04e --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/GeoModulePlugin.java @@ -0,0 +1,106 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo; + +import org.opensearch.geo.search.aggregations.bucket.composite.GeoTileGridValuesSourceBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregator; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoHashGrid; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoTileGrid; +import org.opensearch.geo.search.aggregations.metrics.GeoBounds; +import org.opensearch.geo.search.aggregations.metrics.GeoBoundsAggregationBuilder; +import org.opensearch.geo.search.aggregations.metrics.InternalGeoBounds; +import org.opensearch.index.mapper.GeoShapeFieldMapper; +import org.opensearch.index.mapper.Mapper; +import org.opensearch.plugins.MapperPlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.plugins.SearchPlugin; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregation; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class GeoModulePlugin extends Plugin implements MapperPlugin, SearchPlugin { + + @Override + public Map getMappers() { + return Collections.singletonMap(GeoShapeFieldMapper.CONTENT_TYPE, new GeoShapeFieldMapper.TypeParser()); + } + + /** + * Registering {@link GeoBounds}, {@link InternalGeoHashGrid}, {@link InternalGeoTileGrid} aggregation on GeoPoint and GeoShape + * fields. + */ + @Override + public List getAggregations() { + final AggregationSpec geoBounds = new AggregationSpec( + GeoBoundsAggregationBuilder.NAME, + GeoBoundsAggregationBuilder::new, + GeoBoundsAggregationBuilder.PARSER + ).addResultReader(InternalGeoBounds::new).setAggregatorRegistrar(GeoBoundsAggregationBuilder::registerAggregators); + + final AggregationSpec geoHashGrid = new AggregationSpec( + GeoHashGridAggregationBuilder.NAME, + GeoHashGridAggregationBuilder::new, + GeoHashGridAggregationBuilder.PARSER + ).addResultReader(InternalGeoHashGrid::new).setAggregatorRegistrar(GeoHashGridAggregationBuilder::registerAggregators); + + final AggregationSpec geoTileGrid = new AggregationSpec( + GeoTileGridAggregationBuilder.NAME, + GeoTileGridAggregationBuilder::new, + GeoTileGridAggregationBuilder.PARSER + ).addResultReader(InternalGeoTileGrid::new).setAggregatorRegistrar(GeoTileGridAggregationBuilder::registerAggregators); + return List.of(geoBounds, geoHashGrid, geoTileGrid); + } + + /** + * Registering the {@link GeoTileGridAggregator} in the {@link CompositeAggregation}. + * + * @return a {@link List} of {@link CompositeAggregationSpec} + */ + @Override + public List getCompositeAggregations() { + return Collections.singletonList( + new CompositeAggregationSpec( + GeoTileGridValuesSourceBuilder::register, + GeoTileGridValuesSourceBuilder.class, + GeoTileGridValuesSourceBuilder.COMPOSITE_AGGREGATION_SERIALISATION_BYTE_CODE, + GeoTileGridValuesSourceBuilder::new, + GeoTileGridValuesSourceBuilder::parse, + GeoTileGridValuesSourceBuilder.TYPE + ) + ); + } +} diff --git a/modules/geo/src/main/java/org/opensearch/geo/GeoPlugin.java b/modules/geo/src/main/java/org/opensearch/geo/GeoPlugin.java deleted file mode 100644 index 9b898da33bb12..0000000000000 --- a/modules/geo/src/main/java/org/opensearch/geo/GeoPlugin.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.geo; - -import org.opensearch.index.mapper.GeoShapeFieldMapper; -import org.opensearch.index.mapper.Mapper; -import org.opensearch.plugins.MapperPlugin; -import org.opensearch.plugins.Plugin; - -import java.util.Collections; -import java.util.Map; - -public class GeoPlugin extends Plugin implements MapperPlugin { - - @Override - public Map getMappers() { - return Collections.singletonMap(GeoShapeFieldMapper.CONTENT_TYPE, new GeoShapeFieldMapper.TypeParser()); - } -} diff --git a/modules/geo/src/main/java/org/opensearch/geo/algorithm/PolygonGenerator.java b/modules/geo/src/main/java/org/opensearch/geo/algorithm/PolygonGenerator.java new file mode 100644 index 0000000000000..da1d97260ec96 --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/algorithm/PolygonGenerator.java @@ -0,0 +1,190 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.algorithm; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.core.common.util.CollectionUtils; + +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Random; +import java.util.stream.IntStream; + +/** + * Helper class to generate a polygon. Keeping this in the src folder so that GeoSpatial plugin can take advantage of + * this helper to create the Polygons, rather than hardcoding the values. + */ +public class PolygonGenerator { + + private static final Logger LOG = LogManager.getLogger(PolygonGenerator.class); + + /** + * A helper function to create the Polygons for testing. The returned list of double array where first element + * contains all the X points and second contains all the Y points. + * + * @param xPool a {@link java.util.List} of {@link Double} + * @param yPool a {@link java.util.List} of {@link Double} + * @return a {@link List} of double array. + */ + public static List generatePolygon(final List xPool, final List yPool, final Random random) { + if (CollectionUtils.isEmpty(xPool) || CollectionUtils.isEmpty(yPool)) { + LOG.debug("One of the X or Y list is empty or null. X.size : {} Y.size : {}", xPool, yPool); + return Collections.emptyList(); + } + final List generatedPolygonPointsList = ValtrAlgorithm.generateRandomConvexPolygon(xPool, yPool, random); + final double[] x = new double[generatedPolygonPointsList.size()]; + final double[] y = new double[generatedPolygonPointsList.size()]; + IntStream.range(0, generatedPolygonPointsList.size()).forEach(iterator -> { + x[iterator] = generatedPolygonPointsList.get(iterator).getX(); + y[iterator] = generatedPolygonPointsList.get(iterator).getY(); + }); + final List pointsList = new ArrayList<>(); + pointsList.add(x); + pointsList.add(y); + return pointsList; + } + + /* + * MIT License + * + * Copyright (c) 2017 Sander Verdonschot + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + /** + * Provides a helper function to create a Polygon with a list of points. This source code is used to create the + * polygons in the test cases. + * Reference Link + * Visual Link + */ + private static class ValtrAlgorithm { + /** + * Generates a convex polygon using the points provided as a {@link List} of {@link Double} for both X and Y axis. + * + * @param xPool a {@link List} of {@link Double} + * @param yPool a {@link List} of {@link Double} + * @return a {@link List} of {@link Point2D.Double} + */ + private static List generateRandomConvexPolygon( + final List xPool, + final List yPool, + final Random random + ) { + final int n = xPool.size(); + // Sort them + Collections.sort(xPool); + Collections.sort(yPool); + + // Isolate the extreme points + final Double minX = xPool.get(0); + final Double maxX = xPool.get(n - 1); + final Double minY = yPool.get(0); + final Double maxY = yPool.get(n - 1); + + // Divide the interior points into two chains & Extract the vector components + java.util.List xVec = new ArrayList<>(n); + java.util.List yVec = new ArrayList<>(n); + + double lastTop = minX, lastBot = minX; + + for (int i = 1; i < n - 1; i++) { + double x = xPool.get(i); + + if (random.nextBoolean()) { + xVec.add(x - lastTop); + lastTop = x; + } else { + xVec.add(lastBot - x); + lastBot = x; + } + } + + xVec.add(maxX - lastTop); + xVec.add(lastBot - maxX); + + double lastLeft = minY, lastRight = minY; + + for (int i = 1; i < n - 1; i++) { + double y = yPool.get(i); + + if (random.nextBoolean()) { + yVec.add(y - lastLeft); + lastLeft = y; + } else { + yVec.add(lastRight - y); + lastRight = y; + } + } + + yVec.add(maxY - lastLeft); + yVec.add(lastRight - maxY); + + // Randomly pair up the X- and Y-components + Collections.shuffle(yVec, random); + + // Combine the paired up components into vectors + List vec = new ArrayList<>(n); + + for (int i = 0; i < n; i++) { + vec.add(new Point2D.Double(xVec.get(i), yVec.get(i))); + } + + // Sort the vectors by angle + Collections.sort(vec, Comparator.comparingDouble(v -> Math.atan2(v.getY(), v.getX()))); + + // Lay them end-to-end + double x = 0, y = 0; + double minPolygonX = 0; + double minPolygonY = 0; + List points = new ArrayList<>(n); + + for (int i = 0; i < n; i++) { + points.add(new Point2D.Double(x, y)); + + x += vec.get(i).getX(); + y += vec.get(i).getY(); + + minPolygonX = Math.min(minPolygonX, x); + minPolygonY = Math.min(minPolygonY, y); + } + + // Move the polygon to the original min and max coordinates + double xShift = minX - minPolygonX; + double yShift = minY - minPolygonY; + + for (int i = 0; i < n; i++) { + Point2D.Double p = points.get(i); + points.set(i, new Point2D.Double(p.x + xShift, p.y + yShift)); + } + + return points; + } + } + +} diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java similarity index 82% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java index 5d47a44a26222..c6fcdf7c81ea8 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilder.java @@ -30,25 +30,28 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.composite; +package org.opensearch.geo.search.aggregations.bucket.composite; import org.apache.lucene.index.IndexReader; import org.opensearch.LegacyESVersion; -import org.opensearch.common.ParseField; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.util.BigArrays; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.cells.CellIdSource; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.DocValueFormat; -import org.opensearch.search.aggregations.bucket.geogrid.CellIdSource; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceConfig; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceParserHelper; import org.opensearch.search.aggregations.bucket.missing.MissingOrder; import org.opensearch.search.aggregations.support.CoreValuesSourceType; import org.opensearch.search.aggregations.support.ValuesSource; @@ -62,7 +65,17 @@ import java.util.function.LongConsumer; import java.util.function.LongUnaryOperator; +/** + * Builds values source for geotile_grid agg + * + * @opensearch.internal + */ public class GeoTileGridValuesSourceBuilder extends CompositeValuesSourceBuilder { + /** + * Supplier for a composite geotile + * + * @opensearch.internal + */ @FunctionalInterface public interface GeoTileCompositeSuppier { CompositeValuesSourceConfig apply( @@ -78,13 +91,19 @@ CompositeValuesSourceConfig apply( ); } - static final String TYPE = "geotile_grid"; + public static final String TYPE = "geotile_grid"; + /* + use the TYPE parameter instead of Byte code. The byte code is added for backward compatibility and will be + removed in the next version. + */ + @Deprecated + public static final Byte COMPOSITE_AGGREGATION_SERIALISATION_BYTE_CODE = 3; static final ValuesSourceRegistry.RegistryKey REGISTRY_KEY = new ValuesSourceRegistry.RegistryKey( TYPE, GeoTileCompositeSuppier.class ); - private static final ObjectParser PARSER; + static final ObjectParser PARSER; static { PARSER = new ObjectParser<>(GeoTileGridValuesSourceBuilder.TYPE); PARSER.declareInt(GeoTileGridValuesSourceBuilder::precision, new ParseField("precision")); @@ -96,11 +115,11 @@ CompositeValuesSourceConfig apply( CompositeValuesSourceParserHelper.declareValuesSourceFields(PARSER); } - static GeoTileGridValuesSourceBuilder parse(String name, XContentParser parser) throws IOException { + public static GeoTileGridValuesSourceBuilder parse(String name, XContentParser parser) throws IOException { return PARSER.parse(parser, new GeoTileGridValuesSourceBuilder(name), null); } - static void register(ValuesSourceRegistry.Builder builder) { + public static void register(ValuesSourceRegistry.Builder builder) { builder.register( REGISTRY_KEY, @@ -153,7 +172,7 @@ static void register(ValuesSourceRegistry.Builder builder) { super(name); } - GeoTileGridValuesSourceBuilder(StreamInput in) throws IOException { + public GeoTileGridValuesSourceBuilder(StreamInput in) throws IOException { super(in); this.precision = in.readInt(); if (in.getVersion().onOrAfter(LegacyESVersion.V_7_6_0)) { @@ -193,7 +212,7 @@ protected void doXContentBody(XContentBuilder builder, Params params) throws IOE } @Override - String type() { + protected String type() { return TYPE; } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileValuesSource.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileValuesSource.java similarity index 87% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileValuesSource.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileValuesSource.java index 2daddc9647f44..9149b8939b739 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/composite/GeoTileValuesSource.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileValuesSource.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.composite; +package org.opensearch.geo.search.aggregations.bucket.composite; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.SortedNumericDocValues; @@ -38,7 +38,9 @@ import org.opensearch.common.util.BigArrays; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.search.DocValueFormat; -import org.opensearch.search.aggregations.bucket.geogrid.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.composite.LongValuesSource; +import org.opensearch.search.aggregations.bucket.composite.SingleDimensionValuesSource; import org.opensearch.search.aggregations.bucket.missing.MissingOrder; import java.io.IOException; @@ -49,6 +51,8 @@ * * Since geotile values can be represented as long values, this class is almost the same as {@link LongValuesSource} * The main differences is {@link GeoTileValuesSource#setAfter(Comparable)} as it needs to accept geotile string values i.e. "zoom/x/y". + * + * @opensearch.internal */ class GeoTileValuesSource extends LongValuesSource { GeoTileValuesSource( @@ -66,7 +70,7 @@ class GeoTileValuesSource extends LongValuesSource { } @Override - void setAfter(Comparable value) { + protected void setAfter(Comparable value) { if (missingBucket && value == null) { afterValue = null; } else if (value instanceof Number) { diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BucketPriorityQueue.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BucketPriorityQueue.java similarity index 91% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BucketPriorityQueue.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BucketPriorityQueue.java index 3a8245a52adfc..70d0552b3e80b 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BucketPriorityQueue.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/BucketPriorityQueue.java @@ -29,10 +29,15 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.util.PriorityQueue; +/** + * Internal priority queue for computing geogrid relations + * + * @opensearch.internal + */ class BucketPriorityQueue extends PriorityQueue { BucketPriorityQueue(int size) { diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGrid.java similarity index 95% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGrid.java index 7521f9f58bd96..4ae888640efc8 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGrid.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.search.aggregations.bucket.MultiBucketsAggregation; @@ -38,6 +38,8 @@ /** * A geo-grid aggregation. Defines multiple buckets, each representing a cell in a geo-grid of a specific * precision. + * + * @opensearch.internal */ public interface GeoGrid extends MultiBucketsAggregation { diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java similarity index 93% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java index 5ec17c951d183..11be1bd70fa30 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java @@ -30,18 +30,18 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.common.ParseField; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.aggregations.AggregatorFactories.Builder; import org.opensearch.search.aggregations.AggregatorFactory; @@ -57,6 +57,11 @@ import java.util.Objects; import java.util.function.Function; +/** + * Base Aggregation Builder for geogrid aggs + * + * @opensearch.internal + */ public abstract class GeoGridAggregationBuilder extends ValuesSourceAggregationBuilder { /* recognized field names in JSON */ static final ParseField FIELD_PRECISION = new ParseField("precision"); @@ -68,6 +73,11 @@ public abstract class GeoGridAggregationBuilder extends ValuesSourceAggregationB protected int shardSize; private GeoBoundingBox geoBoundingBox = new GeoBoundingBox(new GeoPoint(Double.NaN, Double.NaN), new GeoPoint(Double.NaN, Double.NaN)); + /** + * A precision parser + * + * @opensearch.internal + */ @FunctionalInterface protected interface PrecisionParser { int parse(XContentParser parser) throws IOException; @@ -83,14 +93,14 @@ public static ObjectParser crea parser.declareField( (p, builder, context) -> builder.precision(precisionParser.parse(p)), FIELD_PRECISION, - org.opensearch.common.xcontent.ObjectParser.ValueType.INT + ObjectParser.ValueType.INT ); parser.declareInt(GeoGridAggregationBuilder::size, FIELD_SIZE); parser.declareInt(GeoGridAggregationBuilder::shardSize, FIELD_SHARD_SIZE); parser.declareField( (p, builder, context) -> { builder.setGeoBoundingBox(GeoBoundingBox.parseBoundingBox(p)); }, GeoBoundingBox.BOUNDS_FIELD, - org.opensearch.common.xcontent.ObjectParser.ValueType.OBJECT + ObjectParser.ValueType.OBJECT ); return parser; } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregator.java similarity index 95% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregator.java index 7eb05adf78e45..01f9f22be9e68 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregator.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregator.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.SortedNumericDocValues; @@ -54,6 +54,8 @@ /** * Aggregates data expressed as longs (for efficiency's sake) but formats results as aggregation-specific strings. + * + * @opensearch.internal */ public abstract class GeoGridAggregator extends BucketsAggregator { @@ -62,7 +64,7 @@ public abstract class GeoGridAggregator extends Bucke protected final ValuesSource.Numeric valuesSource; protected final LongKeyedBucketOrds bucketOrds; - GeoGridAggregator( + protected GeoGridAggregator( String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, @@ -116,14 +118,14 @@ public void collect(int doc, long owningBucketOrd) throws IOException { }; } - abstract T buildAggregation(String name, int requiredSize, List buckets, Map metadata); + protected abstract T buildAggregation(String name, int requiredSize, List buckets, Map metadata); /** * This method is used to return a re-usable instance of the bucket when building * the aggregation. * @return a new {@link InternalGeoGridBucket} implementation with empty parameters */ - abstract InternalGeoGridBucket newEmptyBucket(); + protected abstract InternalGeoGridBucket newEmptyBucket(); @Override public InternalAggregation[] buildAggregations(long[] owningBucketOrds) throws IOException { diff --git a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoGridAggregatorSupplier.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorSupplier.java similarity index 92% rename from server/src/main/java/org/opensearch/search/aggregations/metrics/GeoGridAggregatorSupplier.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorSupplier.java index 3c29c3558eb35..0ef1957f88ef6 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoGridAggregatorSupplier.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorSupplier.java @@ -30,19 +30,23 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.CardinalityUpperBound; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGridAggregator; import org.opensearch.search.aggregations.support.ValuesSource; import org.opensearch.search.internal.SearchContext; import java.io.IOException; import java.util.Map; +/** + * Base Aggregator supplier interface for geo grid aggs + * + * @opensearch.internal + */ @FunctionalInterface public interface GeoGridAggregatorSupplier { GeoGridAggregator build( diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java similarity index 94% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java index a1ae76eb034d0..57f1eceabd3da 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregationBuilder.java @@ -30,17 +30,16 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.common.geo.GeoUtils; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ObjectParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ObjectParser; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.aggregations.AggregationBuilder; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.AggregatorFactory; -import org.opensearch.search.aggregations.metrics.GeoGridAggregatorSupplier; import org.opensearch.search.aggregations.support.ValuesSourceAggregatorFactory; import org.opensearch.search.aggregations.support.ValuesSourceConfig; import org.opensearch.search.aggregations.support.ValuesSourceRegistry; @@ -48,6 +47,11 @@ import java.io.IOException; import java.util.Map; +/** + * Aggregation Builder for geohash_grid + * + * @opensearch.internal + */ public class GeoHashGridAggregationBuilder extends GeoGridAggregationBuilder { public static final String NAME = "geohash_grid"; public static final int DEFAULT_PRECISION = 5; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java similarity index 87% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java index 74c8170458734..16bd8b5a42f5f 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregator.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; @@ -44,6 +44,8 @@ /** * Aggregates data expressed as GeoHash longs (for efficiency's sake) but formats results as Geohash strings. + * + * @opensearch.internal */ public class GeoHashGridAggregator extends GeoGridAggregator { @@ -62,7 +64,12 @@ public GeoHashGridAggregator( } @Override - InternalGeoHashGrid buildAggregation(String name, int requiredSize, List buckets, Map metadata) { + protected InternalGeoHashGrid buildAggregation( + String name, + int requiredSize, + List buckets, + Map metadata + ) { return new InternalGeoHashGrid(name, requiredSize, buckets, metadata); } @@ -71,7 +78,8 @@ public InternalGeoHashGrid buildEmptyAggregation() { return new InternalGeoHashGrid(name, requiredSize, Collections.emptyList(), metadata()); } - InternalGeoGridBucket newEmptyBucket() { + @Override + protected InternalGeoGridBucket newEmptyBucket() { return new InternalGeoHashGridBucket(0, 0, null); } } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java similarity index 76% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java index a52ee2018fdfe..b95b47a630916 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorFactory.java @@ -30,9 +30,12 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.geo.search.aggregations.bucket.geogrid.cells.CellIdSource; +import org.opensearch.geo.search.aggregations.bucket.geogrid.cells.GeoShapeCellIdSource; +import org.opensearch.geo.search.aggregations.bucket.geogrid.util.GeoShapeHashUtil; import org.opensearch.geometry.utils.Geohash; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.aggregations.Aggregator; @@ -53,6 +56,11 @@ import static java.util.Collections.emptyList; +/** + * Aggregation Factory for geohash_grid agg + * + * @opensearch.internal + */ public class GeoHashGridAggregatorFactory extends ValuesSourceAggregatorFactory { private final int precision; @@ -115,6 +123,7 @@ protected Aggregator doCreateInternal( } static void registerAggregators(ValuesSourceRegistry.Builder builder) { + // register GeoPoint Aggregation builder.register( GeoHashGridAggregationBuilder.REGISTRY_KEY, CoreValuesSourceType.GEOPOINT, @@ -150,5 +159,46 @@ static void registerAggregators(ValuesSourceRegistry.Builder builder) { }, true ); + // register GeoShape Aggregation + builder.register( + GeoHashGridAggregationBuilder.REGISTRY_KEY, + CoreValuesSourceType.GEO_SHAPE, + ( + name, + factories, + valuesSource, + precision, + geoBoundingBox, + requiredSize, + shardSize, + aggregationContext, + parent, + cardinality, + metadata) -> { + final GeoShapeCellIdSource cellIdSource = new GeoShapeCellIdSource( + (ValuesSource.GeoShape) valuesSource, + precision, + geoBoundingBox, + GeoShapeHashUtil::encodeShape + ); + return new GeoHashGridAggregator( + name, + factories, + cellIdSource, + requiredSize, + shardSize, + aggregationContext, + parent, + cardinality, + metadata + ); + }, + true + ); + } + + @Override + protected boolean supportsConcurrentSegmentSearch() { + return true; } } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java similarity index 93% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java index 6828077cd4778..3454e5491617d 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregationBuilder.java @@ -30,16 +30,16 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ObjectParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ObjectParser; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.aggregations.AggregationBuilder; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.AggregatorFactory; -import org.opensearch.search.aggregations.metrics.GeoGridAggregatorSupplier; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.search.aggregations.support.ValuesSourceAggregatorFactory; import org.opensearch.search.aggregations.support.ValuesSourceConfig; import org.opensearch.search.aggregations.support.ValuesSourceRegistry; @@ -47,6 +47,11 @@ import java.io.IOException; import java.util.Map; +/** + * Aggregation Builder for geotile_grid agg + * + * @opensearch.internal + */ public class GeoTileGridAggregationBuilder extends GeoGridAggregationBuilder { public static final String NAME = "geotile_grid"; public static final int DEFAULT_PRECISION = 7; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java similarity index 87% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java index 390f613686272..f4492d561aa7f 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregator.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; @@ -45,6 +45,8 @@ /** * Aggregates data expressed as geotile longs (for efficiency's sake) but formats results as geotile strings. + * + * @opensearch.internal */ public class GeoTileGridAggregator extends GeoGridAggregator { @@ -63,7 +65,12 @@ public GeoTileGridAggregator( } @Override - InternalGeoTileGrid buildAggregation(String name, int requiredSize, List buckets, Map metadata) { + protected InternalGeoTileGrid buildAggregation( + String name, + int requiredSize, + List buckets, + Map metadata + ) { return new InternalGeoTileGrid(name, requiredSize, buckets, metadata); } @@ -72,7 +79,8 @@ public InternalGeoTileGrid buildEmptyAggregation() { return new InternalGeoTileGrid(name, requiredSize, Collections.emptyList(), metadata()); } - InternalGeoGridBucket newEmptyBucket() { + @Override + protected InternalGeoGridBucket newEmptyBucket() { return new InternalGeoTileGridBucket(0, 0, null); } } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java similarity index 76% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java index 616451474abc4..7253f45d2db10 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorFactory.java @@ -30,9 +30,11 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.geo.search.aggregations.bucket.geogrid.cells.CellIdSource; +import org.opensearch.geo.search.aggregations.bucket.geogrid.cells.GeoShapeCellIdSource; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; @@ -40,6 +42,7 @@ import org.opensearch.search.aggregations.CardinalityUpperBound; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.NonCollectingAggregator; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.search.aggregations.support.CoreValuesSourceType; import org.opensearch.search.aggregations.support.ValuesSource; import org.opensearch.search.aggregations.support.ValuesSourceAggregatorFactory; @@ -51,6 +54,11 @@ import java.util.Collections; import java.util.Map; +/** + * Aggregation Factory for geo_tile grid agg + * + * @opensearch.internal + */ public class GeoTileGridAggregatorFactory extends ValuesSourceAggregatorFactory { private final int precision; @@ -148,5 +156,47 @@ static void registerAggregators(ValuesSourceRegistry.Builder builder) { }, true ); + + // registers Aggregation on GeoShape + builder.register( + GeoTileGridAggregationBuilder.REGISTRY_KEY, + CoreValuesSourceType.GEO_SHAPE, + ( + name, + factories, + valuesSource, + precision, + geoBoundingBox, + requiredSize, + shardSize, + aggregationContext, + parent, + cardinality, + metadata) -> { + GeoShapeCellIdSource cellIdSource = new GeoShapeCellIdSource( + (ValuesSource.GeoShape) valuesSource, + precision, + geoBoundingBox, + GeoTileUtils::encodeShape + ); + return new GeoTileGridAggregator( + name, + factories, + cellIdSource, + requiredSize, + shardSize, + aggregationContext, + parent, + cardinality, + metadata + ); + }, + true + ); + } + + @Override + protected boolean supportsConcurrentSegmentSearch() { + return true; } } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGrid.java similarity index 89% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGrid.java index e7fed7c735b25..00b7926641659 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGrid.java @@ -29,13 +29,12 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.util.LongObjectPagedHashMap; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.InternalMultiBucketAggregation; @@ -53,6 +52,8 @@ * Represents a grid of cells where each cell's location is determined by a specific geo hashing algorithm. * All geo-grid hash-encoding in a grid are of the same precision and held internally as a single long * for efficiency's sake. + * + * @opensearch.internal */ public abstract class InternalGeoGrid extends InternalMultiBucketAggregation< InternalGeoGrid, @@ -61,13 +62,13 @@ public abstract class InternalGeoGrid extends I protected final int requiredSize; protected final List buckets; - InternalGeoGrid(String name, int requiredSize, List buckets, Map metadata) { + protected InternalGeoGrid(String name, int requiredSize, List buckets, Map metadata) { super(name, metadata); this.requiredSize = requiredSize; this.buckets = buckets; } - abstract Writeable.Reader getBucketReader(); + protected abstract Reader getBucketReader(); /** * Read from a stream. @@ -84,7 +85,12 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeList(buckets); } - abstract InternalGeoGrid create(String name, int requiredSize, List buckets, Map metadata); + protected abstract InternalGeoGrid create( + String name, + int requiredSize, + List buckets, + Map metadata + ); @Override public List getBuckets() { @@ -138,7 +144,7 @@ protected InternalGeoGridBucket reduceBucket(List buckets return createBucket(buckets.get(0).hashAsLong, docCount, aggs); } - abstract B createBucket(long hashAsLong, long docCount, InternalAggregations aggregations); + protected abstract B createBucket(long hashAsLong, long docCount, InternalAggregations aggregations); @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java similarity index 91% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java index 0ddfed84fbe99..148e2d14bb643 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoGridBucket.java @@ -29,11 +29,11 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.Aggregations; import org.opensearch.search.aggregations.InternalAggregations; @@ -42,6 +42,11 @@ import java.io.IOException; import java.util.Objects; +/** + * Base implementation of geogrid aggs + * + * @opensearch.internal + */ public abstract class InternalGeoGridBucket extends InternalMultiBucketAggregation.InternalBucket implements GeoGrid.Bucket, @@ -75,7 +80,7 @@ public void writeTo(StreamOutput out) throws IOException { aggregations.writeTo(out); } - long hashAsLong() { + public long hashAsLong() { return hashAsLong; } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java similarity index 85% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java index bae4f99d3bb5b..5f8f7aabf8e99 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java @@ -29,9 +29,9 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.search.aggregations.InternalAggregations; import java.io.IOException; @@ -42,6 +42,8 @@ * Represents a grid of cells where each cell's location is determined by a geohash. * All geohashes in a grid are of the same precision and held internally as a single long * for efficiency's sake. + * + * @opensearch.internal */ public class InternalGeoHashGrid extends InternalGeoGrid { @@ -64,17 +66,17 @@ public InternalGeoGridBucket createBucket(InternalAggregations aggregations, Int } @Override - InternalGeoGrid create(String name, int requiredSize, List buckets, Map metadata) { + protected InternalGeoGrid create(String name, int requiredSize, List buckets, Map metadata) { return new InternalGeoHashGrid(name, requiredSize, buckets, metadata); } @Override - InternalGeoHashGridBucket createBucket(long hashAsLong, long docCount, InternalAggregations aggregations) { + protected InternalGeoHashGridBucket createBucket(long hashAsLong, long docCount, InternalAggregations aggregations) { return new InternalGeoHashGridBucket(hashAsLong, docCount, aggregations); } @Override - Reader getBucketReader() { + protected Reader getBucketReader() { return InternalGeoHashGridBucket::new; } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java similarity index 90% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java index a5bead1f17280..962053ec1cf7c 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoHashGridBucket.java @@ -29,15 +29,20 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.geometry.utils.Geohash; import org.opensearch.search.aggregations.InternalAggregations; import java.io.IOException; +/** + * Implementation of geohash grid bucket + * + * @opensearch.internal + */ public class InternalGeoHashGridBucket extends InternalGeoGridBucket { InternalGeoHashGridBucket(long hashAsLong, long docCount, InternalAggregations aggregations) { super(hashAsLong, docCount, aggregations); diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java similarity index 85% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java index d7ff2c0a2cd33..1ff6cc6547575 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGrid.java @@ -29,9 +29,9 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.search.aggregations.InternalAggregations; import java.io.IOException; @@ -42,6 +42,8 @@ * Represents a grid of cells where each cell's location is determined by a geohash. * All geohashes in a grid are of the same precision and held internally as a single long * for efficiency's sake. + * + * @opensearch.internal */ public class InternalGeoTileGrid extends InternalGeoGrid { @@ -64,17 +66,17 @@ public InternalGeoGridBucket createBucket(InternalAggregations aggregations, Int } @Override - InternalGeoGrid create(String name, int requiredSize, List buckets, Map metadata) { + protected InternalGeoGrid create(String name, int requiredSize, List buckets, Map metadata) { return new InternalGeoTileGrid(name, requiredSize, buckets, metadata); } @Override - InternalGeoTileGridBucket createBucket(long hashAsLong, long docCount, InternalAggregations aggregations) { + protected InternalGeoTileGridBucket createBucket(long hashAsLong, long docCount, InternalAggregations aggregations) { return new InternalGeoTileGridBucket(hashAsLong, docCount, aggregations); } @Override - Reader getBucketReader() { + protected Reader getBucketReader() { return InternalGeoTileGridBucket::new; } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java similarity index 87% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java index b08ef55a71f5b..a448cb162aacb 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/InternalGeoTileGridBucket.java @@ -30,14 +30,20 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.search.aggregations.InternalAggregations; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import java.io.IOException; +/** + * Implementation of geotile grid bucket + * + * @opensearch.internal + */ public class InternalGeoTileGridBucket extends InternalGeoGridBucket { InternalGeoTileGridBucket(long hashAsLong, long docCount, InternalAggregations aggregations) { super(hashAsLong, docCount, aggregations); diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGrid.java similarity index 88% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGrid.java index 59681a52b1c01..89bc917a0b023 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGrid.java @@ -30,17 +30,22 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.aggregations.ParsedMultiBucketAggregation; import java.io.IOException; import java.util.List; import java.util.function.Supplier; +/** + * A geo grid result parsed between nodes + * + * @opensearch.internal + */ public abstract class ParsedGeoGrid extends ParsedMultiBucketAggregation implements GeoGrid { @Override @@ -58,7 +63,7 @@ public static ObjectParser createParser( return parser; } - protected void setName(String name) { + public void setName(String name) { super.setName(name); } } diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java similarity index 88% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java index f09eb46fd8a48..47b212dca5744 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoGridBucket.java @@ -29,14 +29,19 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.ParsedMultiBucketAggregation; import java.io.IOException; +/** + * A single geo grid bucket result parsed between nodes + * + * @opensearch.internal + */ public abstract class ParsedGeoGridBucket extends ParsedMultiBucketAggregation.ParsedBucket implements GeoGrid.Bucket { protected String hashAsString; diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java similarity index 87% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java index 189bfb4b58c92..cc5ed1c0bff16 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGrid.java @@ -30,13 +30,18 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; +/** + * A geohash grid result parsed between nodes + * + * @opensearch.internal + */ public class ParsedGeoHashGrid extends ParsedGeoGrid { private static final ObjectParser PARSER = createParser( diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java similarity index 88% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java index 3ed6cfe41ba77..6932cc74d3c1f 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoHashGridBucket.java @@ -29,13 +29,18 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; +/** + * A single geohash grid bucket result parsed between nodes + * + * @opensearch.internal + */ public class ParsedGeoHashGridBucket extends ParsedGeoGridBucket { @Override diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java similarity index 87% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java index 011fe2c9102c7..c136ad3fac17d 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGrid.java @@ -30,13 +30,18 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; +/** + * A geotile grid result parsed between nodes + * + * @opensearch.internal + */ public class ParsedGeoTileGrid extends ParsedGeoGrid { private static final ObjectParser PARSER = createParser( diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java similarity index 85% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java index 5b2bdd163d8f2..3f43ef6cdd5a8 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/ParsedGeoTileGridBucket.java @@ -30,13 +30,19 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import java.io.IOException; +/** + * A single geotile grid bucket result parsed between nodes + * + * @opensearch.internal + */ public class ParsedGeoTileGridBucket extends ParsedGeoGridBucket { @Override diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BoundedCellValues.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/BoundedCellValues.java similarity index 95% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BoundedCellValues.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/BoundedCellValues.java index cc4f5835cabf1..588c8bc59c2e0 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/BoundedCellValues.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/BoundedCellValues.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid.cells; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.index.fielddata.MultiGeoPointValues; @@ -39,6 +39,8 @@ * according to whether they are within the specified {@link GeoBoundingBox}. * * The specified bounding box is assumed to be bounded. + * + * @opensearch.internal */ class BoundedCellValues extends CellValues { diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellIdSource.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/CellIdSource.java similarity index 93% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellIdSource.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/CellIdSource.java index c9ea7696cd7f5..42c4722e065af 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellIdSource.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/CellIdSource.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid.cells; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.SortedNumericDocValues; @@ -42,6 +42,8 @@ /** * Wrapper class to help convert {@link MultiGeoPointValues} * to numeric long values for bucketing. + * + * @opensearch.internal */ public class CellIdSource extends ValuesSource.Numeric { private final ValuesSource.GeoPoint valuesSource; @@ -66,6 +68,11 @@ public boolean isFloatingPoint() { return false; } + @Override + public boolean isBigInteger() { + return false; + } + @Override public SortedNumericDocValues longValues(LeafReaderContext ctx) { if (geoBoundingBox.isUnbounded()) { @@ -87,6 +94,8 @@ public SortedBinaryDocValues bytesValues(LeafReaderContext ctx) { /** * The encoder to use to convert a geopoint's (lon, lat, precision) into * a long-encoded bucket key for aggregating. + * + * @opensearch.internal */ @FunctionalInterface public interface GeoPointLongEncoder { diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellValues.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/CellValues.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellValues.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/CellValues.java index 2e8344f203a39..0b69040ec977a 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/CellValues.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/CellValues.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid.cells; import org.opensearch.index.fielddata.AbstractSortingNumericDocValues; import org.opensearch.index.fielddata.MultiGeoPointValues; @@ -40,6 +40,8 @@ * Class representing the long-encoded grid-cells belonging to * the geo-doc-values. Class must encode the values and then * sort them in order to account for the cells correctly. + * + * @opensearch.internal */ abstract class CellValues extends AbstractSortingNumericDocValues { private MultiGeoPointValues geoValues; diff --git a/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/GeoShapeCellIdSource.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/GeoShapeCellIdSource.java new file mode 100644 index 0000000000000..0ea4d96c450ec --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/GeoShapeCellIdSource.java @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket.geogrid.cells; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.SortedNumericDocValues; +import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.common.geo.GeoShapeDocValue; +import org.opensearch.index.fielddata.GeoShapeValue; +import org.opensearch.index.fielddata.SortedBinaryDocValues; +import org.opensearch.index.fielddata.SortedNumericDoubleValues; +import org.opensearch.search.aggregations.support.ValuesSource; + +import java.io.IOException; +import java.util.List; + +/** + * ValueSource class which converts the {@link GeoShapeValue} to numeric long values for bucketing. This class uses the + * {@link GeoShapeCellIdSource.GeoShapeLongEncoder} to encode the geo_shape to {@link Long} values which can be iterated + * to do the bucket aggregation. + * + * @opensearch.internal + */ +public class GeoShapeCellIdSource extends ValuesSource.Numeric { + + private final ValuesSource.GeoShape geoShape; + private final int precision; + private final GeoBoundingBox geoBoundingBox; + private final GeoShapeCellIdSource.GeoShapeLongEncoder encoder; + + public GeoShapeCellIdSource( + final ValuesSource.GeoShape geoShape, + final int precision, + final GeoBoundingBox geoBoundingBox, + final GeoShapeCellIdSource.GeoShapeLongEncoder encoder + ) { + this.geoShape = geoShape; + this.geoBoundingBox = geoBoundingBox; + this.precision = precision; + this.encoder = encoder; + } + + /** + * Get the current {@link SortedBinaryDocValues}. + * + * @param context {@link LeafReaderContext} + */ + @Override + public SortedBinaryDocValues bytesValues(LeafReaderContext context) throws IOException { + throw new UnsupportedOperationException("The bytesValues operation is not supported on GeoShapeCellIdSource"); + } + + /** + * Whether the underlying data is floating-point or not. + */ + @Override + public boolean isFloatingPoint() { + return false; + } + + /** + * Whether the underlying data is big integer or not. + */ + @Override + public boolean isBigInteger() { + return false; + } + + /** + * Get the current {@link SortedNumericDocValues}. + * + * @param context {@link LeafReaderContext} + */ + @Override + public SortedNumericDocValues longValues(final LeafReaderContext context) { + if (geoBoundingBox.isUnbounded()) { + return new GeoShapeCellValues.UnboundedCellValues(geoShape.getGeoShapeValues(context), precision, encoder); + } + return new GeoShapeCellValues.BoundedCellValues(geoShape.getGeoShapeValues(context), precision, encoder, geoBoundingBox); + } + + /** + * Get the current {@link SortedNumericDoubleValues}. + * + * @param context {@link LeafReaderContext} + */ + @Override + public SortedNumericDoubleValues doubleValues(LeafReaderContext context) { + throw new UnsupportedOperationException("The doubleValues operation is not supported on GeoShapeCellIdSource"); + } + + /** + * Encoder to encode the GeoShapes to the specific long values for the aggregation. + * + * @opensearch.internal + */ + @FunctionalInterface + public interface GeoShapeLongEncoder { + List encode(final GeoShapeDocValue geoShapeDocValue, final int precision); + } +} diff --git a/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/GeoShapeCellValues.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/GeoShapeCellValues.java new file mode 100644 index 0000000000000..123a98fab3713 --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/GeoShapeCellValues.java @@ -0,0 +1,127 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket.geogrid.cells; + +import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.common.geo.GeoShapeDocValue; +import org.opensearch.geometry.Rectangle; +import org.opensearch.index.fielddata.AbstractSortingNumericDocValues; +import org.opensearch.index.fielddata.GeoShapeValue; + +import java.io.IOException; +import java.util.List; + +/** + * Class representing the long-encoded grid-cells belonging to the geoshape-doc-values. Class must encode the values + * as long and then sort them in order to account for the cells correctly. + * + * @opensearch.internal + */ +abstract class GeoShapeCellValues extends AbstractSortingNumericDocValues { + private final GeoShapeValue geoShapeValue; + protected int precision; + protected final GeoShapeCellIdSource.GeoShapeLongEncoder encoder; + + public GeoShapeCellValues(GeoShapeValue geoShapeValue, int precision, GeoShapeCellIdSource.GeoShapeLongEncoder encoder) { + this.geoShapeValue = geoShapeValue; + this.precision = precision; + this.encoder = encoder; + } + + @Override + public boolean advanceExact(int docId) throws IOException { + if (geoShapeValue.advanceExact(docId)) { + final GeoShapeDocValue geoShapeDocValue = geoShapeValue.nextValue(); + relateShape(geoShapeDocValue); + sort(); + return true; + } + return false; + } + + /** + * This function relates the shape's with the grid, and then put the intersecting grid's info as long, which + * can be iterated in the aggregation. It uses the encoder to find the relation. + * + * @param geoShapeDocValue {@link GeoShapeDocValue} + */ + abstract void relateShape(final GeoShapeDocValue geoShapeDocValue); + + /** + * Provides the {@link GeoShapeCellValues} for the input bounding box. + * @opensearch.internal + */ + static class BoundedCellValues extends GeoShapeCellValues { + private final Rectangle geoBoundingBox; + + public BoundedCellValues( + final GeoShapeValue geoShapeValue, + final int precision, + final GeoShapeCellIdSource.GeoShapeLongEncoder encoder, + final GeoBoundingBox boundingBox + ) { + super(geoShapeValue, precision, encoder); + this.geoBoundingBox = new Rectangle(boundingBox.left(), boundingBox.right(), boundingBox.top(), boundingBox.bottom()); + } + + /** + * This function relates the shape's with the grid, and then put the intersecting grid's info as long, which + * can be iterated in the aggregation. It uses the encoder to find the relation. + * + * @param geoShapeDocValue {@link GeoShapeDocValue} + */ + @Override + void relateShape(final GeoShapeDocValue geoShapeDocValue) { + if (geoShapeDocValue.isIntersectingRectangle(geoBoundingBox)) { + // now we know the shape is in the bounding rectangle, we need add them in longValues + // generate all grid that this shape intersects + final List encodedValues = encoder.encode(geoShapeDocValue, precision); + resize(encodedValues.size()); + for (int i = 0; i < encodedValues.size(); i++) { + values[i] = encodedValues.get(i); + } + } else { + // As the shape is not intersecting with GeoBounding box, we need to reset the GeoShapeCellValues + // calling this function resets the CellValues for the current shape. + resize(0); + } + } + + } + + /** + * Provides the {@link GeoShapeCellValues} for unbounded cells + * @opensearch.internal + */ + static class UnboundedCellValues extends GeoShapeCellValues { + + public UnboundedCellValues( + final GeoShapeValue geoShapeValue, + final int precision, + final GeoShapeCellIdSource.GeoShapeLongEncoder encoder + ) { + super(geoShapeValue, precision, encoder); + } + + /** + * This function relates the shape's with the grid, and then put the intersecting grid's info as long, which + * can be iterated in the aggregation. It uses the encoder to find the relation. + * + * @param geoShapeDocValue {@link GeoShapeDocValue} + */ + @Override + void relateShape(final GeoShapeDocValue geoShapeDocValue) { + final List encodedValues = encoder.encode(geoShapeDocValue, precision); + resize(encodedValues.size()); + for (int i = 0; i < encodedValues.size(); i++) { + values[i] = encodedValues.get(i); + } + } + } +} diff --git a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/UnboundedCellValues.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/UnboundedCellValues.java similarity index 94% rename from server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/UnboundedCellValues.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/UnboundedCellValues.java index 7dd7f155fb46e..0a520c7162002 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/bucket/geogrid/UnboundedCellValues.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/UnboundedCellValues.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid.cells; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.index.fielddata.MultiGeoPointValues; @@ -37,6 +37,8 @@ /** * Class representing {@link CellValues} that are unbounded by any * {@link GeoBoundingBox}. + * + * @opensearch.internal */ class UnboundedCellValues extends CellValues { diff --git a/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/package-info.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/package-info.java new file mode 100644 index 0000000000000..16a5dd11f6210 --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/cells/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * A Cells package which provide the different grid cells related functionalities for different aggregations + */ +package org.opensearch.geo.search.aggregations.bucket.geogrid.cells; diff --git a/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/package-info.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/package-info.java new file mode 100644 index 0000000000000..d9183a0f742ef --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** geo_grid Aggregation package. */ +package org.opensearch.geo.search.aggregations.bucket.geogrid; diff --git a/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/util/GeoShapeHashUtil.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/util/GeoShapeHashUtil.java new file mode 100644 index 0000000000000..aefb31e623bb5 --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/bucket/geogrid/util/GeoShapeHashUtil.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket.geogrid.util; + +import org.opensearch.common.geo.GeoShapeDocValue; +import org.opensearch.geometry.Rectangle; +import org.opensearch.geometry.utils.Geohash; + +import java.util.ArrayList; +import java.util.List; + +/** + * We have a {@link Geohash} class present at the libs level, not using that because while encoding the shapes we need + * {@link GeoShapeDocValue}. This class provided the utilities encode the shape as GeoHashes + */ +public class GeoShapeHashUtil { + + /** + * The function encodes the shape provided as {@link GeoShapeDocValue} to a {@link List} of {@link Long} values + * (representing the GeoHashes) which are intersecting with the shapes at a given precision. + * + * @param geoShapeDocValue {@link GeoShapeDocValue} + * @param precision int + * @return {@link List} containing encoded {@link Long} values + */ + public static List encodeShape(final GeoShapeDocValue geoShapeDocValue, final int precision) { + final List encodedValues = new ArrayList<>(); + final GeoShapeDocValue.BoundingRectangle boundingRectangle = geoShapeDocValue.getBoundingRectangle(); + long topLeftGeoHash = Geohash.longEncode(boundingRectangle.getMinX(), boundingRectangle.getMaxY(), precision); + long topRightGeoHash = Geohash.longEncode(boundingRectangle.getMaxX(), boundingRectangle.getMaxY(), precision); + long bottomRightGeoHash = Geohash.longEncode(boundingRectangle.getMaxX(), boundingRectangle.getMinY(), precision); + + long currentValue = topLeftGeoHash; + long rightMax = topRightGeoHash; + long tempCurrent = currentValue; + while (true) { + // check if this currentValue intersect with shape. + final Rectangle geohashRectangle = Geohash.toBoundingBox(Geohash.stringEncode(tempCurrent)); + if (geoShapeDocValue.isIntersectingRectangle(geohashRectangle)) { + encodedValues.add(tempCurrent); + } + + // Breaking condition + if (tempCurrent == bottomRightGeoHash) { + break; + } + // now change the iterator => tempCurrent + if (tempCurrent == rightMax) { + // move to next row + tempCurrent = Geohash.longEncode(Geohash.getNeighbor(Geohash.stringEncode(currentValue), precision, 0, -1)); + currentValue = tempCurrent; + // update right max + rightMax = Geohash.longEncode(Geohash.getNeighbor(Geohash.stringEncode(rightMax), precision, 0, -1)); + } else { + // move to next column + tempCurrent = Geohash.longEncode(Geohash.getNeighbor(Geohash.stringEncode(tempCurrent), precision, 1, 0)); + } + } + return encodedValues; + } +} diff --git a/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoBoundsAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoBoundsAggregator.java new file mode 100644 index 0000000000000..4a39fa1da04eb --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/AbstractGeoBoundsAggregator.java @@ -0,0 +1,128 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.opensearch.common.lease.Releasables; +import org.opensearch.common.util.BigArrays; +import org.opensearch.common.util.DoubleArray; +import org.opensearch.search.aggregations.Aggregator; +import org.opensearch.search.aggregations.InternalAggregation; +import org.opensearch.search.aggregations.metrics.MetricsAggregator; +import org.opensearch.search.aggregations.support.ValuesSource; +import org.opensearch.search.aggregations.support.ValuesSourceConfig; +import org.opensearch.search.internal.SearchContext; + +import java.io.IOException; +import java.util.Map; + +/** + * Abstract class for doing the {@link GeoBounds} Aggregation over fields of type geo_shape and geo_point. + * + * @param Class extending the {@link ValuesSource} which will provide the data on which aggregation will happen. + * @opensearch.internal + */ +public abstract class AbstractGeoBoundsAggregator extends MetricsAggregator { + + protected final T valuesSource; + protected final boolean wrapLongitude; + protected DoubleArray tops; + protected DoubleArray bottoms; + protected DoubleArray posLefts; + protected DoubleArray posRights; + protected DoubleArray negLefts; + protected DoubleArray negRights; + + @SuppressWarnings("unchecked") + protected AbstractGeoBoundsAggregator( + String name, + SearchContext searchContext, + Aggregator aggregator, + ValuesSourceConfig valuesSourceConfig, + boolean wrapLongitude, + Map metaData + ) throws IOException { + super(name, searchContext, aggregator, metaData); + this.wrapLongitude = wrapLongitude; + valuesSource = valuesSourceConfig.hasValues() ? (T) valuesSourceConfig.getValuesSource() : null; + + if (valuesSource != null) { + final BigArrays bigArrays = context.bigArrays(); + tops = bigArrays.newDoubleArray(1, false); + tops.fill(0, tops.size(), Double.NEGATIVE_INFINITY); + bottoms = bigArrays.newDoubleArray(1, false); + bottoms.fill(0, bottoms.size(), Double.POSITIVE_INFINITY); + posLefts = bigArrays.newDoubleArray(1, false); + posLefts.fill(0, posLefts.size(), Double.POSITIVE_INFINITY); + posRights = bigArrays.newDoubleArray(1, false); + posRights.fill(0, posRights.size(), Double.NEGATIVE_INFINITY); + negLefts = bigArrays.newDoubleArray(1, false); + negLefts.fill(0, negLefts.size(), Double.POSITIVE_INFINITY); + negRights = bigArrays.newDoubleArray(1, false); + negRights.fill(0, negRights.size(), Double.NEGATIVE_INFINITY); + } + } + + /** + * Build an empty aggregation. + */ + @Override + public InternalAggregation buildEmptyAggregation() { + return new InternalGeoBounds( + name, + Double.NEGATIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + wrapLongitude, + metadata() + ); + } + + /** + * Build an aggregation for data that has been collected into owningBucketOrd. + */ + @Override + public InternalAggregation buildAggregation(long owningBucketOrdinal) throws IOException { + if (valuesSource == null) { + return buildEmptyAggregation(); + } + double top = tops.get(owningBucketOrdinal); + double bottom = bottoms.get(owningBucketOrdinal); + double posLeft = posLefts.get(owningBucketOrdinal); + double posRight = posRights.get(owningBucketOrdinal); + double negLeft = negLefts.get(owningBucketOrdinal); + double negRight = negRights.get(owningBucketOrdinal); + return new InternalGeoBounds(name, top, bottom, posLeft, posRight, negLeft, negRight, wrapLongitude, metadata()); + } + + @Override + public void doClose() { + Releasables.close(tops, bottoms, posLefts, posRights, negLefts, negRights); + } + + protected void setBucketSize(final long bucket, final BigArrays bigArrays) { + if (bucket >= tops.size()) { + long from = tops.size(); + tops = bigArrays.grow(tops, bucket + 1); + tops.fill(from, tops.size(), Double.NEGATIVE_INFINITY); + bottoms = bigArrays.resize(bottoms, tops.size()); + bottoms.fill(from, bottoms.size(), Double.POSITIVE_INFINITY); + posLefts = bigArrays.resize(posLefts, tops.size()); + posLefts.fill(from, posLefts.size(), Double.POSITIVE_INFINITY); + posRights = bigArrays.resize(posRights, tops.size()); + posRights.fill(from, posRights.size(), Double.NEGATIVE_INFINITY); + negLefts = bigArrays.resize(negLefts, tops.size()); + negLefts.fill(from, negLefts.size(), Double.POSITIVE_INFINITY); + negRights = bigArrays.resize(negRights, tops.size()); + negRights.fill(from, negRights.size(), Double.NEGATIVE_INFINITY); + } + } +} diff --git a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBounds.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBounds.java similarity index 94% rename from server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBounds.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBounds.java index ba0e115c383dc..81ef502dda130 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBounds.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBounds.java @@ -30,13 +30,15 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.metrics; import org.opensearch.common.geo.GeoPoint; import org.opensearch.search.aggregations.Aggregation; /** * An aggregation that computes a bounding box in which all documents of the current bucket are. + * + * @opensearch.internal */ public interface GeoBounds extends Aggregation { diff --git a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregationBuilder.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregationBuilder.java similarity index 88% rename from server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregationBuilder.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregationBuilder.java index 30ba6988325e6..f959c6bd6842c 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregationBuilder.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregationBuilder.java @@ -30,16 +30,18 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.metrics; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.aggregations.AggregationBuilder; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.AggregatorFactory; +import org.opensearch.search.aggregations.metrics.GeoBoundsAggregatorSupplier; import org.opensearch.search.aggregations.support.CoreValuesSourceType; import org.opensearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.opensearch.search.aggregations.support.ValuesSourceConfig; @@ -50,8 +52,14 @@ import java.util.Map; import java.util.Objects; +/** + * Aggregation Builder for geo_bounds agg + * + * @opensearch.internal + */ public class GeoBoundsAggregationBuilder extends ValuesSourceAggregationBuilder { public static final String NAME = "geo_bounds"; + private static final ParseField WRAP_LONGITUDE_FIELD = new ParseField("wrap_longitude"); public static final ValuesSourceRegistry.RegistryKey REGISTRY_KEY = new ValuesSourceRegistry.RegistryKey<>( NAME, GeoBoundsAggregatorSupplier.class @@ -63,7 +71,7 @@ public class GeoBoundsAggregationBuilder extends ValuesSourceAggregationBuilder< ); static { ValuesSourceAggregationBuilder.declareFields(PARSER, false, false, false); - PARSER.declareBoolean(GeoBoundsAggregationBuilder::wrapLongitude, GeoBoundsAggregator.WRAP_LONGITUDE_FIELD); + PARSER.declareBoolean(GeoBoundsAggregationBuilder::wrapLongitude, WRAP_LONGITUDE_FIELD); } public static void registerAggregators(ValuesSourceRegistry.Builder builder) { @@ -116,13 +124,6 @@ public GeoBoundsAggregationBuilder wrapLongitude(boolean wrapLongitude) { return this; } - /** - * Get whether to wrap longitudes. - */ - public boolean wrapLongitude() { - return wrapLongitude; - } - @Override public BucketCardinality bucketCardinality() { return BucketCardinality.NONE; @@ -140,7 +141,7 @@ protected GeoBoundsAggregatorFactory innerBuild( @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { - builder.field(GeoBoundsAggregator.WRAP_LONGITUDE_FIELD.getPreferredName(), wrapLongitude); + builder.field(WRAP_LONGITUDE_FIELD.getPreferredName(), wrapLongitude); return builder; } diff --git a/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregator.java new file mode 100644 index 0000000000000..a6518ea702be6 --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregator.java @@ -0,0 +1,118 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.apache.lucene.index.LeafReaderContext; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.util.BigArrays; +import org.opensearch.index.fielddata.MultiGeoPointValues; +import org.opensearch.search.aggregations.Aggregator; +import org.opensearch.search.aggregations.LeafBucketCollector; +import org.opensearch.search.aggregations.LeafBucketCollectorBase; +import org.opensearch.search.aggregations.support.ValuesSource; +import org.opensearch.search.aggregations.support.ValuesSourceConfig; +import org.opensearch.search.internal.SearchContext; + +import java.io.IOException; +import java.util.Map; + +/** + * Aggregate all docs into a geographic bounds for field GeoPoint. + * + * @opensearch.internal + */ +final class GeoBoundsAggregator extends AbstractGeoBoundsAggregator { + + GeoBoundsAggregator( + String name, + SearchContext aggregationContext, + Aggregator parent, + ValuesSourceConfig valuesSourceConfig, + boolean wrapLongitude, + Map metadata + ) throws IOException { + super(name, aggregationContext, parent, valuesSourceConfig, wrapLongitude, metadata); + } + + @Override + public LeafBucketCollector getLeafCollector(LeafReaderContext ctx, LeafBucketCollector sub) { + if (valuesSource == null) { + return LeafBucketCollector.NO_OP_COLLECTOR; + } + final BigArrays bigArrays = context.bigArrays(); + final MultiGeoPointValues values = valuesSource.geoPointValues(ctx); + return new LeafBucketCollectorBase(sub, values) { + @Override + public void collect(int doc, long bucket) throws IOException { + setBucketSize(bucket, bigArrays); + + if (values.advanceExact(doc)) { + final int valuesCount = values.docValueCount(); + for (int i = 0; i < valuesCount; ++i) { + GeoPoint value = values.nextValue(); + double top = tops.get(bucket); + if (value.lat() > top) { + top = value.lat(); + } + double bottom = bottoms.get(bucket); + if (value.lat() < bottom) { + bottom = value.lat(); + } + double posLeft = posLefts.get(bucket); + if (value.lon() >= 0 && value.lon() < posLeft) { + posLeft = value.lon(); + } + double posRight = posRights.get(bucket); + if (value.lon() >= 0 && value.lon() > posRight) { + posRight = value.lon(); + } + double negLeft = negLefts.get(bucket); + if (value.lon() < 0 && value.lon() < negLeft) { + negLeft = value.lon(); + } + double negRight = negRights.get(bucket); + if (value.lon() < 0 && value.lon() > negRight) { + negRight = value.lon(); + } + tops.set(bucket, top); + bottoms.set(bucket, bottom); + posLefts.set(bucket, posLeft); + posRights.set(bucket, posRight); + negLefts.set(bucket, negLeft); + negRights.set(bucket, negRight); + } + } + } + }; + } +} diff --git a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregatorFactory.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregatorFactory.java similarity index 89% rename from server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregatorFactory.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregatorFactory.java index 27744a549c748..fc9cce3cf98c1 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregatorFactory.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregatorFactory.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.metrics; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.aggregations.Aggregator; @@ -46,6 +46,11 @@ import java.io.IOException; import java.util.Map; +/** + * Aggregation Factory for geo_bounds agg + * + * @opensearch.internal + */ class GeoBoundsAggregatorFactory extends ValuesSourceAggregatorFactory { private final boolean wrapLongitude; @@ -82,5 +87,11 @@ protected Aggregator doCreateInternal( static void registerAggregators(ValuesSourceRegistry.Builder builder) { builder.register(GeoBoundsAggregationBuilder.REGISTRY_KEY, CoreValuesSourceType.GEOPOINT, GeoBoundsAggregator::new, true); + builder.register(GeoBoundsAggregationBuilder.REGISTRY_KEY, CoreValuesSourceType.GEO_SHAPE, GeoBoundsGeoShapeAggregator::new, true); + } + + @Override + protected boolean supportsConcurrentSegmentSearch() { + return true; } } diff --git a/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsGeoShapeAggregator.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsGeoShapeAggregator.java new file mode 100644 index 0000000000000..918b9a6701490 --- /dev/null +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsGeoShapeAggregator.java @@ -0,0 +1,116 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.lucene.index.LeafReaderContext; +import org.opensearch.common.geo.GeoShapeDocValue; +import org.opensearch.common.util.BigArrays; +import org.opensearch.index.fielddata.GeoShapeValue; +import org.opensearch.search.aggregations.Aggregator; +import org.opensearch.search.aggregations.LeafBucketCollector; +import org.opensearch.search.aggregations.LeafBucketCollectorBase; +import org.opensearch.search.aggregations.support.ValuesSource; +import org.opensearch.search.aggregations.support.ValuesSourceConfig; +import org.opensearch.search.internal.SearchContext; + +import java.io.IOException; +import java.util.Map; + +/** + * Aggregate all docs into a geographic bounds for field geo_shape. + * + * @opensearch.internal + */ +public final class GeoBoundsGeoShapeAggregator extends AbstractGeoBoundsAggregator { + private static final Logger LOGGER = LogManager.getLogger(GeoBoundsGeoShapeAggregator.class); + + public GeoBoundsGeoShapeAggregator( + String name, + SearchContext searchContext, + Aggregator aggregator, + ValuesSourceConfig valuesSourceConfig, + boolean wrapLongitude, + Map metaData + ) throws IOException { + super(name, searchContext, aggregator, valuesSourceConfig, wrapLongitude, metaData); + } + + @Override + protected LeafBucketCollector getLeafCollector(LeafReaderContext ctx, LeafBucketCollector leafBucketCollector) { + if (valuesSource == null) { + return LeafBucketCollector.NO_OP_COLLECTOR; + } + final BigArrays bigArrays = context.bigArrays(); + final GeoShapeValue values = valuesSource.getGeoShapeValues(ctx); + return new LeafBucketCollectorBase(leafBucketCollector, values) { + @Override + public void collect(int doc, long bucket) throws IOException { + setBucketSize(bucket, bigArrays); + if (values.advanceExact(doc)) { + final GeoShapeDocValue value = values.nextValue(); + final GeoShapeDocValue.BoundingRectangle boundingBox = value.getBoundingRectangle(); + if (boundingBox != null) { + double top = tops.get(bucket); + if (boundingBox.getMaxLatitude() > top) { + top = boundingBox.getMaxLatitude(); + } + + double bottom = bottoms.get(bucket); + if (boundingBox.getMinLatitude() < bottom) { + bottom = boundingBox.getMinLatitude(); + } + + double posLeft = posLefts.get(bucket); + if (boundingBox.getMinLongitude() >= 0 && boundingBox.getMinLongitude() < posLeft) { + posLeft = boundingBox.getMinLongitude(); + } + if (boundingBox.getMaxLongitude() >= 0 && boundingBox.getMaxLongitude() < posLeft) { + posLeft = boundingBox.getMaxLongitude(); + } + + double posRight = posRights.get(bucket); + if (boundingBox.getMaxLongitude() >= 0 && boundingBox.getMaxLongitude() > posRight) { + posRight = boundingBox.getMaxLongitude(); + } + if (boundingBox.getMinLongitude() >= 0 && boundingBox.getMinLongitude() > posRight) { + posRight = boundingBox.getMinLongitude(); + } + + double negLeft = negLefts.get(bucket); + if (boundingBox.getMinLongitude() < 0 && boundingBox.getMinLongitude() < negLeft) { + negLeft = boundingBox.getMinLongitude(); + } + if (boundingBox.getMaxLongitude() < 0 && boundingBox.getMaxLongitude() < negLeft) { + negLeft = boundingBox.getMaxLongitude(); + } + + double negRight = negRights.get(bucket); + if (boundingBox.getMaxLongitude() < 0 && boundingBox.getMaxLongitude() > negRight) { + negRight = boundingBox.getMaxLongitude(); + } + if (boundingBox.getMinLongitude() < 0 && boundingBox.getMinLongitude() > negRight) { + negRight = boundingBox.getMinLongitude(); + } + + tops.set(bucket, top); + bottoms.set(bucket, bottom); + posLefts.set(bucket, posLeft); + posRights.set(bucket, posRight); + negLefts.set(bucket, negLeft); + negRights.set(bucket, negRight); + } else { + LOGGER.error("The bounding box was null for the Doc id {}", doc); + } + } + } + }; + } +} diff --git a/server/src/main/java/org/opensearch/search/aggregations/metrics/InternalGeoBounds.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/InternalGeoBounds.java similarity index 96% rename from server/src/main/java/org/opensearch/search/aggregations/metrics/InternalGeoBounds.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/InternalGeoBounds.java index 84a46ed6c71d6..c3cb47d244c6b 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/metrics/InternalGeoBounds.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/InternalGeoBounds.java @@ -30,13 +30,13 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.metrics; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.InternalAggregation; import java.io.IOException; @@ -44,6 +44,11 @@ import java.util.Map; import java.util.Objects; +/** + * Implementation of geo bounds agg + * + * @opensearch.internal + */ public class InternalGeoBounds extends InternalAggregation implements GeoBounds { public final double top; public final double bottom; diff --git a/server/src/main/java/org/opensearch/search/aggregations/metrics/ParsedGeoBounds.java b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/ParsedGeoBounds.java similarity index 90% rename from server/src/main/java/org/opensearch/search/aggregations/metrics/ParsedGeoBounds.java rename to modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/ParsedGeoBounds.java index e0c734e4dc8c5..d40bec096ed01 100644 --- a/server/src/main/java/org/opensearch/search/aggregations/metrics/ParsedGeoBounds.java +++ b/modules/geo/src/main/java/org/opensearch/geo/search/aggregations/metrics/ParsedGeoBounds.java @@ -30,16 +30,16 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.metrics; import org.opensearch.common.Nullable; import org.opensearch.common.collect.Tuple; import org.opensearch.common.geo.GeoBoundingBox; import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.aggregations.ParsedAggregation; import java.io.IOException; @@ -49,8 +49,13 @@ import static org.opensearch.common.geo.GeoBoundingBox.LAT_FIELD; import static org.opensearch.common.geo.GeoBoundingBox.LON_FIELD; import static org.opensearch.common.geo.GeoBoundingBox.TOP_LEFT_FIELD; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +/** + * A geo bounds agg result parsed between nodes + * + * @opensearch.internal + */ public class ParsedGeoBounds extends ParsedAggregation implements GeoBounds { // A top of Double.NEGATIVE_INFINITY yields an empty xContent, so the bounding box is null diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridAggregationBuilderTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridAggregationBuilderTests.java new file mode 100644 index 0000000000000..55c82351dbab4 --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoHashGridAggregationBuilderTests.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.bucket; + +import org.opensearch.LegacyESVersion; +import org.opensearch.Version; +import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.aggregations.BaseAggregationTestCase; +import org.opensearch.test.VersionUtils; + +import java.util.Collection; +import java.util.Collections; + +import static org.hamcrest.Matchers.equalTo; + +public class GeoHashGridAggregationBuilderTests extends BaseAggregationTestCase { + + protected Collection> getPlugins() { + return Collections.singletonList(GeoModulePlugin.class); + } + + @Override + protected GeoHashGridAggregationBuilder createTestAggregatorBuilder() { + String name = randomAlphaOfLengthBetween(3, 20); + GeoHashGridAggregationBuilder factory = new GeoHashGridAggregationBuilder(name); + factory.field("foo"); + if (randomBoolean()) { + int precision = randomIntBetween(1, 12); + factory.precision(precision); + } + if (randomBoolean()) { + factory.size(randomIntBetween(1, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + factory.shardSize(randomIntBetween(1, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + factory.setGeoBoundingBox(RandomGeoGenerator.randomBBox()); + } + return factory; + } + + public void testSerializationPreBounds() throws Exception { + Version noBoundsSupportVersion = VersionUtils.randomVersionBetween(random(), LegacyESVersion.V_7_0_0, LegacyESVersion.V_7_5_0); + GeoHashGridAggregationBuilder builder = createTestAggregatorBuilder(); + try (BytesStreamOutput output = new BytesStreamOutput()) { + output.setVersion(LegacyESVersion.V_7_6_0); + builder.writeTo(output); + try ( + StreamInput in = new NamedWriteableAwareStreamInput( + output.bytes().streamInput(), + new NamedWriteableRegistry(Collections.emptyList()) + ) + ) { + in.setVersion(noBoundsSupportVersion); + GeoHashGridAggregationBuilder readBuilder = new GeoHashGridAggregationBuilder(in); + assertThat( + readBuilder.geoBoundingBox(), + equalTo(new GeoBoundingBox(new GeoPoint(Double.NaN, Double.NaN), new GeoPoint(Double.NaN, Double.NaN))) + ); + } + } + } +} diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridAggregationBuilderTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridAggregationBuilderTests.java new file mode 100644 index 0000000000000..c1c97cf18847c --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/GeoTileGridAggregationBuilderTests.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.bucket; + +import org.opensearch.LegacyESVersion; +import org.opensearch.Version; +import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.aggregations.BaseAggregationTestCase; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; +import org.opensearch.test.VersionUtils; + +import java.util.Collection; +import java.util.Collections; + +import static org.hamcrest.Matchers.equalTo; + +public class GeoTileGridAggregationBuilderTests extends BaseAggregationTestCase { + + protected Collection> getPlugins() { + return Collections.singletonList(GeoModulePlugin.class); + } + + @Override + protected GeoTileGridAggregationBuilder createTestAggregatorBuilder() { + String name = randomAlphaOfLengthBetween(3, 20); + GeoTileGridAggregationBuilder factory = new GeoTileGridAggregationBuilder(name); + factory.field("foo"); + if (randomBoolean()) { + factory.precision(randomIntBetween(0, GeoTileUtils.MAX_ZOOM)); + } + if (randomBoolean()) { + factory.size(randomIntBetween(1, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + factory.shardSize(randomIntBetween(1, Integer.MAX_VALUE)); + } + if (randomBoolean()) { + factory.setGeoBoundingBox(RandomGeoGenerator.randomBBox()); + } + return factory; + } + + public void testSerializationPreBounds() throws Exception { + Version noBoundsSupportVersion = VersionUtils.randomVersionBetween(random(), LegacyESVersion.V_7_0_0, LegacyESVersion.V_7_5_0); + GeoTileGridAggregationBuilder builder = createTestAggregatorBuilder(); + try (BytesStreamOutput output = new BytesStreamOutput()) { + output.setVersion(LegacyESVersion.V_7_6_0); + builder.writeTo(output); + try ( + StreamInput in = new NamedWriteableAwareStreamInput( + output.bytes().streamInput(), + new NamedWriteableRegistry(Collections.emptyList()) + ) + ) { + in.setVersion(noBoundsSupportVersion); + GeoTileGridAggregationBuilder readBuilder = new GeoTileGridAggregationBuilder(in); + assertThat( + readBuilder.geoBoundingBox(), + equalTo(new GeoBoundingBox(new GeoPoint(Double.NaN, Double.NaN), new GeoPoint(Double.NaN, Double.NaN))) + ); + } + } + } +} diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridAggregationCompositeAggregatorTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridAggregationCompositeAggregatorTests.java new file mode 100644 index 0000000000000..f46bef97194ef --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridAggregationCompositeAggregatorTests.java @@ -0,0 +1,174 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket.composite; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.LatLonPoint; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.search.DocValuesFieldExistsQuery; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregator; +import org.opensearch.index.mapper.GeoPointFieldMapper; +import org.opensearch.plugins.SearchPlugin; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; +import org.opensearch.search.aggregations.composite.BaseCompositeAggregatorTestCase; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Testing the {@link GeoTileGridAggregator} as part of CompositeAggregation. + */ +public class GeoTileGridAggregationCompositeAggregatorTests extends BaseCompositeAggregatorTestCase { + + protected List getSearchPlugins() { + return Collections.singletonList(new GeoModulePlugin()); + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + FIELD_TYPES.add(new GeoPointFieldMapper.GeoPointFieldType("geo_point")); + } + + public void testUnmappedFieldWithGeopoint() throws Exception { + final List>> dataset = new ArrayList<>(); + final String mappedFieldName = "geo_point"; + dataset.addAll( + Arrays.asList( + createDocument(mappedFieldName, new GeoPoint(48.934059, 41.610741)), + createDocument(mappedFieldName, new GeoPoint(-23.065941, 113.610741)), + createDocument(mappedFieldName, new GeoPoint(90.0, 0.0)), + createDocument(mappedFieldName, new GeoPoint(37.2343, -115.8067)), + createDocument(mappedFieldName, new GeoPoint(90.0, 0.0)) + ) + ); + + // just unmapped = no results + testSearchCase( + Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), + dataset, + () -> new CompositeAggregationBuilder("name", Arrays.asList(new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped"))), + (result) -> assertEquals(0, result.getBuckets().size()) + ); + + // unmapped missing bucket = one result + testSearchCase( + Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), + dataset, + () -> new CompositeAggregationBuilder( + "name", + Arrays.asList(new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped").missingBucket(true)) + ), + (result) -> { + assertEquals(1, result.getBuckets().size()); + assertEquals("{unmapped=null}", result.afterKey().toString()); + assertEquals("{unmapped=null}", result.getBuckets().get(0).getKeyAsString()); + assertEquals(5L, result.getBuckets().get(0).getDocCount()); + } + ); + + // field + unmapped, no missing bucket = no results + testSearchCase( + Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), + dataset, + () -> new CompositeAggregationBuilder( + "name", + Arrays.asList( + new GeoTileGridValuesSourceBuilder(mappedFieldName).field(mappedFieldName), + new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped") + ) + ), + (result) -> assertEquals(0, result.getBuckets().size()) + ); + + // field + unmapped with missing bucket = multiple results + testSearchCase( + Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery(mappedFieldName)), + dataset, + () -> new CompositeAggregationBuilder( + "name", + Arrays.asList( + new GeoTileGridValuesSourceBuilder(mappedFieldName).field(mappedFieldName), + new GeoTileGridValuesSourceBuilder("unmapped").field("unmapped").missingBucket(true) + ) + ), + (result) -> { + assertEquals(2, result.getBuckets().size()); + assertEquals("{geo_point=7/64/56, unmapped=null}", result.afterKey().toString()); + assertEquals("{geo_point=7/32/56, unmapped=null}", result.getBuckets().get(0).getKeyAsString()); + assertEquals(2L, result.getBuckets().get(0).getDocCount()); + assertEquals("{geo_point=7/64/56, unmapped=null}", result.getBuckets().get(1).getKeyAsString()); + assertEquals(3L, result.getBuckets().get(1).getDocCount()); + } + ); + + } + + public void testWithGeoPoint() throws Exception { + final List>> dataset = new ArrayList<>(); + dataset.addAll( + Arrays.asList( + createDocument("geo_point", new GeoPoint(48.934059, 41.610741)), + createDocument("geo_point", new GeoPoint(-23.065941, 113.610741)), + createDocument("geo_point", new GeoPoint(90.0, 0.0)), + createDocument("geo_point", new GeoPoint(37.2343, -115.8067)), + createDocument("geo_point", new GeoPoint(90.0, 0.0)) + ) + ); + testSearchCase(Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("geo_point")), dataset, () -> { + GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder("geo_point").field("geo_point"); + return new CompositeAggregationBuilder("name", Collections.singletonList(geoTile)); + }, (result) -> { + assertEquals(2, result.getBuckets().size()); + assertEquals("{geo_point=7/64/56}", result.afterKey().toString()); + assertEquals("{geo_point=7/32/56}", result.getBuckets().get(0).getKeyAsString()); + assertEquals(2L, result.getBuckets().get(0).getDocCount()); + assertEquals("{geo_point=7/64/56}", result.getBuckets().get(1).getKeyAsString()); + assertEquals(3L, result.getBuckets().get(1).getDocCount()); + }); + + testSearchCase(Arrays.asList(new MatchAllDocsQuery(), new DocValuesFieldExistsQuery("geo_point")), dataset, () -> { + GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder("geo_point").field("geo_point"); + return new CompositeAggregationBuilder("name", Collections.singletonList(geoTile)).aggregateAfter( + Collections.singletonMap("geo_point", "7/32/56") + ); + }, (result) -> { + assertEquals(1, result.getBuckets().size()); + assertEquals("{geo_point=7/64/56}", result.afterKey().toString()); + assertEquals("{geo_point=7/64/56}", result.getBuckets().get(0).getKeyAsString()); + assertEquals(3L, result.getBuckets().get(0).getDocCount()); + }); + } + + @Override + protected boolean addValueToDocument(final Document doc, final String name, final Object value) { + if (value instanceof GeoPoint) { + GeoPoint point = (GeoPoint) value; + doc.add( + new SortedNumericDocValuesField( + name, + GeoTileUtils.longEncode(point.lon(), point.lat(), GeoTileGridAggregationBuilder.DEFAULT_PRECISION) + ) + ); + doc.add(new LatLonPoint(name, point.lat(), point.lon())); + return true; + } + return false; + } +} diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridCompositeAggregationBuilderTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridCompositeAggregationBuilderTests.java new file mode 100644 index 0000000000000..ea7a2a83945c2 --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridCompositeAggregationBuilderTests.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.bucket.composite; + +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.aggregations.BaseAggregationTestCase; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; +import org.opensearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class GeoTileGridCompositeAggregationBuilderTests extends BaseAggregationTestCase { + + protected Collection> getPlugins() { + return Collections.singletonList(GeoModulePlugin.class); + } + + private GeoTileGridValuesSourceBuilder randomGeoTileGridValuesSourceBuilder() { + GeoTileGridValuesSourceBuilder geoTile = new GeoTileGridValuesSourceBuilder(randomAlphaOfLengthBetween(5, 10)); + if (randomBoolean()) { + geoTile.precision(randomIntBetween(0, GeoTileUtils.MAX_ZOOM)); + } + if (randomBoolean()) { + geoTile.geoBoundingBox(RandomGeoGenerator.randomBBox()); + } + return geoTile; + } + + @Override + protected CompositeAggregationBuilder createTestAggregatorBuilder() { + int numSources = randomIntBetween(1, 10); + List> sources = new ArrayList<>(); + for (int i = 0; i < numSources; i++) { + sources.add(randomGeoTileGridValuesSourceBuilder()); + } + return new CompositeAggregationBuilder(randomAlphaOfLength(10), sources); + } +} diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java similarity index 85% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java index 58199741b1ee2..a9e495c487ab0 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/composite/GeoTileGridValuesSourceBuilderTests.java @@ -30,17 +30,18 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.composite; +package org.opensearch.geo.search.aggregations.bucket.composite; import org.opensearch.LegacyESVersion; import org.opensearch.Version; import org.opensearch.common.geo.GeoBoundingBox; -import org.opensearch.common.geo.GeoBoundingBoxTests; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.geo.tests.common.RandomGeoGenerator; +import org.opensearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.VersionUtils; @@ -60,7 +61,7 @@ public void testBWCBounds() throws IOException { Version noBoundsSupportVersion = VersionUtils.randomVersionBetween(random(), LegacyESVersion.V_7_0_0, LegacyESVersion.V_7_5_0); GeoTileGridValuesSourceBuilder builder = new GeoTileGridValuesSourceBuilder("name"); if (randomBoolean()) { - builder.geoBoundingBox(GeoBoundingBoxTests.randomBBox()); + builder.geoBoundingBox(RandomGeoGenerator.randomBBox()); } try (BytesStreamOutput output = new BytesStreamOutput()) { output.setVersion(LegacyESVersion.V_7_6_0); diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java similarity index 94% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java index 17fddb8978499..75a3c5b2e38c1 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridAggregatorTestCase.java @@ -29,7 +29,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.document.LatLonDocValuesField; import org.apache.lucene.document.SortedSetDocValuesField; @@ -37,25 +37,27 @@ import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexableField; -import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; import org.opensearch.common.CheckedConsumer; import org.opensearch.common.geo.GeoBoundingBox; -import org.opensearch.common.geo.GeoBoundingBoxTests; import org.opensearch.common.geo.GeoUtils; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.tests.common.AggregationInspectionHelper; +import org.opensearch.geo.tests.common.RandomGeoGenerator; import org.opensearch.index.mapper.GeoPointFieldMapper; import org.opensearch.index.mapper.MappedFieldType; +import org.opensearch.plugins.SearchPlugin; import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorTestCase; import org.opensearch.search.aggregations.MultiBucketConsumerService; import org.opensearch.search.aggregations.bucket.terms.StringTerms; import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder; -import org.opensearch.search.aggregations.support.AggregationInspectionHelper; import java.io.IOException; import java.util.ArrayList; @@ -91,6 +93,16 @@ public abstract class GeoGridAggregatorTestCase */ protected abstract GeoGridAggregationBuilder createBuilder(String name); + /** + * Overriding the Search Plugins list with {@link GeoModulePlugin} so that the testcase will know that this plugin is + * to be loaded during the tests. + * @return List of {@link SearchPlugin} + */ + @Override + protected List getSearchPlugins() { + return Collections.singletonList(new GeoModulePlugin()); + } + public void testNoDocs() throws IOException { testCase( new MatchAllDocsQuery(), @@ -111,7 +123,9 @@ public void testUnmapped() throws IOException { randomPrecision(), null, geoGrid -> { assertEquals(0, geoGrid.getBuckets().size()); }, - iw -> { iw.addDocument(Collections.singleton(new LatLonDocValuesField(FIELD_NAME, 10D, 10D))); } + iw -> { + iw.addDocument(Collections.singleton(new LatLonDocValuesField(FIELD_NAME, 10D, 10D))); + } ); } @@ -225,7 +239,7 @@ public void testBounds() throws IOException { // only consider bounding boxes that are at least GEOHASH_TOLERANCE wide and have quantized coordinates GeoBoundingBox bbox = randomValueOtherThanMany( (b) -> Math.abs(GeoUtils.normalizeLon(b.right()) - GeoUtils.normalizeLon(b.left())) < GEOHASH_TOLERANCE, - GeoBoundingBoxTests::randomBBox + RandomGeoGenerator::randomBBox ); Function encodeDecodeLat = (lat) -> GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lat)); Function encodeDecodeLon = (lon) -> GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lon)); diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridTestCase.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridTestCase.java similarity index 79% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridTestCase.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridTestCase.java index ce286a4443660..cb61ff77dde08 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoGridTestCase.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoGridTestCase.java @@ -29,9 +29,16 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.apache.lucene.index.IndexWriter; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.search.aggregations.metrics.ParsedGeoBounds; +import org.opensearch.plugins.SearchPlugin; +import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.ParsedMultiBucketAggregation; import org.opensearch.test.InternalMultiBucketAggregationTestCase; @@ -76,6 +83,36 @@ protected int maxNumberOfBuckets() { return 3; } + /** + * Overriding the method so that tests can get the aggregation specs for namedWriteable. + * + * @return GeoPlugin + */ + @Override + protected SearchPlugin registerPlugin() { + return new GeoModulePlugin(); + } + + /** + * Overriding with the {@link ParsedGeoBounds} so that it can be parsed. We need to do this as {@link GeoModulePlugin} + * is registering this Aggregation. + * + * @return a List of {@link NamedXContentRegistry.Entry} + */ + @Override + protected List getNamedXContents() { + final List namedXContents = new ArrayList<>(getDefaultNamedXContents()); + final ContextParser hashGridParser = (p, c) -> ParsedGeoHashGrid.fromXContent(p, (String) c); + final ContextParser geoTileParser = (p, c) -> ParsedGeoTileGrid.fromXContent(p, (String) c); + namedXContents.add( + new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(GeoHashGridAggregationBuilder.NAME), hashGridParser) + ); + namedXContents.add( + new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(GeoTileGridAggregationBuilder.NAME), geoTileParser) + ); + return namedXContents; + } + @Override protected T createTestInstance(String name, Map metadata, InternalAggregations aggregations) { final int precision = randomPrecision(); diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java similarity index 96% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java index 5c63b15c7f614..04fa815366f6b 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java @@ -30,7 +30,7 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import static org.opensearch.geometry.utils.Geohash.stringEncode; diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java similarity index 97% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java index e81e22b3b562f..a195a1ade781a 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridParserTests.java @@ -29,21 +29,21 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; - -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.lessThanOrEqualTo; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.common.unit.DistanceUnit; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.geo.GeometryTestUtils; import org.opensearch.geometry.Rectangle; import org.opensearch.test.OpenSearchTestCase; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.lessThanOrEqualTo; + public class GeoHashGridParserTests extends OpenSearchTestCase { public void testParseValidFromInts() throws Exception { int precision = randomIntBetween(1, 12); diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridTests.java new file mode 100644 index 0000000000000..c84c6ef5ec076 --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoHashGridTests.java @@ -0,0 +1,66 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.bucket.geogrid; + +import org.opensearch.geometry.utils.Geohash; +import org.opensearch.search.aggregations.InternalAggregations; + +import java.util.List; +import java.util.Map; + +public class GeoHashGridTests extends GeoGridTestCase { + + @Override + protected InternalGeoHashGrid createInternalGeoGrid( + String name, + int size, + List buckets, + Map metadata + ) { + return new InternalGeoHashGrid(name, size, buckets, metadata); + } + + @Override + protected InternalGeoHashGridBucket createInternalGeoGridBucket(Long key, long docCount, InternalAggregations aggregations) { + return new InternalGeoHashGridBucket(key, docCount, aggregations); + } + + @Override + protected long longEncode(double lng, double lat, int precision) { + return Geohash.longEncode(lng, lat, precision); + } + + @Override + protected int randomPrecision() { + return randomIntBetween(4, 12); + } +} diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java similarity index 94% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java index 4e88111ac2dfc..f2f641ea794c0 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridAggregatorTests.java @@ -30,7 +30,9 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; + +import org.opensearch.search.aggregations.bucket.GeoTileUtils; public class GeoTileGridAggregatorTests extends GeoGridAggregatorTestCase { diff --git a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java similarity index 95% rename from server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java index 567bcd57d23e5..02b7c10198d80 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridParserTests.java @@ -29,14 +29,15 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.bucket.geogrid; +package org.opensearch.geo.search.aggregations.bucket.geogrid; import org.opensearch.ExceptionsHelper; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.geo.GeometryTestUtils; import org.opensearch.geometry.Rectangle; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; import org.opensearch.test.OpenSearchTestCase; import static org.hamcrest.Matchers.containsString; diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridTests.java new file mode 100644 index 0000000000000..ead67e0455d94 --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/bucket/geogrid/GeoTileGridTests.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.geo.search.aggregations.bucket.geogrid; + +import org.opensearch.search.aggregations.InternalAggregations; +import org.opensearch.search.aggregations.bucket.GeoTileUtils; + +import java.util.List; +import java.util.Map; + +public class GeoTileGridTests extends GeoGridTestCase { + + @Override + protected InternalGeoTileGrid createInternalGeoGrid( + String name, + int size, + List buckets, + Map metadata + ) { + return new InternalGeoTileGrid(name, size, buckets, metadata); + } + + @Override + protected InternalGeoTileGridBucket createInternalGeoGridBucket(Long key, long docCount, InternalAggregations aggregations) { + return new InternalGeoTileGridBucket(key, docCount, aggregations); + } + + @Override + protected long longEncode(double lng, double lat, int precision) { + return GeoTileUtils.longEncode(lng, lat, precision); + } + + @Override + protected int randomPrecision() { + // precision values below 8 can lead to parsing errors + return randomIntBetween(8, GeoTileUtils.MAX_ZOOM); + } +} diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregationBuilderTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregationBuilderTests.java new file mode 100644 index 0000000000000..49b455bbf389e --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregationBuilderTests.java @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.aggregations.BaseAggregationTestCase; + +import java.util.Collection; +import java.util.Collections; + +public class GeoBoundsAggregationBuilderTests extends BaseAggregationTestCase { + + /** + * This registers the GeoShape mapper with the Tests so that it can be used for testing the aggregation builders + * + * @return A Collection containing {@link GeoModulePlugin} + */ + protected Collection> getPlugins() { + return Collections.singletonList(GeoModulePlugin.class); + } + + @Override + protected GeoBoundsAggregationBuilder createTestAggregatorBuilder() { + GeoBoundsAggregationBuilder factory = new GeoBoundsAggregationBuilder(randomAlphaOfLengthBetween(1, 20)); + String field = randomAlphaOfLengthBetween(3, 20); + factory.field(field); + if (randomBoolean()) { + factory.wrapLongitude(randomBoolean()); + } + if (randomBoolean()) { + factory.missing("0,0"); + } + return factory; + } + +} diff --git a/server/src/test/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregatorTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregatorTests.java similarity index 88% rename from server/src/test/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregatorTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregatorTests.java index 6440c62e58e18..ee7a3c7e3faa2 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/metrics/GeoBoundsAggregatorTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsAggregatorTests.java @@ -30,26 +30,42 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.metrics; import org.apache.lucene.document.Document; import org.apache.lucene.document.LatLonDocValuesField; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; import org.opensearch.common.geo.GeoPoint; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.tests.common.AggregationInspectionHelper; +import org.opensearch.geo.tests.common.RandomGeoGenerator; import org.opensearch.index.mapper.GeoPointFieldMapper; import org.opensearch.index.mapper.MappedFieldType; +import org.opensearch.plugins.SearchPlugin; import org.opensearch.search.aggregations.AggregatorTestCase; -import org.opensearch.search.aggregations.support.AggregationInspectionHelper; -import org.opensearch.test.geo.RandomGeoGenerator; -import static org.opensearch.search.aggregations.metrics.InternalGeoBoundsTests.GEOHASH_TOLERANCE; +import java.util.Collections; +import java.util.List; + import static org.hamcrest.Matchers.closeTo; public class GeoBoundsAggregatorTests extends AggregatorTestCase { + public static final double GEOHASH_TOLERANCE = 1E-5D; + + /** + * Overriding the Search Plugins list with {@link GeoModulePlugin} so that the testcase will know that this plugin is + * to be loaded during the tests. + * @return List of {@link SearchPlugin} + */ + @Override + protected List getSearchPlugins() { + return Collections.singletonList(new GeoModulePlugin()); + } + public void testEmpty() throws Exception { try (Directory dir = newDirectory(); RandomIndexWriter w = new RandomIndexWriter(random(), dir)) { GeoBoundsAggregationBuilder aggBuilder = new GeoBoundsAggregationBuilder("my_agg").field("field").wrapLongitude(false); diff --git a/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsGeoShapeAggregatorTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsGeoShapeAggregatorTests.java new file mode 100644 index 0000000000000..cd88635f6c5cc --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/GeoBoundsGeoShapeAggregatorTests.java @@ -0,0 +1,237 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.search.aggregations.metrics; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.LatLonShape; +import org.apache.lucene.document.ShapeDocValuesField; +import org.apache.lucene.geo.LatLonGeometry; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.geo.GeoShapeUtils; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.geo.tests.common.AggregationInspectionHelper; +import org.opensearch.geo.tests.common.RandomGeoGeometryGenerator; +import org.opensearch.geometry.Circle; +import org.opensearch.geometry.Geometry; +import org.opensearch.geometry.Line; +import org.opensearch.geometry.Point; +import org.opensearch.geometry.Polygon; +import org.opensearch.geometry.ShapeType; +import org.opensearch.index.mapper.GeoShapeFieldMapper; +import org.opensearch.index.mapper.GeoShapeIndexer; +import org.opensearch.index.mapper.MappedFieldType; +import org.opensearch.plugins.SearchPlugin; +import org.opensearch.search.aggregations.AggregatorTestCase; +import org.opensearch.test.OpenSearchTestCase; +import org.hamcrest.MatcherAssert; +import org.junit.Assert; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Random; + +import static org.hamcrest.Matchers.closeTo; + +public class GeoBoundsGeoShapeAggregatorTests extends AggregatorTestCase { + private static final Logger LOG = LogManager.getLogger(GeoBoundsGeoShapeAggregatorTests.class); + private static final double GEOHASH_TOLERANCE = 1E-5D; + private static final String AGGREGATION_NAME = "my_agg"; + private static final String FIELD_NAME = "field"; + + /** + * Overriding the Search Plugins list with {@link GeoModulePlugin} so that the testcase will know that this plugin is + * to be loaded during the tests. + * + * @return List of {@link SearchPlugin} + */ + @Override + protected List getSearchPlugins() { + return Collections.singletonList(new GeoModulePlugin()); + } + + /** + * Testing Empty aggregator results. + * + * @throws Exception + */ + public void testEmpty() throws Exception { + try (Directory dir = newDirectory(); RandomIndexWriter w = new RandomIndexWriter(random(), dir)) { + final GeoBoundsAggregationBuilder aggBuilder = new GeoBoundsAggregationBuilder(AGGREGATION_NAME).field(FIELD_NAME) + .wrapLongitude(false); + + final MappedFieldType fieldType = new GeoShapeFieldMapper.GeoShapeFieldType(FIELD_NAME); + try (IndexReader reader = w.getReader()) { + IndexSearcher searcher = new IndexSearcher(reader); + InternalGeoBounds bounds = searchAndReduce(searcher, new MatchAllDocsQuery(), aggBuilder, fieldType); + assertTrue(Double.isInfinite(bounds.top)); + assertTrue(Double.isInfinite(bounds.bottom)); + assertTrue(Double.isInfinite(bounds.posLeft)); + assertTrue(Double.isInfinite(bounds.posRight)); + assertTrue(Double.isInfinite(bounds.negLeft)); + assertTrue(Double.isInfinite(bounds.negRight)); + assertFalse(AggregationInspectionHelper.hasValue(bounds)); + } + } + } + + /** + * Testing GeoBoundAggregator for random shapes which are indexed. + * + * @throws Exception + */ + public void testRandom() throws Exception { + final int numDocs = randomIntBetween(50, 100); + final List Y = new ArrayList<>(); + final List X = new ArrayList<>(); + final Random random = random(); + try (Directory dir = newDirectory(); RandomIndexWriter w = new RandomIndexWriter(random, dir)) { + for (int i = 0; i < numDocs; i++) { + final Document document = new Document(); + final Geometry geometry = randomLuceneGeometry(random); + LOG.debug("Random Geometry created for Indexing : {}", geometry); + document.add(createShapeDocValue(geometry)); + w.addDocument(document); + getAllXAndYPoints(geometry, X, Y); + } + final GeoBoundsAggregationBuilder aggBuilder = new GeoBoundsAggregationBuilder(AGGREGATION_NAME).field(FIELD_NAME) + .wrapLongitude(false); + final MappedFieldType fieldType = new GeoShapeFieldMapper.GeoShapeFieldType(FIELD_NAME); + try (IndexReader reader = w.getReader()) { + final IndexSearcher searcher = new IndexSearcher(reader); + final InternalGeoBounds actualBounds = searchAndReduce(searcher, new MatchAllDocsQuery(), aggBuilder, fieldType); + final GeoBoundingBox expectedGeoBounds = getExpectedGeoBounds(X, Y); + MatcherAssert.assertThat( + actualBounds.bottomRight().getLat(), + closeTo(expectedGeoBounds.bottomRight().getLat(), GEOHASH_TOLERANCE) + ); + MatcherAssert.assertThat( + actualBounds.bottomRight().getLon(), + closeTo(expectedGeoBounds.bottomRight().getLon(), GEOHASH_TOLERANCE) + ); + MatcherAssert.assertThat(actualBounds.topLeft().getLat(), closeTo(expectedGeoBounds.topLeft().getLat(), GEOHASH_TOLERANCE)); + MatcherAssert.assertThat(actualBounds.topLeft().getLon(), closeTo(expectedGeoBounds.topLeft().getLon(), GEOHASH_TOLERANCE)); + assertTrue(AggregationInspectionHelper.hasValue(actualBounds)); + } + } + } + + private GeoBoundingBox getExpectedGeoBounds(final List X, final List Y) { + double top = Double.NEGATIVE_INFINITY; + double bottom = Double.POSITIVE_INFINITY; + double posLeft = Double.POSITIVE_INFINITY; + double posRight = Double.NEGATIVE_INFINITY; + double negLeft = Double.POSITIVE_INFINITY; + double negRight = Double.NEGATIVE_INFINITY; + // Finding the bounding box for the shapes. + for (final Double lon : X) { + if (lon >= 0 && lon < posLeft) { + posLeft = lon; + } + if (lon >= 0 && lon > posRight) { + posRight = lon; + } + if (lon < 0 && lon < negLeft) { + negLeft = lon; + } + if (lon < 0 && lon > negRight) { + negRight = lon; + } + } + for (final Double lat : Y) { + if (lat > top) { + top = lat; + } + if (lat < bottom) { + bottom = lat; + } + } + if (Double.isInfinite(posLeft)) { + return new GeoBoundingBox(new GeoPoint(top, negLeft), new GeoPoint(bottom, negRight)); + } else if (Double.isInfinite(negLeft)) { + return new GeoBoundingBox(new GeoPoint(top, posLeft), new GeoPoint(bottom, posRight)); + } else { + return new GeoBoundingBox(new GeoPoint(top, negLeft), new GeoPoint(bottom, posRight)); + } + } + + private void getAllXAndYPoints(final Geometry geometry, final List X, final List Y) { + if (geometry instanceof Point) { + final Point point = (Point) geometry; + X.add(point.getX()); + Y.add(point.getY()); + return; + } else if (geometry instanceof Polygon) { + final Polygon polygon = (Polygon) geometry; + for (int i = 0; i < polygon.getPolygon().getX().length; i++) { + X.add(polygon.getPolygon().getX(i)); + Y.add(polygon.getPolygon().getY(i)); + } + return; + } else if (geometry instanceof Line) { + final Line line = (Line) geometry; + for (int i = 0; i < line.getX().length; i++) { + X.add(line.getX(i)); + Y.add(line.getY(i)); + } + return; + } + Assert.fail( + String.format(Locale.ROOT, "Error cannot convert the %s to a valid indexable format[POINT, POLYGON, LINE]", geometry.getClass()) + ); + } + + private ShapeDocValuesField createShapeDocValue(final Geometry geometry) { + if (geometry instanceof Point) { + final Point point = (Point) geometry; + return LatLonShape.createDocValueField(FIELD_NAME, point.getLat(), point.getLon()); + } else if (geometry instanceof Polygon) { + return LatLonShape.createDocValueField(FIELD_NAME, GeoShapeUtils.toLucenePolygon((Polygon) geometry)); + } else if (geometry instanceof Line) { + return LatLonShape.createDocValueField(FIELD_NAME, GeoShapeUtils.toLuceneLine((Line) geometry)); + } + Assert.fail( + String.format(Locale.ROOT, "Error cannot convert the %s to a valid indexable format[POINT, POLYGON, LINE]", geometry.getClass()) + ); + return null; + } + + /** + * Random function to generate a {@link LatLonGeometry}. Now for indexing of GeoShape field, we index all the + * different Geometry shapes that we support({@link ShapeType}) in OpenSearch are broken down into 3 shapes only. + * Hence, we are generating only 3 shapes : {@link org.apache.lucene.geo.Point}, + * {@link org.apache.lucene.geo.Line}, {@link org.apache.lucene.geo.Polygon}. {@link Circle} is not supported. + * Check {@link GeoShapeIndexer#prepareForIndexing(org.opensearch.geometry.Geometry)} + * + * @return {@link LatLonGeometry} + */ + private static Geometry randomLuceneGeometry(final Random r) { + int shapeNumber = OpenSearchTestCase.randomIntBetween(0, 2); + if (shapeNumber == 0) { + // Point + return RandomGeoGeometryGenerator.randomPoint(r); + } else if (shapeNumber == 1) { + // LineString + return RandomGeoGeometryGenerator.randomLine(r); + } else { + // Polygon + return RandomGeoGeometryGenerator.randomPolygon(r); + } + } + +} diff --git a/server/src/test/java/org/opensearch/search/aggregations/metrics/InternalGeoBoundsTests.java b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/InternalGeoBoundsTests.java similarity index 81% rename from server/src/test/java/org/opensearch/search/aggregations/metrics/InternalGeoBoundsTests.java rename to modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/InternalGeoBoundsTests.java index e3857efff5d4d..41fb0c5b03cbf 100644 --- a/server/src/test/java/org/opensearch/search/aggregations/metrics/InternalGeoBoundsTests.java +++ b/modules/geo/src/test/java/org/opensearch/geo/search/aggregations/metrics/InternalGeoBoundsTests.java @@ -30,11 +30,18 @@ * GitHub history for details. */ -package org.opensearch.search.aggregations.metrics; +package org.opensearch.geo.search.aggregations.metrics; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.geo.GeoModulePlugin; +import org.opensearch.plugins.SearchPlugin; +import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.ParsedAggregation; import org.opensearch.test.InternalAggregationTestCase; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -44,6 +51,30 @@ public class InternalGeoBoundsTests extends InternalAggregationTestCase { static final double GEOHASH_TOLERANCE = 1E-5D; + /** + * Overriding the method so that tests can get the aggregation specs for namedWriteable. + * + * @return GeoPlugin + */ + @Override + protected SearchPlugin registerPlugin() { + return new GeoModulePlugin(); + } + + /** + * Overriding with the {@link ParsedGeoBounds} so that it can be parsed. We need to do this as {@link GeoModulePlugin} + * is registering this Aggregation. + * + * @return a List of {@link NamedXContentRegistry.Entry} + */ + @Override + protected List getNamedXContents() { + final List namedXContents = new ArrayList<>(getDefaultNamedXContents()); + final ContextParser parser = (p, c) -> ParsedGeoBounds.fromXContent(p, (String) c); + namedXContents.add(new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(GeoBoundsAggregationBuilder.NAME), parser)); + return namedXContents; + } + @Override protected InternalGeoBounds createTestInstance(String name, Map metadata) { // we occasionally want to test top = Double.NEGATIVE_INFINITY since this triggers empty xContent object diff --git a/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationBuilders.java b/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationBuilders.java new file mode 100644 index 0000000000000..c0d7e51047c6b --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationBuilders.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.tests.common; + +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoHashGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.GeoTileGridAggregationBuilder; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoHashGrid; +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoTileGrid; +import org.opensearch.geo.search.aggregations.metrics.GeoBounds; +import org.opensearch.geo.search.aggregations.metrics.GeoBoundsAggregationBuilder; + +public class AggregationBuilders { + /** + * Create a new {@link GeoBounds} aggregation with the given name. + */ + public static GeoBoundsAggregationBuilder geoBounds(String name) { + return new GeoBoundsAggregationBuilder(name); + } + + /** + * Create a new {@link InternalGeoHashGrid} aggregation with the given name. + */ + public static GeoHashGridAggregationBuilder geohashGrid(String name) { + return new GeoHashGridAggregationBuilder(name); + } + + /** + * Create a new {@link InternalGeoTileGrid} aggregation with the given name. + */ + public static GeoTileGridAggregationBuilder geotileGrid(String name) { + return new GeoTileGridAggregationBuilder(name); + } +} diff --git a/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationInspectionHelper.java b/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationInspectionHelper.java new file mode 100644 index 0000000000000..3473cf2d94b76 --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/tests/common/AggregationInspectionHelper.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.tests.common; + +import org.opensearch.geo.search.aggregations.bucket.geogrid.InternalGeoGrid; +import org.opensearch.geo.search.aggregations.metrics.InternalGeoBounds; + +public class AggregationInspectionHelper { + + public static boolean hasValue(InternalGeoBounds agg) { + return (agg.topLeft() == null && agg.bottomRight() == null) == false; + } + + public static boolean hasValue(InternalGeoGrid agg) { + return agg.getBuckets().stream().anyMatch(bucket -> bucket.getDocCount() > 0); + } +} diff --git a/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGenerator.java b/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGenerator.java new file mode 100644 index 0000000000000..a3def686b282d --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGenerator.java @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.tests.common; + +import org.opensearch.common.geo.GeoBoundingBox; +import org.opensearch.common.geo.GeoPoint; +import org.opensearch.geo.GeometryTestUtils; +import org.opensearch.geometry.Rectangle; + +import java.util.Random; + +/** + * Random geo generation utilities for randomized {@code geo_point} type testing + * does not depend on jts or spatial4j. Use RandomShapeGenerator to create random OGC compliant shapes. + * This is a copy of the file present in the server folder. We need to keep both as there are tests which are + * dependent on that file. + */ +public class RandomGeoGenerator { + + public static void randomPoint(Random r, double[] pt) { + final double[] min = { -180, -90 }; + final double[] max = { 180, 90 }; + randomPointIn(r, min[0], min[1], max[0], max[1], pt); + } + + public static void randomPointIn( + Random r, + final double minLon, + final double minLat, + final double maxLon, + final double maxLat, + double[] pt + ) { + assert pt != null && pt.length == 2; + + // normalize min and max + double[] min = { normalizeLongitude(minLon), normalizeLatitude(minLat) }; + double[] max = { normalizeLongitude(maxLon), normalizeLatitude(maxLat) }; + final double[] tMin = new double[2]; + final double[] tMax = new double[2]; + tMin[0] = Math.min(min[0], max[0]); + tMax[0] = Math.max(min[0], max[0]); + tMin[1] = Math.min(min[1], max[1]); + tMax[1] = Math.max(min[1], max[1]); + + pt[0] = tMin[0] + r.nextDouble() * (tMax[0] - tMin[0]); + pt[1] = tMin[1] + r.nextDouble() * (tMax[1] - tMin[1]); + } + + public static GeoPoint randomPoint(Random r) { + return randomPointIn(r, -180, -90, 180, 90); + } + + public static GeoPoint randomPointIn(Random r, final double minLon, final double minLat, final double maxLon, final double maxLat) { + double[] pt = new double[2]; + randomPointIn(r, minLon, minLat, maxLon, maxLat, pt); + return new GeoPoint(pt[1], pt[0]); + } + + /** Puts latitude in range of -90 to 90. */ + public static double normalizeLatitude(double latitude) { + if (latitude >= -90 && latitude <= 90) { + return latitude; // common case, and avoids slight double precision shifting + } + double off = Math.abs((latitude + 90) % 360); + return (off <= 180 ? off : 360 - off) - 90; + } + + /** Puts longitude in range of -180 to +180. */ + public static double normalizeLongitude(double longitude) { + if (longitude >= -180 && longitude <= 180) { + return longitude; // common case, and avoids slight double precision shifting + } + double off = (longitude + 180) % 360; + if (off < 0) { + return 180 + off; + } else if (off == 0 && longitude > 0) { + return 180; + } else { + return -180 + off; + } + } + + public static GeoBoundingBox randomBBox() { + Rectangle rectangle = GeometryTestUtils.randomRectangle(); + return new GeoBoundingBox( + new GeoPoint(rectangle.getMaxLat(), rectangle.getMinLon()), + new GeoPoint(rectangle.getMinLat(), rectangle.getMaxLon()) + ); + } +} diff --git a/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGeometryGenerator.java b/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGeometryGenerator.java new file mode 100644 index 0000000000000..45a72c7103ae9 --- /dev/null +++ b/modules/geo/src/test/java/org/opensearch/geo/tests/common/RandomGeoGeometryGenerator.java @@ -0,0 +1,260 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.geo.tests.common; + +import org.opensearch.geo.algorithm.PolygonGenerator; +import org.opensearch.geometry.Geometry; +import org.opensearch.geometry.GeometryCollection; +import org.opensearch.geometry.Line; +import org.opensearch.geometry.LinearRing; +import org.opensearch.geometry.MultiLine; +import org.opensearch.geometry.MultiPoint; +import org.opensearch.geometry.MultiPolygon; +import org.opensearch.geometry.Point; +import org.opensearch.geometry.Polygon; +import org.opensearch.geometry.Rectangle; +import org.opensearch.geometry.ShapeType; +import org.opensearch.index.mapper.GeoShapeIndexer; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.Assert; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * Random geo generation utilities for randomized geo_shape type testing. + */ +public class RandomGeoGeometryGenerator { + // Just picking a number 10 to be the max edges of a polygon. Don't want to make too large which can impact + // debugging. + private static final int MAX_VERTEXES = 10; + private static final int MAX_MULTIPLE_GEOMETRIES = 10; + + private static final Predicate NOT_SUPPORTED_SHAPES = shapeType -> shapeType != ShapeType.CIRCLE + && shapeType != ShapeType.LINEARRING; + + /** + * Creating list of only supported geometries defined here: {@link GeoShapeIndexer#prepareForIndexing(Geometry)} + */ + private static final List SUPPORTED_SHAPE_TYPES = Arrays.stream(ShapeType.values()) + .filter(NOT_SUPPORTED_SHAPES) + .collect(Collectors.toList()); + + /** + * Returns a random Geometry. It makes sure that only that geometry is returned which is supported by OpenSearch + * while indexing. Check {@link GeoShapeIndexer#prepareForIndexing(Geometry)} + * + * @return {@link Geometry} + */ + public static Geometry randomGeometry(final Random r) { + final ShapeType randomShapeType = SUPPORTED_SHAPE_TYPES.get( + OpenSearchTestCase.randomIntBetween(0, SUPPORTED_SHAPE_TYPES.size() - 1) + ); + switch (randomShapeType) { + case POINT: + return randomPoint(r); + case MULTIPOINT: + return randomMultiPoint(r); + case POLYGON: + return randomPolygon(r); + case LINESTRING: + return randomLine(r); + case MULTIPOLYGON: + return randomMultiPolygon(r); + case GEOMETRYCOLLECTION: + return randomGeometryCollection(r); + case MULTILINESTRING: + return randomMultiLine(r); + case ENVELOPE: + return randomRectangle(r); + default: + Assert.fail(String.format(Locale.ROOT, "Cannot create a geometry of type %s ", randomShapeType)); + } + return null; + } + + /** + * Generate a random point on the Earth Surface. + * + * @param r {@link Random} + * @return {@link Point} + */ + public static Point randomPoint(final Random r) { + double[] pt = getLonAndLatitude(r); + return new Point(pt[0], pt[1]); + } + + /** + * Generate a random polygon on earth surface. + * + * @param r {@link Random} + * @return {@link Polygon} + */ + public static Polygon randomPolygon(final Random r) { + final int vertexCount = OpenSearchTestCase.randomIntBetween(3, MAX_VERTEXES); + return randomPolygonWithFixedVertexCount(r, vertexCount); + } + + /** + * Generate a random line on the earth Surface. + * + * @param r {@link Random} + * @return {@link Line} + */ + public static Line randomLine(final Random r) { + final double[] pt1 = getLonAndLatitude(r); + final double[] pt2 = getLonAndLatitude(r); + final double[] x = { pt1[0], pt2[0] }; + final double[] y = { pt1[1], pt2[1] }; + return new Line(x, y); + } + + /** + * Returns an object of {@link MultiPoint} denoting a list of points on earth surface. + * @param r {@link Random} + * @return {@link MultiPoint} + */ + public static MultiPoint randomMultiPoint(final Random r) { + int multiplePoints = OpenSearchTestCase.randomIntBetween(1, MAX_MULTIPLE_GEOMETRIES); + final List pointsList = new ArrayList<>(); + IntStream.range(0, multiplePoints).forEach(i -> pointsList.add(randomPoint(r))); + return new MultiPoint(pointsList); + } + + /** + * Returns an object of {@link MultiPolygon} denoting various polygons on earth surface. + * + * @param r {@link Random} + * @return {@link MultiPolygon} + */ + public static MultiPolygon randomMultiPolygon(final Random r) { + int multiplePolygons = OpenSearchTestCase.randomIntBetween(1, MAX_MULTIPLE_GEOMETRIES); + final List polygonList = new ArrayList<>(); + IntStream.range(0, multiplePolygons).forEach(i -> polygonList.add(randomPolygon(r))); + return new MultiPolygon(polygonList); + } + + /** + * Returns an object of {@link GeometryCollection} having various shapes on earth surface. + * + * @param r {@link Random} + * @return {@link GeometryCollection} + */ + public static GeometryCollection randomGeometryCollection(final Random r) { + final List geometries = new ArrayList<>(); + geometries.addAll(randomMultiPoint(r).getAll()); + geometries.addAll(randomMultiPolygon(r).getAll()); + geometries.addAll(randomMultiLine(r).getAll()); + geometries.add(randomPoint(r)); + geometries.add(randomLine(r)); + geometries.add(randomPolygon(r)); + geometries.add(randomRectangle(r)); + return new GeometryCollection<>(geometries); + } + + /** + * Returns a {@link MultiLine} object containing multiple lines on earth surface. + * + * @param r {@link Random} + * @return {@link MultiLine} + */ + public static MultiLine randomMultiLine(Random r) { + int multiLines = OpenSearchTestCase.randomIntBetween(1, MAX_MULTIPLE_GEOMETRIES); + final List linesList = new ArrayList<>(); + IntStream.range(0, multiLines).forEach(i -> linesList.add(randomLine(r))); + return new MultiLine(linesList); + } + + /** + * Returns a random {@link Rectangle} created on earth surface. + * + * @param r {@link Random} + * @return {@link Rectangle} + */ + public static Rectangle randomRectangle(final Random r) { + final Polygon polygon = randomPolygonWithFixedVertexCount(r, 4); + double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY, minY = + Double.POSITIVE_INFINITY; + for (int i = 0; i < polygon.getPolygon().length(); i++) { + double x = polygon.getPolygon().getX()[i]; + double y = polygon.getPolygon().getY()[i]; + + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); + } + return new Rectangle(minX, maxX, maxY, minY); + } + + /** + * Generates a {@link Rectangle} of a specific radius. The generated rectangle can cross the international date line. + * + * @param r {@link Random} + * @param radius double + * @return {@link Rectangle} + */ + public static Rectangle randomRectangle(final Random r, double radius) { + final double[] centre = new double[2]; + RandomGeoGenerator.randomPointIn(r, -180, -(90 - radius), 180, 90 - radius, centre); + final double centreX = centre[0]; + final double centreY = centre[1]; + return new Rectangle( + RandomGeoGenerator.normalizeLongitude(centreX - radius), + RandomGeoGenerator.normalizeLongitude(centreX + radius), + centreY + radius, + centreY - radius + ); + } + + /** + * Returns a double array where pt[0] : longitude and pt[1] : latitude + * + * @param r {@link Random} + * @return double[] + */ + private static double[] getLonAndLatitude(final Random r) { + double[] pt = new double[2]; + RandomGeoGenerator.randomPoint(r, pt); + return pt; + } + + private static Polygon randomPolygonWithFixedVertexCount(final Random r, final int vertexCount) { + final List xPool = new ArrayList<>(vertexCount); + final List yPool = new ArrayList<>(vertexCount); + IntStream.range(0, vertexCount).forEach(iterator -> { + double[] pt = getLonAndLatitude(r); + xPool.add(pt[0]); + yPool.add(pt[1]); + }); + final List pointsList = PolygonGenerator.generatePolygon(xPool, yPool, r); + // Checking the list + assert vertexCount == pointsList.get(0).length; + assert vertexCount == pointsList.get(1).length; + // Create the linearRing, as we need to close the polygon hence increasing vertexes count by 1 + final double[] x = new double[vertexCount + 1]; + final double[] y = new double[vertexCount + 1]; + IntStream.range(0, vertexCount).forEach(iterator -> { + x[iterator] = pointsList.get(0)[iterator]; + y[iterator] = pointsList.get(1)[iterator]; + }); + // making sure to close the polygon + x[vertexCount] = x[0]; + y[vertexCount] = y[0]; + final LinearRing linearRing = new LinearRing(x, y); + return new Polygon(linearRing); + } + +} diff --git a/modules/geo/src/yamlRestTest/java/org/opensearch/geo/GeoClientYamlTestSuiteIT.java b/modules/geo/src/yamlRestTest/java/org/opensearch/geo/GeoClientYamlTestSuiteIT.java index 22604cff0fcf1..1f734b156689d 100644 --- a/modules/geo/src/yamlRestTest/java/org/opensearch/geo/GeoClientYamlTestSuiteIT.java +++ b/modules/geo/src/yamlRestTest/java/org/opensearch/geo/GeoClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/230_composite.yml b/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/230_composite.yml new file mode 100644 index 0000000000000..211f3c3f46b88 --- /dev/null +++ b/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/230_composite.yml @@ -0,0 +1,168 @@ +--- +setup: + - do: + indices.create: + index: test + body: + mappings: + properties: + date: + type: date + keyword: + type: keyword + long: + type: long + geo_point: + type: geo_point + nested: + type: nested + properties: + nested_long: + type: long + + - do: + indices.create: + index: other + body: + mappings: + properties: + date: + type: date + long: + type: long + nested: + type: nested + properties: + nested_long: + type: long + + - do: + index: + index: test + id: 1 + body: { "keyword": "foo", "long": [10, 20], "geo_point": "37.2343,-115.8067", "nested": [{"nested_long": 10}, {"nested_long": 20}] } + + - do: + index: + index: test + id: 2 + body: { "keyword": ["foo", "bar"], "geo_point": "41.12,-71.34" } + + - do: + index: + index: test + id: 3 + body: { "keyword": "bar", "long": [100, 0], "geo_point": "90.0,0.0", "nested": [{"nested_long": 10}, {"nested_long": 0}] } + + - do: + index: + index: test + id: 4 + body: { "keyword": "bar", "long": [1000, 0], "geo_point": "41.12,-71.34", "nested": [{"nested_long": 1000}, {"nested_long": 20}] } + + - do: + index: + index: test + id: 5 + body: { "date": "2017-10-20T03:08:45" } + + - do: + index: + index: test + id: 6 + body: { "date": "2017-10-21T07:00:00" } + + - do: + index: + index: other + id: 0 + body: { "date": "2017-10-20T03:08:45" } + + - do: + indices.refresh: + index: [test, other] +--- +"Simple Composite aggregation with GeoTile grid": + - skip: + version: " - 7.4.99" + reason: geotile_grid is not supported until 7.5.0 + - do: + search: + rest_total_hits_as_int: true + index: test + body: + aggregations: + test: + composite: + sources: [ + "geo": { + "geotile_grid": { + "field": "geo_point", + "precision": 12 + } + }, + { + "kw": { + "terms": { + "field": "keyword" + } + } + } + ] + + - match: {hits.total: 6} + - length: { aggregations.test.buckets: 4 } + - match: { aggregations.test.buckets.0.key.geo: "12/730/1590" } + - match: { aggregations.test.buckets.0.key.kw: "foo" } + - match: { aggregations.test.buckets.0.doc_count: 1 } + - match: { aggregations.test.buckets.1.key.geo: "12/1236/1533" } + - match: { aggregations.test.buckets.1.key.kw: "bar" } + - match: { aggregations.test.buckets.1.doc_count: 2 } + - match: { aggregations.test.buckets.2.key.geo: "12/1236/1533" } + - match: { aggregations.test.buckets.2.key.kw: "foo" } + - match: { aggregations.test.buckets.2.doc_count: 1 } + - match: { aggregations.test.buckets.3.key.geo: "12/2048/0" } + - match: { aggregations.test.buckets.3.key.kw: "bar" } + - match: { aggregations.test.buckets.3.doc_count: 1 } + +--- +"Simple Composite aggregation with geotile grid add aggregate after": + - skip: + version: " - 7.4.99" + reason: geotile_grid is not supported until 7.5.0 + - do: + search: + index: test + body: + aggregations: + test: + composite: + sources: [ + "geo": { + "geotile_grid": { + "field": "geo_point", + "precision": 12 + } + }, + { + "kw": { + "terms": { + "field": "keyword" + } + } + } + ] + after: { "geo": "12/730/1590", "kw": "foo" } + + - match: { hits.total.value: 6 } + - match: { hits.total.relation: "eq" } + - length: { aggregations.test.buckets: 3 } + - match: { aggregations.test.buckets.0.key.geo: "12/1236/1533" } + - match: { aggregations.test.buckets.0.key.kw: "bar" } + - match: { aggregations.test.buckets.0.doc_count: 2 } + - match: { aggregations.test.buckets.1.key.geo: "12/1236/1533" } + - match: { aggregations.test.buckets.1.key.kw: "foo" } + - match: { aggregations.test.buckets.1.doc_count: 1 } + - match: { aggregations.test.buckets.2.key.geo: "12/2048/0" } + - match: { aggregations.test.buckets.2.key.kw: "bar" } + - match: { aggregations.test.buckets.2.doc_count: 1 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/280_geohash_grid.yml b/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/280_geohash_grid.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/280_geohash_grid.yml rename to modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/280_geohash_grid.yml diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/290_geotile_grid.yml b/modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/290_geotile_grid.yml similarity index 100% rename from rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/290_geotile_grid.yml rename to modules/geo/src/yamlRestTest/resources/rest-api-spec/test/geo_shape/290_geotile_grid.yml diff --git a/modules/ingest-common/src/internalClusterTest/java/org/opensearch/ingest/common/IngestRestartIT.java b/modules/ingest-common/src/internalClusterTest/java/org/opensearch/ingest/common/IngestRestartIT.java index aeaa7246f33b8..c5f9c6ce24b34 100644 --- a/modules/ingest-common/src/internalClusterTest/java/org/opensearch/ingest/common/IngestRestartIT.java +++ b/modules/ingest-common/src/internalClusterTest/java/org/opensearch/ingest/common/IngestRestartIT.java @@ -33,16 +33,16 @@ import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.opensearch.action.support.WriteRequest; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.ingest.IngestStats; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptEngine; import org.opensearch.script.MockScriptPlugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Arrays; import java.util.Collection; @@ -86,7 +86,7 @@ protected Map, Object>> pluginScripts() { public void testFailureInConditionalProcessor() { internalCluster().ensureAtLeastNumDataNodes(1); - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String pipelineId = "foo"; client().admin() .cluster() @@ -108,7 +108,7 @@ public void testFailureInConditionalProcessor() { + " ]\n" + "}" ), - XContentType.JSON + MediaTypeRegistry.JSON ) .get(); @@ -132,7 +132,7 @@ public void testFailureInConditionalProcessor() { for (int k = 0; k < nodeCount; k++) { List stats = r.getNodes().get(k).getIngestStats().getProcessorStats().get(pipelineId); for (IngestStats.ProcessorStat st : stats) { - assertThat(st.getStats().getIngestCurrent(), greaterThanOrEqualTo(0L)); + assertThat(st.getStats().getCurrent(), greaterThanOrEqualTo(0L)); } } } @@ -160,13 +160,13 @@ public void testScriptDisabled() throws Exception { equalTo(id) ); - client().admin().cluster().preparePutPipeline(pipelineIdWithScript, pipelineWithScript, XContentType.JSON).get(); - client().admin().cluster().preparePutPipeline(pipelineIdWithoutScript, pipelineWithoutScript, XContentType.JSON).get(); + client().admin().cluster().preparePutPipeline(pipelineIdWithScript, pipelineWithScript, MediaTypeRegistry.JSON).get(); + client().admin().cluster().preparePutPipeline(pipelineIdWithoutScript, pipelineWithoutScript, MediaTypeRegistry.JSON).get(); checkPipelineExists.accept(pipelineIdWithScript); checkPipelineExists.accept(pipelineIdWithoutScript); - internalCluster().restartNode(internalCluster().getMasterName(), new InternalTestCluster.RestartCallback() { + internalCluster().restartNode(internalCluster().getClusterManagerName(), new InternalTestCluster.RestartCallback() { @Override public Settings onNodeStopped(String nodeName) { @@ -225,7 +225,7 @@ public void testPipelineWithScriptProcessorThatHasStoredScript() throws Exceptio .setId("1") .setContent( new BytesArray("{\"script\": {\"lang\": \"" + MockScriptEngine.NAME + "\", \"source\": \"my_script\"} }"), - XContentType.JSON + MediaTypeRegistry.JSON ) .get(); BytesReference pipeline = new BytesArray( @@ -236,7 +236,7 @@ public void testPipelineWithScriptProcessorThatHasStoredScript() throws Exceptio + " ]\n" + "}" ); - client().admin().cluster().preparePutPipeline("_id", pipeline, XContentType.JSON).get(); + client().admin().cluster().preparePutPipeline("_id", pipeline, MediaTypeRegistry.JSON).get(); client().prepareIndex("index") .setId("1") @@ -277,7 +277,7 @@ public void testWithDedicatedIngestNode() throws Exception { BytesReference pipeline = new BytesArray( "{\n" + " \"processors\" : [\n" + " {\"set\" : {\"field\": \"y\", \"value\": 0}}\n" + " ]\n" + "}" ); - client().admin().cluster().preparePutPipeline("_id", pipeline, XContentType.JSON).get(); + client().admin().cluster().preparePutPipeline("_id", pipeline, MediaTypeRegistry.JSON).get(); client().prepareIndex("index") .setId("1") diff --git a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/BytesProcessor.java b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/BytesProcessor.java index 3bd1137975800..b76fe41c8e67d 100644 --- a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/BytesProcessor.java +++ b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/BytesProcessor.java @@ -32,7 +32,7 @@ package org.opensearch.ingest.common; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeValue; import java.util.Map; diff --git a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/GrokProcessorGetAction.java b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/GrokProcessorGetAction.java index bb587350f4256..06709de73be01 100644 --- a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/GrokProcessorGetAction.java +++ b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/GrokProcessorGetAction.java @@ -32,19 +32,19 @@ package org.opensearch.ingest.common; import org.opensearch.LegacyESVersion; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.client.node.NodeClient; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.grok.Grok; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; diff --git a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/IngestCommonPlugin.java b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/IngestCommonPlugin.java index 969f77aa85152..672760b284004 100644 --- a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/IngestCommonPlugin.java +++ b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/IngestCommonPlugin.java @@ -33,7 +33,6 @@ package org.opensearch.ingest.common; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.common.settings.ClusterSettings; @@ -42,6 +41,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionResponse; import org.opensearch.grok.Grok; import org.opensearch.grok.MatcherWatchdog; import org.opensearch.ingest.DropProcessor; diff --git a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/JsonProcessor.java b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/JsonProcessor.java index a2b740291d04e..6de3e236ee40c 100644 --- a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/JsonProcessor.java +++ b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/JsonProcessor.java @@ -32,12 +32,12 @@ package org.opensearch.ingest.common; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.ingest.AbstractProcessor; import org.opensearch.ingest.ConfigurationUtils; import org.opensearch.ingest.IngestDocument; diff --git a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/RemoveProcessor.java b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/RemoveProcessor.java index 5da3b6bea7bc2..93a35eef4d396 100644 --- a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/RemoveProcessor.java +++ b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/RemoveProcessor.java @@ -32,6 +32,7 @@ package org.opensearch.ingest.common; +import org.opensearch.core.common.Strings; import org.opensearch.ingest.AbstractProcessor; import org.opensearch.ingest.ConfigurationUtils; import org.opensearch.ingest.IngestDocument; @@ -66,16 +67,20 @@ public List getFields() { @Override public IngestDocument execute(IngestDocument document) { - if (ignoreMissing) { - fields.forEach(field -> { - String path = document.renderTemplate(field); - if (document.hasField(path)) { - document.removeField(path); + fields.forEach(field -> { + String path = document.renderTemplate(field); + final boolean fieldPathIsNullOrEmpty = Strings.isNullOrEmpty(path); + if (fieldPathIsNullOrEmpty || document.hasField(path) == false) { + if (ignoreMissing) { + return; + } else if (fieldPathIsNullOrEmpty) { + throw new IllegalArgumentException("field path cannot be null nor empty"); + } else { + throw new IllegalArgumentException("field [" + path + "] doesn't exist"); } - }); - } else { - fields.forEach(document::removeField); - } + } + document.removeField(path); + }); return document; } diff --git a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/RenameProcessor.java b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/RenameProcessor.java index af356eb10d79c..7564bbdf95f45 100644 --- a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/RenameProcessor.java +++ b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/RenameProcessor.java @@ -32,6 +32,7 @@ package org.opensearch.ingest.common; +import org.opensearch.core.common.Strings; import org.opensearch.ingest.AbstractProcessor; import org.opensearch.ingest.ConfigurationUtils; import org.opensearch.ingest.IngestDocument; @@ -80,9 +81,12 @@ boolean isIgnoreMissing() { @Override public IngestDocument execute(IngestDocument document) { String path = document.renderTemplate(field); - if (document.hasField(path, true) == false) { + final boolean fieldPathIsNullOrEmpty = Strings.isNullOrEmpty(path); + if (fieldPathIsNullOrEmpty || document.hasField(path, true) == false) { if (ignoreMissing) { return document; + } else if (fieldPathIsNullOrEmpty) { + throw new IllegalArgumentException("field path cannot be null nor empty"); } else { throw new IllegalArgumentException("field [" + path + "] doesn't exist"); } diff --git a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/ScriptProcessor.java b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/ScriptProcessor.java index f2568826fa484..cc8889af27621 100644 --- a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/ScriptProcessor.java +++ b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/ScriptProcessor.java @@ -33,19 +33,17 @@ package org.opensearch.ingest.common; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.logging.DeprecationLogger; -import org.opensearch.common.util.CollectionUtils; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.ingest.AbstractProcessor; import org.opensearch.ingest.IngestDocument; import org.opensearch.ingest.Processor; -import org.opensearch.script.DynamicMap; import org.opensearch.script.IngestScript; import org.opensearch.script.Script; import org.opensearch.script.ScriptException; @@ -55,7 +53,6 @@ import java.io.InputStream; import java.util.Arrays; import java.util.Map; -import java.util.function.Function; import static org.opensearch.ingest.ConfigurationUtils.newConfigurationException; @@ -64,12 +61,6 @@ */ public final class ScriptProcessor extends AbstractProcessor { - private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(DynamicMap.class); - private static final Map> PARAMS_FUNCTIONS = org.opensearch.common.collect.Map.of("_type", value -> { - deprecationLogger.deprecate("script_processor", "[types removal] Looking up doc types [_type] in scripts is deprecated."); - return value; - }); - public static final String TYPE = "script"; private final Script script; @@ -111,7 +102,7 @@ public IngestDocument execute(IngestDocument document) { } else { ingestScript = precompiledIngestScript; } - ingestScript.execute(new DynamicMap(document.getSourceAndMetadata(), PARAMS_FUNCTIONS)); + ingestScript.execute(document.getSourceAndMetadata()); CollectionUtils.ensureNoSelfReferences(document.getSourceAndMetadata(), "ingest script"); return document; } @@ -146,7 +137,7 @@ public ScriptProcessor create( try ( XContentBuilder builder = XContentBuilder.builder(JsonXContent.jsonXContent).map(config); InputStream stream = BytesReference.bytes(builder).streamInput(); - XContentParser parser = XContentType.JSON.xContent() + XContentParser parser = MediaTypeRegistry.JSON.xContent() .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, stream) ) { Script script = Script.parse(parser); diff --git a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/URLDecodeProcessor.java b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/URLDecodeProcessor.java index bf80c5b064703..bb236f957a587 100644 --- a/modules/ingest-common/src/main/java/org/opensearch/ingest/common/URLDecodeProcessor.java +++ b/modules/ingest-common/src/main/java/org/opensearch/ingest/common/URLDecodeProcessor.java @@ -32,8 +32,8 @@ package org.opensearch.ingest.common; -import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Map; /** @@ -48,11 +48,7 @@ public final class URLDecodeProcessor extends AbstractStringProcessor { } public static String apply(String value) { - try { - return URLDecoder.decode(value, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("Could not URL-decode value.", e); - } + return URLDecoder.decode(value, StandardCharsets.UTF_8); } @Override diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/AppendProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/AppendProcessorTests.java index 7caa63792f347..1cb1ad7e408f6 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/AppendProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/AppendProcessorTests.java @@ -203,7 +203,7 @@ public void testAppendingUniqueValueToScalar() throws Exception { appendProcessor.execute(ingestDocument); List list = ingestDocument.getFieldValue(field, List.class); assertThat(list.size(), equalTo(2)); - assertThat(list, equalTo(org.opensearch.common.collect.List.of(originalValue, newValue))); + assertThat(list, equalTo(List.of(originalValue, newValue))); } public void testAppendingToListWithDuplicatesDisallowed() throws Exception { diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/BytesProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/BytesProcessorTests.java index bbd9ff4c8b912..ce8c182b60a61 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/BytesProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/BytesProcessorTests.java @@ -33,15 +33,14 @@ package org.opensearch.ingest.common; import org.opensearch.OpenSearchException; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.OpenSearchParseException; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.ingest.IngestDocument; import org.opensearch.ingest.Processor; import org.opensearch.ingest.RandomDocumentPicks; import org.hamcrest.CoreMatchers; -import static org.hamcrest.Matchers.equalTo; - public class BytesProcessorTests extends AbstractStringProcessorTestCase { private String modifiedInput; @@ -101,14 +100,16 @@ public void testMissingUnits() { assertThat(exception.getMessage(), CoreMatchers.containsString("unit is missing or unrecognized")); } - public void testFractional() throws Exception { + public void testFractional() { IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random()); String fieldName = RandomDocumentPicks.addRandomField(random(), ingestDocument, "1.1kb"); Processor processor = newProcessor(fieldName, randomBoolean(), fieldName); - processor.execute(ingestDocument); - assertThat(ingestDocument.getFieldValue(fieldName, expectedResultType()), equalTo(1126L)); - assertWarnings( - "Fractional bytes values are deprecated. Use non-fractional bytes values instead: [1.1kb] found for setting " + "[Ingest Field]" + OpenSearchParseException e = expectThrows(OpenSearchParseException.class, () -> processor.execute(ingestDocument)); + assertThat( + e.getMessage(), + CoreMatchers.containsString( + "Fractional bytes values have been deprecated since Legacy 6.2. " + "Use non-fractional bytes values instead:" + ) ); } } diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ConvertProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ConvertProcessorTests.java index 6eed29e330f2c..0ba0a39261d00 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ConvertProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ConvertProcessorTests.java @@ -32,6 +32,11 @@ package org.opensearch.ingest.common; +import org.opensearch.ingest.IngestDocument; +import org.opensearch.ingest.Processor; +import org.opensearch.ingest.RandomDocumentPicks; +import org.opensearch.test.OpenSearchTestCase; + import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -39,11 +44,6 @@ import java.util.Locale; import java.util.Map; -import org.opensearch.ingest.IngestDocument; -import org.opensearch.ingest.Processor; -import org.opensearch.ingest.RandomDocumentPicks; -import org.opensearch.test.OpenSearchTestCase; - import static org.opensearch.ingest.IngestDocumentMatcher.assertIngestDocument; import static org.opensearch.ingest.common.ConvertProcessor.Type; import static org.hamcrest.Matchers.containsString; diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/CsvProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/CsvProcessorTests.java index 1359750dc16ea..650104ac3e9a0 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/CsvProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/CsvProcessorTests.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ingest.IngestDocument; import org.opensearch.ingest.RandomDocumentPicks; import org.opensearch.test.OpenSearchTestCase; diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/DateFormatTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/DateFormatTests.java index 951b93deb6e8b..04900fe6f7496 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/DateFormatTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/DateFormatTests.java @@ -32,7 +32,6 @@ package org.opensearch.ingest.common; -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.common.time.DateFormatter; import org.opensearch.common.time.DateUtils; import org.opensearch.test.OpenSearchTestCase; @@ -96,10 +95,6 @@ public void testParseJavaDefaultYear() { } public void testParseWeekBased() { - assumeFalse( - "won't work in jdk8 " + "because SPI mechanism is not looking at classpath - needs ISOCalendarDataProvider in jre's ext/libs", - JavaVersion.current().equals(JavaVersion.parse("8")) - ); String format = randomFrom("YYYY-ww"); ZoneId timezone = DateUtils.of("Europe/Amsterdam"); Function javaFunction = DateFormat.Java.getFunction(format, timezone, Locale.ROOT); @@ -108,10 +103,6 @@ public void testParseWeekBased() { } public void testParseWeekBasedWithLocale() { - assumeFalse( - "won't work in jdk8 " + "because SPI mechanism is not looking at classpath - needs ISOCalendarDataProvider in jre's ext/libs", - JavaVersion.current().equals(JavaVersion.parse("8")) - ); String format = randomFrom("YYYY-ww"); ZoneId timezone = DateUtils.of("Europe/Amsterdam"); Function javaFunction = DateFormat.Java.getFunction(format, timezone, Locale.US); diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ForEachProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ForEachProcessorTests.java index 8db3cefc3a6fd..241945e58fa06 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ForEachProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ForEachProcessorTests.java @@ -127,7 +127,6 @@ public void testMetadataAvailable() throws Exception { TestProcessor innerProcessor = new TestProcessor(id -> { id.setFieldValue("_ingest._value.index", id.getSourceAndMetadata().get("_index")); - id.setFieldValue("_ingest._value.type", id.getSourceAndMetadata().get("_type")); id.setFieldValue("_ingest._value.id", id.getSourceAndMetadata().get("_id")); }); ForEachProcessor processor = new ForEachProcessor("_tag", null, "values", innerProcessor, false); @@ -227,12 +226,8 @@ public void testModifyFieldsOutsideArray() throws Exception { "values", new CompoundProcessor( false, - org.opensearch.common.collect.List.of( - new UppercaseProcessor("_tag_upper", null, "_ingest._value", false, "_ingest._value") - ), - org.opensearch.common.collect.List.of( - new AppendProcessor("_tag", null, template, (model) -> (Collections.singletonList("added")), true) - ) + List.of(new UppercaseProcessor("_tag_upper", null, "_ingest._value", false, "_ingest._value")), + List.of(new AppendProcessor("_tag", null, template, (model) -> (Collections.singletonList("added")), true)) ), false ); diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/GrokProcessorGetActionTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/GrokProcessorGetActionTests.java index 5ccf85dd53f0e..f8c760d48920f 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/GrokProcessorGetActionTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/GrokProcessorGetActionTests.java @@ -32,15 +32,15 @@ package org.opensearch.ingest.common; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.transport.TransportService; @@ -57,7 +57,7 @@ import static org.mockito.Mockito.mock; public class GrokProcessorGetActionTests extends OpenSearchTestCase { - private static final Map TEST_PATTERNS = org.opensearch.common.collect.Map.of("PATTERN2", "foo2", "PATTERN1", "foo1"); + private static final Map TEST_PATTERNS = Map.of("PATTERN2", "foo2", "PATTERN1", "foo1"); public void testRequest() throws Exception { GrokProcessorGetAction.Request request = new GrokProcessorGetAction.Request(false); diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/JsonProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/JsonProcessorTests.java index 9aad2f9c7edd1..bde5dcd90e951 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/JsonProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/JsonProcessorTests.java @@ -32,11 +32,11 @@ package org.opensearch.ingest.common; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.ingest.IngestDocument; import org.opensearch.ingest.RandomDocumentPicks; import org.opensearch.test.OpenSearchTestCase; @@ -61,7 +61,7 @@ public void testExecute() throws Exception { Map randomJsonMap = RandomDocumentPicks.randomSource(random()); XContentBuilder builder = JsonXContent.contentBuilder().map(randomJsonMap); - String randomJson = XContentHelper.convertToJson(BytesReference.bytes(builder), false, XContentType.JSON); + String randomJson = XContentHelper.convertToJson(BytesReference.bytes(builder), false, MediaTypeRegistry.JSON); document.put(randomField, randomJson); IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), document); diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/RemoveProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/RemoveProcessorTests.java index cf65236157111..8f729c6a39bbd 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/RemoveProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/RemoveProcessorTests.java @@ -42,7 +42,6 @@ import java.util.HashMap; import java.util.Map; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; public class RemoveProcessorTests extends OpenSearchTestCase { @@ -67,12 +66,44 @@ public void testRemoveNonExistingField() throws Exception { config.put("field", fieldName); String processorTag = randomAlphaOfLength(10); Processor processor = new RemoveProcessor.Factory(TestTemplateService.instance()).create(null, processorTag, null, config); - try { - processor.execute(ingestDocument); - fail("remove field should have failed"); - } catch (IllegalArgumentException e) { - assertThat(e.getMessage(), containsString("not present as part of path [" + fieldName + "]")); - } + assertThrows( + "field [" + fieldName + "] doesn't exist", + IllegalArgumentException.class, + () -> { processor.execute(ingestDocument); } + ); + + Map configWithEmptyField = new HashMap<>(); + configWithEmptyField.put("field", ""); + processorTag = randomAlphaOfLength(10); + Processor removeProcessorWithEmptyField = new RemoveProcessor.Factory(TestTemplateService.instance()).create( + null, + processorTag, + null, + configWithEmptyField + ); + assertThrows( + "field path cannot be null nor empty", + IllegalArgumentException.class, + () -> removeProcessorWithEmptyField.execute(ingestDocument) + ); + } + + public void testRemoveEmptyField() throws Exception { + IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); + Map config = new HashMap<>(); + config.put("field", ""); + String processorTag = randomAlphaOfLength(10); + Processor removeProcessorWithEmptyField = new RemoveProcessor.Factory(TestTemplateService.instance()).create( + null, + processorTag, + null, + config + ); + assertThrows( + "field path cannot be null nor empty", + IllegalArgumentException.class, + () -> removeProcessorWithEmptyField.execute(ingestDocument) + ); } public void testIgnoreMissing() throws Exception { @@ -84,5 +115,13 @@ public void testIgnoreMissing() throws Exception { String processorTag = randomAlphaOfLength(10); Processor processor = new RemoveProcessor.Factory(TestTemplateService.instance()).create(null, processorTag, null, config); processor.execute(ingestDocument); + + // when using template snippet, the resolved field path maybe empty + Map configWithEmptyField = new HashMap<>(); + configWithEmptyField.put("field", ""); + configWithEmptyField.put("ignore_missing", true); + processorTag = randomAlphaOfLength(10); + processor = new RemoveProcessor.Factory(TestTemplateService.instance()).create(null, processorTag, null, configWithEmptyField); + processor.execute(ingestDocument); } } diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/RenameProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/RenameProcessorTests.java index fc95693024cb0..a600464371af8 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/RenameProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/RenameProcessorTests.java @@ -112,6 +112,15 @@ public void testRenameNonExistingField() throws Exception { } catch (IllegalArgumentException e) { assertThat(e.getMessage(), equalTo("field [" + fieldName + "] doesn't exist")); } + + // when using template snippet, the resolved field path maybe empty + processor = createRenameProcessor("", RandomDocumentPicks.randomFieldName(random()), false); + try { + processor.execute(ingestDocument); + fail("processor execute should have failed"); + } catch (IllegalArgumentException e) { + assertThat(e.getMessage(), equalTo("field path cannot be null nor empty")); + } } public void testRenameNonExistingFieldWithIgnoreMissing() throws Exception { @@ -121,6 +130,11 @@ public void testRenameNonExistingFieldWithIgnoreMissing() throws Exception { Processor processor = createRenameProcessor(fieldName, RandomDocumentPicks.randomFieldName(random()), true); processor.execute(ingestDocument); assertIngestDocument(originalIngestDocument, ingestDocument); + + // when using template snippet, the resolved field path maybe empty + processor = createRenameProcessor("", RandomDocumentPicks.randomFieldName(random()), true); + processor.execute(ingestDocument); + assertIngestDocument(originalIngestDocument, ingestDocument); } public void testRenameNewFieldAlreadyExists() throws Exception { diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ScriptProcessorFactoryTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ScriptProcessorFactoryTests.java index e5d2334f75ac9..35b56c2677134 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ScriptProcessorFactoryTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ScriptProcessorFactoryTests.java @@ -34,7 +34,7 @@ import org.opensearch.OpenSearchException; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParseException; import org.opensearch.script.IngestScript; import org.opensearch.script.MockScriptEngine; import org.opensearch.script.Script; diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ScriptProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ScriptProcessorTests.java index 1aa4898441598..96d9be75c4ab7 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ScriptProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/ScriptProcessorTests.java @@ -105,24 +105,4 @@ private void assertIngestDocument(IngestDocument ingestDocument) { int bytesTotal = ingestDocument.getFieldValue("bytes_in", Integer.class) + ingestDocument.getFieldValue("bytes_out", Integer.class); assertThat(ingestDocument.getSourceAndMetadata().get("bytes_total"), is(bytesTotal)); } - - public void testTypeDeprecation() throws Exception { - String scriptName = "script"; - ScriptService scriptService = new ScriptService( - Settings.builder().build(), - Collections.singletonMap( - Script.DEFAULT_SCRIPT_LANG, - new MockScriptEngine(Script.DEFAULT_SCRIPT_LANG, Collections.singletonMap(scriptName, ctx -> { - ctx.get("_type"); - return null; - }), Collections.emptyMap()) - ), - new HashMap<>(ScriptModule.CORE_CONTEXTS) - ); - Script script = new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, scriptName, Collections.emptyMap()); - IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), Collections.emptyMap()); - ScriptProcessor processor = new ScriptProcessor(randomAlphaOfLength(10), null, script, null, scriptService); - processor.execute(ingestDocument); - assertWarnings("[types removal] Looking up doc types [_type] in scripts is deprecated."); - } } diff --git a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/URLDecodeProcessorTests.java b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/URLDecodeProcessorTests.java index 81ed3c89768b7..3d68648825594 100644 --- a/modules/ingest-common/src/test/java/org/opensearch/ingest/common/URLDecodeProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/opensearch/ingest/common/URLDecodeProcessorTests.java @@ -32,13 +32,14 @@ package org.opensearch.ingest.common; -import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; public class URLDecodeProcessorTests extends AbstractStringProcessorTestCase { @Override protected String modifyInput(String input) { - return "Hello%20G%C3%BCnter" + input; + return "Hello%20G%C3%BCnter" + urlEncode(input); } @Override @@ -48,10 +49,10 @@ protected AbstractStringProcessor newProcessor(String field, boolean ign @Override protected String expectedResult(String input) { - try { - return "Hello Günter" + URLDecoder.decode(input, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("invalid"); - } + return "Hello Günter" + URLDecoder.decode(urlEncode(input), StandardCharsets.UTF_8); + } + + private static String urlEncode(String s) { + return URLEncoder.encode(s, StandardCharsets.UTF_8); } } diff --git a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/10_basic.yml b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/10_basic.yml index 8a803eae1fc3d..f44cc1f9f9fcf 100644 --- a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/10_basic.yml +++ b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/10_basic.yml @@ -5,34 +5,34 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: ingest-common } } - - contains: { nodes.$master.ingest.processors: { type: append } } - - contains: { nodes.$master.ingest.processors: { type: bytes } } - - contains: { nodes.$master.ingest.processors: { type: convert } } - - contains: { nodes.$master.ingest.processors: { type: date } } - - contains: { nodes.$master.ingest.processors: { type: date_index_name } } - - contains: { nodes.$master.ingest.processors: { type: dissect } } - - contains: { nodes.$master.ingest.processors: { type: dot_expander } } - - contains: { nodes.$master.ingest.processors: { type: fail } } - - contains: { nodes.$master.ingest.processors: { type: foreach } } - - contains: { nodes.$master.ingest.processors: { type: grok } } - - contains: { nodes.$master.ingest.processors: { type: gsub } } - - contains: { nodes.$master.ingest.processors: { type: html_strip } } - - contains: { nodes.$master.ingest.processors: { type: join } } - - contains: { nodes.$master.ingest.processors: { type: json } } - - contains: { nodes.$master.ingest.processors: { type: kv } } - - contains: { nodes.$master.ingest.processors: { type: lowercase } } - - contains: { nodes.$master.ingest.processors: { type: remove } } - - contains: { nodes.$master.ingest.processors: { type: rename } } - - contains: { nodes.$master.ingest.processors: { type: script } } - - contains: { nodes.$master.ingest.processors: { type: set } } - - contains: { nodes.$master.ingest.processors: { type: sort } } - - contains: { nodes.$master.ingest.processors: { type: split } } - - contains: { nodes.$master.ingest.processors: { type: trim } } - - contains: { nodes.$master.ingest.processors: { type: uppercase } } + - contains: { nodes.$cluster_manager.modules: { name: ingest-common } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: append } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: bytes } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: convert } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: date } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: date_index_name } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: dissect } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: dot_expander } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: fail } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: foreach } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: grok } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: gsub } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: html_strip } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: join } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: json } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: kv } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: lowercase } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: remove } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: rename } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: script } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: set } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: sort } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: split } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: trim } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: uppercase } } diff --git a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/120_grok.yml b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/120_grok.yml index 14c70c17265af..c0aec0e3d7392 100644 --- a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/120_grok.yml +++ b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/120_grok.yml @@ -134,7 +134,6 @@ teardown: "docs": [ { "_index": "index", - "_type": "type", "_id": "id", "_source": { "field": "abc2xyz" diff --git a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/220_drop_processor.yml b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/220_drop_processor.yml index 77a1df81a296a..ef8332c2670d0 100644 --- a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/220_drop_processor.yml +++ b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/220_drop_processor.yml @@ -91,4 +91,3 @@ teardown: get: index: test id: 3 - diff --git a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/280_rename_processor.yml b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/280_rename_processor.yml new file mode 100644 index 0000000000000..96b2256bcc1dc --- /dev/null +++ b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/280_rename_processor.yml @@ -0,0 +1,66 @@ +--- +teardown: + - do: + ingest.delete_pipeline: + id: "my_pipeline" + ignore: 404 + +--- +"Test rename processor with non-existing field and without ignore_missing": + - do: + ingest.put_pipeline: + id: "my_pipeline" + body: > + { + "description": "_description", + "processors": [ + { + "rename" : { + "field" : "{{field_foo}}", + "target_field" : "bar" + } + } + ] + } + - match: { acknowledged: true } + + - do: + catch: '/field path cannot be null nor empty/' + index: + index: test + id: 1 + pipeline: "my_pipeline" + body: { message: "foo bar baz" } + +--- +"Test rename processor with non-existing field and ignore_missing": + - do: + ingest.put_pipeline: + id: "my_pipeline" + body: > + { + "description": "_description", + "processors": [ + { + "rename" : { + "field" : "{{field_foo}}", + "target_field" : "bar", + "ignore_missing" : true + } + } + ] + } + - match: { acknowledged: true } + + - do: + index: + index: test + id: 1 + pipeline: "my_pipeline" + body: { message: "foo bar baz" } + + - do: + get: + index: test + id: 1 + - match: { _source.message: "foo bar baz" } diff --git a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/290_remove_processor.yml b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/290_remove_processor.yml new file mode 100644 index 0000000000000..ff5a17136afa2 --- /dev/null +++ b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/290_remove_processor.yml @@ -0,0 +1,93 @@ +--- +teardown: + - do: + ingest.delete_pipeline: + id: "my_pipeline" + ignore: 404 + +--- +"Test remove processor with non-existing field and without ignore_missing": + - do: + ingest.put_pipeline: + id: "my_pipeline" + body: > + { + "description": "_description", + "processors": [ + { + "remove" : { + "field" : "{{unknown}}" + } + } + ] + } + - match: { acknowledged: true } + + - do: + catch: /field path cannot be null nor empty/ + index: + index: test + id: 1 + pipeline: "my_pipeline" + body: { message: "foo bar baz" } + +--- +"Test remove processor with resolved field path doesn't exist": + - do: + ingest.put_pipeline: + id: "my_pipeline" + body: > + { + "description": "_description", + "processors": [ + { + "remove" : { + "field" : "{{foo}}" + } + } + ] + } + - match: { acknowledged: true } + + - do: + catch: /field \[bar\] doesn\'t exist/ + index: + index: test + id: 1 + pipeline: "my_pipeline" + body: { + message: "foo bar baz", + foo: "bar" + } + +--- +"Test remove processor with non-existing field and ignore_missing": + - do: + ingest.put_pipeline: + id: "my_pipeline" + body: > + { + "description": "_description", + "processors": [ + { + "remove" : { + "field" : "{{unknown}}", + "ignore_missing" : true + } + } + ] + } + - match: { acknowledged: true } + + - do: + index: + index: test + id: 1 + pipeline: "my_pipeline" + body: { message: "foo bar baz" } + + - do: + get: + index: test + id: 1 + - match: { _source.message: "foo bar baz" } diff --git a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/70_bulk.yml b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/70_bulk.yml index 5b442456e64d0..2dfa17174b139 100644 --- a/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/70_bulk.yml +++ b/modules/ingest-common/src/yamlRestTest/resources/rest-api-spec/test/ingest/70_bulk.yml @@ -77,21 +77,21 @@ teardown: - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.stats: metric: [ ingest ] #we can't assert anything here since we might have more than one node in the cluster - - gte: {nodes.$master.ingest.total.count: 0} - - gte: {nodes.$master.ingest.total.failed: 0} - - gte: {nodes.$master.ingest.total.time_in_millis: 0} - - match: {nodes.$master.ingest.total.current: 0} - - gte: {nodes.$master.ingest.pipelines.pipeline1.count: 0} - - match: {nodes.$master.ingest.pipelines.pipeline1.failed: 0} - - gte: {nodes.$master.ingest.pipelines.pipeline1.time_in_millis: 0} - - match: {nodes.$master.ingest.pipelines.pipeline1.current: 0} + - gte: {nodes.$cluster_manager.ingest.total.count: 0} + - gte: {nodes.$cluster_manager.ingest.total.failed: 0} + - gte: {nodes.$cluster_manager.ingest.total.time_in_millis: 0} + - match: {nodes.$cluster_manager.ingest.total.current: 0} + - gte: {nodes.$cluster_manager.ingest.pipelines.pipeline1.count: 0} + - match: {nodes.$cluster_manager.ingest.pipelines.pipeline1.failed: 0} + - gte: {nodes.$cluster_manager.ingest.pipelines.pipeline1.time_in_millis: 0} + - match: {nodes.$cluster_manager.ingest.pipelines.pipeline1.current: 0} --- "Test bulk request with default pipeline": @@ -113,21 +113,21 @@ teardown: - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.stats: metric: [ ingest ] #we can't assert anything here since we might have more than one node in the cluster - - gte: {nodes.$master.ingest.total.count: 0} - - gte: {nodes.$master.ingest.total.failed: 0} - - gte: {nodes.$master.ingest.total.time_in_millis: 0} - - match: {nodes.$master.ingest.total.current: 0} - - gte: {nodes.$master.ingest.pipelines.pipeline2.count: 0} - - match: {nodes.$master.ingest.pipelines.pipeline2.failed: 0} - - gte: {nodes.$master.ingest.pipelines.pipeline2.time_in_millis: 0} - - match: {nodes.$master.ingest.pipelines.pipeline2.current: 0} + - gte: {nodes.$cluster_manager.ingest.total.count: 0} + - gte: {nodes.$cluster_manager.ingest.total.failed: 0} + - gte: {nodes.$cluster_manager.ingest.total.time_in_millis: 0} + - match: {nodes.$cluster_manager.ingest.total.current: 0} + - gte: {nodes.$cluster_manager.ingest.pipelines.pipeline2.count: 0} + - match: {nodes.$cluster_manager.ingest.pipelines.pipeline2.failed: 0} + - gte: {nodes.$cluster_manager.ingest.pipelines.pipeline2.time_in_millis: 0} + - match: {nodes.$cluster_manager.ingest.pipelines.pipeline2.current: 0} - do: get: diff --git a/modules/ingest-geoip/build.gradle b/modules/ingest-geoip/build.gradle index f78dc49e9fb8a..129c40666028f 100644 --- a/modules/ingest-geoip/build.gradle +++ b/modules/ingest-geoip/build.gradle @@ -39,11 +39,11 @@ opensearchplugin { } dependencies { - api('com.maxmind.geoip2:geoip2:2.16.1') + api('com.maxmind.geoip2:geoip2:4.1.0') // geoip2 dependencies: + api('com.maxmind.db:maxmind-db:3.0.0') api("com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}") - api("com.fasterxml.jackson.core:jackson-databind:${versions.jackson}") - api('com.maxmind.db:maxmind-db:2.0.0') + api("com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}") testImplementation 'org.elasticsearch:geolite2-databases:20191119' } @@ -67,24 +67,6 @@ tasks.named("bundlePlugin").configure { } } -tasks.named("thirdPartyAudit").configure { - ignoreMissingClasses( - // geoip WebServiceClient needs apache http client, but we're not using WebServiceClient: - 'org.apache.http.HttpEntity', - 'org.apache.http.HttpResponse', - 'org.apache.http.StatusLine', - 'org.apache.http.client.config.RequestConfig$Builder', - 'org.apache.http.client.config.RequestConfig', - 'org.apache.http.client.methods.CloseableHttpResponse', - 'org.apache.http.client.methods.HttpGet', - 'org.apache.http.client.utils.URIBuilder', - 'org.apache.http.impl.auth.BasicScheme', - 'org.apache.http.impl.client.CloseableHttpClient', - 'org.apache.http.impl.client.HttpClientBuilder', - 'org.apache.http.util.EntityUtils' - ) -} - if (Os.isFamily(Os.FAMILY_WINDOWS)) { tasks.named("test").configure { // Windows cannot cleanup database files properly unless it loads everything on heap. diff --git a/modules/ingest-geoip/licenses/geoip2-2.16.1.jar.sha1 b/modules/ingest-geoip/licenses/geoip2-2.16.1.jar.sha1 deleted file mode 100644 index 0221476794d3a..0000000000000 --- a/modules/ingest-geoip/licenses/geoip2-2.16.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c92040bd6ef2cb59be71c6749d08c141ca546caf \ No newline at end of file diff --git a/modules/ingest-geoip/licenses/geoip2-4.1.0.jar.sha1 b/modules/ingest-geoip/licenses/geoip2-4.1.0.jar.sha1 new file mode 100644 index 0000000000000..0d124299e4cfb --- /dev/null +++ b/modules/ingest-geoip/licenses/geoip2-4.1.0.jar.sha1 @@ -0,0 +1 @@ +b6b356cc91863409ba3475a148ee11a3a6d6aa4b \ No newline at end of file diff --git a/modules/ingest-geoip/licenses/jackson-annotations-2.13.2.jar.sha1 b/modules/ingest-geoip/licenses/jackson-annotations-2.13.2.jar.sha1 deleted file mode 100644 index ecd3fb49d5b12..0000000000000 --- a/modules/ingest-geoip/licenses/jackson-annotations-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -ec18851f1976d5b810ae1a5fcc32520d2d38f77a \ No newline at end of file diff --git a/modules/ingest-geoip/licenses/jackson-annotations-2.15.2.jar.sha1 b/modules/ingest-geoip/licenses/jackson-annotations-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f63416ddb8ceb --- /dev/null +++ b/modules/ingest-geoip/licenses/jackson-annotations-2.15.2.jar.sha1 @@ -0,0 +1 @@ +4724a65ac8e8d156a24898d50fd5dbd3642870b8 \ No newline at end of file diff --git a/modules/ingest-geoip/licenses/jackson-databind-2.13.2.jar.sha1 b/modules/ingest-geoip/licenses/jackson-databind-2.13.2.jar.sha1 deleted file mode 100644 index 5d356f3fd045f..0000000000000 --- a/modules/ingest-geoip/licenses/jackson-databind-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -926e48c451166a291f1ce6c6276d9abbefa7c00f \ No newline at end of file diff --git a/modules/ingest-geoip/licenses/jackson-databind-2.15.2.jar.sha1 b/modules/ingest-geoip/licenses/jackson-databind-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f16d80af8dce6 --- /dev/null +++ b/modules/ingest-geoip/licenses/jackson-databind-2.15.2.jar.sha1 @@ -0,0 +1 @@ +9353b021f10c307c00328f52090de2bdb4b6ff9c \ No newline at end of file diff --git a/modules/ingest-geoip/licenses/maxmind-db-2.0.0.jar.sha1 b/modules/ingest-geoip/licenses/maxmind-db-2.0.0.jar.sha1 deleted file mode 100644 index 32c18f89c6a29..0000000000000 --- a/modules/ingest-geoip/licenses/maxmind-db-2.0.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e7e0fd82da0a160b7928ba214e699a7e6a74fff4 \ No newline at end of file diff --git a/modules/ingest-geoip/licenses/maxmind-db-3.0.0.jar.sha1 b/modules/ingest-geoip/licenses/maxmind-db-3.0.0.jar.sha1 new file mode 100644 index 0000000000000..89b0c4c49b450 --- /dev/null +++ b/modules/ingest-geoip/licenses/maxmind-db-3.0.0.jar.sha1 @@ -0,0 +1 @@ +79dcda62168a77caf595f8fda101baa17fef125d \ No newline at end of file diff --git a/modules/ingest-geoip/src/internalClusterTest/java/org/opensearch/ingest/geoip/GeoIpProcessorNonIngestNodeIT.java b/modules/ingest-geoip/src/internalClusterTest/java/org/opensearch/ingest/geoip/GeoIpProcessorNonIngestNodeIT.java index e88c77b8e33f4..5658f083db2d2 100644 --- a/modules/ingest-geoip/src/internalClusterTest/java/org/opensearch/ingest/geoip/GeoIpProcessorNonIngestNodeIT.java +++ b/modules/ingest-geoip/src/internalClusterTest/java/org/opensearch/ingest/geoip/GeoIpProcessorNonIngestNodeIT.java @@ -36,17 +36,17 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.ingest.PutPipelineRequest; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.ingest.IngestService; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.NodeRoles; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.StreamsUtils; import java.io.ByteArrayInputStream; @@ -158,7 +158,7 @@ public void testLazyLoading() throws IOException { builder.endObject(); bytes = BytesReference.bytes(builder); } - assertAcked(client().admin().cluster().putPipeline(new PutPipelineRequest("geoip", bytes, XContentType.JSON)).actionGet()); + assertAcked(client().admin().cluster().putPipeline(new PutPipelineRequest("geoip", bytes, MediaTypeRegistry.JSON)).actionGet()); // the geo-IP databases should not be loaded on any nodes as they are all non-ingest nodes Arrays.stream(internalCluster().getNodeNames()).forEach(node -> assertDatabaseLoadStatus(node, false)); diff --git a/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/DatabaseReaderLazyLoader.java b/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/DatabaseReaderLazyLoader.java index 1cafae0e50cef..0c2cde786dd40 100644 --- a/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/DatabaseReaderLazyLoader.java +++ b/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/DatabaseReaderLazyLoader.java @@ -33,11 +33,12 @@ package org.opensearch.ingest.geoip; import com.maxmind.geoip2.DatabaseReader; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.lucene.util.SetOnce; import org.opensearch.common.CheckedSupplier; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.SetOnce; +import org.opensearch.common.util.io.IOUtils; import java.io.Closeable; import java.io.IOException; @@ -71,7 +72,7 @@ class DatabaseReaderLazyLoader implements Closeable { /** * Read the database type from the database. We do this manually instead of relying on the built-in mechanism to avoid reading the - * entire database into memory merely to read the type. This is especially important to maintain on master nodes where pipelines are + * entire database into memory merely to read the type. This is especially important to maintain on cluster-manager nodes where pipelines are * validated. If we read the entire database into memory, we could potentially run into low-memory constraints on such nodes where * loading this data would otherwise be wasteful if they are not also ingest nodes. * diff --git a/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/GeoIpProcessor.java b/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/GeoIpProcessor.java index 384ae6f14dc4d..6801d85d1424c 100644 --- a/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/GeoIpProcessor.java +++ b/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/GeoIpProcessor.java @@ -42,6 +42,7 @@ import com.maxmind.geoip2.record.Country; import com.maxmind.geoip2.record.Location; import com.maxmind.geoip2.record.Subdivision; + import org.opensearch.OpenSearchParseException; import org.opensearch.SpecialPermission; import org.opensearch.common.network.InetAddresses; @@ -364,7 +365,7 @@ private Map retrieveAsnGeoData(InetAddress ipAddress) { }) ); - Integer asn = response.getAutonomousSystemNumber(); + Long asn = response.getAutonomousSystemNumber(); String organization_name = response.getAutonomousSystemOrganization(); Network network = response.getNetwork(); diff --git a/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/IngestGeoIpPlugin.java b/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/IngestGeoIpPlugin.java index 6af408c185374..8e51255b6123d 100644 --- a/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/IngestGeoIpPlugin.java +++ b/modules/ingest-geoip/src/main/java/org/opensearch/ingest/geoip/IngestGeoIpPlugin.java @@ -37,13 +37,14 @@ import com.maxmind.db.Reader; import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.model.AbstractResponse; + import org.opensearch.common.Booleans; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.cache.Cache; import org.opensearch.common.cache.CacheBuilder; import org.opensearch.common.io.PathUtils; import org.opensearch.common.settings.Setting; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.ingest.Processor; import org.opensearch.plugins.IngestPlugin; import org.opensearch.plugins.Plugin; diff --git a/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/GeoIpProcessorFactoryTests.java b/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/GeoIpProcessorFactoryTests.java index cda2f5692b0db..ac5a0f8a7d886 100644 --- a/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/GeoIpProcessorFactoryTests.java +++ b/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/GeoIpProcessorFactoryTests.java @@ -33,6 +33,7 @@ package org.opensearch.ingest.geoip; import com.carrotsearch.randomizedtesting.generators.RandomPicks; + import org.opensearch.OpenSearchParseException; import org.opensearch.common.Randomness; import org.opensearch.index.VersionType; diff --git a/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/GeoIpProcessorTests.java b/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/GeoIpProcessorTests.java index f06802af8b571..e0c93d8584878 100644 --- a/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/GeoIpProcessorTests.java +++ b/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/GeoIpProcessorTests.java @@ -33,6 +33,7 @@ package org.opensearch.ingest.geoip; import com.maxmind.geoip2.DatabaseReader; + import org.opensearch.common.CheckedSupplier; import org.opensearch.common.io.PathUtils; import org.opensearch.ingest.IngestDocument; @@ -308,7 +309,7 @@ public void testAsn() throws Exception { Map geoData = (Map) ingestDocument.getSourceAndMetadata().get("target_field"); assertThat(geoData.size(), equalTo(4)); assertThat(geoData.get("ip"), equalTo(ip)); - assertThat(geoData.get("asn"), equalTo(1136)); + assertThat(geoData.get("asn"), equalTo(1136L)); assertThat(geoData.get("organization_name"), equalTo("KPN B.V.")); assertThat(geoData.get("network"), equalTo("82.168.0.0/14")); } diff --git a/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/IngestGeoIpPluginTests.java b/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/IngestGeoIpPluginTests.java index 540d68b0982eb..a1120675872bb 100644 --- a/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/IngestGeoIpPluginTests.java +++ b/modules/ingest-geoip/src/test/java/org/opensearch/ingest/geoip/IngestGeoIpPluginTests.java @@ -33,6 +33,7 @@ package org.opensearch.ingest.geoip; import com.maxmind.geoip2.model.AbstractResponse; + import org.opensearch.common.network.InetAddresses; import org.opensearch.ingest.geoip.IngestGeoIpPlugin.GeoIpCache; import org.opensearch.test.OpenSearchTestCase; @@ -65,11 +66,9 @@ public void testThrowsFunctionsException() { GeoIpCache cache = new GeoIpCache(1); IllegalArgumentException ex = expectThrows( IllegalArgumentException.class, - () -> cache.putIfAbsent( - InetAddresses.forString("127.0.0.1"), - AbstractResponse.class, - ip -> { throw new IllegalArgumentException("bad"); } - ) + () -> cache.putIfAbsent(InetAddresses.forString("127.0.0.1"), AbstractResponse.class, ip -> { + throw new IllegalArgumentException("bad"); + }) ); assertEquals("bad", ex.getMessage()); } diff --git a/modules/ingest-geoip/src/yamlRestTest/resources/rest-api-spec/test/ingest_geoip/10_basic.yml b/modules/ingest-geoip/src/yamlRestTest/resources/rest-api-spec/test/ingest_geoip/10_basic.yml index 5cd45da2bda38..3b414022ede66 100644 --- a/modules/ingest-geoip/src/yamlRestTest/resources/rest-api-spec/test/ingest_geoip/10_basic.yml +++ b/modules/ingest-geoip/src/yamlRestTest/resources/rest-api-spec/test/ingest_geoip/10_basic.yml @@ -5,10 +5,10 @@ - do: cluster.state: {} - - set: {master_node: master} + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: ingest-geoip } } - - contains: { nodes.$master.ingest.processors: { type: geoip } } + - contains: { nodes.$cluster_manager.modules: { name: ingest-geoip } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: geoip } } diff --git a/modules/ingest-user-agent/build.gradle b/modules/ingest-user-agent/build.gradle index cd04925287b8f..a3752ad1c7f7e 100644 --- a/modules/ingest-user-agent/build.gradle +++ b/modules/ingest-user-agent/build.gradle @@ -43,4 +43,3 @@ restResources { testClusters.all { extraConfigFile 'ingest-user-agent/test-regexes.yml', file('src/test/test-regexes.yml') } - diff --git a/modules/ingest-user-agent/src/main/java/org/opensearch/ingest/useragent/UserAgentParser.java b/modules/ingest-user-agent/src/main/java/org/opensearch/ingest/useragent/UserAgentParser.java index 7dc3ee4346371..88129ffb922b8 100644 --- a/modules/ingest-user-agent/src/main/java/org/opensearch/ingest/useragent/UserAgentParser.java +++ b/modules/ingest-user-agent/src/main/java/org/opensearch/ingest/useragent/UserAgentParser.java @@ -34,10 +34,10 @@ import org.opensearch.OpenSearchParseException; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; + import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -68,7 +68,7 @@ final class UserAgentParser { private void init(InputStream regexStream) throws IOException { // EMPTY is safe here because we don't use namedObject - XContentParser yamlParser = XContentFactory.xContent(XContentType.YAML) + XContentParser yamlParser = XContentType.YAML.xContent() .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, regexStream); XContentParser.Token token = yamlParser.nextToken(); diff --git a/modules/ingest-user-agent/src/main/java/org/opensearch/ingest/useragent/UserAgentProcessor.java b/modules/ingest-user-agent/src/main/java/org/opensearch/ingest/useragent/UserAgentProcessor.java index 0625f1f8fd1af..5c6106b694144 100644 --- a/modules/ingest-user-agent/src/main/java/org/opensearch/ingest/useragent/UserAgentProcessor.java +++ b/modules/ingest-user-agent/src/main/java/org/opensearch/ingest/useragent/UserAgentProcessor.java @@ -346,7 +346,7 @@ public UserAgentProcessor create( deprecationLogger.deprecate( "ecs_false_non_common_schema", "setting [ecs] to false for non-common schema " - + "format is deprecated and will be removed in 8.0, set to true or remove to use the non-deprecated format" + + "format is deprecated and will be removed in 3.0, set to true or remove to use the non-deprecated format" ); } diff --git a/modules/ingest-user-agent/src/test/java/org/opensearch/ingest/useragent/UserAgentProcessorFactoryTests.java b/modules/ingest-user-agent/src/test/java/org/opensearch/ingest/useragent/UserAgentProcessorFactoryTests.java index 72815a37f46de..bfb0e2c9ab9a4 100644 --- a/modules/ingest-user-agent/src/test/java/org/opensearch/ingest/useragent/UserAgentProcessorFactoryTests.java +++ b/modules/ingest-user-agent/src/test/java/org/opensearch/ingest/useragent/UserAgentProcessorFactoryTests.java @@ -33,7 +33,7 @@ package org.opensearch.ingest.useragent; import org.opensearch.OpenSearchParseException; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import org.opensearch.test.OpenSearchTestCase; import org.junit.BeforeClass; diff --git a/modules/ingest-user-agent/src/test/java/org/opensearch/ingest/useragent/UserAgentProcessorTests.java b/modules/ingest-user-agent/src/test/java/org/opensearch/ingest/useragent/UserAgentProcessorTests.java index 51ff8aae21365..b7dd45b209113 100644 --- a/modules/ingest-user-agent/src/test/java/org/opensearch/ingest/useragent/UserAgentProcessorTests.java +++ b/modules/ingest-user-agent/src/test/java/org/opensearch/ingest/useragent/UserAgentProcessorTests.java @@ -32,8 +32,8 @@ package org.opensearch.ingest.useragent; -import org.opensearch.ingest.RandomDocumentPicks; import org.opensearch.ingest.IngestDocument; +import org.opensearch.ingest.RandomDocumentPicks; import org.opensearch.test.OpenSearchTestCase; import org.junit.BeforeClass; diff --git a/modules/ingest-user-agent/src/test/test-regexes.yml b/modules/ingest-user-agent/src/test/test-regexes.yml index e41dec700c047..8815c85c7c6e9 100644 --- a/modules/ingest-user-agent/src/test/test-regexes.yml +++ b/modules/ingest-user-agent/src/test/test-regexes.yml @@ -1,3 +1,3 @@ user_agent_parsers: - regex: '.*' - family_replacement: 'Test' \ No newline at end of file + family_replacement: 'Test' diff --git a/modules/ingest-user-agent/src/yamlRestTest/resources/rest-api-spec/test/ingest-useragent/10_basic.yml b/modules/ingest-user-agent/src/yamlRestTest/resources/rest-api-spec/test/ingest-useragent/10_basic.yml index 327d9cf451257..c0e1d14489a64 100644 --- a/modules/ingest-user-agent/src/yamlRestTest/resources/rest-api-spec/test/ingest-useragent/10_basic.yml +++ b/modules/ingest-user-agent/src/yamlRestTest/resources/rest-api-spec/test/ingest-useragent/10_basic.yml @@ -5,10 +5,10 @@ - do: cluster.state: {} - - set: {master_node: master} + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: ingest-user-agent } } - - contains: { nodes.$master.ingest.processors: { type: user_agent } } + - contains: { nodes.$cluster_manager.modules: { name: ingest-user-agent } } + - contains: { nodes.$cluster_manager.ingest.processors: { type: user_agent } } diff --git a/modules/ingest-user-agent/src/yamlRestTest/resources/rest-api-spec/test/ingest-useragent/20_useragent_processor.yml b/modules/ingest-user-agent/src/yamlRestTest/resources/rest-api-spec/test/ingest-useragent/20_useragent_processor.yml index c7ab1751d1b0d..a3c9afc02dac4 100644 --- a/modules/ingest-user-agent/src/yamlRestTest/resources/rest-api-spec/test/ingest-useragent/20_useragent_processor.yml +++ b/modules/ingest-user-agent/src/yamlRestTest/resources/rest-api-spec/test/ingest-useragent/20_useragent_processor.yml @@ -87,7 +87,7 @@ - do: allowed_warnings: - - "setting [ecs] to false for non-common schema format is deprecated and will be removed in 8.0, set to true or remove to use the non-deprecated format" + - "setting [ecs] to false for non-common schema format is deprecated and will be removed in 3.0, set to true or remove to use the non-deprecated format" - "the [os_major] property is deprecated for the user-agent processor" ingest.put_pipeline: id: "my_pipeline" diff --git a/modules/lang-expression/build.gradle b/modules/lang-expression/build.gradle index e3feacd71f060..0070923dc4be5 100644 --- a/modules/lang-expression/build.gradle +++ b/modules/lang-expression/build.gradle @@ -37,10 +37,10 @@ opensearchplugin { dependencies { api "org.apache.lucene:lucene-expressions:${versions.lucene}" - api 'org.antlr:antlr4-runtime:4.9.3' - api 'org.ow2.asm:asm:9.2' - api 'org.ow2.asm:asm-commons:9.2' - api 'org.ow2.asm:asm-tree:9.2' + api "org.antlr:antlr4-runtime:${versions.antlr4}" + api "org.ow2.asm:asm:${versions.asm}" + api "org.ow2.asm:asm-commons:${versions.asm}" + api "org.ow2.asm:asm-tree:${versions.asm}" } restResources { restApi { @@ -52,4 +52,3 @@ tasks.named("dependencyLicenses").configure { mapping from: /lucene-.*/, to: 'lucene' mapping from: /asm-.*/, to: 'asm' } - diff --git a/modules/lang-expression/licenses/antlr4-runtime-4.11.1.jar.sha1 b/modules/lang-expression/licenses/antlr4-runtime-4.11.1.jar.sha1 new file mode 100644 index 0000000000000..f1b328a6de624 --- /dev/null +++ b/modules/lang-expression/licenses/antlr4-runtime-4.11.1.jar.sha1 @@ -0,0 +1 @@ +069214c1de1960040729702eb58deac8827135e7 \ No newline at end of file diff --git a/modules/lang-expression/licenses/antlr4-runtime-4.9.3.jar.sha1 b/modules/lang-expression/licenses/antlr4-runtime-4.9.3.jar.sha1 deleted file mode 100644 index 13a2367439ede..0000000000000 --- a/modules/lang-expression/licenses/antlr4-runtime-4.9.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -81befc16ebedb8b8aea3e4c0835dd5ca7e8523a8 \ No newline at end of file diff --git a/modules/lang-expression/licenses/asm-9.2.jar.sha1 b/modules/lang-expression/licenses/asm-9.2.jar.sha1 deleted file mode 100644 index 28f456d3cbcb2..0000000000000 --- a/modules/lang-expression/licenses/asm-9.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -81a03f76019c67362299c40e0ba13405f5467bff \ No newline at end of file diff --git a/modules/lang-expression/licenses/asm-9.6.jar.sha1 b/modules/lang-expression/licenses/asm-9.6.jar.sha1 new file mode 100644 index 0000000000000..2d9e6a9d3cfd6 --- /dev/null +++ b/modules/lang-expression/licenses/asm-9.6.jar.sha1 @@ -0,0 +1 @@ +aa205cf0a06dbd8e04ece91c0b37c3f5d567546a \ No newline at end of file diff --git a/modules/lang-expression/licenses/asm-commons-9.2.jar.sha1 b/modules/lang-expression/licenses/asm-commons-9.2.jar.sha1 deleted file mode 100644 index 7beb3d29afe86..0000000000000 --- a/modules/lang-expression/licenses/asm-commons-9.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f4d7f0fc9054386f2893b602454d48e07d4fbead \ No newline at end of file diff --git a/modules/lang-expression/licenses/asm-commons-9.6.jar.sha1 b/modules/lang-expression/licenses/asm-commons-9.6.jar.sha1 new file mode 100644 index 0000000000000..a0814f495771f --- /dev/null +++ b/modules/lang-expression/licenses/asm-commons-9.6.jar.sha1 @@ -0,0 +1 @@ +f1a9e5508eff490744144565c47326c8648be309 \ No newline at end of file diff --git a/modules/lang-expression/licenses/asm-tree-9.2.jar.sha1 b/modules/lang-expression/licenses/asm-tree-9.2.jar.sha1 deleted file mode 100644 index 7b486521ecef3..0000000000000 --- a/modules/lang-expression/licenses/asm-tree-9.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d96c99a30f5e1a19b0e609dbb19a44d8518ac01e \ No newline at end of file diff --git a/modules/lang-expression/licenses/asm-tree-9.6.jar.sha1 b/modules/lang-expression/licenses/asm-tree-9.6.jar.sha1 new file mode 100644 index 0000000000000..101eb03b4b736 --- /dev/null +++ b/modules/lang-expression/licenses/asm-tree-9.6.jar.sha1 @@ -0,0 +1 @@ +c0cdda9d211e965d2a4448aa3fd86110f2f8c2de \ No newline at end of file diff --git a/modules/lang-expression/licenses/lucene-expressions-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/modules/lang-expression/licenses/lucene-expressions-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index fb85ff4827c36..0000000000000 --- a/modules/lang-expression/licenses/lucene-expressions-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c7317bb4e72b820a516e0c8a90beac5acc82c2e2 \ No newline at end of file diff --git a/modules/lang-expression/licenses/lucene-expressions-9.7.0.jar.sha1 b/modules/lang-expression/licenses/lucene-expressions-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..ecf696b4b3b83 --- /dev/null +++ b/modules/lang-expression/licenses/lucene-expressions-9.7.0.jar.sha1 @@ -0,0 +1 @@ +297e1cfade4ef71466cc9d4f361d81807c8dc4c8 \ No newline at end of file diff --git a/modules/lang-expression/src/internalClusterTest/java/org/opensearch/script/expression/MoreExpressionIT.java b/modules/lang-expression/src/internalClusterTest/java/org/opensearch/script/expression/MoreExpressionIT.java index 952b00dda608c..80974b3b3f104 100644 --- a/modules/lang-expression/src/internalClusterTest/java/org/opensearch/script/expression/MoreExpressionIT.java +++ b/modules/lang-expression/src/internalClusterTest/java/org/opensearch/script/expression/MoreExpressionIT.java @@ -32,14 +32,18 @@ package org.opensearch.script.expression; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.SearchType; import org.opensearch.action.update.UpdateRequestBuilder; import org.opensearch.common.lucene.search.function.CombineFunction; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.functionscore.ScoreFunctionBuilders; import org.opensearch.index.query.functionscore.ScriptScoreFunctionBuilder; @@ -53,9 +57,10 @@ import org.opensearch.search.aggregations.pipeline.SimpleValue; import org.opensearch.search.sort.SortBuilders; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -64,6 +69,7 @@ import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.PipelineAggregatorBuilders.bucketScript; @@ -74,7 +80,24 @@ import static org.hamcrest.Matchers.notNullValue; // TODO: please convert to unit tests! -public class MoreExpressionIT extends OpenSearchIntegTestCase { +public class MoreExpressionIT extends ParameterizedOpenSearchIntegTestCase { + + public MoreExpressionIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { diff --git a/modules/lang-expression/src/internalClusterTest/java/org/opensearch/script/expression/StoredExpressionIT.java b/modules/lang-expression/src/internalClusterTest/java/org/opensearch/script/expression/StoredExpressionIT.java index 5aade265439d2..f3c994521692c 100644 --- a/modules/lang-expression/src/internalClusterTest/java/org/opensearch/script/expression/StoredExpressionIT.java +++ b/modules/lang-expression/src/internalClusterTest/java/org/opensearch/script/expression/StoredExpressionIT.java @@ -32,24 +32,47 @@ package org.opensearch.script.expression; -import org.opensearch.common.bytes.BytesArray; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.aggregations.AggregationBuilders; import org.opensearch.search.builder.SearchSourceBuilder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.hamcrest.Matchers.containsString; //TODO: please convert to unit tests! -public class StoredExpressionIT extends OpenSearchIntegTestCase { +public class StoredExpressionIT extends ParameterizedOpenSearchIntegTestCase { + + public StoredExpressionIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Settings nodeSettings(int nodeOrdinal) { Settings.Builder builder = Settings.builder().put(super.nodeSettings(nodeOrdinal)); @@ -67,9 +90,9 @@ public void testAllOpsDisabledIndexedScripts() throws IOException { .cluster() .preparePutStoredScript() .setId("script1") - .setContent(new BytesArray("{\"script\": {\"lang\": \"expression\", \"source\": \"2\"} }"), XContentType.JSON) + .setContent(new BytesArray("{\"script\": {\"lang\": \"expression\", \"source\": \"2\"} }"), MediaTypeRegistry.JSON) .get(); - client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", XContentType.JSON).get(); + client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", MediaTypeRegistry.JSON).get(); try { client().prepareUpdate("test", "1").setScript(new Script(ScriptType.STORED, null, "script1", Collections.emptyMap())).get(); fail("update script should have been rejected"); diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/CountMethodValueSource.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/CountMethodValueSource.java index 97ea247c15bbd..77580f88c16f7 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/CountMethodValueSource.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/CountMethodValueSource.java @@ -34,8 +34,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.DoubleValues; -import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.IndexFieldData; +import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.SortedNumericDoubleValues; import java.io.IOException; diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateMethodValueSource.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateMethodValueSource.java index 6aa7e640c77cb..31be3cc3b1264 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateMethodValueSource.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateMethodValueSource.java @@ -34,8 +34,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.DoubleValues; -import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.IndexFieldData; +import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.NumericDoubleValues; import org.opensearch.search.MultiValueMode; diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObjectValueSource.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObjectValueSource.java index 1ab778008e8c3..69d7b5ad7f769 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObjectValueSource.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/DateObjectValueSource.java @@ -34,8 +34,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.DoubleValues; -import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.IndexFieldData; +import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.NumericDoubleValues; import org.opensearch.search.MultiValueMode; import org.joda.time.DateTimeZone; diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/EmptyMemberValueSource.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/EmptyMemberValueSource.java index da400b27b0fd3..1fba4241f80f3 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/EmptyMemberValueSource.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/EmptyMemberValueSource.java @@ -34,8 +34,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.DoubleValues; -import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.IndexFieldData; +import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.SortedNumericDoubleValues; import java.io.IOException; diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionAggregationScript.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionAggregationScript.java index ba131473be4fb..5eebb9c4d60ad 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionAggregationScript.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionAggregationScript.java @@ -32,7 +32,6 @@ package org.opensearch.script.expression; -import java.io.IOException; import org.apache.lucene.expressions.Bindings; import org.apache.lucene.expressions.Expression; import org.apache.lucene.expressions.SimpleBindings; @@ -42,6 +41,8 @@ import org.opensearch.script.AggregationScript; import org.opensearch.script.GeneralScriptException; +import java.io.IOException; + /** * A bridge to evaluate an {@link Expression} against {@link Bindings} in the context * of a {@link AggregationScript}. diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionNumberSortScript.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionNumberSortScript.java index fc50e7355f492..72710591fece8 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionNumberSortScript.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionNumberSortScript.java @@ -32,7 +32,6 @@ package org.opensearch.script.expression; -import java.io.IOException; import org.apache.lucene.expressions.Bindings; import org.apache.lucene.expressions.Expression; import org.apache.lucene.expressions.SimpleBindings; @@ -42,6 +41,8 @@ import org.opensearch.script.GeneralScriptException; import org.opensearch.script.NumberSortScript; +import java.io.IOException; + /** * A bridge to evaluate an {@link Expression} against {@link Bindings} in the context * of a {@link NumberSortScript}. diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionPlugin.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionPlugin.java index 440a03c63bd98..47cafc377e701 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionPlugin.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionPlugin.java @@ -32,14 +32,14 @@ package org.opensearch.script.expression; -import java.util.Collection; - import org.opensearch.common.settings.Settings; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.ScriptPlugin; import org.opensearch.script.ScriptContext; import org.opensearch.script.ScriptEngine; +import java.util.Collection; + public class ExpressionPlugin extends Plugin implements ScriptPlugin { @Override diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionScoreScript.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionScoreScript.java index 6be299146a181..3932559f7685c 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionScoreScript.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionScoreScript.java @@ -66,7 +66,7 @@ public boolean needs_score() { @Override public ScoreScript newInstance(final LeafReaderContext leaf) throws IOException { - return new ScoreScript(null, null, null) { + return new ScoreScript(null, null, null, null) { // Fake the scorer until setScorer is called. DoubleValues values = source.getValues(leaf, new DoubleValues() { @Override diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionScriptEngine.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionScriptEngine.java index 1c3dc69359952..035d2402857e0 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionScriptEngine.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionScriptEngine.java @@ -37,6 +37,7 @@ import org.apache.lucene.expressions.js.JavascriptCompiler; import org.apache.lucene.expressions.js.VariableContext; import org.apache.lucene.search.DoubleValuesSource; +import org.apache.lucene.search.IndexSearcher; import org.opensearch.SpecialPermission; import org.opensearch.common.Nullable; import org.opensearch.index.fielddata.IndexFieldData; @@ -110,7 +111,7 @@ public FilterScript.LeafFactory newFactory(Map params, SearchLoo contexts.put(ScoreScript.CONTEXT, (Expression expr) -> new ScoreScript.Factory() { @Override - public ScoreScript.LeafFactory newFactory(Map params, SearchLookup lookup) { + public ScoreScript.LeafFactory newFactory(Map params, SearchLookup lookup, IndexSearcher indexSearcher) { return newScoreScript(expr, lookup, params); } diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionTermSetQueryScript.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionTermSetQueryScript.java index 0c14a45804c3e..39be670b7e303 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionTermSetQueryScript.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/ExpressionTermSetQueryScript.java @@ -32,7 +32,6 @@ package org.opensearch.script.expression; -import java.io.IOException; import org.apache.lucene.expressions.Bindings; import org.apache.lucene.expressions.Expression; import org.apache.lucene.expressions.SimpleBindings; @@ -42,6 +41,8 @@ import org.opensearch.script.GeneralScriptException; import org.opensearch.script.TermsSetQueryScript; +import java.io.IOException; + /** * A bridge to evaluate an {@link Expression} against {@link Bindings} in the context * of a {@link TermsSetQueryScript}. diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/FieldDataValueSource.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/FieldDataValueSource.java index c0488a267507c..21f9d0a94b0c9 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/FieldDataValueSource.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/FieldDataValueSource.java @@ -35,8 +35,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.search.DoubleValues; -import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.IndexFieldData; +import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.NumericDoubleValues; import org.opensearch.search.MultiValueMode; diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/GeoLatitudeValueSource.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/GeoLatitudeValueSource.java index b167393d1b864..3ae45503657be 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/GeoLatitudeValueSource.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/GeoLatitudeValueSource.java @@ -34,8 +34,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.DoubleValues; -import org.opensearch.index.fielddata.LeafGeoPointFieldData; import org.opensearch.index.fielddata.IndexFieldData; +import org.opensearch.index.fielddata.LeafGeoPointFieldData; import org.opensearch.index.fielddata.MultiGeoPointValues; import java.io.IOException; diff --git a/modules/lang-expression/src/main/java/org/opensearch/script/expression/GeoLongitudeValueSource.java b/modules/lang-expression/src/main/java/org/opensearch/script/expression/GeoLongitudeValueSource.java index 10d836b7d219f..aa67b11833972 100644 --- a/modules/lang-expression/src/main/java/org/opensearch/script/expression/GeoLongitudeValueSource.java +++ b/modules/lang-expression/src/main/java/org/opensearch/script/expression/GeoLongitudeValueSource.java @@ -34,8 +34,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.DoubleValues; -import org.opensearch.index.fielddata.LeafGeoPointFieldData; import org.opensearch.index.fielddata.IndexFieldData; +import org.opensearch.index.fielddata.LeafGeoPointFieldData; import org.opensearch.index.fielddata.MultiGeoPointValues; import java.io.IOException; diff --git a/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionFieldScriptTests.java b/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionFieldScriptTests.java index d0941cbc9452f..143ff4f5c51bd 100644 --- a/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionFieldScriptTests.java +++ b/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionFieldScriptTests.java @@ -47,8 +47,8 @@ import java.util.Collections; import static org.hamcrest.Matchers.equalTo; -import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionNumberSortScriptTests.java b/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionNumberSortScriptTests.java index f3559da59f992..498c0542e9c3e 100644 --- a/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionNumberSortScriptTests.java +++ b/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionNumberSortScriptTests.java @@ -32,11 +32,8 @@ package org.opensearch.script.expression; -import java.io.IOException; -import java.text.ParseException; -import java.util.Collections; -import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.IndexNumericFieldData; +import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.SortedNumericDoubleValues; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.mapper.NumberFieldMapper.NumberFieldType; @@ -46,8 +43,12 @@ import org.opensearch.search.lookup.SearchLookup; import org.opensearch.test.OpenSearchTestCase; -import static org.mockito.Mockito.anyInt; +import java.io.IOException; +import java.text.ParseException; +import java.util.Collections; + import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionTermsSetQueryTests.java b/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionTermsSetQueryTests.java index af7fc580f8a65..499f94afcb6af 100644 --- a/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionTermsSetQueryTests.java +++ b/modules/lang-expression/src/test/java/org/opensearch/script/expression/ExpressionTermsSetQueryTests.java @@ -32,11 +32,8 @@ package org.opensearch.script.expression; -import java.io.IOException; -import java.text.ParseException; -import java.util.Collections; -import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.IndexNumericFieldData; +import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.SortedNumericDoubleValues; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.mapper.NumberFieldMapper.NumberFieldType; @@ -46,8 +43,12 @@ import org.opensearch.search.lookup.SearchLookup; import org.opensearch.test.OpenSearchTestCase; -import static org.mockito.Mockito.anyInt; +import java.io.IOException; +import java.text.ParseException; +import java.util.Collections; + import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/modules/lang-expression/src/yamlRestTest/resources/rest-api-spec/test/lang_expression/10_basic.yml b/modules/lang-expression/src/yamlRestTest/resources/rest-api-spec/test/lang_expression/10_basic.yml index 00ad6f890b0e0..d56ad1a8a244c 100644 --- a/modules/lang-expression/src/yamlRestTest/resources/rest-api-spec/test/lang_expression/10_basic.yml +++ b/modules/lang-expression/src/yamlRestTest/resources/rest-api-spec/test/lang_expression/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: lang-expression } } + - contains: { nodes.$cluster_manager.modules: { name: lang-expression } } diff --git a/modules/lang-mustache/src/internalClusterTest/java/org/opensearch/script/mustache/MultiSearchTemplateIT.java b/modules/lang-mustache/src/internalClusterTest/java/org/opensearch/script/mustache/MultiSearchTemplateIT.java index 617f1f4f738a0..204082ba10ded 100644 --- a/modules/lang-mustache/src/internalClusterTest/java/org/opensearch/script/mustache/MultiSearchTemplateIT.java +++ b/modules/lang-mustache/src/internalClusterTest/java/org/opensearch/script/mustache/MultiSearchTemplateIT.java @@ -32,13 +32,16 @@ package org.opensearch.script.mustache; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchRequest; -import org.opensearch.common.Strings; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.IndexNotFoundException; import org.opensearch.plugins.Plugin; import org.opensearch.script.ScriptType; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.Arrays; import java.util.Collection; @@ -47,6 +50,7 @@ import java.util.Map; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.equalTo; @@ -54,7 +58,24 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.core.Is.is; -public class MultiSearchTemplateIT extends OpenSearchIntegTestCase { +public class MultiSearchTemplateIT extends ParameterizedOpenSearchIntegTestCase { + + public MultiSearchTemplateIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -72,15 +93,14 @@ public void testBasic() throws Exception { } indexRandom(true, indexRequestBuilders); - final String template = Strings.toString( - jsonBuilder().startObject() - .startObject("query") - .startObject("{{query_type}}") - .field("{{field_name}}", "{{field_value}}") - .endObject() - .endObject() - .endObject() - ); + final String template = jsonBuilder().startObject() + .startObject("query") + .startObject("{{query_type}}") + .field("{{field_name}}", "{{field_value}}") + .endObject() + .endObject() + .endObject() + .toString(); MultiSearchTemplateRequest multiRequest = new MultiSearchTemplateRequest(); diff --git a/modules/lang-mustache/src/internalClusterTest/java/org/opensearch/script/mustache/SearchTemplateIT.java b/modules/lang-mustache/src/internalClusterTest/java/org/opensearch/script/mustache/SearchTemplateIT.java index 61f047a32f1c1..8799f3096452d 100644 --- a/modules/lang-mustache/src/internalClusterTest/java/org/opensearch/script/mustache/SearchTemplateIT.java +++ b/modules/lang-mustache/src/internalClusterTest/java/org/opensearch/script/mustache/SearchTemplateIT.java @@ -35,9 +35,9 @@ import org.opensearch.action.admin.cluster.storedscripts.GetStoredScriptResponse; import org.opensearch.action.bulk.BulkRequestBuilder; import org.opensearch.action.search.SearchRequest; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.plugins.Plugin; import org.opensearch.script.ScriptType; import org.opensearch.test.OpenSearchSingleNodeTestCase; @@ -177,7 +177,7 @@ public void testIndexedTemplateClient() throws Exception { + " }" + "}" ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -185,11 +185,11 @@ public void testIndexedTemplateClient() throws Exception { assertNotNull(getResponse.getSource()); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); - bulkRequestBuilder.add(client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("2").setSource("{\"theField\":\"foo 2\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("3").setSource("{\"theField\":\"foo 3\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("4").setSource("{\"theField\":\"foo 4\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("5").setSource("{\"theField\":\"bar\"}", XContentType.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("2").setSource("{\"theField\":\"foo 2\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("3").setSource("{\"theField\":\"foo 3\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("4").setSource("{\"theField\":\"foo 4\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("5").setSource("{\"theField\":\"bar\"}", MediaTypeRegistry.JSON)); bulkRequestBuilder.get(); client().admin().indices().prepareRefresh().get(); @@ -224,16 +224,22 @@ public void testIndexedTemplate() throws Exception { + " }" + "}"; - assertAcked(client().admin().cluster().preparePutStoredScript().setId("1a").setContent(new BytesArray(script), XContentType.JSON)); - assertAcked(client().admin().cluster().preparePutStoredScript().setId("2").setContent(new BytesArray(script), XContentType.JSON)); - assertAcked(client().admin().cluster().preparePutStoredScript().setId("3").setContent(new BytesArray(script), XContentType.JSON)); + assertAcked( + client().admin().cluster().preparePutStoredScript().setId("1a").setContent(new BytesArray(script), MediaTypeRegistry.JSON) + ); + assertAcked( + client().admin().cluster().preparePutStoredScript().setId("2").setContent(new BytesArray(script), MediaTypeRegistry.JSON) + ); + assertAcked( + client().admin().cluster().preparePutStoredScript().setId("3").setContent(new BytesArray(script), MediaTypeRegistry.JSON) + ); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); - bulkRequestBuilder.add(client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("2").setSource("{\"theField\":\"foo 2\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("3").setSource("{\"theField\":\"foo 3\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("4").setSource("{\"theField\":\"foo 4\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("5").setSource("{\"theField\":\"bar\"}", XContentType.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("2").setSource("{\"theField\":\"foo 2\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("3").setSource("{\"theField\":\"foo 3\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("4").setSource("{\"theField\":\"foo 4\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("5").setSource("{\"theField\":\"bar\"}", MediaTypeRegistry.JSON)); bulkRequestBuilder.get(); client().admin().indices().prepareRefresh().get(); @@ -295,7 +301,7 @@ public void testIndexedTemplateOverwrite() throws Exception { .cluster() .preparePutStoredScript() .setId("git01") - .setContent(new BytesArray(query.replace("{{slop}}", Integer.toString(-1))), XContentType.JSON) + .setContent(new BytesArray(query.replace("{{slop}}", Integer.toString(-1))), MediaTypeRegistry.JSON) ); GetStoredScriptResponse getResponse = client().admin().cluster().prepareGetStoredScript("git01").get(); @@ -319,7 +325,7 @@ public void testIndexedTemplateOverwrite() throws Exception { .cluster() .preparePutStoredScript() .setId("git01") - .setContent(new BytesArray(query.replace("{{slop}}", Integer.toString(0))), XContentType.JSON) + .setContent(new BytesArray(query.replace("{{slop}}", Integer.toString(0))), MediaTypeRegistry.JSON) ); SearchTemplateResponse searchResponse = new SearchTemplateRequestBuilder(client()).setRequest(new SearchRequest("testindex")) @@ -349,14 +355,14 @@ public void testIndexedTemplateWithArray() throws Exception { + " }\n" + "}"; assertAcked( - client().admin().cluster().preparePutStoredScript().setId("4").setContent(new BytesArray(multiQuery), XContentType.JSON) + client().admin().cluster().preparePutStoredScript().setId("4").setContent(new BytesArray(multiQuery), MediaTypeRegistry.JSON) ); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); - bulkRequestBuilder.add(client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("2").setSource("{\"theField\":\"foo 2\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("3").setSource("{\"theField\":\"foo 3\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("4").setSource("{\"theField\":\"foo 4\"}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("test").setId("5").setSource("{\"theField\":\"bar\"}", XContentType.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("1").setSource("{\"theField\":\"foo\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("2").setSource("{\"theField\":\"foo 2\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("3").setSource("{\"theField\":\"foo 3\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("4").setSource("{\"theField\":\"foo 4\"}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("test").setId("5").setSource("{\"theField\":\"bar\"}", MediaTypeRegistry.JSON)); bulkRequestBuilder.get(); client().admin().indices().prepareRefresh().get(); diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomMustacheFactory.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomMustacheFactory.java index 09c52142dfea5..c8d28575fef47 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomMustacheFactory.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomMustacheFactory.java @@ -33,6 +33,7 @@ package org.opensearch.script.mustache; import com.fasterxml.jackson.core.io.JsonStringEncoder; + import com.github.mustachejava.Code; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.DefaultMustacheVisitor; @@ -44,9 +45,8 @@ import com.github.mustachejava.codes.IterableCode; import com.github.mustachejava.codes.WriteCode; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.io.StringWriter; @@ -215,7 +215,7 @@ protected Function createFunction(Object resolved) { if (resolved == null) { return null; } - try (XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent())) { + try (XContentBuilder builder = MediaTypeRegistry.JSON.contentBuilder()) { if (resolved instanceof Iterable) { builder.startArray(); for (Object o : (Iterable) resolved) { @@ -228,7 +228,7 @@ protected Function createFunction(Object resolved) { // Do not handle as JSON return oh.stringify(resolved); } - return Strings.toString(builder); + return builder.toString(); } catch (IOException e) { throw new MustacheException("Failed to convert object to JSON", e); } diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomReflectionObjectHandler.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomReflectionObjectHandler.java index 57451a027c5d7..0adac121d77e3 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomReflectionObjectHandler.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/CustomReflectionObjectHandler.java @@ -33,16 +33,17 @@ package org.opensearch.script.mustache; import com.github.mustachejava.reflect.ReflectionObjectHandler; -import org.opensearch.common.util.CollectionUtils; + import org.opensearch.common.util.iterable.Iterables; +import org.opensearch.core.common.util.CollectionUtils; import java.lang.reflect.Array; import java.util.AbstractMap; import java.util.Collection; -import java.util.Set; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.HashMap; +import java.util.Set; final class CustomReflectionObjectHandler extends ReflectionObjectHandler { diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MultiSearchTemplateRequest.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MultiSearchTemplateRequest.java index b645eeeedf151..dc3b52cd0b946 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MultiSearchTemplateRequest.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MultiSearchTemplateRequest.java @@ -38,12 +38,12 @@ import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MultiSearchTemplateResponse.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MultiSearchTemplateResponse.java index 1802d03e20942..20d09849dfcea 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MultiSearchTemplateResponse.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MultiSearchTemplateResponse.java @@ -34,18 +34,19 @@ import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; @@ -206,6 +207,6 @@ public static MultiSearchTemplateResponse fromXContext(XContentParser parser) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MustachePlugin.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MustachePlugin.java index 498dc2ac57cc7..bb02b0c1d16b8 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MustachePlugin.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MustachePlugin.java @@ -33,13 +33,13 @@ package org.opensearch.script.mustache; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; +import org.opensearch.core.action.ActionResponse; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.ScriptPlugin; diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MustacheScriptEngine.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MustacheScriptEngine.java index e94dac2a3bfad..f4d7198dc2124 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MustacheScriptEngine.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/MustacheScriptEngine.java @@ -34,6 +34,7 @@ import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheException; import com.github.mustachejava.MustacheFactory; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/RestRenderSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/RestRenderSearchTemplateAction.java index 1cf61a24bbbb7..7a94fc45837d9 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/RestRenderSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/RestRenderSearchTemplateAction.java @@ -33,7 +33,7 @@ package org.opensearch.script.mustache; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestToXContentListener; diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/RestSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/RestSearchTemplateAction.java index 68ba824955468..74a8d23958efa 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/RestSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/RestSearchTemplateAction.java @@ -34,7 +34,7 @@ import org.opensearch.action.search.SearchRequest; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestStatusToXContentListener; diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/SearchTemplateRequest.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/SearchTemplateRequest.java index 6e8dc815eb552..1aabea30fc651 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/SearchTemplateRequest.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/SearchTemplateRequest.java @@ -35,17 +35,18 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.CompositeIndicesRequest; +import org.opensearch.action.IndicesRequest; import org.opensearch.action.search.SearchRequest; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.script.ScriptType; import java.io.IOException; @@ -57,7 +58,7 @@ /** * A request to execute a search based on a search template. */ -public class SearchTemplateRequest extends ActionRequest implements CompositeIndicesRequest, ToXContentObject { +public class SearchTemplateRequest extends ActionRequest implements IndicesRequest.Replaceable, CompositeIndicesRequest, ToXContentObject { private SearchRequest request; private boolean simulate = false; @@ -207,8 +208,8 @@ public ActionRequestValidationException validate() { request.setScriptType(ScriptType.INLINE); if (parser.currentToken() == XContentParser.Token.START_OBJECT) { // convert the template to json which is the only supported XContentType (see CustomMustacheFactory#createEncoder) - try (XContentBuilder builder = XContentFactory.jsonBuilder()) { - request.setScript(Strings.toString(builder.copyCurrentStructure(parser))); + try (XContentBuilder builder = MediaTypeRegistry.JSON.contentBuilder()) { + request.setScript(builder.copyCurrentStructure(parser).toString()); } catch (IOException e) { throw new ParsingException(parser.getTokenLocation(), "Could not parse inline template", e); } @@ -255,4 +256,19 @@ public void writeTo(StreamOutput out) throws IOException { out.writeMap(scriptParams); } } + + @Override + public String[] indices() { + return request.indices(); + } + + @Override + public IndicesOptions indicesOptions() { + return request.indicesOptions(); + } + + @Override + public IndicesRequest indices(String... indices) { + return request.indices(indices); + } } diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/SearchTemplateResponse.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/SearchTemplateResponse.java index 027fff3d79b44..9cb6ac127786a 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/SearchTemplateResponse.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/SearchTemplateResponse.java @@ -32,18 +32,18 @@ package org.opensearch.script.mustache; -import org.opensearch.action.ActionResponse; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.InputStream; @@ -103,11 +103,11 @@ public static SearchTemplateResponse fromXContent(XContentParser parser) throws if (contentAsMap.containsKey(TEMPLATE_OUTPUT_FIELD.getPreferredName())) { Object source = contentAsMap.get(TEMPLATE_OUTPUT_FIELD.getPreferredName()); - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON).value(source); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(MediaTypeRegistry.JSON).value(source); searchTemplateResponse.setSource(BytesReference.bytes(builder)); } else { - XContentType contentType = parser.contentType(); - XContentBuilder builder = XContentFactory.contentBuilder(contentType).map(contentAsMap); + MediaType contentType = parser.contentType(); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(contentType).map(contentAsMap); XContentParser searchResponseParser = contentType.xContent() .createParser(parser.getXContentRegistry(), parser.getDeprecationHandler(), BytesReference.bytes(builder).streamInput()); @@ -125,7 +125,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.startObject(); // we can assume the template is always json as we convert it before compiling it try (InputStream stream = source.streamInput()) { - builder.rawField(TEMPLATE_OUTPUT_FIELD.getPreferredName(), stream, XContentType.JSON); + builder.rawField(TEMPLATE_OUTPUT_FIELD.getPreferredName(), stream, MediaTypeRegistry.JSON); } builder.endObject(); } diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/TransportMultiSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/TransportMultiSearchTemplateAction.java index 8ebc5ace8d58f..b17b48ec1cbf8 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/TransportMultiSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/TransportMultiSearchTemplateAction.java @@ -32,7 +32,6 @@ package org.opensearch.script.mustache; -import org.opensearch.action.ActionListener; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.SearchRequest; @@ -40,7 +39,8 @@ import org.opensearch.action.support.HandledTransportAction; import org.opensearch.client.node.NodeClient; import org.opensearch.common.inject.Inject; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.script.ScriptService; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; diff --git a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/TransportSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/TransportSearchTemplateAction.java index 4e83f733d84b3..6e8b9d059b583 100644 --- a/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/TransportSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/opensearch/script/mustache/TransportSearchTemplateAction.java @@ -32,19 +32,18 @@ package org.opensearch.script.mustache; -import org.opensearch.action.ActionListener; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.inject.Inject; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.rest.action.search.RestSearchAction; import org.opensearch.script.Script; import org.opensearch.script.ScriptService; @@ -132,7 +131,7 @@ static SearchRequest convert( } try ( - XContentParser parser = XContentFactory.xContent(XContentType.JSON) + XContentParser parser = MediaTypeRegistry.JSON.xContent() .createParser(xContentRegistry, LoggingDeprecationHandler.INSTANCE, source) ) { SearchSourceBuilder builder = SearchSourceBuilder.searchSource(); diff --git a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MultiSearchTemplateRequestTests.java b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MultiSearchTemplateRequestTests.java index 1a663dcb18235..7499d86f70b2b 100644 --- a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MultiSearchTemplateRequestTests.java +++ b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MultiSearchTemplateRequestTests.java @@ -33,8 +33,8 @@ package org.opensearch.script.mustache; import org.opensearch.action.search.SearchRequest; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.rest.RestRequest; import org.opensearch.script.ScriptType; import org.opensearch.search.Scroll; @@ -56,7 +56,7 @@ public class MultiSearchTemplateRequestTests extends OpenSearchTestCase { public void testParseRequest() throws Exception { byte[] data = StreamsUtils.copyToBytesFromClasspath("/org/opensearch/script/mustache/simple-msearch-template.json"); - RestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()).withContent(new BytesArray(data), XContentType.JSON) + RestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()).withContent(new BytesArray(data), MediaTypeRegistry.JSON) .build(); MultiSearchTemplateRequest request = RestMultiSearchTemplateAction.parseRequest(restRequest, true); @@ -92,8 +92,10 @@ public void testParseRequest() throws Exception { public void testParseWithCarriageReturn() throws Exception { final String content = "{\"index\":[\"test0\", \"test1\"], \"request_cache\": true}\r\n" + "{\"source\": {\"query\" : {\"match_{{template}}\" :{}}}, \"params\": {\"template\": \"all\" } }\r\n"; - RestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()).withContent(new BytesArray(content), XContentType.JSON) - .build(); + RestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()).withContent( + new BytesArray(content), + MediaTypeRegistry.JSON + ).build(); MultiSearchTemplateRequest request = RestMultiSearchTemplateAction.parseRequest(restRequest, true); @@ -144,8 +146,10 @@ public void testMultiSearchTemplateToJson() throws Exception { String serialized = toJsonString(multiSearchTemplateRequest); // Deserialize the request - RestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()).withContent(new BytesArray(serialized), XContentType.JSON) - .build(); + RestRequest restRequest = new FakeRestRequest.Builder(xContentRegistry()).withContent( + new BytesArray(serialized), + MediaTypeRegistry.JSON + ).build(); MultiSearchTemplateRequest deser = RestMultiSearchTemplateAction.parseRequest(restRequest, true); // For object equality purposes need to set the search requests' source to non-null @@ -163,7 +167,7 @@ public void testMultiSearchTemplateToJson() throws Exception { } protected String toJsonString(MultiSearchTemplateRequest multiSearchTemplateRequest) throws IOException { - byte[] bytes = MultiSearchTemplateRequest.writeMultiLineFormat(multiSearchTemplateRequest, XContentType.JSON.xContent()); + byte[] bytes = MultiSearchTemplateRequest.writeMultiLineFormat(multiSearchTemplateRequest, MediaTypeRegistry.JSON.xContent()); return new String(bytes, StandardCharsets.UTF_8); } diff --git a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MultiSearchTemplateResponseTests.java b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MultiSearchTemplateResponseTests.java index 4aee84beb4db1..aeb7cdc2c6a28 100644 --- a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MultiSearchTemplateResponseTests.java +++ b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MultiSearchTemplateResponseTests.java @@ -34,9 +34,9 @@ import org.opensearch.OpenSearchException; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.ShardSearchFailure; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.internal.InternalSearchResponse; import org.opensearch.test.AbstractXContentTestCase; diff --git a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MustacheScriptEngineTests.java b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MustacheScriptEngineTests.java index 326990d804b4f..9e97863306148 100644 --- a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MustacheScriptEngineTests.java +++ b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MustacheScriptEngineTests.java @@ -33,10 +33,10 @@ import com.github.mustachejava.MustacheFactory; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.script.TemplateScript; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.script.Script; +import org.opensearch.script.TemplateScript; import org.opensearch.test.OpenSearchTestCase; import org.junit.Before; diff --git a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MustacheTests.java b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MustacheTests.java index a74fc48423553..93e4c2ce928b0 100644 --- a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MustacheTests.java +++ b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/MustacheTests.java @@ -31,9 +31,9 @@ package org.opensearch.script.mustache; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.script.ScriptEngine; import org.opensearch.script.ScriptException; import org.opensearch.script.TemplateScript; diff --git a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateRequestTests.java b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateRequestTests.java index 6c8e91d8c4d17..72443d1323b44 100644 --- a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateRequestTests.java +++ b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateRequestTests.java @@ -32,7 +32,7 @@ package org.opensearch.script.mustache; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.script.ScriptType; import org.opensearch.search.RandomSearchRequestGenerator; import org.opensearch.search.builder.SearchSourceBuilder; diff --git a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateRequestXContentTests.java b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateRequestXContentTests.java index 4783b8d3f890e..08d7841306161 100644 --- a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateRequestXContentTests.java +++ b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateRequestXContentTests.java @@ -32,14 +32,14 @@ package org.opensearch.script.mustache; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.script.ScriptType; import org.opensearch.test.AbstractXContentTestCase; @@ -101,7 +101,7 @@ public void testToXContentWithInlineTemplate() throws IOException { request.setScriptParams(scriptParams); XContentType contentType = randomFrom(XContentType.values()); - XContentBuilder expectedRequest = XContentFactory.contentBuilder(contentType) + XContentBuilder expectedRequest = MediaTypeRegistry.contentBuilder(contentType) .startObject() .field("source", "{\"query\": { \"match\" : { \"{{my_field}}\" : \"{{my_value}}\" } } }") .startObject("params") @@ -112,7 +112,7 @@ public void testToXContentWithInlineTemplate() throws IOException { .field("profile", true) .endObject(); - XContentBuilder actualRequest = XContentFactory.contentBuilder(contentType); + XContentBuilder actualRequest = MediaTypeRegistry.contentBuilder(contentType); request.toXContent(actualRequest, ToXContent.EMPTY_PARAMS); assertToXContentEquivalent(BytesReference.bytes(expectedRequest), BytesReference.bytes(actualRequest), contentType); @@ -131,7 +131,7 @@ public void testToXContentWithStoredTemplate() throws IOException { request.setScriptParams(params); XContentType contentType = randomFrom(XContentType.values()); - XContentBuilder expectedRequest = XContentFactory.contentBuilder(contentType) + XContentBuilder expectedRequest = MediaTypeRegistry.contentBuilder(contentType) .startObject() .field("id", "match_template") .startObject("params") @@ -142,7 +142,7 @@ public void testToXContentWithStoredTemplate() throws IOException { .field("profile", false) .endObject(); - XContentBuilder actualRequest = XContentFactory.contentBuilder(contentType); + XContentBuilder actualRequest = MediaTypeRegistry.contentBuilder(contentType); request.toXContent(actualRequest, ToXContent.EMPTY_PARAMS); assertToXContentEquivalent(BytesReference.bytes(expectedRequest), BytesReference.bytes(actualRequest), contentType); diff --git a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateResponseTests.java b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateResponseTests.java index 0a2bb247e3c1a..c2685e45ecb6b 100644 --- a/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateResponseTests.java +++ b/modules/lang-mustache/src/test/java/org/opensearch/script/mustache/SearchTemplateResponseTests.java @@ -35,12 +35,13 @@ import org.apache.lucene.search.TotalHits; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.ShardSearchFailure; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.internal.InternalSearchResponse; @@ -129,7 +130,7 @@ protected void assertEqualInstances(SearchTemplateResponse expectedInstance, Sea assertEquals(expectedSource == null, newSource == null); if (expectedSource != null) { try { - assertToXContentEquivalent(expectedSource, newSource, XContentType.JSON); + assertToXContentEquivalent(expectedSource, newSource, MediaTypeRegistry.JSON); } catch (IOException e) { throw new RuntimeException(e); } @@ -164,7 +165,7 @@ public void testSourceToXContent() throws IOException { response.setSource(BytesReference.bytes(source)); XContentType contentType = randomFrom(XContentType.values()); - XContentBuilder expectedResponse = XContentFactory.contentBuilder(contentType) + XContentBuilder expectedResponse = MediaTypeRegistry.contentBuilder(contentType) .startObject() .startObject("template_output") .startObject("query") @@ -175,7 +176,7 @@ public void testSourceToXContent() throws IOException { .endObject() .endObject(); - XContentBuilder actualResponse = XContentFactory.contentBuilder(contentType); + XContentBuilder actualResponse = MediaTypeRegistry.contentBuilder(contentType); response.toXContent(actualResponse, ToXContent.EMPTY_PARAMS); assertToXContentEquivalent(BytesReference.bytes(expectedResponse), BytesReference.bytes(actualResponse), contentType); @@ -210,7 +211,7 @@ public void testSearchResponseToXContent() throws IOException { response.setResponse(searchResponse); XContentType contentType = randomFrom(XContentType.values()); - XContentBuilder expectedResponse = XContentFactory.contentBuilder(contentType) + XContentBuilder expectedResponse = MediaTypeRegistry.contentBuilder(contentType) .startObject() .field("took", 0) .field("timed_out", false) @@ -235,7 +236,7 @@ public void testSearchResponseToXContent() throws IOException { .endObject() .endObject(); - XContentBuilder actualResponse = XContentFactory.contentBuilder(contentType); + XContentBuilder actualResponse = MediaTypeRegistry.contentBuilder(contentType); response.toXContent(actualResponse, ToXContent.EMPTY_PARAMS); assertToXContentEquivalent(BytesReference.bytes(expectedResponse), BytesReference.bytes(actualResponse), contentType); diff --git a/modules/lang-mustache/src/yamlRestTest/resources/rest-api-spec/test/lang_mustache/10_basic.yml b/modules/lang-mustache/src/yamlRestTest/resources/rest-api-spec/test/lang_mustache/10_basic.yml index 0e853d6273142..5fc67b766cb1a 100644 --- a/modules/lang-mustache/src/yamlRestTest/resources/rest-api-spec/test/lang_mustache/10_basic.yml +++ b/modules/lang-mustache/src/yamlRestTest/resources/rest-api-spec/test/lang_mustache/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: lang-mustache } } + - contains: { nodes.$cluster_manager.modules: { name: lang-mustache } } diff --git a/modules/lang-painless/build.gradle b/modules/lang-painless/build.gradle index 7f37b5e76d904..669a5363d6294 100644 --- a/modules/lang-painless/build.gradle +++ b/modules/lang-painless/build.gradle @@ -29,14 +29,22 @@ */ import org.opensearch.gradle.testclusters.DefaultTestClustersTask; +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin + apply plugin: 'opensearch.validate-rest-spec' apply plugin: 'opensearch.yaml-rest-test' +apply plugin: 'com.github.johnrengelman.shadow' opensearchplugin { description 'An easy, safe and fast scripting language for OpenSearch' classname 'org.opensearch.painless.PainlessPlugin' } +ext { + // Do not fail on `javadoc` warning (ANTLR generated code) + failOnJavadocWarning = false +} + testClusters.all { module ':modules:mapper-extras' systemProperty 'opensearch.scripting.update.ctx_in_params', 'false' @@ -45,15 +53,39 @@ testClusters.all { } dependencies { - api 'org.antlr:antlr4-runtime:4.9.3' - api 'org.ow2.asm:asm-util:9.2' - api 'org.ow2.asm:asm-tree:7.2' - api 'org.ow2.asm:asm-commons:9.2' - api 'org.ow2.asm:asm-analysis:7.2' - api 'org.ow2.asm:asm:9.2' + api "org.antlr:antlr4-runtime:${versions.antlr4}" + api "org.ow2.asm:asm-util:${versions.asm}" + api "org.ow2.asm:asm-tree:${versions.asm}" + api "org.ow2.asm:asm-commons:${versions.asm}" + api "org.ow2.asm:asm-analysis:${versions.asm}" + api "org.ow2.asm:asm:${versions.asm}" api project('spi') } +test { + doFirst { + test.classpath -= project.files(project.tasks.named('shadowJar')) + test.classpath -= project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) + test.classpath += project.extensions.getByType(SourceSetContainer).getByName(SourceSet.MAIN_SOURCE_SET_NAME).runtimeClasspath + } +} + +shadowJar { + archiveClassifier.set('') + relocate 'org.objectweb', 'org.opensearch.repackage.org.objectweb' + dependencies { + include(dependency("org.ow2.asm:asm:${versions.asm}")) + include(dependency("org.ow2.asm:asm-util:${versions.asm}")) + include(dependency("org.ow2.asm:asm-tree:${versions.asm}")) + include(dependency("org.ow2.asm:asm-commons:${versions.asm}")) + include(dependency("org.ow2.asm:asm-analysis:${versions.asm}")) + } +} + +tasks.validateNebulaPom.dependsOn tasks.generatePomFileForShadowPublication +tasks.validateShadowPom.dependsOn tasks.generatePomFileForNebulaPublication +tasks.withType(AbstractPublishToMaven)*.dependsOn "generatePomFileForShadowPublication", "generatePomFileForNebulaPublication" + tasks.named("dependencyLicenses").configure { mapping from: /asm-.*/, to: 'asm' } @@ -76,7 +108,7 @@ tasks.register("apiJavadoc", Javadoc) { source = sourceSets.main.allJava classpath = sourceSets.main.runtimeClasspath include '**/org/opensearch/painless/api/' - destinationDir = new File(docsDir, 'apiJavadoc') + destinationDir = new File(file(java.docsDir), 'apiJavadoc') } tasks.register("apiJavadocJar", Jar) { @@ -111,7 +143,7 @@ tasks.register("generateContextDoc", DefaultTestClustersTask) { useCluster testClusters.generateContextCluster doFirst { project.javaexec { - main = 'org.opensearch.painless.ContextDocGenerator' + mainClass = 'org.opensearch.painless.ContextDocGenerator' classpath = sourceSets.doc.runtimeClasspath systemProperty "cluster.uri", "${-> testClusters.generateContextCluster.singleNode().getAllHttpSocketURI().get(0)}" }.assertNormalExitValue() @@ -127,7 +159,7 @@ configurations { } dependencies { - regenerate 'org.antlr:antlr4:4.5.3' + regenerate "org.antlr:antlr4:${versions.antlr4}" } String grammarPath = 'src/main/antlr' @@ -144,7 +176,7 @@ tasks.register("cleanGenerated", Delete) { tasks.register("regenLexer", JavaExec) { dependsOn "cleanGenerated" - main = 'org.antlr.v4.Tool' + mainClass = 'org.antlr.v4.Tool' classpath = configurations.regenerate systemProperty 'file.encoding', 'UTF-8' systemProperty 'user.language', 'en' @@ -158,7 +190,7 @@ tasks.register("regenLexer", JavaExec) { tasks.register("regenParser", JavaExec) { dependsOn "regenLexer" - main = 'org.antlr.v4.Tool' + mainClass = 'org.antlr.v4.Tool' classpath = configurations.regenerate systemProperty 'file.encoding', 'UTF-8' systemProperty 'user.language', 'en' diff --git a/modules/lang-painless/licenses/antlr4-runtime-4.11.1.jar.sha1 b/modules/lang-painless/licenses/antlr4-runtime-4.11.1.jar.sha1 new file mode 100644 index 0000000000000..f1b328a6de624 --- /dev/null +++ b/modules/lang-painless/licenses/antlr4-runtime-4.11.1.jar.sha1 @@ -0,0 +1 @@ +069214c1de1960040729702eb58deac8827135e7 \ No newline at end of file diff --git a/modules/lang-painless/licenses/antlr4-runtime-4.9.3.jar.sha1 b/modules/lang-painless/licenses/antlr4-runtime-4.9.3.jar.sha1 deleted file mode 100644 index 13a2367439ede..0000000000000 --- a/modules/lang-painless/licenses/antlr4-runtime-4.9.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -81befc16ebedb8b8aea3e4c0835dd5ca7e8523a8 \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-9.2.jar.sha1 b/modules/lang-painless/licenses/asm-9.2.jar.sha1 deleted file mode 100644 index 28f456d3cbcb2..0000000000000 --- a/modules/lang-painless/licenses/asm-9.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -81a03f76019c67362299c40e0ba13405f5467bff \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-9.6.jar.sha1 b/modules/lang-painless/licenses/asm-9.6.jar.sha1 new file mode 100644 index 0000000000000..2d9e6a9d3cfd6 --- /dev/null +++ b/modules/lang-painless/licenses/asm-9.6.jar.sha1 @@ -0,0 +1 @@ +aa205cf0a06dbd8e04ece91c0b37c3f5d567546a \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-analysis-7.2.jar.sha1 b/modules/lang-painless/licenses/asm-analysis-7.2.jar.sha1 deleted file mode 100644 index 849b5e0bfa671..0000000000000 --- a/modules/lang-painless/licenses/asm-analysis-7.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -b6e6abe057f23630113f4167c34bda7086691258 \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-analysis-9.6.jar.sha1 b/modules/lang-painless/licenses/asm-analysis-9.6.jar.sha1 new file mode 100644 index 0000000000000..fa42ea1198165 --- /dev/null +++ b/modules/lang-painless/licenses/asm-analysis-9.6.jar.sha1 @@ -0,0 +1 @@ +9ce6c7b174bd997fc2552dff47964546bd7a5ec3 \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-commons-9.2.jar.sha1 b/modules/lang-painless/licenses/asm-commons-9.2.jar.sha1 deleted file mode 100644 index 7beb3d29afe86..0000000000000 --- a/modules/lang-painless/licenses/asm-commons-9.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f4d7f0fc9054386f2893b602454d48e07d4fbead \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-commons-9.6.jar.sha1 b/modules/lang-painless/licenses/asm-commons-9.6.jar.sha1 new file mode 100644 index 0000000000000..a0814f495771f --- /dev/null +++ b/modules/lang-painless/licenses/asm-commons-9.6.jar.sha1 @@ -0,0 +1 @@ +f1a9e5508eff490744144565c47326c8648be309 \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-tree-7.2.jar.sha1 b/modules/lang-painless/licenses/asm-tree-7.2.jar.sha1 deleted file mode 100644 index 986a1c55f5e8f..0000000000000 --- a/modules/lang-painless/licenses/asm-tree-7.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3a23cc36edaf8fc5a89cb100182758ccb5991487 \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-tree-9.6.jar.sha1 b/modules/lang-painless/licenses/asm-tree-9.6.jar.sha1 new file mode 100644 index 0000000000000..101eb03b4b736 --- /dev/null +++ b/modules/lang-painless/licenses/asm-tree-9.6.jar.sha1 @@ -0,0 +1 @@ +c0cdda9d211e965d2a4448aa3fd86110f2f8c2de \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-util-9.2.jar.sha1 b/modules/lang-painless/licenses/asm-util-9.2.jar.sha1 deleted file mode 100644 index 5cb89aa115f30..0000000000000 --- a/modules/lang-painless/licenses/asm-util-9.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -fbc178fc5ba3dab50fd7e8a5317b8b647c8e8946 \ No newline at end of file diff --git a/modules/lang-painless/licenses/asm-util-9.6.jar.sha1 b/modules/lang-painless/licenses/asm-util-9.6.jar.sha1 new file mode 100644 index 0000000000000..1f42ac62dc69c --- /dev/null +++ b/modules/lang-painless/licenses/asm-util-9.6.jar.sha1 @@ -0,0 +1 @@ +f77caf84eb93786a749b2baa40865b9613e3eaee \ No newline at end of file diff --git a/modules/lang-painless/spi/build.gradle b/modules/lang-painless/spi/build.gradle index 7811ee0e41dd8..59a77870b4987 100644 --- a/modules/lang-painless/spi/build.gradle +++ b/modules/lang-painless/spi/build.gradle @@ -31,8 +31,10 @@ apply plugin: 'opensearch.build' apply plugin: 'opensearch.publish' -group = 'org.opensearch.plugin' -archivesBaseName = 'opensearch-scripting-painless-spi' +base { + group = 'org.opensearch.plugin' + archivesBaseName = 'opensearch-scripting-painless-spi' +} dependencies { api project(":server") diff --git a/modules/lang-painless/src/doc/java/org/opensearch/painless/ContextDocGenerator.java b/modules/lang-painless/src/doc/java/org/opensearch/painless/ContextDocGenerator.java index cc3fcaa828e55..c31f279d712f5 100644 --- a/modules/lang-painless/src/doc/java/org/opensearch/painless/ContextDocGenerator.java +++ b/modules/lang-painless/src/doc/java/org/opensearch/painless/ContextDocGenerator.java @@ -34,9 +34,9 @@ import org.opensearch.common.SuppressForbidden; import org.opensearch.common.io.PathUtils; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.painless.action.PainlessContextClassBindingInfo; import org.opensearch.painless.action.PainlessContextClassInfo; import org.opensearch.painless.action.PainlessContextConstructorInfo; diff --git a/modules/lang-painless/src/main/antlr/PainlessLexer.g4 b/modules/lang-painless/src/main/antlr/PainlessLexer.g4 index 7dd168833e347..21b03b85d8edd 100644 --- a/modules/lang-painless/src/main/antlr/PainlessLexer.g4 +++ b/modules/lang-painless/src/main/antlr/PainlessLexer.g4 @@ -124,4 +124,4 @@ ID: [_a-zA-Z] [_a-zA-Z0-9]*; mode AFTER_DOT; DOTINTEGER: ( '0' | [1-9] [0-9]* ) -> mode(DEFAULT_MODE); -DOTID: [_a-zA-Z] [_a-zA-Z0-9]* -> mode(DEFAULT_MODE); \ No newline at end of file +DOTID: [_a-zA-Z] [_a-zA-Z0-9]* -> mode(DEFAULT_MODE); diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/AnalyzerCaster.java b/modules/lang-painless/src/main/java/org/opensearch/painless/AnalyzerCaster.java index e375ff14db67e..d830ef2ab6290 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/AnalyzerCaster.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/AnalyzerCaster.java @@ -412,7 +412,7 @@ public static PainlessCast getLegalCast(Location location, Class actual, Clas } } - if (actual == def.class + if ((actual == def.class && expected != void.class) || (actual != void.class && expected == def.class) || expected.isAssignableFrom(actual) || (actual.isAssignableFrom(expected) && explicit)) { diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/Compiler.java b/modules/lang-painless/src/main/java/org/opensearch/painless/Compiler.java index eca931d87b68c..1f57c5cbd1149 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/Compiler.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/Compiler.java @@ -212,8 +212,9 @@ private static void addFactoryMethod(Map> additionalClasses, Cl } additionalClasses.put(factoryClass.getName(), factoryClass); - for (int i = 0; i < factoryMethod.getParameterTypes().length; ++i) { - Class parameterClazz = factoryMethod.getParameterTypes()[i]; + final Class[] parameterTypes = factoryMethod.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; ++i) { + Class parameterClazz = parameterTypes[i]; additionalClasses.put(parameterClazz.getName(), parameterClazz); } } diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/PainlessPlugin.java b/modules/lang-painless/src/main/java/org/opensearch/painless/PainlessPlugin.java index 09a23c15f346d..aa4081dc70ba6 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/PainlessPlugin.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/PainlessPlugin.java @@ -32,20 +32,20 @@ package org.opensearch.painless; -import org.apache.lucene.util.SetOnce; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.SetOnce; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.painless.action.PainlessContextAction; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/PainlessScriptEngine.java b/modules/lang-painless/src/main/java/org/opensearch/painless/PainlessScriptEngine.java index 1663185ae7c4e..4555ce5cb1ed0 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/PainlessScriptEngine.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/PainlessScriptEngine.java @@ -195,11 +195,12 @@ private Type generateStatefulFactory(Loader loader, ScriptContext context } } - for (int count = 0; count < newFactory.getParameterTypes().length; ++count) { + final Class[] parameterTypes = newFactory.getParameterTypes(); + for (int count = 0; count < parameterTypes.length; ++count) { writer.visitField( Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "$arg" + count, - Type.getType(newFactory.getParameterTypes()[count]).getDescriptor(), + Type.getType(parameterTypes[count]).getDescriptor(), null, null ).visitEnd(); @@ -211,7 +212,7 @@ private Type generateStatefulFactory(Loader loader, ScriptContext context ); org.objectweb.asm.commons.Method init = new org.objectweb.asm.commons.Method( "", - MethodType.methodType(void.class, newFactory.getParameterTypes()).toMethodDescriptorString() + MethodType.methodType(void.class, parameterTypes).toMethodDescriptorString() ); GeneratorAdapter constructor = new GeneratorAdapter( @@ -223,10 +224,10 @@ private Type generateStatefulFactory(Loader loader, ScriptContext context constructor.loadThis(); constructor.invokeConstructor(OBJECT_TYPE, base); - for (int count = 0; count < newFactory.getParameterTypes().length; ++count) { + for (int count = 0; count < parameterTypes.length; ++count) { constructor.loadThis(); constructor.loadArg(count); - constructor.putField(Type.getType("L" + className + ";"), "$arg" + count, Type.getType(newFactory.getParameterTypes()[count])); + constructor.putField(Type.getType("L" + className + ";"), "$arg" + count, Type.getType(parameterTypes[count])); } constructor.returnValue(); @@ -247,7 +248,7 @@ private Type generateStatefulFactory(Loader loader, ScriptContext context MethodType.methodType(newInstance.getReturnType(), newInstance.getParameterTypes()).toMethodDescriptorString() ); - List> parameters = new ArrayList<>(Arrays.asList(newFactory.getParameterTypes())); + List> parameters = new ArrayList<>(Arrays.asList(parameterTypes)); parameters.addAll(Arrays.asList(newInstance.getParameterTypes())); org.objectweb.asm.commons.Method constru = new org.objectweb.asm.commons.Method( @@ -264,9 +265,9 @@ private Type generateStatefulFactory(Loader loader, ScriptContext context adapter.newInstance(WriterConstants.CLASS_TYPE); adapter.dup(); - for (int count = 0; count < newFactory.getParameterTypes().length; ++count) { + for (int count = 0; count < parameterTypes.length; ++count) { adapter.loadThis(); - adapter.getField(Type.getType("L" + className + ";"), "$arg" + count, Type.getType(newFactory.getParameterTypes()[count])); + adapter.getField(Type.getType("L" + className + ";"), "$arg" + count, Type.getType(parameterTypes[count])); } adapter.loadArgs(); @@ -334,13 +335,14 @@ private T generateFactory(Loader loader, ScriptContext context, Type clas } } + final Class[] parameterTypes = reflect.getParameterTypes(); org.objectweb.asm.commons.Method instance = new org.objectweb.asm.commons.Method( reflect.getName(), - MethodType.methodType(reflect.getReturnType(), reflect.getParameterTypes()).toMethodDescriptorString() + MethodType.methodType(reflect.getReturnType(), parameterTypes).toMethodDescriptorString() ); org.objectweb.asm.commons.Method constru = new org.objectweb.asm.commons.Method( "", - MethodType.methodType(void.class, reflect.getParameterTypes()).toMethodDescriptorString() + MethodType.methodType(void.class, parameterTypes).toMethodDescriptorString() ); GeneratorAdapter adapter = new GeneratorAdapter( @@ -421,9 +423,7 @@ private T generateFactory(Loader loader, ScriptContext context, Type clas private void writeNeedsMethods(Class clazz, ClassWriter writer, Set extractedVariables) { for (Method method : clazz.getMethods()) { - if (method.getName().startsWith("needs") - && method.getReturnType().equals(boolean.class) - && method.getParameterTypes().length == 0) { + if (method.getName().startsWith("needs") && method.getReturnType().equals(boolean.class) && method.getParameterCount() == 0) { String name = method.getName(); name = name.substring(5); name = Character.toLowerCase(name.charAt(0)) + name.substring(1); diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/ScriptClassInfo.java b/modules/lang-painless/src/main/java/org/opensearch/painless/ScriptClassInfo.java index a9d5a7abbb150..69aa3f157951e 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/ScriptClassInfo.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/ScriptClassInfo.java @@ -88,7 +88,7 @@ public ScriptClassInfo(PainlessLookup painlessLookup, Class baseClass) { + "] has more than one." ); } - } else if (m.getName().startsWith("needs") && m.getReturnType() == boolean.class && m.getParameterTypes().length == 0) { + } else if (m.getName().startsWith("needs") && m.getReturnType() == boolean.class && m.getParameterCount() == 0) { needsMethods.add(new org.objectweb.asm.commons.Method(m.getName(), NEEDS_PARAMETER_METHOD_TYPE.toMethodDescriptorString())); } else if (m.getName().startsWith("get") && m.getName().equals("getClass") == false @@ -124,7 +124,7 @@ public ScriptClassInfo(PainlessLookup painlessLookup, Class baseClass) { FunctionTable.LocalFunction defConverter = null; for (java.lang.reflect.Method m : baseClass.getMethods()) { if (m.getName().startsWith("convertFrom") - && m.getParameterTypes().length == 1 + && m.getParameterCount() == 1 && m.getReturnType() == returnType && Modifier.isStatic(m.getModifiers())) { diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextAction.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextAction.java index a9333fde6b443..a26cb79a9717d 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextAction.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextAction.java @@ -32,21 +32,21 @@ package org.opensearch.painless.action; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.ParseField; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.painless.PainlessScriptEngine; import org.opensearch.painless.lookup.PainlessLookup; import org.opensearch.rest.BaseRestHandler; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextClassBindingInfo.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextClassBindingInfo.java index 301c1ca369570..0fa5cc5658b6a 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextClassBindingInfo.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextClassBindingInfo.java @@ -32,15 +32,15 @@ package org.opensearch.painless.action; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.painless.lookup.PainlessClassBinding; import org.opensearch.painless.lookup.PainlessLookupUtility; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextClassInfo.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextClassInfo.java index 272b4d9fa16d7..25f63e1c8bdfa 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextClassInfo.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextClassInfo.java @@ -32,14 +32,14 @@ package org.opensearch.painless.action; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.painless.lookup.PainlessClass; import java.io.IOException; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextConstructorInfo.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextConstructorInfo.java index 9058be6d2c06b..cc1fc7448d134 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextConstructorInfo.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextConstructorInfo.java @@ -32,15 +32,15 @@ package org.opensearch.painless.action; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.painless.lookup.PainlessConstructor; import org.opensearch.painless.lookup.PainlessLookupUtility; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextFieldInfo.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextFieldInfo.java index 1cf1f0937b46e..7187f3403795e 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextFieldInfo.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextFieldInfo.java @@ -32,14 +32,14 @@ package org.opensearch.painless.action; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.painless.lookup.PainlessField; import org.opensearch.painless.lookup.PainlessLookupUtility; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextInfo.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextInfo.java index e82f2299c51b3..e4642b98154a5 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextInfo.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextInfo.java @@ -32,14 +32,14 @@ package org.opensearch.painless.action; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.painless.lookup.PainlessClassBinding; import org.opensearch.painless.lookup.PainlessInstanceBinding; import org.opensearch.painless.lookup.PainlessLookup; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextInstanceBindingInfo.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextInstanceBindingInfo.java index 74637a790da3b..848868f675422 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextInstanceBindingInfo.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextInstanceBindingInfo.java @@ -32,15 +32,15 @@ package org.opensearch.painless.action; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.painless.lookup.PainlessInstanceBinding; import org.opensearch.painless.lookup.PainlessLookupUtility; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextMethodInfo.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextMethodInfo.java index eaf751ea63188..02061b4543d9f 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextMethodInfo.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessContextMethodInfo.java @@ -32,15 +32,15 @@ package org.opensearch.painless.action; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.painless.lookup.PainlessLookupUtility; import org.opensearch.painless.lookup.PainlessMethod; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessExecuteAction.java b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessExecuteAction.java index be26e69ec22d1..67b298eee7973 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessExecuteAction.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/action/PainlessExecuteAction.java @@ -45,7 +45,6 @@ import org.apache.lucene.store.ByteBuffersDirectory; import org.apache.lucene.store.Directory; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.IndicesOptions; @@ -58,28 +57,29 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.CheckedBiFunction; -import org.opensearch.common.ParseField; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.ParsedDocument; import org.opensearch.index.mapper.SourceToParse; import org.opensearch.index.query.AbstractQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryShardContext; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; @@ -178,7 +178,7 @@ static class ContextSetup implements Writeable, ToXContentObject { private final BytesReference document; private final QueryBuilder query; - private XContentType xContentType; + private MediaType mediaType; static ContextSetup parse(XContentParser parser, Void context) throws IOException { ContextSetup contextSetup = PARSER.parse(parser, null); @@ -195,9 +195,9 @@ static ContextSetup parse(XContentParser parser, Void context) throws IOExceptio ContextSetup(StreamInput in) throws IOException { index = in.readOptionalString(); document = in.readOptionalBytesReference(); - String xContentType = in.readOptionalString(); - if (xContentType != null) { - this.xContentType = XContentType.fromMediaType(xContentType); + String mediaType = in.readOptionalString(); + if (mediaType != null) { + this.mediaType = MediaType.fromMediaType(mediaType); } query = in.readOptionalNamedWriteable(QueryBuilder.class); } @@ -214,12 +214,12 @@ public QueryBuilder getQuery() { return query; } - public XContentType getXContentType() { - return xContentType; + public MediaType getXContentType() { + return mediaType; } - public void setXContentType(XContentType xContentType) { - this.xContentType = xContentType; + public void setXContentType(MediaType mediaType) { + this.mediaType = mediaType; } @Override @@ -230,19 +230,19 @@ public boolean equals(Object o) { return Objects.equals(index, that.index) && Objects.equals(document, that.document) && Objects.equals(query, that.query) - && Objects.equals(xContentType, that.xContentType); + && Objects.equals(mediaType, that.mediaType); } @Override public int hashCode() { - return Objects.hash(index, document, query, xContentType); + return Objects.hash(index, document, query, mediaType); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(index); out.writeOptionalBytesReference(document); - out.writeOptionalString(xContentType != null ? xContentType.mediaTypeWithoutParameters() : null); + out.writeOptionalString(mediaType != null ? mediaType.mediaTypeWithoutParameters() : null); out.writeOptionalNamedWriteable(query); } @@ -257,7 +257,7 @@ public String toString() { + ", query=" + query + ", xContentType=" - + xContentType + + mediaType + '}'; } @@ -275,7 +275,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, document, - xContentType + mediaType ) ) { builder.generator().copyCurrentStructure(parser); @@ -558,7 +558,11 @@ static Response innerShardOperation(Request request, ScriptService scriptService } else if (scriptContext == ScoreScript.CONTEXT) { return prepareRamIndex(request, (context, leafReaderContext) -> { ScoreScript.Factory factory = scriptService.compile(request.script, ScoreScript.CONTEXT); - ScoreScript.LeafFactory leafFactory = factory.newFactory(request.getScript().getParams(), context.lookup()); + ScoreScript.LeafFactory leafFactory = factory.newFactory( + request.getScript().getParams(), + context.lookup(), + context.searcher() + ); ScoreScript scoreScript = leafFactory.newInstance(leafReaderContext); scoreScript.setDocument(0); @@ -594,8 +598,8 @@ private static Response prepareRamIndex( try (IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig(defaultAnalyzer))) { String index = indexService.index().getName(); BytesReference document = request.contextSetup.document; - XContentType xContentType = request.contextSetup.xContentType; - SourceToParse sourceToParse = new SourceToParse(index, "_id", document, xContentType); + MediaType mediaType = request.contextSetup.mediaType; + SourceToParse sourceToParse = new SourceToParse(index, "_id", document, mediaType); ParsedDocument parsedDocument = indexService.mapperService().documentMapper().parse(sourceToParse); indexWriter.addDocuments(parsedDocument.docs()); try (IndexReader indexReader = DirectoryReader.open(indexWriter)) { diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/EnhancedPainlessLexer.java b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/EnhancedPainlessLexer.java index 805b8a8a45bdc..5cc28e060c7cd 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/EnhancedPainlessLexer.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/EnhancedPainlessLexer.java @@ -36,6 +36,7 @@ import org.antlr.v4.runtime.LexerNoViableAltException; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.misc.Interval; + import org.opensearch.painless.Location; /** diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/PainlessLexer.java b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/PainlessLexer.java index 82b8bc939e910..fb33cf6e2a6f5 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/PainlessLexer.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/PainlessLexer.java @@ -33,17 +33,22 @@ package org.opensearch.painless.antlr; -import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.LexerATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; -@SuppressWarnings({ "all", "warnings", "unchecked", "unused", "cast" }) +@SuppressWarnings({ "all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue" }) abstract class PainlessLexer extends Lexer { static { - RuntimeMetaData.checkVersion("4.5.3", RuntimeMetaData.VERSION); + RuntimeMetaData.checkVersion("4.11.1", RuntimeMetaData.VERSION); } protected static final DFA[] _decisionToDFA; @@ -57,266 +62,281 @@ abstract class PainlessLexer extends Lexer { 68, ALSH = 69, ARSH = 70, AUSH = 71, OCTAL = 72, HEX = 73, INTEGER = 74, DECIMAL = 75, STRING = 76, REGEX = 77, TRUE = 78, FALSE = 79, NULL = 80, PRIMITIVE = 81, DEF = 82, ID = 83, DOTINTEGER = 84, DOTID = 85; public static final int AFTER_DOT = 1; + public static String[] channelNames = { "DEFAULT_TOKEN_CHANNEL", "HIDDEN" }; + public static String[] modeNames = { "DEFAULT_MODE", "AFTER_DOT" }; - public static final String[] ruleNames = { - "WS", - "COMMENT", - "LBRACK", - "RBRACK", - "LBRACE", - "RBRACE", - "LP", - "RP", - "DOT", - "NSDOT", - "COMMA", - "SEMICOLON", - "IF", - "IN", - "ELSE", - "WHILE", - "DO", - "FOR", - "CONTINUE", - "BREAK", - "RETURN", - "NEW", - "TRY", - "CATCH", - "THROW", - "THIS", - "INSTANCEOF", - "BOOLNOT", - "BWNOT", - "MUL", - "DIV", - "REM", - "ADD", - "SUB", - "LSH", - "RSH", - "USH", - "LT", - "LTE", - "GT", - "GTE", - "EQ", - "EQR", - "NE", - "NER", - "BWAND", - "XOR", - "BWOR", - "BOOLAND", - "BOOLOR", - "COND", - "COLON", - "ELVIS", - "REF", - "ARROW", - "FIND", - "MATCH", - "INCR", - "DECR", - "ASSIGN", - "AADD", - "ASUB", - "AMUL", - "ADIV", - "AREM", - "AAND", - "AXOR", - "AOR", - "ALSH", - "ARSH", - "AUSH", - "OCTAL", - "HEX", - "INTEGER", - "DECIMAL", - "STRING", - "REGEX", - "TRUE", - "FALSE", - "NULL", - "PRIMITIVE", - "DEF", - "ID", - "DOTINTEGER", - "DOTID" }; + private static String[] makeRuleNames() { + return new String[] { + "WS", + "COMMENT", + "LBRACK", + "RBRACK", + "LBRACE", + "RBRACE", + "LP", + "RP", + "DOT", + "NSDOT", + "COMMA", + "SEMICOLON", + "IF", + "IN", + "ELSE", + "WHILE", + "DO", + "FOR", + "CONTINUE", + "BREAK", + "RETURN", + "NEW", + "TRY", + "CATCH", + "THROW", + "THIS", + "INSTANCEOF", + "BOOLNOT", + "BWNOT", + "MUL", + "DIV", + "REM", + "ADD", + "SUB", + "LSH", + "RSH", + "USH", + "LT", + "LTE", + "GT", + "GTE", + "EQ", + "EQR", + "NE", + "NER", + "BWAND", + "XOR", + "BWOR", + "BOOLAND", + "BOOLOR", + "COND", + "COLON", + "ELVIS", + "REF", + "ARROW", + "FIND", + "MATCH", + "INCR", + "DECR", + "ASSIGN", + "AADD", + "ASUB", + "AMUL", + "ADIV", + "AREM", + "AAND", + "AXOR", + "AOR", + "ALSH", + "ARSH", + "AUSH", + "OCTAL", + "HEX", + "INTEGER", + "DECIMAL", + "STRING", + "REGEX", + "TRUE", + "FALSE", + "NULL", + "PRIMITIVE", + "DEF", + "ID", + "DOTINTEGER", + "DOTID" }; + } + + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, + null, + null, + "'{'", + "'}'", + "'['", + "']'", + "'('", + "')'", + "'.'", + "'?.'", + "','", + "';'", + "'if'", + "'in'", + "'else'", + "'while'", + "'do'", + "'for'", + "'continue'", + "'break'", + "'return'", + "'new'", + "'try'", + "'catch'", + "'throw'", + "'this'", + "'instanceof'", + "'!'", + "'~'", + "'*'", + "'/'", + "'%'", + "'+'", + "'-'", + "'<<'", + "'>>'", + "'>>>'", + "'<'", + "'<='", + "'>'", + "'>='", + "'=='", + "'==='", + "'!='", + "'!=='", + "'&'", + "'^'", + "'|'", + "'&&'", + "'||'", + "'?'", + "':'", + "'?:'", + "'::'", + "'->'", + "'=~'", + "'==~'", + "'++'", + "'--'", + "'='", + "'+='", + "'-='", + "'*='", + "'/='", + "'%='", + "'&='", + "'^='", + "'|='", + "'<<='", + "'>>='", + "'>>>='", + null, + null, + null, + null, + null, + null, + "'true'", + "'false'", + "'null'", + null, + "'def'" }; + } - private static final String[] _LITERAL_NAMES = { - null, - null, - null, - "'{'", - "'}'", - "'['", - "']'", - "'('", - "')'", - "'.'", - "'?.'", - "','", - "';'", - "'if'", - "'in'", - "'else'", - "'while'", - "'do'", - "'for'", - "'continue'", - "'break'", - "'return'", - "'new'", - "'try'", - "'catch'", - "'throw'", - "'this'", - "'instanceof'", - "'!'", - "'~'", - "'*'", - "'/'", - "'%'", - "'+'", - "'-'", - "'<<'", - "'>>'", - "'>>>'", - "'<'", - "'<='", - "'>'", - "'>='", - "'=='", - "'==='", - "'!='", - "'!=='", - "'&'", - "'^'", - "'|'", - "'&&'", - "'||'", - "'?'", - "':'", - "'?:'", - "'::'", - "'->'", - "'=~'", - "'==~'", - "'++'", - "'--'", - "'='", - "'+='", - "'-='", - "'*='", - "'/='", - "'%='", - "'&='", - "'^='", - "'|='", - "'<<='", - "'>>='", - "'>>>='", - null, - null, - null, - null, - null, - null, - "'true'", - "'false'", - "'null'", - null, - "'def'" }; - private static final String[] _SYMBOLIC_NAMES = { - null, - "WS", - "COMMENT", - "LBRACK", - "RBRACK", - "LBRACE", - "RBRACE", - "LP", - "RP", - "DOT", - "NSDOT", - "COMMA", - "SEMICOLON", - "IF", - "IN", - "ELSE", - "WHILE", - "DO", - "FOR", - "CONTINUE", - "BREAK", - "RETURN", - "NEW", - "TRY", - "CATCH", - "THROW", - "THIS", - "INSTANCEOF", - "BOOLNOT", - "BWNOT", - "MUL", - "DIV", - "REM", - "ADD", - "SUB", - "LSH", - "RSH", - "USH", - "LT", - "LTE", - "GT", - "GTE", - "EQ", - "EQR", - "NE", - "NER", - "BWAND", - "XOR", - "BWOR", - "BOOLAND", - "BOOLOR", - "COND", - "COLON", - "ELVIS", - "REF", - "ARROW", - "FIND", - "MATCH", - "INCR", - "DECR", - "ASSIGN", - "AADD", - "ASUB", - "AMUL", - "ADIV", - "AREM", - "AAND", - "AXOR", - "AOR", - "ALSH", - "ARSH", - "AUSH", - "OCTAL", - "HEX", - "INTEGER", - "DECIMAL", - "STRING", - "REGEX", - "TRUE", - "FALSE", - "NULL", - "PRIMITIVE", - "DEF", - "ID", - "DOTINTEGER", - "DOTID" }; + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + + private static String[] makeSymbolicNames() { + return new String[] { + null, + "WS", + "COMMENT", + "LBRACK", + "RBRACK", + "LBRACE", + "RBRACE", + "LP", + "RP", + "DOT", + "NSDOT", + "COMMA", + "SEMICOLON", + "IF", + "IN", + "ELSE", + "WHILE", + "DO", + "FOR", + "CONTINUE", + "BREAK", + "RETURN", + "NEW", + "TRY", + "CATCH", + "THROW", + "THIS", + "INSTANCEOF", + "BOOLNOT", + "BWNOT", + "MUL", + "DIV", + "REM", + "ADD", + "SUB", + "LSH", + "RSH", + "USH", + "LT", + "LTE", + "GT", + "GTE", + "EQ", + "EQR", + "NE", + "NER", + "BWAND", + "XOR", + "BWOR", + "BOOLAND", + "BOOLOR", + "COND", + "COLON", + "ELVIS", + "REF", + "ARROW", + "FIND", + "MATCH", + "INCR", + "DECR", + "ASSIGN", + "AADD", + "ASUB", + "AMUL", + "ADIV", + "AREM", + "AAND", + "AXOR", + "AOR", + "ALSH", + "ARSH", + "AUSH", + "OCTAL", + "HEX", + "INTEGER", + "DECIMAL", + "STRING", + "REGEX", + "TRUE", + "FALSE", + "NULL", + "PRIMITIVE", + "DEF", + "ID", + "DOTINTEGER", + "DOTID" }; + } + + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); /** @@ -373,6 +393,11 @@ public String getSerializedATN() { return _serializedATN; } + @Override + public String[] getChannelNames() { + return channelNames; + } + @Override public String[] getModeNames() { return modeNames; @@ -410,228 +435,405 @@ private boolean REGEX_sempred(RuleContext _localctx, int predIndex) { return true; } - public static final String _serializedATN = "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\2W\u027a\b\1\b\1\4" - + "\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n" - + "\4\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22" - + "\t\22\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31" - + "\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t" - + " \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t" - + "+\4,\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64" - + "\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t" - + "=\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4" - + "I\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\t" - + "T\4U\tU\4V\tV\3\2\6\2\u00b0\n\2\r\2\16\2\u00b1\3\2\3\2\3\3\3\3\3\3\3\3" - + "\7\3\u00ba\n\3\f\3\16\3\u00bd\13\3\3\3\3\3\3\3\3\3\3\3\7\3\u00c4\n\3\f" - + "\3\16\3\u00c7\13\3\3\3\3\3\5\3\u00cb\n\3\3\3\3\3\3\4\3\4\3\5\3\5\3\6\3" - + "\6\3\7\3\7\3\b\3\b\3\t\3\t\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3" - + "\f\3\f\3\r\3\r\3\16\3\16\3\16\3\17\3\17\3\17\3\20\3\20\3\20\3\20\3\20" - + "\3\21\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\24" - + "\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\25" - + "\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\27\3\27\3\27\3\27\3\30\3\30\3\30" - + "\3\30\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\33" - + "\3\33\3\33\3\33\3\33\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\34" - + "\3\34\3\35\3\35\3\36\3\36\3\37\3\37\3 \3 \3 \3!\3!\3\"\3\"\3#\3#\3$\3" - + "$\3$\3%\3%\3%\3&\3&\3&\3&\3\'\3\'\3(\3(\3(\3)\3)\3*\3*\3*\3+\3+\3+\3," - + "\3,\3,\3,\3-\3-\3-\3.\3.\3.\3.\3/\3/\3\60\3\60\3\61\3\61\3\62\3\62\3\62" - + "\3\63\3\63\3\63\3\64\3\64\3\65\3\65\3\66\3\66\3\66\3\67\3\67\3\67\38\3" - + "8\38\39\39\39\3:\3:\3:\3:\3;\3;\3;\3<\3<\3<\3=\3=\3>\3>\3>\3?\3?\3?\3" - + "@\3@\3@\3A\3A\3A\3B\3B\3B\3C\3C\3C\3D\3D\3D\3E\3E\3E\3F\3F\3F\3F\3G\3" - + "G\3G\3G\3H\3H\3H\3H\3H\3I\3I\6I\u01ba\nI\rI\16I\u01bb\3I\5I\u01bf\nI\3" - + "J\3J\3J\6J\u01c4\nJ\rJ\16J\u01c5\3J\5J\u01c9\nJ\3K\3K\3K\7K\u01ce\nK\f" - + "K\16K\u01d1\13K\5K\u01d3\nK\3K\5K\u01d6\nK\3L\3L\3L\7L\u01db\nL\fL\16" - + "L\u01de\13L\5L\u01e0\nL\3L\3L\6L\u01e4\nL\rL\16L\u01e5\5L\u01e8\nL\3L" - + "\3L\5L\u01ec\nL\3L\6L\u01ef\nL\rL\16L\u01f0\5L\u01f3\nL\3L\5L\u01f6\n" - + "L\3M\3M\3M\3M\3M\3M\7M\u01fe\nM\fM\16M\u0201\13M\3M\3M\3M\3M\3M\3M\3M" - + "\7M\u020a\nM\fM\16M\u020d\13M\3M\5M\u0210\nM\3N\3N\3N\3N\6N\u0216\nN\r" - + "N\16N\u0217\3N\3N\7N\u021c\nN\fN\16N\u021f\13N\3N\3N\3O\3O\3O\3O\3O\3" - + "P\3P\3P\3P\3P\3P\3Q\3Q\3Q\3Q\3Q\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3" - + "R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3R\3" - + "R\3R\3R\5R\u0259\nR\3S\3S\3S\3S\3T\3T\7T\u0261\nT\fT\16T\u0264\13T\3U" - + "\3U\3U\7U\u0269\nU\fU\16U\u026c\13U\5U\u026e\nU\3U\3U\3V\3V\7V\u0274\n" - + "V\fV\16V\u0277\13V\3V\3V\7\u00bb\u00c5\u01ff\u020b\u0217\2W\4\3\6\4\b" - + "\5\n\6\f\7\16\b\20\t\22\n\24\13\26\f\30\r\32\16\34\17\36\20 \21\"\22$" - + "\23&\24(\25*\26,\27.\30\60\31\62\32\64\33\66\348\35:\36<\37> @!B\"D#F" - + "$H%J&L\'N(P)R*T+V,X-Z.\\/^\60`\61b\62d\63f\64h\65j\66l\67n8p9r:t;v|?~@\u0080A\u0082B\u0084C\u0086D\u0088E\u008aF\u008cG\u008eH\u0090I" - + "\u0092J\u0094K\u0096L\u0098M\u009aN\u009cO\u009eP\u00a0Q\u00a2R\u00a4" - + "S\u00a6T\u00a8U\u00aaV\u00acW\4\2\3\25\5\2\13\f\17\17\"\"\4\2\f\f\17\17" - + "\3\2\629\4\2NNnn\4\2ZZzz\5\2\62;CHch\3\2\63;\3\2\62;\b\2FFHHNNffhhnn\4" - + "\2GGgg\4\2--//\6\2FFHHffhh\4\2$$^^\4\2))^^\3\2\f\f\4\2\f\f\61\61\t\2W" - + "Weekknouuwwzz\5\2C\\aac|\6\2\62;C\\aac|\u02a0\2\4\3\2\2\2\2\6\3\2\2\2" - + "\2\b\3\2\2\2\2\n\3\2\2\2\2\f\3\2\2\2\2\16\3\2\2\2\2\20\3\2\2\2\2\22\3" - + "\2\2\2\2\24\3\2\2\2\2\26\3\2\2\2\2\30\3\2\2\2\2\32\3\2\2\2\2\34\3\2\2" - + "\2\2\36\3\2\2\2\2 \3\2\2\2\2\"\3\2\2\2\2$\3\2\2\2\2&\3\2\2\2\2(\3\2\2" - + "\2\2*\3\2\2\2\2,\3\2\2\2\2.\3\2\2\2\2\60\3\2\2\2\2\62\3\2\2\2\2\64\3\2" - + "\2\2\2\66\3\2\2\2\28\3\2\2\2\2:\3\2\2\2\2<\3\2\2\2\2>\3\2\2\2\2@\3\2\2" - + "\2\2B\3\2\2\2\2D\3\2\2\2\2F\3\2\2\2\2H\3\2\2\2\2J\3\2\2\2\2L\3\2\2\2\2" - + "N\3\2\2\2\2P\3\2\2\2\2R\3\2\2\2\2T\3\2\2\2\2V\3\2\2\2\2X\3\2\2\2\2Z\3" - + "\2\2\2\2\\\3\2\2\2\2^\3\2\2\2\2`\3\2\2\2\2b\3\2\2\2\2d\3\2\2\2\2f\3\2" - + "\2\2\2h\3\2\2\2\2j\3\2\2\2\2l\3\2\2\2\2n\3\2\2\2\2p\3\2\2\2\2r\3\2\2\2" - + "\2t\3\2\2\2\2v\3\2\2\2\2x\3\2\2\2\2z\3\2\2\2\2|\3\2\2\2\2~\3\2\2\2\2\u0080" - + "\3\2\2\2\2\u0082\3\2\2\2\2\u0084\3\2\2\2\2\u0086\3\2\2\2\2\u0088\3\2\2" - + "\2\2\u008a\3\2\2\2\2\u008c\3\2\2\2\2\u008e\3\2\2\2\2\u0090\3\2\2\2\2\u0092" - + "\3\2\2\2\2\u0094\3\2\2\2\2\u0096\3\2\2\2\2\u0098\3\2\2\2\2\u009a\3\2\2" - + "\2\2\u009c\3\2\2\2\2\u009e\3\2\2\2\2\u00a0\3\2\2\2\2\u00a2\3\2\2\2\2\u00a4" - + "\3\2\2\2\2\u00a6\3\2\2\2\2\u00a8\3\2\2\2\3\u00aa\3\2\2\2\3\u00ac\3\2\2" - + "\2\4\u00af\3\2\2\2\6\u00ca\3\2\2\2\b\u00ce\3\2\2\2\n\u00d0\3\2\2\2\f\u00d2" - + "\3\2\2\2\16\u00d4\3\2\2\2\20\u00d6\3\2\2\2\22\u00d8\3\2\2\2\24\u00da\3" - + "\2\2\2\26\u00de\3\2\2\2\30\u00e3\3\2\2\2\32\u00e5\3\2\2\2\34\u00e7\3\2" - + "\2\2\36\u00ea\3\2\2\2 \u00ed\3\2\2\2\"\u00f2\3\2\2\2$\u00f8\3\2\2\2&\u00fb" - + "\3\2\2\2(\u00ff\3\2\2\2*\u0108\3\2\2\2,\u010e\3\2\2\2.\u0115\3\2\2\2\60" - + "\u0119\3\2\2\2\62\u011d\3\2\2\2\64\u0123\3\2\2\2\66\u0129\3\2\2\28\u012e" - + "\3\2\2\2:\u0139\3\2\2\2<\u013b\3\2\2\2>\u013d\3\2\2\2@\u013f\3\2\2\2B" - + "\u0142\3\2\2\2D\u0144\3\2\2\2F\u0146\3\2\2\2H\u0148\3\2\2\2J\u014b\3\2" - + "\2\2L\u014e\3\2\2\2N\u0152\3\2\2\2P\u0154\3\2\2\2R\u0157\3\2\2\2T\u0159" - + "\3\2\2\2V\u015c\3\2\2\2X\u015f\3\2\2\2Z\u0163\3\2\2\2\\\u0166\3\2\2\2" - + "^\u016a\3\2\2\2`\u016c\3\2\2\2b\u016e\3\2\2\2d\u0170\3\2\2\2f\u0173\3" - + "\2\2\2h\u0176\3\2\2\2j\u0178\3\2\2\2l\u017a\3\2\2\2n\u017d\3\2\2\2p\u0180" - + "\3\2\2\2r\u0183\3\2\2\2t\u0186\3\2\2\2v\u018a\3\2\2\2x\u018d\3\2\2\2z" - + "\u0190\3\2\2\2|\u0192\3\2\2\2~\u0195\3\2\2\2\u0080\u0198\3\2\2\2\u0082" - + "\u019b\3\2\2\2\u0084\u019e\3\2\2\2\u0086\u01a1\3\2\2\2\u0088\u01a4\3\2" - + "\2\2\u008a\u01a7\3\2\2\2\u008c\u01aa\3\2\2\2\u008e\u01ae\3\2\2\2\u0090" - + "\u01b2\3\2\2\2\u0092\u01b7\3\2\2\2\u0094\u01c0\3\2\2\2\u0096\u01d2\3\2" - + "\2\2\u0098\u01df\3\2\2\2\u009a\u020f\3\2\2\2\u009c\u0211\3\2\2\2\u009e" - + "\u0222\3\2\2\2\u00a0\u0227\3\2\2\2\u00a2\u022d\3\2\2\2\u00a4\u0258\3\2" - + "\2\2\u00a6\u025a\3\2\2\2\u00a8\u025e\3\2\2\2\u00aa\u026d\3\2\2\2\u00ac" - + "\u0271\3\2\2\2\u00ae\u00b0\t\2\2\2\u00af\u00ae\3\2\2\2\u00b0\u00b1\3\2" - + "\2\2\u00b1\u00af\3\2\2\2\u00b1\u00b2\3\2\2\2\u00b2\u00b3\3\2\2\2\u00b3" - + "\u00b4\b\2\2\2\u00b4\5\3\2\2\2\u00b5\u00b6\7\61\2\2\u00b6\u00b7\7\61\2" - + "\2\u00b7\u00bb\3\2\2\2\u00b8\u00ba\13\2\2\2\u00b9\u00b8\3\2\2\2\u00ba" - + "\u00bd\3\2\2\2\u00bb\u00bc\3\2\2\2\u00bb\u00b9\3\2\2\2\u00bc\u00be\3\2" - + "\2\2\u00bd\u00bb\3\2\2\2\u00be\u00cb\t\3\2\2\u00bf\u00c0\7\61\2\2\u00c0" - + "\u00c1\7,\2\2\u00c1\u00c5\3\2\2\2\u00c2\u00c4\13\2\2\2\u00c3\u00c2\3\2" - + "\2\2\u00c4\u00c7\3\2\2\2\u00c5\u00c6\3\2\2\2\u00c5\u00c3\3\2\2\2\u00c6" - + "\u00c8\3\2\2\2\u00c7\u00c5\3\2\2\2\u00c8\u00c9\7,\2\2\u00c9\u00cb\7\61" - + "\2\2\u00ca\u00b5\3\2\2\2\u00ca\u00bf\3\2\2\2\u00cb\u00cc\3\2\2\2\u00cc" - + "\u00cd\b\3\2\2\u00cd\7\3\2\2\2\u00ce\u00cf\7}\2\2\u00cf\t\3\2\2\2\u00d0" - + "\u00d1\7\177\2\2\u00d1\13\3\2\2\2\u00d2\u00d3\7]\2\2\u00d3\r\3\2\2\2\u00d4" - + "\u00d5\7_\2\2\u00d5\17\3\2\2\2\u00d6\u00d7\7*\2\2\u00d7\21\3\2\2\2\u00d8" - + "\u00d9\7+\2\2\u00d9\23\3\2\2\2\u00da\u00db\7\60\2\2\u00db\u00dc\3\2\2" - + "\2\u00dc\u00dd\b\n\3\2\u00dd\25\3\2\2\2\u00de\u00df\7A\2\2\u00df\u00e0" - + "\7\60\2\2\u00e0\u00e1\3\2\2\2\u00e1\u00e2\b\13\3\2\u00e2\27\3\2\2\2\u00e3" - + "\u00e4\7.\2\2\u00e4\31\3\2\2\2\u00e5\u00e6\7=\2\2\u00e6\33\3\2\2\2\u00e7" - + "\u00e8\7k\2\2\u00e8\u00e9\7h\2\2\u00e9\35\3\2\2\2\u00ea\u00eb\7k\2\2\u00eb" - + "\u00ec\7p\2\2\u00ec\37\3\2\2\2\u00ed\u00ee\7g\2\2\u00ee\u00ef\7n\2\2\u00ef" - + "\u00f0\7u\2\2\u00f0\u00f1\7g\2\2\u00f1!\3\2\2\2\u00f2\u00f3\7y\2\2\u00f3" - + "\u00f4\7j\2\2\u00f4\u00f5\7k\2\2\u00f5\u00f6\7n\2\2\u00f6\u00f7\7g\2\2" - + "\u00f7#\3\2\2\2\u00f8\u00f9\7f\2\2\u00f9\u00fa\7q\2\2\u00fa%\3\2\2\2\u00fb" - + "\u00fc\7h\2\2\u00fc\u00fd\7q\2\2\u00fd\u00fe\7t\2\2\u00fe\'\3\2\2\2\u00ff" - + "\u0100\7e\2\2\u0100\u0101\7q\2\2\u0101\u0102\7p\2\2\u0102\u0103\7v\2\2" - + "\u0103\u0104\7k\2\2\u0104\u0105\7p\2\2\u0105\u0106\7w\2\2\u0106\u0107" - + "\7g\2\2\u0107)\3\2\2\2\u0108\u0109\7d\2\2\u0109\u010a\7t\2\2\u010a\u010b" - + "\7g\2\2\u010b\u010c\7c\2\2\u010c\u010d\7m\2\2\u010d+\3\2\2\2\u010e\u010f" - + "\7t\2\2\u010f\u0110\7g\2\2\u0110\u0111\7v\2\2\u0111\u0112\7w\2\2\u0112" - + "\u0113\7t\2\2\u0113\u0114\7p\2\2\u0114-\3\2\2\2\u0115\u0116\7p\2\2\u0116" - + "\u0117\7g\2\2\u0117\u0118\7y\2\2\u0118/\3\2\2\2\u0119\u011a\7v\2\2\u011a" - + "\u011b\7t\2\2\u011b\u011c\7{\2\2\u011c\61\3\2\2\2\u011d\u011e\7e\2\2\u011e" - + "\u011f\7c\2\2\u011f\u0120\7v\2\2\u0120\u0121\7e\2\2\u0121\u0122\7j\2\2" - + "\u0122\63\3\2\2\2\u0123\u0124\7v\2\2\u0124\u0125\7j\2\2\u0125\u0126\7" - + "t\2\2\u0126\u0127\7q\2\2\u0127\u0128\7y\2\2\u0128\65\3\2\2\2\u0129\u012a" - + "\7v\2\2\u012a\u012b\7j\2\2\u012b\u012c\7k\2\2\u012c\u012d\7u\2\2\u012d" - + "\67\3\2\2\2\u012e\u012f\7k\2\2\u012f\u0130\7p\2\2\u0130\u0131\7u\2\2\u0131" - + "\u0132\7v\2\2\u0132\u0133\7c\2\2\u0133\u0134\7p\2\2\u0134\u0135\7e\2\2" - + "\u0135\u0136\7g\2\2\u0136\u0137\7q\2\2\u0137\u0138\7h\2\2\u01389\3\2\2" - + "\2\u0139\u013a\7#\2\2\u013a;\3\2\2\2\u013b\u013c\7\u0080\2\2\u013c=\3" - + "\2\2\2\u013d\u013e\7,\2\2\u013e?\3\2\2\2\u013f\u0140\7\61\2\2\u0140\u0141" - + "\6 \2\2\u0141A\3\2\2\2\u0142\u0143\7\'\2\2\u0143C\3\2\2\2\u0144\u0145" - + "\7-\2\2\u0145E\3\2\2\2\u0146\u0147\7/\2\2\u0147G\3\2\2\2\u0148\u0149\7" - + ">\2\2\u0149\u014a\7>\2\2\u014aI\3\2\2\2\u014b\u014c\7@\2\2\u014c\u014d" - + "\7@\2\2\u014dK\3\2\2\2\u014e\u014f\7@\2\2\u014f\u0150\7@\2\2\u0150\u0151" - + "\7@\2\2\u0151M\3\2\2\2\u0152\u0153\7>\2\2\u0153O\3\2\2\2\u0154\u0155\7" - + ">\2\2\u0155\u0156\7?\2\2\u0156Q\3\2\2\2\u0157\u0158\7@\2\2\u0158S\3\2" - + "\2\2\u0159\u015a\7@\2\2\u015a\u015b\7?\2\2\u015bU\3\2\2\2\u015c\u015d" - + "\7?\2\2\u015d\u015e\7?\2\2\u015eW\3\2\2\2\u015f\u0160\7?\2\2\u0160\u0161" - + "\7?\2\2\u0161\u0162\7?\2\2\u0162Y\3\2\2\2\u0163\u0164\7#\2\2\u0164\u0165" - + "\7?\2\2\u0165[\3\2\2\2\u0166\u0167\7#\2\2\u0167\u0168\7?\2\2\u0168\u0169" - + "\7?\2\2\u0169]\3\2\2\2\u016a\u016b\7(\2\2\u016b_\3\2\2\2\u016c\u016d\7" - + "`\2\2\u016da\3\2\2\2\u016e\u016f\7~\2\2\u016fc\3\2\2\2\u0170\u0171\7(" - + "\2\2\u0171\u0172\7(\2\2\u0172e\3\2\2\2\u0173\u0174\7~\2\2\u0174\u0175" - + "\7~\2\2\u0175g\3\2\2\2\u0176\u0177\7A\2\2\u0177i\3\2\2\2\u0178\u0179\7" - + "<\2\2\u0179k\3\2\2\2\u017a\u017b\7A\2\2\u017b\u017c\7<\2\2\u017cm\3\2" - + "\2\2\u017d\u017e\7<\2\2\u017e\u017f\7<\2\2\u017fo\3\2\2\2\u0180\u0181" - + "\7/\2\2\u0181\u0182\7@\2\2\u0182q\3\2\2\2\u0183\u0184\7?\2\2\u0184\u0185" - + "\7\u0080\2\2\u0185s\3\2\2\2\u0186\u0187\7?\2\2\u0187\u0188\7?\2\2\u0188" - + "\u0189\7\u0080\2\2\u0189u\3\2\2\2\u018a\u018b\7-\2\2\u018b\u018c\7-\2" - + "\2\u018cw\3\2\2\2\u018d\u018e\7/\2\2\u018e\u018f\7/\2\2\u018fy\3\2\2\2" - + "\u0190\u0191\7?\2\2\u0191{\3\2\2\2\u0192\u0193\7-\2\2\u0193\u0194\7?\2" - + "\2\u0194}\3\2\2\2\u0195\u0196\7/\2\2\u0196\u0197\7?\2\2\u0197\177\3\2" - + "\2\2\u0198\u0199\7,\2\2\u0199\u019a\7?\2\2\u019a\u0081\3\2\2\2\u019b\u019c" - + "\7\61\2\2\u019c\u019d\7?\2\2\u019d\u0083\3\2\2\2\u019e\u019f\7\'\2\2\u019f" - + "\u01a0\7?\2\2\u01a0\u0085\3\2\2\2\u01a1\u01a2\7(\2\2\u01a2\u01a3\7?\2" - + "\2\u01a3\u0087\3\2\2\2\u01a4\u01a5\7`\2\2\u01a5\u01a6\7?\2\2\u01a6\u0089" - + "\3\2\2\2\u01a7\u01a8\7~\2\2\u01a8\u01a9\7?\2\2\u01a9\u008b\3\2\2\2\u01aa" - + "\u01ab\7>\2\2\u01ab\u01ac\7>\2\2\u01ac\u01ad\7?\2\2\u01ad\u008d\3\2\2" - + "\2\u01ae\u01af\7@\2\2\u01af\u01b0\7@\2\2\u01b0\u01b1\7?\2\2\u01b1\u008f" - + "\3\2\2\2\u01b2\u01b3\7@\2\2\u01b3\u01b4\7@\2\2\u01b4\u01b5\7@\2\2\u01b5" - + "\u01b6\7?\2\2\u01b6\u0091\3\2\2\2\u01b7\u01b9\7\62\2\2\u01b8\u01ba\t\4" - + "\2\2\u01b9\u01b8\3\2\2\2\u01ba\u01bb\3\2\2\2\u01bb\u01b9\3\2\2\2\u01bb" - + "\u01bc\3\2\2\2\u01bc\u01be\3\2\2\2\u01bd\u01bf\t\5\2\2\u01be\u01bd\3\2" - + "\2\2\u01be\u01bf\3\2\2\2\u01bf\u0093\3\2\2\2\u01c0\u01c1\7\62\2\2\u01c1" - + "\u01c3\t\6\2\2\u01c2\u01c4\t\7\2\2\u01c3\u01c2\3\2\2\2\u01c4\u01c5\3\2" - + "\2\2\u01c5\u01c3\3\2\2\2\u01c5\u01c6\3\2\2\2\u01c6\u01c8\3\2\2\2\u01c7" - + "\u01c9\t\5\2\2\u01c8\u01c7\3\2\2\2\u01c8\u01c9\3\2\2\2\u01c9\u0095\3\2" - + "\2\2\u01ca\u01d3\7\62\2\2\u01cb\u01cf\t\b\2\2\u01cc\u01ce\t\t\2\2\u01cd" - + "\u01cc\3\2\2\2\u01ce\u01d1\3\2\2\2\u01cf\u01cd\3\2\2\2\u01cf\u01d0\3\2" - + "\2\2\u01d0\u01d3\3\2\2\2\u01d1\u01cf\3\2\2\2\u01d2\u01ca\3\2\2\2\u01d2" - + "\u01cb\3\2\2\2\u01d3\u01d5\3\2\2\2\u01d4\u01d6\t\n\2\2\u01d5\u01d4\3\2" - + "\2\2\u01d5\u01d6\3\2\2\2\u01d6\u0097\3\2\2\2\u01d7\u01e0\7\62\2\2\u01d8" - + "\u01dc\t\b\2\2\u01d9\u01db\t\t\2\2\u01da\u01d9\3\2\2\2\u01db\u01de\3\2" - + "\2\2\u01dc\u01da\3\2\2\2\u01dc\u01dd\3\2\2\2\u01dd\u01e0\3\2\2\2\u01de" - + "\u01dc\3\2\2\2\u01df\u01d7\3\2\2\2\u01df\u01d8\3\2\2\2\u01e0\u01e7\3\2" - + "\2\2\u01e1\u01e3\5\24\n\2\u01e2\u01e4\t\t\2\2\u01e3\u01e2\3\2\2\2\u01e4" - + "\u01e5\3\2\2\2\u01e5\u01e3\3\2\2\2\u01e5\u01e6\3\2\2\2\u01e6\u01e8\3\2" - + "\2\2\u01e7\u01e1\3\2\2\2\u01e7\u01e8\3\2\2\2\u01e8\u01f2\3\2\2\2\u01e9" - + "\u01eb\t\13\2\2\u01ea\u01ec\t\f\2\2\u01eb\u01ea\3\2\2\2\u01eb\u01ec\3" - + "\2\2\2\u01ec\u01ee\3\2\2\2\u01ed\u01ef\t\t\2\2\u01ee\u01ed\3\2\2\2\u01ef" - + "\u01f0\3\2\2\2\u01f0\u01ee\3\2\2\2\u01f0\u01f1\3\2\2\2\u01f1\u01f3\3\2" - + "\2\2\u01f2\u01e9\3\2\2\2\u01f2\u01f3\3\2\2\2\u01f3\u01f5\3\2\2\2\u01f4" - + "\u01f6\t\r\2\2\u01f5\u01f4\3\2\2\2\u01f5\u01f6\3\2\2\2\u01f6\u0099\3\2" - + "\2\2\u01f7\u01ff\7$\2\2\u01f8\u01f9\7^\2\2\u01f9\u01fe\7$\2\2\u01fa\u01fb" - + "\7^\2\2\u01fb\u01fe\7^\2\2\u01fc\u01fe\n\16\2\2\u01fd\u01f8\3\2\2\2\u01fd" - + "\u01fa\3\2\2\2\u01fd\u01fc\3\2\2\2\u01fe\u0201\3\2\2\2\u01ff\u0200\3\2" - + "\2\2\u01ff\u01fd\3\2\2\2\u0200\u0202\3\2\2\2\u0201\u01ff\3\2\2\2\u0202" - + "\u0210\7$\2\2\u0203\u020b\7)\2\2\u0204\u0205\7^\2\2\u0205\u020a\7)\2\2" - + "\u0206\u0207\7^\2\2\u0207\u020a\7^\2\2\u0208\u020a\n\17\2\2\u0209\u0204" - + "\3\2\2\2\u0209\u0206\3\2\2\2\u0209\u0208\3\2\2\2\u020a\u020d\3\2\2\2\u020b" - + "\u020c\3\2\2\2\u020b\u0209\3\2\2\2\u020c\u020e\3\2\2\2\u020d\u020b\3\2" - + "\2\2\u020e\u0210\7)\2\2\u020f\u01f7\3\2\2\2\u020f\u0203\3\2\2\2\u0210" - + "\u009b\3\2\2\2\u0211\u0215\7\61\2\2\u0212\u0213\7^\2\2\u0213\u0216\n\20" - + "\2\2\u0214\u0216\n\21\2\2\u0215\u0212\3\2\2\2\u0215\u0214\3\2\2\2\u0216" - + "\u0217\3\2\2\2\u0217\u0218\3\2\2\2\u0217\u0215\3\2\2\2\u0218\u0219\3\2" - + "\2\2\u0219\u021d\7\61\2\2\u021a\u021c\t\22\2\2\u021b\u021a\3\2\2\2\u021c" - + "\u021f\3\2\2\2\u021d\u021b\3\2\2\2\u021d\u021e\3\2\2\2\u021e\u0220\3\2" - + "\2\2\u021f\u021d\3\2\2\2\u0220\u0221\6N\3\2\u0221\u009d\3\2\2\2\u0222" - + "\u0223\7v\2\2\u0223\u0224\7t\2\2\u0224\u0225\7w\2\2\u0225\u0226\7g\2\2" - + "\u0226\u009f\3\2\2\2\u0227\u0228\7h\2\2\u0228\u0229\7c\2\2\u0229\u022a" - + "\7n\2\2\u022a\u022b\7u\2\2\u022b\u022c\7g\2\2\u022c\u00a1\3\2\2\2\u022d" - + "\u022e\7p\2\2\u022e\u022f\7w\2\2\u022f\u0230\7n\2\2\u0230\u0231\7n\2\2" - + "\u0231\u00a3\3\2\2\2\u0232\u0233\7d\2\2\u0233\u0234\7q\2\2\u0234\u0235" - + "\7q\2\2\u0235\u0236\7n\2\2\u0236\u0237\7g\2\2\u0237\u0238\7c\2\2\u0238" - + "\u0259\7p\2\2\u0239\u023a\7d\2\2\u023a\u023b\7{\2\2\u023b\u023c\7v\2\2" - + "\u023c\u0259\7g\2\2\u023d\u023e\7u\2\2\u023e\u023f\7j\2\2\u023f\u0240" - + "\7q\2\2\u0240\u0241\7t\2\2\u0241\u0259\7v\2\2\u0242\u0243\7e\2\2\u0243" - + "\u0244\7j\2\2\u0244\u0245\7c\2\2\u0245\u0259\7t\2\2\u0246\u0247\7k\2\2" - + "\u0247\u0248\7p\2\2\u0248\u0259\7v\2\2\u0249\u024a\7n\2\2\u024a\u024b" - + "\7q\2\2\u024b\u024c\7p\2\2\u024c\u0259\7i\2\2\u024d\u024e\7h\2\2\u024e" - + "\u024f\7n\2\2\u024f\u0250\7q\2\2\u0250\u0251\7c\2\2\u0251\u0259\7v\2\2" - + "\u0252\u0253\7f\2\2\u0253\u0254\7q\2\2\u0254\u0255\7w\2\2\u0255\u0256" - + "\7d\2\2\u0256\u0257\7n\2\2\u0257\u0259\7g\2\2\u0258\u0232\3\2\2\2\u0258" - + "\u0239\3\2\2\2\u0258\u023d\3\2\2\2\u0258\u0242\3\2\2\2\u0258\u0246\3\2" - + "\2\2\u0258\u0249\3\2\2\2\u0258\u024d\3\2\2\2\u0258\u0252\3\2\2\2\u0259" - + "\u00a5\3\2\2\2\u025a\u025b\7f\2\2\u025b\u025c\7g\2\2\u025c\u025d\7h\2" - + "\2\u025d\u00a7\3\2\2\2\u025e\u0262\t\23\2\2\u025f\u0261\t\24\2\2\u0260" - + "\u025f\3\2\2\2\u0261\u0264\3\2\2\2\u0262\u0260\3\2\2\2\u0262\u0263\3\2" - + "\2\2\u0263\u00a9\3\2\2\2\u0264\u0262\3\2\2\2\u0265\u026e\7\62\2\2\u0266" - + "\u026a\t\b\2\2\u0267\u0269\t\t\2\2\u0268\u0267\3\2\2\2\u0269\u026c\3\2" - + "\2\2\u026a\u0268\3\2\2\2\u026a\u026b\3\2\2\2\u026b\u026e\3\2\2\2\u026c" - + "\u026a\3\2\2\2\u026d\u0265\3\2\2\2\u026d\u0266\3\2\2\2\u026e\u026f\3\2" - + "\2\2\u026f\u0270\bU\4\2\u0270\u00ab\3\2\2\2\u0271\u0275\t\23\2\2\u0272" - + "\u0274\t\24\2\2\u0273\u0272\3\2\2\2\u0274\u0277\3\2\2\2\u0275\u0273\3" - + "\2\2\2\u0275\u0276\3\2\2\2\u0276\u0278\3\2\2\2\u0277\u0275\3\2\2\2\u0278" - + "\u0279\bV\4\2\u0279\u00ad\3\2\2\2$\2\3\u00b1\u00bb\u00c5\u00ca\u01bb\u01be" - + "\u01c5\u01c8\u01cf\u01d2\u01d5\u01dc\u01df\u01e5\u01e7\u01eb\u01f0\u01f2" - + "\u01f5\u01fd\u01ff\u0209\u020b\u020f\u0215\u0217\u021d\u0258\u0262\u026a" - + "\u026d\u0275\5\b\2\2\4\3\2\4\2\2"; + public static final String _serializedATN = "\u0004\u0000U\u0278\u0006\uffff\uffff\u0006\uffff\uffff\u0002\u0000\u0007" + + "\u0000\u0002\u0001\u0007\u0001\u0002\u0002\u0007\u0002\u0002\u0003\u0007" + + "\u0003\u0002\u0004\u0007\u0004\u0002\u0005\u0007\u0005\u0002\u0006\u0007" + + "\u0006\u0002\u0007\u0007\u0007\u0002\b\u0007\b\u0002\t\u0007\t\u0002\n" + + "\u0007\n\u0002\u000b\u0007\u000b\u0002\f\u0007\f\u0002\r\u0007\r\u0002" + + "\u000e\u0007\u000e\u0002\u000f\u0007\u000f\u0002\u0010\u0007\u0010\u0002" + + "\u0011\u0007\u0011\u0002\u0012\u0007\u0012\u0002\u0013\u0007\u0013\u0002" + + "\u0014\u0007\u0014\u0002\u0015\u0007\u0015\u0002\u0016\u0007\u0016\u0002" + + "\u0017\u0007\u0017\u0002\u0018\u0007\u0018\u0002\u0019\u0007\u0019\u0002" + + "\u001a\u0007\u001a\u0002\u001b\u0007\u001b\u0002\u001c\u0007\u001c\u0002" + + "\u001d\u0007\u001d\u0002\u001e\u0007\u001e\u0002\u001f\u0007\u001f\u0002" + + " \u0007 \u0002!\u0007!\u0002\"\u0007\"\u0002#\u0007#\u0002$\u0007$\u0002" + + "%\u0007%\u0002&\u0007&\u0002\'\u0007\'\u0002(\u0007(\u0002)\u0007)\u0002" + + "*\u0007*\u0002+\u0007+\u0002,\u0007,\u0002-\u0007-\u0002.\u0007.\u0002" + + "/\u0007/\u00020\u00070\u00021\u00071\u00022\u00072\u00023\u00073\u0002" + + "4\u00074\u00025\u00075\u00026\u00076\u00027\u00077\u00028\u00078\u0002" + + "9\u00079\u0002:\u0007:\u0002;\u0007;\u0002<\u0007<\u0002=\u0007=\u0002" + + ">\u0007>\u0002?\u0007?\u0002@\u0007@\u0002A\u0007A\u0002B\u0007B\u0002" + + "C\u0007C\u0002D\u0007D\u0002E\u0007E\u0002F\u0007F\u0002G\u0007G\u0002" + + "H\u0007H\u0002I\u0007I\u0002J\u0007J\u0002K\u0007K\u0002L\u0007L\u0002" + + "M\u0007M\u0002N\u0007N\u0002O\u0007O\u0002P\u0007P\u0002Q\u0007Q\u0002" + + "R\u0007R\u0002S\u0007S\u0002T\u0007T\u0001\u0000\u0004\u0000\u00ae\b\u0000" + + "\u000b\u0000\f\u0000\u00af\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001" + + "\u0001\u0001\u0001\u0001\u0005\u0001\u00b8\b\u0001\n\u0001\f\u0001\u00bb" + + "\t\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005" + + "\u0001\u00c2\b\u0001\n\u0001\f\u0001\u00c5\t\u0001\u0001\u0001\u0001\u0001" + + "\u0003\u0001\u00c9\b\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002" + + "\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005" + + "\u0001\u0006\u0001\u0006\u0001\u0007\u0001\u0007\u0001\b\u0001\b\u0001" + + "\b\u0001\b\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\n\u0001\n\u0001" + + "\u000b\u0001\u000b\u0001\f\u0001\f\u0001\f\u0001\r\u0001\r\u0001\r\u0001" + + "\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000e\u0001\u000f\u0001" + + "\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u0010\u0001" + + "\u0010\u0001\u0010\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001" + + "\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0001" + + "\u0012\u0001\u0012\u0001\u0012\u0001\u0013\u0001\u0013\u0001\u0013\u0001" + + "\u0013\u0001\u0013\u0001\u0013\u0001\u0014\u0001\u0014\u0001\u0014\u0001" + + "\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0015\u0001\u0015\u0001" + + "\u0015\u0001\u0015\u0001\u0016\u0001\u0016\u0001\u0016\u0001\u0016\u0001" + + "\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001\u0017\u0001" + + "\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001" + + "\u0019\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u0019\u0001\u001a\u0001" + + "\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001" + + "\u001a\u0001\u001a\u0001\u001a\u0001\u001a\u0001\u001b\u0001\u001b\u0001" + + "\u001c\u0001\u001c\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001" + + "\u001e\u0001\u001f\u0001\u001f\u0001 \u0001 \u0001!\u0001!\u0001\"\u0001" + + "\"\u0001\"\u0001#\u0001#\u0001#\u0001$\u0001$\u0001$\u0001$\u0001%\u0001" + + "%\u0001&\u0001&\u0001&\u0001\'\u0001\'\u0001(\u0001(\u0001(\u0001)\u0001" + + ")\u0001)\u0001*\u0001*\u0001*\u0001*\u0001+\u0001+\u0001+\u0001,\u0001" + + ",\u0001,\u0001,\u0001-\u0001-\u0001.\u0001.\u0001/\u0001/\u00010\u0001" + + "0\u00010\u00011\u00011\u00011\u00012\u00012\u00013\u00013\u00014\u0001" + + "4\u00014\u00015\u00015\u00015\u00016\u00016\u00016\u00017\u00017\u0001" + + "7\u00018\u00018\u00018\u00018\u00019\u00019\u00019\u0001:\u0001:\u0001" + + ":\u0001;\u0001;\u0001<\u0001<\u0001<\u0001=\u0001=\u0001=\u0001>\u0001" + + ">\u0001>\u0001?\u0001?\u0001?\u0001@\u0001@\u0001@\u0001A\u0001A\u0001" + + "A\u0001B\u0001B\u0001B\u0001C\u0001C\u0001C\u0001D\u0001D\u0001D\u0001" + + "D\u0001E\u0001E\u0001E\u0001E\u0001F\u0001F\u0001F\u0001F\u0001F\u0001" + + "G\u0001G\u0004G\u01b8\bG\u000bG\fG\u01b9\u0001G\u0003G\u01bd\bG\u0001" + + "H\u0001H\u0001H\u0004H\u01c2\bH\u000bH\fH\u01c3\u0001H\u0003H\u01c7\b" + + "H\u0001I\u0001I\u0001I\u0005I\u01cc\bI\nI\fI\u01cf\tI\u0003I\u01d1\bI" + + "\u0001I\u0003I\u01d4\bI\u0001J\u0001J\u0001J\u0005J\u01d9\bJ\nJ\fJ\u01dc" + + "\tJ\u0003J\u01de\bJ\u0001J\u0001J\u0004J\u01e2\bJ\u000bJ\fJ\u01e3\u0003" + + "J\u01e6\bJ\u0001J\u0001J\u0003J\u01ea\bJ\u0001J\u0004J\u01ed\bJ\u000b" + + "J\fJ\u01ee\u0003J\u01f1\bJ\u0001J\u0003J\u01f4\bJ\u0001K\u0001K\u0001" + + "K\u0001K\u0001K\u0001K\u0005K\u01fc\bK\nK\fK\u01ff\tK\u0001K\u0001K\u0001" + + "K\u0001K\u0001K\u0001K\u0001K\u0005K\u0208\bK\nK\fK\u020b\tK\u0001K\u0003" + + "K\u020e\bK\u0001L\u0001L\u0001L\u0001L\u0004L\u0214\bL\u000bL\fL\u0215" + + "\u0001L\u0001L\u0005L\u021a\bL\nL\fL\u021d\tL\u0001L\u0001L\u0001M\u0001" + + "M\u0001M\u0001M\u0001M\u0001N\u0001N\u0001N\u0001N\u0001N\u0001N\u0001" + + "O\u0001O\u0001O\u0001O\u0001O\u0001P\u0001P\u0001P\u0001P\u0001P\u0001" + + "P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001" + + "P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001" + + "P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001P\u0001" + + "P\u0001P\u0001P\u0003P\u0257\bP\u0001Q\u0001Q\u0001Q\u0001Q\u0001R\u0001" + + "R\u0005R\u025f\bR\nR\fR\u0262\tR\u0001S\u0001S\u0001S\u0005S\u0267\bS" + + "\nS\fS\u026a\tS\u0003S\u026c\bS\u0001S\u0001S\u0001T\u0001T\u0005T\u0272" + + "\bT\nT\fT\u0275\tT\u0001T\u0001T\u0005\u00b9\u00c3\u01fd\u0209\u0215\u0000" + + "U\u0002\u0001\u0004\u0002\u0006\u0003\b\u0004\n\u0005\f\u0006\u000e\u0007" + + "\u0010\b\u0012\t\u0014\n\u0016\u000b\u0018\f\u001a\r\u001c\u000e\u001e" + + "\u000f \u0010\"\u0011$\u0012&\u0013(\u0014*\u0015,\u0016.\u00170\u0018" + + "2\u00194\u001a6\u001b8\u001c:\u001d<\u001e>\u001f@ B!D\"F#H$J%L&N\'P(" + + "R)T*V+X,Z-\\.^/`0b1d2f3h4j5l6n7p8r9t:v;x~?\u0080@\u0082A\u0084B\u0086" + + "C\u0088D\u008aE\u008cF\u008eG\u0090H\u0092I\u0094J\u0096K\u0098L\u009a" + + "M\u009cN\u009eO\u00a0P\u00a2Q\u00a4R\u00a6S\u00a8T\u00aaU\u0002\u0000" + + "\u0001\u0013\u0003\u0000\t\n\r\r \u0002\u0000\n\n\r\r\u0001\u000007\u0002" + + "\u0000LLll\u0002\u0000XXxx\u0003\u000009AFaf\u0001\u000019\u0001\u0000" + + "09\u0006\u0000DDFFLLddffll\u0002\u0000EEee\u0002\u0000++--\u0004\u0000" + + "DDFFddff\u0002\u0000\"\"\\\\\u0002\u0000\'\'\\\\\u0001\u0000\n\n\u0002" + + "\u0000\n\n//\u0007\u0000UUcciilmssuuxx\u0003\u0000AZ__az\u0004\u00000" + + "9AZ__az\u029e\u0000\u0002\u0001\u0000\u0000\u0000\u0000\u0004\u0001\u0000" + + "\u0000\u0000\u0000\u0006\u0001\u0000\u0000\u0000\u0000\b\u0001\u0000\u0000" + + "\u0000\u0000\n\u0001\u0000\u0000\u0000\u0000\f\u0001\u0000\u0000\u0000" + + "\u0000\u000e\u0001\u0000\u0000\u0000\u0000\u0010\u0001\u0000\u0000\u0000" + + "\u0000\u0012\u0001\u0000\u0000\u0000\u0000\u0014\u0001\u0000\u0000\u0000" + + "\u0000\u0016\u0001\u0000\u0000\u0000\u0000\u0018\u0001\u0000\u0000\u0000" + + "\u0000\u001a\u0001\u0000\u0000\u0000\u0000\u001c\u0001\u0000\u0000\u0000" + + "\u0000\u001e\u0001\u0000\u0000\u0000\u0000 \u0001\u0000\u0000\u0000\u0000" + + "\"\u0001\u0000\u0000\u0000\u0000$\u0001\u0000\u0000\u0000\u0000&\u0001" + + "\u0000\u0000\u0000\u0000(\u0001\u0000\u0000\u0000\u0000*\u0001\u0000\u0000" + + "\u0000\u0000,\u0001\u0000\u0000\u0000\u0000.\u0001\u0000\u0000\u0000\u0000" + + "0\u0001\u0000\u0000\u0000\u00002\u0001\u0000\u0000\u0000\u00004\u0001" + + "\u0000\u0000\u0000\u00006\u0001\u0000\u0000\u0000\u00008\u0001\u0000\u0000" + + "\u0000\u0000:\u0001\u0000\u0000\u0000\u0000<\u0001\u0000\u0000\u0000\u0000" + + ">\u0001\u0000\u0000\u0000\u0000@\u0001\u0000\u0000\u0000\u0000B\u0001" + + "\u0000\u0000\u0000\u0000D\u0001\u0000\u0000\u0000\u0000F\u0001\u0000\u0000" + + "\u0000\u0000H\u0001\u0000\u0000\u0000\u0000J\u0001\u0000\u0000\u0000\u0000" + + "L\u0001\u0000\u0000\u0000\u0000N\u0001\u0000\u0000\u0000\u0000P\u0001" + + "\u0000\u0000\u0000\u0000R\u0001\u0000\u0000\u0000\u0000T\u0001\u0000\u0000" + + "\u0000\u0000V\u0001\u0000\u0000\u0000\u0000X\u0001\u0000\u0000\u0000\u0000" + + "Z\u0001\u0000\u0000\u0000\u0000\\\u0001\u0000\u0000\u0000\u0000^\u0001" + + "\u0000\u0000\u0000\u0000`\u0001\u0000\u0000\u0000\u0000b\u0001\u0000\u0000" + + "\u0000\u0000d\u0001\u0000\u0000\u0000\u0000f\u0001\u0000\u0000\u0000\u0000" + + "h\u0001\u0000\u0000\u0000\u0000j\u0001\u0000\u0000\u0000\u0000l\u0001" + + "\u0000\u0000\u0000\u0000n\u0001\u0000\u0000\u0000\u0000p\u0001\u0000\u0000" + + "\u0000\u0000r\u0001\u0000\u0000\u0000\u0000t\u0001\u0000\u0000\u0000\u0000" + + "v\u0001\u0000\u0000\u0000\u0000x\u0001\u0000\u0000\u0000\u0000z\u0001" + + "\u0000\u0000\u0000\u0000|\u0001\u0000\u0000\u0000\u0000~\u0001\u0000\u0000" + + "\u0000\u0000\u0080\u0001\u0000\u0000\u0000\u0000\u0082\u0001\u0000\u0000" + + "\u0000\u0000\u0084\u0001\u0000\u0000\u0000\u0000\u0086\u0001\u0000\u0000" + + "\u0000\u0000\u0088\u0001\u0000\u0000\u0000\u0000\u008a\u0001\u0000\u0000" + + "\u0000\u0000\u008c\u0001\u0000\u0000\u0000\u0000\u008e\u0001\u0000\u0000" + + "\u0000\u0000\u0090\u0001\u0000\u0000\u0000\u0000\u0092\u0001\u0000\u0000" + + "\u0000\u0000\u0094\u0001\u0000\u0000\u0000\u0000\u0096\u0001\u0000\u0000" + + "\u0000\u0000\u0098\u0001\u0000\u0000\u0000\u0000\u009a\u0001\u0000\u0000" + + "\u0000\u0000\u009c\u0001\u0000\u0000\u0000\u0000\u009e\u0001\u0000\u0000" + + "\u0000\u0000\u00a0\u0001\u0000\u0000\u0000\u0000\u00a2\u0001\u0000\u0000" + + "\u0000\u0000\u00a4\u0001\u0000\u0000\u0000\u0000\u00a6\u0001\u0000\u0000" + + "\u0000\u0001\u00a8\u0001\u0000\u0000\u0000\u0001\u00aa\u0001\u0000\u0000" + + "\u0000\u0002\u00ad\u0001\u0000\u0000\u0000\u0004\u00c8\u0001\u0000\u0000" + + "\u0000\u0006\u00cc\u0001\u0000\u0000\u0000\b\u00ce\u0001\u0000\u0000\u0000" + + "\n\u00d0\u0001\u0000\u0000\u0000\f\u00d2\u0001\u0000\u0000\u0000\u000e" + + "\u00d4\u0001\u0000\u0000\u0000\u0010\u00d6\u0001\u0000\u0000\u0000\u0012" + + "\u00d8\u0001\u0000\u0000\u0000\u0014\u00dc\u0001\u0000\u0000\u0000\u0016" + + "\u00e1\u0001\u0000\u0000\u0000\u0018\u00e3\u0001\u0000\u0000\u0000\u001a" + + "\u00e5\u0001\u0000\u0000\u0000\u001c\u00e8\u0001\u0000\u0000\u0000\u001e" + + "\u00eb\u0001\u0000\u0000\u0000 \u00f0\u0001\u0000\u0000\u0000\"\u00f6" + + "\u0001\u0000\u0000\u0000$\u00f9\u0001\u0000\u0000\u0000&\u00fd\u0001\u0000" + + "\u0000\u0000(\u0106\u0001\u0000\u0000\u0000*\u010c\u0001\u0000\u0000\u0000" + + ",\u0113\u0001\u0000\u0000\u0000.\u0117\u0001\u0000\u0000\u00000\u011b" + + "\u0001\u0000\u0000\u00002\u0121\u0001\u0000\u0000\u00004\u0127\u0001\u0000" + + "\u0000\u00006\u012c\u0001\u0000\u0000\u00008\u0137\u0001\u0000\u0000\u0000" + + ":\u0139\u0001\u0000\u0000\u0000<\u013b\u0001\u0000\u0000\u0000>\u013d" + + "\u0001\u0000\u0000\u0000@\u0140\u0001\u0000\u0000\u0000B\u0142\u0001\u0000" + + "\u0000\u0000D\u0144\u0001\u0000\u0000\u0000F\u0146\u0001\u0000\u0000\u0000" + + "H\u0149\u0001\u0000\u0000\u0000J\u014c\u0001\u0000\u0000\u0000L\u0150" + + "\u0001\u0000\u0000\u0000N\u0152\u0001\u0000\u0000\u0000P\u0155\u0001\u0000" + + "\u0000\u0000R\u0157\u0001\u0000\u0000\u0000T\u015a\u0001\u0000\u0000\u0000" + + "V\u015d\u0001\u0000\u0000\u0000X\u0161\u0001\u0000\u0000\u0000Z\u0164" + + "\u0001\u0000\u0000\u0000\\\u0168\u0001\u0000\u0000\u0000^\u016a\u0001" + + "\u0000\u0000\u0000`\u016c\u0001\u0000\u0000\u0000b\u016e\u0001\u0000\u0000" + + "\u0000d\u0171\u0001\u0000\u0000\u0000f\u0174\u0001\u0000\u0000\u0000h" + + "\u0176\u0001\u0000\u0000\u0000j\u0178\u0001\u0000\u0000\u0000l\u017b\u0001" + + "\u0000\u0000\u0000n\u017e\u0001\u0000\u0000\u0000p\u0181\u0001\u0000\u0000" + + "\u0000r\u0184\u0001\u0000\u0000\u0000t\u0188\u0001\u0000\u0000\u0000v" + + "\u018b\u0001\u0000\u0000\u0000x\u018e\u0001\u0000\u0000\u0000z\u0190\u0001" + + "\u0000\u0000\u0000|\u0193\u0001\u0000\u0000\u0000~\u0196\u0001\u0000\u0000" + + "\u0000\u0080\u0199\u0001\u0000\u0000\u0000\u0082\u019c\u0001\u0000\u0000" + + "\u0000\u0084\u019f\u0001\u0000\u0000\u0000\u0086\u01a2\u0001\u0000\u0000" + + "\u0000\u0088\u01a5\u0001\u0000\u0000\u0000\u008a\u01a8\u0001\u0000\u0000" + + "\u0000\u008c\u01ac\u0001\u0000\u0000\u0000\u008e\u01b0\u0001\u0000\u0000" + + "\u0000\u0090\u01b5\u0001\u0000\u0000\u0000\u0092\u01be\u0001\u0000\u0000" + + "\u0000\u0094\u01d0\u0001\u0000\u0000\u0000\u0096\u01dd\u0001\u0000\u0000" + + "\u0000\u0098\u020d\u0001\u0000\u0000\u0000\u009a\u020f\u0001\u0000\u0000" + + "\u0000\u009c\u0220\u0001\u0000\u0000\u0000\u009e\u0225\u0001\u0000\u0000" + + "\u0000\u00a0\u022b\u0001\u0000\u0000\u0000\u00a2\u0256\u0001\u0000\u0000" + + "\u0000\u00a4\u0258\u0001\u0000\u0000\u0000\u00a6\u025c\u0001\u0000\u0000" + + "\u0000\u00a8\u026b\u0001\u0000\u0000\u0000\u00aa\u026f\u0001\u0000\u0000" + + "\u0000\u00ac\u00ae\u0007\u0000\u0000\u0000\u00ad\u00ac\u0001\u0000\u0000" + + "\u0000\u00ae\u00af\u0001\u0000\u0000\u0000\u00af\u00ad\u0001\u0000\u0000" + + "\u0000\u00af\u00b0\u0001\u0000\u0000\u0000\u00b0\u00b1\u0001\u0000\u0000" + + "\u0000\u00b1\u00b2\u0006\u0000\u0000\u0000\u00b2\u0003\u0001\u0000\u0000" + + "\u0000\u00b3\u00b4\u0005/\u0000\u0000\u00b4\u00b5\u0005/\u0000\u0000\u00b5" + + "\u00b9\u0001\u0000\u0000\u0000\u00b6\u00b8\t\u0000\u0000\u0000\u00b7\u00b6" + + "\u0001\u0000\u0000\u0000\u00b8\u00bb\u0001\u0000\u0000\u0000\u00b9\u00ba" + + "\u0001\u0000\u0000\u0000\u00b9\u00b7\u0001\u0000\u0000\u0000\u00ba\u00bc" + + "\u0001\u0000\u0000\u0000\u00bb\u00b9\u0001\u0000\u0000\u0000\u00bc\u00c9" + + "\u0007\u0001\u0000\u0000\u00bd\u00be\u0005/\u0000\u0000\u00be\u00bf\u0005" + + "*\u0000\u0000\u00bf\u00c3\u0001\u0000\u0000\u0000\u00c0\u00c2\t\u0000" + + "\u0000\u0000\u00c1\u00c0\u0001\u0000\u0000\u0000\u00c2\u00c5\u0001\u0000" + + "\u0000\u0000\u00c3\u00c4\u0001\u0000\u0000\u0000\u00c3\u00c1\u0001\u0000" + + "\u0000\u0000\u00c4\u00c6\u0001\u0000\u0000\u0000\u00c5\u00c3\u0001\u0000" + + "\u0000\u0000\u00c6\u00c7\u0005*\u0000\u0000\u00c7\u00c9\u0005/\u0000\u0000" + + "\u00c8\u00b3\u0001\u0000\u0000\u0000\u00c8\u00bd\u0001\u0000\u0000\u0000" + + "\u00c9\u00ca\u0001\u0000\u0000\u0000\u00ca\u00cb\u0006\u0001\u0000\u0000" + + "\u00cb\u0005\u0001\u0000\u0000\u0000\u00cc\u00cd\u0005{\u0000\u0000\u00cd" + + "\u0007\u0001\u0000\u0000\u0000\u00ce\u00cf\u0005}\u0000\u0000\u00cf\t" + + "\u0001\u0000\u0000\u0000\u00d0\u00d1\u0005[\u0000\u0000\u00d1\u000b\u0001" + + "\u0000\u0000\u0000\u00d2\u00d3\u0005]\u0000\u0000\u00d3\r\u0001\u0000" + + "\u0000\u0000\u00d4\u00d5\u0005(\u0000\u0000\u00d5\u000f\u0001\u0000\u0000" + + "\u0000\u00d6\u00d7\u0005)\u0000\u0000\u00d7\u0011\u0001\u0000\u0000\u0000" + + "\u00d8\u00d9\u0005.\u0000\u0000\u00d9\u00da\u0001\u0000\u0000\u0000\u00da" + + "\u00db\u0006\b\u0001\u0000\u00db\u0013\u0001\u0000\u0000\u0000\u00dc\u00dd" + + "\u0005?\u0000\u0000\u00dd\u00de\u0005.\u0000\u0000\u00de\u00df\u0001\u0000" + + "\u0000\u0000\u00df\u00e0\u0006\t\u0001\u0000\u00e0\u0015\u0001\u0000\u0000" + + "\u0000\u00e1\u00e2\u0005,\u0000\u0000\u00e2\u0017\u0001\u0000\u0000\u0000" + + "\u00e3\u00e4\u0005;\u0000\u0000\u00e4\u0019\u0001\u0000\u0000\u0000\u00e5" + + "\u00e6\u0005i\u0000\u0000\u00e6\u00e7\u0005f\u0000\u0000\u00e7\u001b\u0001" + + "\u0000\u0000\u0000\u00e8\u00e9\u0005i\u0000\u0000\u00e9\u00ea\u0005n\u0000" + + "\u0000\u00ea\u001d\u0001\u0000\u0000\u0000\u00eb\u00ec\u0005e\u0000\u0000" + + "\u00ec\u00ed\u0005l\u0000\u0000\u00ed\u00ee\u0005s\u0000\u0000\u00ee\u00ef" + + "\u0005e\u0000\u0000\u00ef\u001f\u0001\u0000\u0000\u0000\u00f0\u00f1\u0005" + + "w\u0000\u0000\u00f1\u00f2\u0005h\u0000\u0000\u00f2\u00f3\u0005i\u0000" + + "\u0000\u00f3\u00f4\u0005l\u0000\u0000\u00f4\u00f5\u0005e\u0000\u0000\u00f5" + + "!\u0001\u0000\u0000\u0000\u00f6\u00f7\u0005d\u0000\u0000\u00f7\u00f8\u0005" + + "o\u0000\u0000\u00f8#\u0001\u0000\u0000\u0000\u00f9\u00fa\u0005f\u0000" + + "\u0000\u00fa\u00fb\u0005o\u0000\u0000\u00fb\u00fc\u0005r\u0000\u0000\u00fc" + + "%\u0001\u0000\u0000\u0000\u00fd\u00fe\u0005c\u0000\u0000\u00fe\u00ff\u0005" + + "o\u0000\u0000\u00ff\u0100\u0005n\u0000\u0000\u0100\u0101\u0005t\u0000" + + "\u0000\u0101\u0102\u0005i\u0000\u0000\u0102\u0103\u0005n\u0000\u0000\u0103" + + "\u0104\u0005u\u0000\u0000\u0104\u0105\u0005e\u0000\u0000\u0105\'\u0001" + + "\u0000\u0000\u0000\u0106\u0107\u0005b\u0000\u0000\u0107\u0108\u0005r\u0000" + + "\u0000\u0108\u0109\u0005e\u0000\u0000\u0109\u010a\u0005a\u0000\u0000\u010a" + + "\u010b\u0005k\u0000\u0000\u010b)\u0001\u0000\u0000\u0000\u010c\u010d\u0005" + + "r\u0000\u0000\u010d\u010e\u0005e\u0000\u0000\u010e\u010f\u0005t\u0000" + + "\u0000\u010f\u0110\u0005u\u0000\u0000\u0110\u0111\u0005r\u0000\u0000\u0111" + + "\u0112\u0005n\u0000\u0000\u0112+\u0001\u0000\u0000\u0000\u0113\u0114\u0005" + + "n\u0000\u0000\u0114\u0115\u0005e\u0000\u0000\u0115\u0116\u0005w\u0000" + + "\u0000\u0116-\u0001\u0000\u0000\u0000\u0117\u0118\u0005t\u0000\u0000\u0118" + + "\u0119\u0005r\u0000\u0000\u0119\u011a\u0005y\u0000\u0000\u011a/\u0001" + + "\u0000\u0000\u0000\u011b\u011c\u0005c\u0000\u0000\u011c\u011d\u0005a\u0000" + + "\u0000\u011d\u011e\u0005t\u0000\u0000\u011e\u011f\u0005c\u0000\u0000\u011f" + + "\u0120\u0005h\u0000\u0000\u01201\u0001\u0000\u0000\u0000\u0121\u0122\u0005" + + "t\u0000\u0000\u0122\u0123\u0005h\u0000\u0000\u0123\u0124\u0005r\u0000" + + "\u0000\u0124\u0125\u0005o\u0000\u0000\u0125\u0126\u0005w\u0000\u0000\u0126" + + "3\u0001\u0000\u0000\u0000\u0127\u0128\u0005t\u0000\u0000\u0128\u0129\u0005" + + "h\u0000\u0000\u0129\u012a\u0005i\u0000\u0000\u012a\u012b\u0005s\u0000" + + "\u0000\u012b5\u0001\u0000\u0000\u0000\u012c\u012d\u0005i\u0000\u0000\u012d" + + "\u012e\u0005n\u0000\u0000\u012e\u012f\u0005s\u0000\u0000\u012f\u0130\u0005" + + "t\u0000\u0000\u0130\u0131\u0005a\u0000\u0000\u0131\u0132\u0005n\u0000" + + "\u0000\u0132\u0133\u0005c\u0000\u0000\u0133\u0134\u0005e\u0000\u0000\u0134" + + "\u0135\u0005o\u0000\u0000\u0135\u0136\u0005f\u0000\u0000\u01367\u0001" + + "\u0000\u0000\u0000\u0137\u0138\u0005!\u0000\u0000\u01389\u0001\u0000\u0000" + + "\u0000\u0139\u013a\u0005~\u0000\u0000\u013a;\u0001\u0000\u0000\u0000\u013b" + + "\u013c\u0005*\u0000\u0000\u013c=\u0001\u0000\u0000\u0000\u013d\u013e\u0005" + + "/\u0000\u0000\u013e\u013f\u0004\u001e\u0000\u0000\u013f?\u0001\u0000\u0000" + + "\u0000\u0140\u0141\u0005%\u0000\u0000\u0141A\u0001\u0000\u0000\u0000\u0142" + + "\u0143\u0005+\u0000\u0000\u0143C\u0001\u0000\u0000\u0000\u0144\u0145\u0005" + + "-\u0000\u0000\u0145E\u0001\u0000\u0000\u0000\u0146\u0147\u0005<\u0000" + + "\u0000\u0147\u0148\u0005<\u0000\u0000\u0148G\u0001\u0000\u0000\u0000\u0149" + + "\u014a\u0005>\u0000\u0000\u014a\u014b\u0005>\u0000\u0000\u014bI\u0001" + + "\u0000\u0000\u0000\u014c\u014d\u0005>\u0000\u0000\u014d\u014e\u0005>\u0000" + + "\u0000\u014e\u014f\u0005>\u0000\u0000\u014fK\u0001\u0000\u0000\u0000\u0150" + + "\u0151\u0005<\u0000\u0000\u0151M\u0001\u0000\u0000\u0000\u0152\u0153\u0005" + + "<\u0000\u0000\u0153\u0154\u0005=\u0000\u0000\u0154O\u0001\u0000\u0000" + + "\u0000\u0155\u0156\u0005>\u0000\u0000\u0156Q\u0001\u0000\u0000\u0000\u0157" + + "\u0158\u0005>\u0000\u0000\u0158\u0159\u0005=\u0000\u0000\u0159S\u0001" + + "\u0000\u0000\u0000\u015a\u015b\u0005=\u0000\u0000\u015b\u015c\u0005=\u0000" + + "\u0000\u015cU\u0001\u0000\u0000\u0000\u015d\u015e\u0005=\u0000\u0000\u015e" + + "\u015f\u0005=\u0000\u0000\u015f\u0160\u0005=\u0000\u0000\u0160W\u0001" + + "\u0000\u0000\u0000\u0161\u0162\u0005!\u0000\u0000\u0162\u0163\u0005=\u0000" + + "\u0000\u0163Y\u0001\u0000\u0000\u0000\u0164\u0165\u0005!\u0000\u0000\u0165" + + "\u0166\u0005=\u0000\u0000\u0166\u0167\u0005=\u0000\u0000\u0167[\u0001" + + "\u0000\u0000\u0000\u0168\u0169\u0005&\u0000\u0000\u0169]\u0001\u0000\u0000" + + "\u0000\u016a\u016b\u0005^\u0000\u0000\u016b_\u0001\u0000\u0000\u0000\u016c" + + "\u016d\u0005|\u0000\u0000\u016da\u0001\u0000\u0000\u0000\u016e\u016f\u0005" + + "&\u0000\u0000\u016f\u0170\u0005&\u0000\u0000\u0170c\u0001\u0000\u0000" + + "\u0000\u0171\u0172\u0005|\u0000\u0000\u0172\u0173\u0005|\u0000\u0000\u0173" + + "e\u0001\u0000\u0000\u0000\u0174\u0175\u0005?\u0000\u0000\u0175g\u0001" + + "\u0000\u0000\u0000\u0176\u0177\u0005:\u0000\u0000\u0177i\u0001\u0000\u0000" + + "\u0000\u0178\u0179\u0005?\u0000\u0000\u0179\u017a\u0005:\u0000\u0000\u017a" + + "k\u0001\u0000\u0000\u0000\u017b\u017c\u0005:\u0000\u0000\u017c\u017d\u0005" + + ":\u0000\u0000\u017dm\u0001\u0000\u0000\u0000\u017e\u017f\u0005-\u0000" + + "\u0000\u017f\u0180\u0005>\u0000\u0000\u0180o\u0001\u0000\u0000\u0000\u0181" + + "\u0182\u0005=\u0000\u0000\u0182\u0183\u0005~\u0000\u0000\u0183q\u0001" + + "\u0000\u0000\u0000\u0184\u0185\u0005=\u0000\u0000\u0185\u0186\u0005=\u0000" + + "\u0000\u0186\u0187\u0005~\u0000\u0000\u0187s\u0001\u0000\u0000\u0000\u0188" + + "\u0189\u0005+\u0000\u0000\u0189\u018a\u0005+\u0000\u0000\u018au\u0001" + + "\u0000\u0000\u0000\u018b\u018c\u0005-\u0000\u0000\u018c\u018d\u0005-\u0000" + + "\u0000\u018dw\u0001\u0000\u0000\u0000\u018e\u018f\u0005=\u0000\u0000\u018f" + + "y\u0001\u0000\u0000\u0000\u0190\u0191\u0005+\u0000\u0000\u0191\u0192\u0005" + + "=\u0000\u0000\u0192{\u0001\u0000\u0000\u0000\u0193\u0194\u0005-\u0000" + + "\u0000\u0194\u0195\u0005=\u0000\u0000\u0195}\u0001\u0000\u0000\u0000\u0196" + + "\u0197\u0005*\u0000\u0000\u0197\u0198\u0005=\u0000\u0000\u0198\u007f\u0001" + + "\u0000\u0000\u0000\u0199\u019a\u0005/\u0000\u0000\u019a\u019b\u0005=\u0000" + + "\u0000\u019b\u0081\u0001\u0000\u0000\u0000\u019c\u019d\u0005%\u0000\u0000" + + "\u019d\u019e\u0005=\u0000\u0000\u019e\u0083\u0001\u0000\u0000\u0000\u019f" + + "\u01a0\u0005&\u0000\u0000\u01a0\u01a1\u0005=\u0000\u0000\u01a1\u0085\u0001" + + "\u0000\u0000\u0000\u01a2\u01a3\u0005^\u0000\u0000\u01a3\u01a4\u0005=\u0000" + + "\u0000\u01a4\u0087\u0001\u0000\u0000\u0000\u01a5\u01a6\u0005|\u0000\u0000" + + "\u01a6\u01a7\u0005=\u0000\u0000\u01a7\u0089\u0001\u0000\u0000\u0000\u01a8" + + "\u01a9\u0005<\u0000\u0000\u01a9\u01aa\u0005<\u0000\u0000\u01aa\u01ab\u0005" + + "=\u0000\u0000\u01ab\u008b\u0001\u0000\u0000\u0000\u01ac\u01ad\u0005>\u0000" + + "\u0000\u01ad\u01ae\u0005>\u0000\u0000\u01ae\u01af\u0005=\u0000\u0000\u01af" + + "\u008d\u0001\u0000\u0000\u0000\u01b0\u01b1\u0005>\u0000\u0000\u01b1\u01b2" + + "\u0005>\u0000\u0000\u01b2\u01b3\u0005>\u0000\u0000\u01b3\u01b4\u0005=" + + "\u0000\u0000\u01b4\u008f\u0001\u0000\u0000\u0000\u01b5\u01b7\u00050\u0000" + + "\u0000\u01b6\u01b8\u0007\u0002\u0000\u0000\u01b7\u01b6\u0001\u0000\u0000" + + "\u0000\u01b8\u01b9\u0001\u0000\u0000\u0000\u01b9\u01b7\u0001\u0000\u0000" + + "\u0000\u01b9\u01ba\u0001\u0000\u0000\u0000\u01ba\u01bc\u0001\u0000\u0000" + + "\u0000\u01bb\u01bd\u0007\u0003\u0000\u0000\u01bc\u01bb\u0001\u0000\u0000" + + "\u0000\u01bc\u01bd\u0001\u0000\u0000\u0000\u01bd\u0091\u0001\u0000\u0000" + + "\u0000\u01be\u01bf\u00050\u0000\u0000\u01bf\u01c1\u0007\u0004\u0000\u0000" + + "\u01c0\u01c2\u0007\u0005\u0000\u0000\u01c1\u01c0\u0001\u0000\u0000\u0000" + + "\u01c2\u01c3\u0001\u0000\u0000\u0000\u01c3\u01c1\u0001\u0000\u0000\u0000" + + "\u01c3\u01c4\u0001\u0000\u0000\u0000\u01c4\u01c6\u0001\u0000\u0000\u0000" + + "\u01c5\u01c7\u0007\u0003\u0000\u0000\u01c6\u01c5\u0001\u0000\u0000\u0000" + + "\u01c6\u01c7\u0001\u0000\u0000\u0000\u01c7\u0093\u0001\u0000\u0000\u0000" + + "\u01c8\u01d1\u00050\u0000\u0000\u01c9\u01cd\u0007\u0006\u0000\u0000\u01ca" + + "\u01cc\u0007\u0007\u0000\u0000\u01cb\u01ca\u0001\u0000\u0000\u0000\u01cc" + + "\u01cf\u0001\u0000\u0000\u0000\u01cd\u01cb\u0001\u0000\u0000\u0000\u01cd" + + "\u01ce\u0001\u0000\u0000\u0000\u01ce\u01d1\u0001\u0000\u0000\u0000\u01cf" + + "\u01cd\u0001\u0000\u0000\u0000\u01d0\u01c8\u0001\u0000\u0000\u0000\u01d0" + + "\u01c9\u0001\u0000\u0000\u0000\u01d1\u01d3\u0001\u0000\u0000\u0000\u01d2" + + "\u01d4\u0007\b\u0000\u0000\u01d3\u01d2\u0001\u0000\u0000\u0000\u01d3\u01d4" + + "\u0001\u0000\u0000\u0000\u01d4\u0095\u0001\u0000\u0000\u0000\u01d5\u01de" + + "\u00050\u0000\u0000\u01d6\u01da\u0007\u0006\u0000\u0000\u01d7\u01d9\u0007" + + "\u0007\u0000\u0000\u01d8\u01d7\u0001\u0000\u0000\u0000\u01d9\u01dc\u0001" + + "\u0000\u0000\u0000\u01da\u01d8\u0001\u0000\u0000\u0000\u01da\u01db\u0001" + + "\u0000\u0000\u0000\u01db\u01de\u0001\u0000\u0000\u0000\u01dc\u01da\u0001" + + "\u0000\u0000\u0000\u01dd\u01d5\u0001\u0000\u0000\u0000\u01dd\u01d6\u0001" + + "\u0000\u0000\u0000\u01de\u01e5\u0001\u0000\u0000\u0000\u01df\u01e1\u0003" + + "\u0012\b\u0000\u01e0\u01e2\u0007\u0007\u0000\u0000\u01e1\u01e0\u0001\u0000" + + "\u0000\u0000\u01e2\u01e3\u0001\u0000\u0000\u0000\u01e3\u01e1\u0001\u0000" + + "\u0000\u0000\u01e3\u01e4\u0001\u0000\u0000\u0000\u01e4\u01e6\u0001\u0000" + + "\u0000\u0000\u01e5\u01df\u0001\u0000\u0000\u0000\u01e5\u01e6\u0001\u0000" + + "\u0000\u0000\u01e6\u01f0\u0001\u0000\u0000\u0000\u01e7\u01e9\u0007\t\u0000" + + "\u0000\u01e8\u01ea\u0007\n\u0000\u0000\u01e9\u01e8\u0001\u0000\u0000\u0000" + + "\u01e9\u01ea\u0001\u0000\u0000\u0000\u01ea\u01ec\u0001\u0000\u0000\u0000" + + "\u01eb\u01ed\u0007\u0007\u0000\u0000\u01ec\u01eb\u0001\u0000\u0000\u0000" + + "\u01ed\u01ee\u0001\u0000\u0000\u0000\u01ee\u01ec\u0001\u0000\u0000\u0000" + + "\u01ee\u01ef\u0001\u0000\u0000\u0000\u01ef\u01f1\u0001\u0000\u0000\u0000" + + "\u01f0\u01e7\u0001\u0000\u0000\u0000\u01f0\u01f1\u0001\u0000\u0000\u0000" + + "\u01f1\u01f3\u0001\u0000\u0000\u0000\u01f2\u01f4\u0007\u000b\u0000\u0000" + + "\u01f3\u01f2\u0001\u0000\u0000\u0000\u01f3\u01f4\u0001\u0000\u0000\u0000" + + "\u01f4\u0097\u0001\u0000\u0000\u0000\u01f5\u01fd\u0005\"\u0000\u0000\u01f6" + + "\u01f7\u0005\\\u0000\u0000\u01f7\u01fc\u0005\"\u0000\u0000\u01f8\u01f9" + + "\u0005\\\u0000\u0000\u01f9\u01fc\u0005\\\u0000\u0000\u01fa\u01fc\b\f\u0000" + + "\u0000\u01fb\u01f6\u0001\u0000\u0000\u0000\u01fb\u01f8\u0001\u0000\u0000" + + "\u0000\u01fb\u01fa\u0001\u0000\u0000\u0000\u01fc\u01ff\u0001\u0000\u0000" + + "\u0000\u01fd\u01fe\u0001\u0000\u0000\u0000\u01fd\u01fb\u0001\u0000\u0000" + + "\u0000\u01fe\u0200\u0001\u0000\u0000\u0000\u01ff\u01fd\u0001\u0000\u0000" + + "\u0000\u0200\u020e\u0005\"\u0000\u0000\u0201\u0209\u0005\'\u0000\u0000" + + "\u0202\u0203\u0005\\\u0000\u0000\u0203\u0208\u0005\'\u0000\u0000\u0204" + + "\u0205\u0005\\\u0000\u0000\u0205\u0208\u0005\\\u0000\u0000\u0206\u0208" + + "\b\r\u0000\u0000\u0207\u0202\u0001\u0000\u0000\u0000\u0207\u0204\u0001" + + "\u0000\u0000\u0000\u0207\u0206\u0001\u0000\u0000\u0000\u0208\u020b\u0001" + + "\u0000\u0000\u0000\u0209\u020a\u0001\u0000\u0000\u0000\u0209\u0207\u0001" + + "\u0000\u0000\u0000\u020a\u020c\u0001\u0000\u0000\u0000\u020b\u0209\u0001" + + "\u0000\u0000\u0000\u020c\u020e\u0005\'\u0000\u0000\u020d\u01f5\u0001\u0000" + + "\u0000\u0000\u020d\u0201\u0001\u0000\u0000\u0000\u020e\u0099\u0001\u0000" + + "\u0000\u0000\u020f\u0213\u0005/\u0000\u0000\u0210\u0211\u0005\\\u0000" + + "\u0000\u0211\u0214\b\u000e\u0000\u0000\u0212\u0214\b\u000f\u0000\u0000" + + "\u0213\u0210\u0001\u0000\u0000\u0000\u0213\u0212\u0001\u0000\u0000\u0000" + + "\u0214\u0215\u0001\u0000\u0000\u0000\u0215\u0216\u0001\u0000\u0000\u0000" + + "\u0215\u0213\u0001\u0000\u0000\u0000\u0216\u0217\u0001\u0000\u0000\u0000" + + "\u0217\u021b\u0005/\u0000\u0000\u0218\u021a\u0007\u0010\u0000\u0000\u0219" + + "\u0218\u0001\u0000\u0000\u0000\u021a\u021d\u0001\u0000\u0000\u0000\u021b" + + "\u0219\u0001\u0000\u0000\u0000\u021b\u021c\u0001\u0000\u0000\u0000\u021c" + + "\u021e\u0001\u0000\u0000\u0000\u021d\u021b\u0001\u0000\u0000\u0000\u021e" + + "\u021f\u0004L\u0001\u0000\u021f\u009b\u0001\u0000\u0000\u0000\u0220\u0221" + + "\u0005t\u0000\u0000\u0221\u0222\u0005r\u0000\u0000\u0222\u0223\u0005u" + + "\u0000\u0000\u0223\u0224\u0005e\u0000\u0000\u0224\u009d\u0001\u0000\u0000" + + "\u0000\u0225\u0226\u0005f\u0000\u0000\u0226\u0227\u0005a\u0000\u0000\u0227" + + "\u0228\u0005l\u0000\u0000\u0228\u0229\u0005s\u0000\u0000\u0229\u022a\u0005" + + "e\u0000\u0000\u022a\u009f\u0001\u0000\u0000\u0000\u022b\u022c\u0005n\u0000" + + "\u0000\u022c\u022d\u0005u\u0000\u0000\u022d\u022e\u0005l\u0000\u0000\u022e" + + "\u022f\u0005l\u0000\u0000\u022f\u00a1\u0001\u0000\u0000\u0000\u0230\u0231" + + "\u0005b\u0000\u0000\u0231\u0232\u0005o\u0000\u0000\u0232\u0233\u0005o" + + "\u0000\u0000\u0233\u0234\u0005l\u0000\u0000\u0234\u0235\u0005e\u0000\u0000" + + "\u0235\u0236\u0005a\u0000\u0000\u0236\u0257\u0005n\u0000\u0000\u0237\u0238" + + "\u0005b\u0000\u0000\u0238\u0239\u0005y\u0000\u0000\u0239\u023a\u0005t" + + "\u0000\u0000\u023a\u0257\u0005e\u0000\u0000\u023b\u023c\u0005s\u0000\u0000" + + "\u023c\u023d\u0005h\u0000\u0000\u023d\u023e\u0005o\u0000\u0000\u023e\u023f" + + "\u0005r\u0000\u0000\u023f\u0257\u0005t\u0000\u0000\u0240\u0241\u0005c" + + "\u0000\u0000\u0241\u0242\u0005h\u0000\u0000\u0242\u0243\u0005a\u0000\u0000" + + "\u0243\u0257\u0005r\u0000\u0000\u0244\u0245\u0005i\u0000\u0000\u0245\u0246" + + "\u0005n\u0000\u0000\u0246\u0257\u0005t\u0000\u0000\u0247\u0248\u0005l" + + "\u0000\u0000\u0248\u0249\u0005o\u0000\u0000\u0249\u024a\u0005n\u0000\u0000" + + "\u024a\u0257\u0005g\u0000\u0000\u024b\u024c\u0005f\u0000\u0000\u024c\u024d" + + "\u0005l\u0000\u0000\u024d\u024e\u0005o\u0000\u0000\u024e\u024f\u0005a" + + "\u0000\u0000\u024f\u0257\u0005t\u0000\u0000\u0250\u0251\u0005d\u0000\u0000" + + "\u0251\u0252\u0005o\u0000\u0000\u0252\u0253\u0005u\u0000\u0000\u0253\u0254" + + "\u0005b\u0000\u0000\u0254\u0255\u0005l\u0000\u0000\u0255\u0257\u0005e" + + "\u0000\u0000\u0256\u0230\u0001\u0000\u0000\u0000\u0256\u0237\u0001\u0000" + + "\u0000\u0000\u0256\u023b\u0001\u0000\u0000\u0000\u0256\u0240\u0001\u0000" + + "\u0000\u0000\u0256\u0244\u0001\u0000\u0000\u0000\u0256\u0247\u0001\u0000" + + "\u0000\u0000\u0256\u024b\u0001\u0000\u0000\u0000\u0256\u0250\u0001\u0000" + + "\u0000\u0000\u0257\u00a3\u0001\u0000\u0000\u0000\u0258\u0259\u0005d\u0000" + + "\u0000\u0259\u025a\u0005e\u0000\u0000\u025a\u025b\u0005f\u0000\u0000\u025b" + + "\u00a5\u0001\u0000\u0000\u0000\u025c\u0260\u0007\u0011\u0000\u0000\u025d" + + "\u025f\u0007\u0012\u0000\u0000\u025e\u025d\u0001\u0000\u0000\u0000\u025f" + + "\u0262\u0001\u0000\u0000\u0000\u0260\u025e\u0001\u0000\u0000\u0000\u0260" + + "\u0261\u0001\u0000\u0000\u0000\u0261\u00a7\u0001\u0000\u0000\u0000\u0262" + + "\u0260\u0001\u0000\u0000\u0000\u0263\u026c\u00050\u0000\u0000\u0264\u0268" + + "\u0007\u0006\u0000\u0000\u0265\u0267\u0007\u0007\u0000\u0000\u0266\u0265" + + "\u0001\u0000\u0000\u0000\u0267\u026a\u0001\u0000\u0000\u0000\u0268\u0266" + + "\u0001\u0000\u0000\u0000\u0268\u0269\u0001\u0000\u0000\u0000\u0269\u026c" + + "\u0001\u0000\u0000\u0000\u026a\u0268\u0001\u0000\u0000\u0000\u026b\u0263" + + "\u0001\u0000\u0000\u0000\u026b\u0264\u0001\u0000\u0000\u0000\u026c\u026d" + + "\u0001\u0000\u0000\u0000\u026d\u026e\u0006S\u0002\u0000\u026e\u00a9\u0001" + + "\u0000\u0000\u0000\u026f\u0273\u0007\u0011\u0000\u0000\u0270\u0272\u0007" + + "\u0012\u0000\u0000\u0271\u0270\u0001\u0000\u0000\u0000\u0272\u0275\u0001" + + "\u0000\u0000\u0000\u0273\u0271\u0001\u0000\u0000\u0000\u0273\u0274\u0001" + + "\u0000\u0000\u0000\u0274\u0276\u0001\u0000\u0000\u0000\u0275\u0273\u0001" + + "\u0000\u0000\u0000\u0276\u0277\u0006T\u0002\u0000\u0277\u00ab\u0001\u0000" + + "\u0000\u0000\"\u0000\u0001\u00af\u00b9\u00c3\u00c8\u01b9\u01bc\u01c3\u01c6" + + "\u01cd\u01d0\u01d3\u01da\u01dd\u01e3\u01e5\u01e9\u01ee\u01f0\u01f3\u01fb" + + "\u01fd\u0207\u0209\u020d\u0213\u0215\u021b\u0256\u0260\u0268\u026b\u0273" + + "\u0003\u0006\u0000\u0000\u0002\u0001\u0000\u0002\u0000\u0000"; public static final ATN _ATN = new ATNDeserializer().deserialize(_serializedATN.toCharArray()); static { _decisionToDFA = new DFA[_ATN.getNumberOfDecisions()]; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/PainlessParser.java b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/PainlessParser.java index 1e064724c2451..40e76194f50b2 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/PainlessParser.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/PainlessParser.java @@ -30,20 +30,33 @@ * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ - package org.opensearch.painless.antlr; -import org.antlr.v4.runtime.atn.*; +import org.antlr.v4.runtime.FailedPredicateException; +import org.antlr.v4.runtime.NoViableAltException; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ParserATNSimulator; +import org.antlr.v4.runtime.atn.PredictionContextCache; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.misc.*; -import org.antlr.v4.runtime.tree.*; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; +import org.antlr.v4.runtime.tree.TerminalNode; + import java.util.List; -@SuppressWarnings({ "all", "warnings", "unchecked", "unused", "cast" }) +@SuppressWarnings({ "all", "warnings", "unchecked", "unused", "cast", "CheckReturnValue" }) class PainlessParser extends Parser { static { - RuntimeMetaData.checkVersion("4.5.3", RuntimeMetaData.VERSION); + RuntimeMetaData.checkVersion("4.11.1", RuntimeMetaData.VERSION); } protected static final DFA[] _decisionToDFA; @@ -63,218 +76,232 @@ class PainlessParser extends Parser { RULE_refcasttype = 22, RULE_chain = 23, RULE_primary = 24, RULE_postfix = 25, RULE_postdot = 26, RULE_callinvoke = 27, RULE_fieldaccess = 28, RULE_braceaccess = 29, RULE_arrayinitializer = 30, RULE_listinitializer = 31, RULE_mapinitializer = 32, RULE_maptoken = 33, RULE_arguments = 34, RULE_argument = 35, RULE_lambda = 36, RULE_lamtype = 37, RULE_funcref = 38; - public static final String[] ruleNames = { - "source", - "function", - "parameters", - "statement", - "rstatement", - "dstatement", - "trailer", - "block", - "empty", - "initializer", - "afterthought", - "declaration", - "decltype", - "type", - "declvar", - "trap", - "noncondexpression", - "expression", - "unary", - "unarynotaddsub", - "castexpression", - "primordefcasttype", - "refcasttype", - "chain", - "primary", - "postfix", - "postdot", - "callinvoke", - "fieldaccess", - "braceaccess", - "arrayinitializer", - "listinitializer", - "mapinitializer", - "maptoken", - "arguments", - "argument", - "lambda", - "lamtype", - "funcref" }; - - private static final String[] _LITERAL_NAMES = { - null, - null, - null, - "'{'", - "'}'", - "'['", - "']'", - "'('", - "')'", - "'.'", - "'?.'", - "','", - "';'", - "'if'", - "'in'", - "'else'", - "'while'", - "'do'", - "'for'", - "'continue'", - "'break'", - "'return'", - "'new'", - "'try'", - "'catch'", - "'throw'", - "'this'", - "'instanceof'", - "'!'", - "'~'", - "'*'", - "'/'", - "'%'", - "'+'", - "'-'", - "'<<'", - "'>>'", - "'>>>'", - "'<'", - "'<='", - "'>'", - "'>='", - "'=='", - "'==='", - "'!='", - "'!=='", - "'&'", - "'^'", - "'|'", - "'&&'", - "'||'", - "'?'", - "':'", - "'?:'", - "'::'", - "'->'", - "'=~'", - "'==~'", - "'++'", - "'--'", - "'='", - "'+='", - "'-='", - "'*='", - "'/='", - "'%='", - "'&='", - "'^='", - "'|='", - "'<<='", - "'>>='", - "'>>>='", - null, - null, - null, - null, - null, - null, - "'true'", - "'false'", - "'null'", - null, - "'def'" }; - private static final String[] _SYMBOLIC_NAMES = { - null, - "WS", - "COMMENT", - "LBRACK", - "RBRACK", - "LBRACE", - "RBRACE", - "LP", - "RP", - "DOT", - "NSDOT", - "COMMA", - "SEMICOLON", - "IF", - "IN", - "ELSE", - "WHILE", - "DO", - "FOR", - "CONTINUE", - "BREAK", - "RETURN", - "NEW", - "TRY", - "CATCH", - "THROW", - "THIS", - "INSTANCEOF", - "BOOLNOT", - "BWNOT", - "MUL", - "DIV", - "REM", - "ADD", - "SUB", - "LSH", - "RSH", - "USH", - "LT", - "LTE", - "GT", - "GTE", - "EQ", - "EQR", - "NE", - "NER", - "BWAND", - "XOR", - "BWOR", - "BOOLAND", - "BOOLOR", - "COND", - "COLON", - "ELVIS", - "REF", - "ARROW", - "FIND", - "MATCH", - "INCR", - "DECR", - "ASSIGN", - "AADD", - "ASUB", - "AMUL", - "ADIV", - "AREM", - "AAND", - "AXOR", - "AOR", - "ALSH", - "ARSH", - "AUSH", - "OCTAL", - "HEX", - "INTEGER", - "DECIMAL", - "STRING", - "REGEX", - "TRUE", - "FALSE", - "NULL", - "PRIMITIVE", - "DEF", - "ID", - "DOTINTEGER", - "DOTID" }; + + private static String[] makeRuleNames() { + return new String[] { + "source", + "function", + "parameters", + "statement", + "rstatement", + "dstatement", + "trailer", + "block", + "empty", + "initializer", + "afterthought", + "declaration", + "decltype", + "type", + "declvar", + "trap", + "noncondexpression", + "expression", + "unary", + "unarynotaddsub", + "castexpression", + "primordefcasttype", + "refcasttype", + "chain", + "primary", + "postfix", + "postdot", + "callinvoke", + "fieldaccess", + "braceaccess", + "arrayinitializer", + "listinitializer", + "mapinitializer", + "maptoken", + "arguments", + "argument", + "lambda", + "lamtype", + "funcref" }; + } + + public static final String[] ruleNames = makeRuleNames(); + + private static String[] makeLiteralNames() { + return new String[] { + null, + null, + null, + "'{'", + "'}'", + "'['", + "']'", + "'('", + "')'", + "'.'", + "'?.'", + "','", + "';'", + "'if'", + "'in'", + "'else'", + "'while'", + "'do'", + "'for'", + "'continue'", + "'break'", + "'return'", + "'new'", + "'try'", + "'catch'", + "'throw'", + "'this'", + "'instanceof'", + "'!'", + "'~'", + "'*'", + "'/'", + "'%'", + "'+'", + "'-'", + "'<<'", + "'>>'", + "'>>>'", + "'<'", + "'<='", + "'>'", + "'>='", + "'=='", + "'==='", + "'!='", + "'!=='", + "'&'", + "'^'", + "'|'", + "'&&'", + "'||'", + "'?'", + "':'", + "'?:'", + "'::'", + "'->'", + "'=~'", + "'==~'", + "'++'", + "'--'", + "'='", + "'+='", + "'-='", + "'*='", + "'/='", + "'%='", + "'&='", + "'^='", + "'|='", + "'<<='", + "'>>='", + "'>>>='", + null, + null, + null, + null, + null, + null, + "'true'", + "'false'", + "'null'", + null, + "'def'" }; + } + + private static final String[] _LITERAL_NAMES = makeLiteralNames(); + + private static String[] makeSymbolicNames() { + return new String[] { + null, + "WS", + "COMMENT", + "LBRACK", + "RBRACK", + "LBRACE", + "RBRACE", + "LP", + "RP", + "DOT", + "NSDOT", + "COMMA", + "SEMICOLON", + "IF", + "IN", + "ELSE", + "WHILE", + "DO", + "FOR", + "CONTINUE", + "BREAK", + "RETURN", + "NEW", + "TRY", + "CATCH", + "THROW", + "THIS", + "INSTANCEOF", + "BOOLNOT", + "BWNOT", + "MUL", + "DIV", + "REM", + "ADD", + "SUB", + "LSH", + "RSH", + "USH", + "LT", + "LTE", + "GT", + "GTE", + "EQ", + "EQR", + "NE", + "NER", + "BWAND", + "XOR", + "BWOR", + "BOOLAND", + "BOOLOR", + "COND", + "COLON", + "ELVIS", + "REF", + "ARROW", + "FIND", + "MATCH", + "INCR", + "DECR", + "ASSIGN", + "AADD", + "ASUB", + "AMUL", + "ADIV", + "AREM", + "AAND", + "AXOR", + "AOR", + "ALSH", + "ARSH", + "AUSH", + "OCTAL", + "HEX", + "INTEGER", + "DECIMAL", + "STRING", + "REGEX", + "TRUE", + "FALSE", + "NULL", + "PRIMITIVE", + "DEF", + "ID", + "DOTINTEGER", + "DOTID" }; + } + + private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames(); public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES); /** @@ -310,7 +337,7 @@ public Vocabulary getVocabulary() { @Override public String getGrammarFileName() { - return "PainlessParser.g4"; + return "java-escape"; } @Override @@ -333,6 +360,7 @@ public PainlessParser(TokenStream input) { _interp = new ParserATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache); } + @SuppressWarnings("CheckReturnValue") public static class SourceContext extends ParserRuleContext { public TerminalNode EOF() { return getToken(PainlessParser.EOF, 0); @@ -397,14 +425,8 @@ public final SourceContext source() throws RecognitionException { setState(87); _errHandler.sync(this); _la = _input.LA(1); - while ((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << IF) | (1L << WHILE) | (1L << DO) | (1L << FOR) | (1L - << CONTINUE) | (1L << BREAK) | (1L << RETURN) | (1L << NEW) | (1L << TRY) | (1L << THROW) | (1L << BOOLNOT) | (1L - << BWNOT) | (1L << ADD) | (1L << SUB) | (1L << INCR) | (1L << DECR))) != 0) - || ((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)) | (1L << (STRING - 72)) | (1L << (REGEX - 72)) | (1L << (TRUE - 72)) | (1L << (FALSE - 72)) | (1L - << (NULL - 72)) | (1L << (PRIMITIVE - 72)) | (1L << (DEF - 72)) | (1L << (ID - 72)))) != 0)) { + while (((_la) & ~0x3f) == 0 && ((1L << _la) & 864691155080519840L) != 0 + || (((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 4095L) != 0) { { { setState(84); @@ -428,6 +450,7 @@ public final SourceContext source() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class FunctionContext extends ParserRuleContext { public DecltypeContext decltype() { return getRuleContext(DecltypeContext.class, 0); @@ -486,6 +509,7 @@ public final FunctionContext function() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class ParametersContext extends ParserRuleContext { public TerminalNode LP() { return getToken(PainlessParser.LP, 0); @@ -545,9 +569,9 @@ public final ParametersContext parameters() throws RecognitionException { setState(97); match(LP); setState(109); + _errHandler.sync(this); _la = _input.LA(1); - if (((((_la - 81)) & ~0x3f) == 0 - && ((1L << (_la - 81)) & ((1L << (PRIMITIVE - 81)) | (1L << (DEF - 81)) | (1L << (ID - 81)))) != 0)) { + if ((((_la - 81)) & ~0x3f) == 0 && ((1L << (_la - 81)) & 7L) != 0) { { setState(98); decltype(); @@ -587,6 +611,7 @@ public final ParametersContext parameters() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class StatementContext extends ParserRuleContext { public RstatementContext rstatement() { return getRuleContext(RstatementContext.class, 0); @@ -626,6 +651,7 @@ public final StatementContext statement() throws RecognitionException { int _la; try { setState(117); + _errHandler.sync(this); switch (_input.LA(1)) { case IF: case WHILE: @@ -670,6 +696,8 @@ public final StatementContext statement() throws RecognitionException { if (!(_la == EOF || _la == SEMICOLON)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } } @@ -687,6 +715,7 @@ public final StatementContext statement() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class RstatementContext extends ParserRuleContext { public RstatementContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -704,6 +733,7 @@ public void copyFrom(RstatementContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class ForContext extends RstatementContext { public TerminalNode FOR() { return getToken(PainlessParser.FOR, 0); @@ -756,6 +786,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class TryContext extends RstatementContext { public TerminalNode TRY() { return getToken(PainlessParser.TRY, 0); @@ -784,6 +815,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class WhileContext extends RstatementContext { public TerminalNode WHILE() { return getToken(PainlessParser.WHILE, 0); @@ -820,6 +852,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class IneachContext extends RstatementContext { public TerminalNode FOR() { return getToken(PainlessParser.FOR, 0); @@ -860,6 +893,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class IfContext extends RstatementContext { public TerminalNode IF() { return getToken(PainlessParser.IF, 0); @@ -900,6 +934,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class EachContext extends RstatementContext { public TerminalNode FOR() { return getToken(PainlessParser.FOR, 0); @@ -996,6 +1031,7 @@ public final RstatementContext rstatement() throws RecognitionException { setState(132); match(RP); setState(135); + _errHandler.sync(this); switch (_input.LA(1)) { case LBRACK: case LBRACE: @@ -1050,14 +1086,10 @@ public final RstatementContext rstatement() throws RecognitionException { setState(138); match(LP); setState(140); + _errHandler.sync(this); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << NEW) | (1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L - << SUB) | (1L << INCR) | (1L << DECR))) != 0) - || ((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)) | (1L << (STRING - 72)) | (1L << (REGEX - 72)) | (1L << (TRUE - 72)) | (1L << (FALSE - 72)) | (1L - << (NULL - 72)) | (1L << (PRIMITIVE - 72)) | (1L << (DEF - 72)) | (1L << (ID - 72)))) != 0)) { + if (((_la) & ~0x3f) == 0 && ((1L << _la) & 864691155034439840L) != 0 + || (((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 4095L) != 0) { { setState(139); initializer(); @@ -1067,14 +1099,10 @@ public final RstatementContext rstatement() throws RecognitionException { setState(142); match(SEMICOLON); setState(144); + _errHandler.sync(this); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << NEW) | (1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L - << SUB) | (1L << INCR) | (1L << DECR))) != 0) - || ((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)) | (1L << (STRING - 72)) | (1L << (REGEX - 72)) | (1L << (TRUE - 72)) | (1L << (FALSE - 72)) | (1L - << (NULL - 72)) | (1L << (ID - 72)))) != 0)) { + if (((_la) & ~0x3f) == 0 && ((1L << _la) & 864691155034439840L) != 0 + || (((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 2559L) != 0) { { setState(143); expression(); @@ -1084,14 +1112,10 @@ public final RstatementContext rstatement() throws RecognitionException { setState(146); match(SEMICOLON); setState(148); + _errHandler.sync(this); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << NEW) | (1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L - << SUB) | (1L << INCR) | (1L << DECR))) != 0) - || ((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)) | (1L << (STRING - 72)) | (1L << (REGEX - 72)) | (1L << (TRUE - 72)) | (1L << (FALSE - 72)) | (1L - << (NULL - 72)) | (1L << (ID - 72)))) != 0)) { + if (((_la) & ~0x3f) == 0 && ((1L << _la) & 864691155034439840L) != 0 + || (((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 2559L) != 0) { { setState(147); afterthought(); @@ -1101,6 +1125,7 @@ public final RstatementContext rstatement() throws RecognitionException { setState(150); match(RP); setState(153); + _errHandler.sync(this); switch (_input.LA(1)) { case LBRACK: case LBRACE: @@ -1226,6 +1251,7 @@ public final RstatementContext rstatement() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class DstatementContext extends ParserRuleContext { public DstatementContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -1243,6 +1269,7 @@ public void copyFrom(DstatementContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class DeclContext extends DstatementContext { public DeclarationContext declaration() { return getRuleContext(DeclarationContext.class, 0); @@ -1259,6 +1286,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class BreakContext extends DstatementContext { public TerminalNode BREAK() { return getToken(PainlessParser.BREAK, 0); @@ -1275,6 +1303,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class ThrowContext extends DstatementContext { public TerminalNode THROW() { return getToken(PainlessParser.THROW, 0); @@ -1295,6 +1324,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class ContinueContext extends DstatementContext { public TerminalNode CONTINUE() { return getToken(PainlessParser.CONTINUE, 0); @@ -1311,6 +1341,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class ExprContext extends DstatementContext { public ExpressionContext expression() { return getRuleContext(ExpressionContext.class, 0); @@ -1327,6 +1358,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class DoContext extends DstatementContext { public TerminalNode DO() { return getToken(PainlessParser.DO, 0); @@ -1363,6 +1395,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class ReturnContext extends DstatementContext { public TerminalNode RETURN() { return getToken(PainlessParser.RETURN, 0); @@ -1435,14 +1468,10 @@ public final DstatementContext dstatement() throws RecognitionException { setState(191); match(RETURN); setState(193); + _errHandler.sync(this); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << NEW) | (1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L - << SUB) | (1L << INCR) | (1L << DECR))) != 0) - || ((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)) | (1L << (STRING - 72)) | (1L << (REGEX - 72)) | (1L << (TRUE - 72)) | (1L << (FALSE - 72)) | (1L - << (NULL - 72)) | (1L << (ID - 72)))) != 0)) { + if (((_la) & ~0x3f) == 0 && ((1L << _la) & 864691155034439840L) != 0 + || (((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 2559L) != 0) { { setState(192); expression(); @@ -1478,6 +1507,7 @@ public final DstatementContext dstatement() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class TrailerContext extends ParserRuleContext { public BlockContext block() { return getRuleContext(BlockContext.class, 0); @@ -1508,6 +1538,7 @@ public final TrailerContext trailer() throws RecognitionException { enterRule(_localctx, 12, RULE_trailer); try { setState(202); + _errHandler.sync(this); switch (_input.LA(1)) { case LBRACK: enterOuterAlt(_localctx, 1); { @@ -1563,6 +1594,7 @@ public final TrailerContext trailer() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class BlockContext extends ParserRuleContext { public TerminalNode LBRACK() { return getToken(PainlessParser.LBRACK, 0); @@ -1627,15 +1659,10 @@ public final BlockContext block() throws RecognitionException { _alt = getInterpreter().adaptivePredict(_input, 16, _ctx); } setState(212); + _errHandler.sync(this); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << DO) | (1L << CONTINUE) | (1L << BREAK) | (1L << RETURN) | (1L - << NEW) | (1L << THROW) | (1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L << SUB) | (1L << INCR) | (1L - << DECR))) != 0) - || ((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)) | (1L << (STRING - 72)) | (1L << (REGEX - 72)) | (1L << (TRUE - 72)) | (1L << (FALSE - 72)) | (1L - << (NULL - 72)) | (1L << (PRIMITIVE - 72)) | (1L << (DEF - 72)) | (1L << (ID - 72)))) != 0)) { + if (((_la) & ~0x3f) == 0 && ((1L << _la) & 864691155071795360L) != 0 + || (((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 4095L) != 0) { { setState(211); dstatement(); @@ -1655,6 +1682,7 @@ public final BlockContext block() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class EmptyContext extends ParserRuleContext { public TerminalNode SEMICOLON() { return getToken(PainlessParser.SEMICOLON, 0); @@ -1695,6 +1723,7 @@ public final EmptyContext empty() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class InitializerContext extends ParserRuleContext { public DeclarationContext declaration() { return getRuleContext(DeclarationContext.class, 0); @@ -1750,6 +1779,7 @@ public final InitializerContext initializer() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class AfterthoughtContext extends ParserRuleContext { public ExpressionContext expression() { return getRuleContext(ExpressionContext.class, 0); @@ -1790,6 +1820,7 @@ public final AfterthoughtContext afterthought() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class DeclarationContext extends ParserRuleContext { public DecltypeContext decltype() { return getRuleContext(DecltypeContext.class, 0); @@ -1865,6 +1896,7 @@ public final DeclarationContext declaration() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class DecltypeContext extends ParserRuleContext { public TypeContext type() { return getRuleContext(TypeContext.class, 0); @@ -1940,6 +1972,7 @@ public final DecltypeContext decltype() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class TypeContext extends ParserRuleContext { public TerminalNode DEF() { return getToken(PainlessParser.DEF, 0); @@ -1991,6 +2024,7 @@ public final TypeContext type() throws RecognitionException { try { int _alt; setState(251); + _errHandler.sync(this); switch (_input.LA(1)) { case DEF: enterOuterAlt(_localctx, 1); { @@ -2041,6 +2075,7 @@ public final TypeContext type() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class DeclvarContext extends ParserRuleContext { public TerminalNode ID() { return getToken(PainlessParser.ID, 0); @@ -2080,6 +2115,7 @@ public final DeclvarContext declvar() throws RecognitionException { setState(253); match(ID); setState(256); + _errHandler.sync(this); _la = _input.LA(1); if (_la == ASSIGN) { { @@ -2101,6 +2137,7 @@ public final DeclvarContext declvar() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class TrapContext extends ParserRuleContext { public TerminalNode CATCH() { return getToken(PainlessParser.CATCH, 0); @@ -2171,6 +2208,7 @@ public final TrapContext trap() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class NoncondexpressionContext extends ParserRuleContext { public NoncondexpressionContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -2188,6 +2226,7 @@ public void copyFrom(NoncondexpressionContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class SingleContext extends NoncondexpressionContext { public UnaryContext unary() { return getRuleContext(UnaryContext.class, 0); @@ -2204,6 +2243,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class CompContext extends NoncondexpressionContext { public List noncondexpression() { return getRuleContexts(NoncondexpressionContext.class); @@ -2256,6 +2296,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class BoolContext extends NoncondexpressionContext { public List noncondexpression() { return getRuleContexts(NoncondexpressionContext.class); @@ -2284,6 +2325,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class BinaryContext extends NoncondexpressionContext { public List noncondexpression() { return getRuleContexts(NoncondexpressionContext.class); @@ -2356,6 +2398,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class ElvisContext extends NoncondexpressionContext { public List noncondexpression() { return getRuleContexts(NoncondexpressionContext.class); @@ -2380,6 +2423,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class InstanceofContext extends NoncondexpressionContext { public NoncondexpressionContext noncondexpression() { return getRuleContext(NoncondexpressionContext.class, 0); @@ -2447,9 +2491,11 @@ private NoncondexpressionContext noncondexpression(int _p) throws RecognitionExc if (!(precpred(_ctx, 13))) throw new FailedPredicateException(this, "precpred(_ctx, 13)"); setState(269); _la = _input.LA(1); - if (!((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << MUL) | (1L << DIV) | (1L << REM))) != 0))) { + if (!(((_la) & ~0x3f) == 0 && ((1L << _la) & 7516192768L) != 0)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(270); @@ -2466,6 +2512,8 @@ private NoncondexpressionContext noncondexpression(int _p) throws RecognitionExc if (!(_la == ADD || _la == SUB)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(273); @@ -2482,6 +2530,8 @@ private NoncondexpressionContext noncondexpression(int _p) throws RecognitionExc if (!(_la == FIND || _la == MATCH)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(276); @@ -2495,9 +2545,11 @@ private NoncondexpressionContext noncondexpression(int _p) throws RecognitionExc if (!(precpred(_ctx, 10))) throw new FailedPredicateException(this, "precpred(_ctx, 10)"); setState(278); _la = _input.LA(1); - if (!((((_la) & ~0x3f) == 0 && ((1L << _la) & ((1L << LSH) | (1L << RSH) | (1L << USH))) != 0))) { + if (!(((_la) & ~0x3f) == 0 && ((1L << _la) & 240518168576L) != 0)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(279); @@ -2511,10 +2563,11 @@ private NoncondexpressionContext noncondexpression(int _p) throws RecognitionExc if (!(precpred(_ctx, 9))) throw new FailedPredicateException(this, "precpred(_ctx, 9)"); setState(281); _la = _input.LA(1); - if (!((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LT) | (1L << LTE) | (1L << GT) | (1L << GTE))) != 0))) { + if (!(((_la) & ~0x3f) == 0 && ((1L << _la) & 4123168604160L) != 0)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(282); @@ -2528,10 +2581,11 @@ private NoncondexpressionContext noncondexpression(int _p) throws RecognitionExc if (!(precpred(_ctx, 7))) throw new FailedPredicateException(this, "precpred(_ctx, 7)"); setState(284); _la = _input.LA(1); - if (!((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << EQ) | (1L << EQR) | (1L << NE) | (1L << NER))) != 0))) { + if (!(((_la) & ~0x3f) == 0 && ((1L << _la) & 65970697666560L) != 0)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(285); @@ -2633,6 +2687,7 @@ private NoncondexpressionContext noncondexpression(int _p) throws RecognitionExc return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class ExpressionContext extends ParserRuleContext { public ExpressionContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -2650,6 +2705,7 @@ public void copyFrom(ExpressionContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class ConditionalContext extends ExpressionContext { public NoncondexpressionContext noncondexpression() { return getRuleContext(NoncondexpressionContext.class, 0); @@ -2682,6 +2738,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class AssignmentContext extends ExpressionContext { public NoncondexpressionContext noncondexpression() { return getRuleContext(NoncondexpressionContext.class, 0); @@ -2750,6 +2807,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class NonconditionalContext extends ExpressionContext { public NoncondexpressionContext noncondexpression() { return getRuleContext(NoncondexpressionContext.class, 0); @@ -2803,12 +2861,11 @@ public final ExpressionContext expression() throws RecognitionException { noncondexpression(0); setState(320); _la = _input.LA(1); - if (!(((((_la - 60)) & ~0x3f) == 0 - && ((1L << (_la - 60)) & ((1L << (ASSIGN - 60)) | (1L << (AADD - 60)) | (1L << (ASUB - 60)) | (1L << (AMUL - 60)) - | (1L << (ADIV - 60)) | (1L << (AREM - 60)) | (1L << (AAND - 60)) | (1L << (AXOR - 60)) | (1L << (AOR - 60)) - | (1L << (ALSH - 60)) | (1L << (ARSH - 60)) | (1L << (AUSH - 60)))) != 0))) { + if (!((((_la - 60)) & ~0x3f) == 0 && ((1L << (_la - 60)) & 4095L) != 0)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(321); @@ -2826,6 +2883,7 @@ public final ExpressionContext expression() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class UnaryContext extends ParserRuleContext { public UnaryContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -2843,6 +2901,7 @@ public void copyFrom(UnaryContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class NotaddsubContext extends UnaryContext { public UnarynotaddsubContext unarynotaddsub() { return getRuleContext(UnarynotaddsubContext.class, 0); @@ -2859,6 +2918,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class PreContext extends UnaryContext { public ChainContext chain() { return getRuleContext(ChainContext.class, 0); @@ -2883,6 +2943,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class AddsubContext extends UnaryContext { public UnaryContext unary() { return getRuleContext(UnaryContext.class, 0); @@ -2913,6 +2974,7 @@ public final UnaryContext unary() throws RecognitionException { int _la; try { setState(330); + _errHandler.sync(this); switch (_input.LA(1)) { case INCR: case DECR: @@ -2923,6 +2985,8 @@ public final UnaryContext unary() throws RecognitionException { if (!(_la == INCR || _la == DECR)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(326); @@ -2938,6 +3002,8 @@ public final UnaryContext unary() throws RecognitionException { if (!(_la == ADD || _la == SUB)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(328); @@ -2978,6 +3044,7 @@ public final UnaryContext unary() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class UnarynotaddsubContext extends ParserRuleContext { public UnarynotaddsubContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -2995,6 +3062,7 @@ public void copyFrom(UnarynotaddsubContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class CastContext extends UnarynotaddsubContext { public CastexpressionContext castexpression() { return getRuleContext(CastexpressionContext.class, 0); @@ -3011,6 +3079,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class NotContext extends UnarynotaddsubContext { public UnaryContext unary() { return getRuleContext(UnaryContext.class, 0); @@ -3035,6 +3104,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class ReadContext extends UnarynotaddsubContext { public ChainContext chain() { return getRuleContext(ChainContext.class, 0); @@ -3051,6 +3121,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class PostContext extends UnarynotaddsubContext { public ChainContext chain() { return getRuleContext(ChainContext.class, 0); @@ -3100,6 +3171,8 @@ public final UnarynotaddsubContext unarynotaddsub() throws RecognitionException if (!(_la == INCR || _la == DECR)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } } @@ -3112,6 +3185,8 @@ public final UnarynotaddsubContext unarynotaddsub() throws RecognitionException if (!(_la == BOOLNOT || _la == BWNOT)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(337); @@ -3136,6 +3211,7 @@ public final UnarynotaddsubContext unarynotaddsub() throws RecognitionException return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class CastexpressionContext extends ParserRuleContext { public CastexpressionContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -3153,6 +3229,7 @@ public void copyFrom(CastexpressionContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class RefcastContext extends CastexpressionContext { public TerminalNode LP() { return getToken(PainlessParser.LP, 0); @@ -3181,6 +3258,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class PrimordefcastContext extends CastexpressionContext { public TerminalNode LP() { return getToken(PainlessParser.LP, 0); @@ -3253,6 +3331,7 @@ public final CastexpressionContext castexpression() throws RecognitionException return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class PrimordefcasttypeContext extends ParserRuleContext { public TerminalNode DEF() { return getToken(PainlessParser.DEF, 0); @@ -3292,6 +3371,8 @@ public final PrimordefcasttypeContext primordefcasttype() throws RecognitionExce if (!(_la == PRIMITIVE || _la == DEF)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } } @@ -3305,6 +3386,7 @@ public final PrimordefcasttypeContext primordefcasttype() throws RecognitionExce return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class RefcasttypeContext extends ParserRuleContext { public TerminalNode DEF() { return getToken(PainlessParser.DEF, 0); @@ -3372,6 +3454,7 @@ public final RefcasttypeContext refcasttype() throws RecognitionException { int _la; try { setState(384); + _errHandler.sync(this); switch (_input.LA(1)) { case DEF: enterOuterAlt(_localctx, 1); { @@ -3468,6 +3551,7 @@ public final RefcasttypeContext refcasttype() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class ChainContext extends ParserRuleContext { public ChainContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -3485,6 +3569,7 @@ public void copyFrom(ChainContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class DynamicContext extends ChainContext { public PrimaryContext primary() { return getRuleContext(PrimaryContext.class, 0); @@ -3509,6 +3594,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class NewarrayContext extends ChainContext { public ArrayinitializerContext arrayinitializer() { return getRuleContext(ArrayinitializerContext.class, 0); @@ -3574,6 +3660,7 @@ public final ChainContext chain() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class PrimaryContext extends ParserRuleContext { public PrimaryContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -3591,6 +3678,7 @@ public void copyFrom(PrimaryContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class ListinitContext extends PrimaryContext { public ListinitializerContext listinitializer() { return getRuleContext(ListinitializerContext.class, 0); @@ -3607,6 +3695,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class RegexContext extends PrimaryContext { public TerminalNode REGEX() { return getToken(PainlessParser.REGEX, 0); @@ -3623,6 +3712,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class NullContext extends PrimaryContext { public TerminalNode NULL() { return getToken(PainlessParser.NULL, 0); @@ -3639,6 +3729,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class StringContext extends PrimaryContext { public TerminalNode STRING() { return getToken(PainlessParser.STRING, 0); @@ -3655,6 +3746,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class MapinitContext extends PrimaryContext { public MapinitializerContext mapinitializer() { return getRuleContext(MapinitializerContext.class, 0); @@ -3671,6 +3763,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class CalllocalContext extends PrimaryContext { public TerminalNode ID() { return getToken(PainlessParser.ID, 0); @@ -3691,6 +3784,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class TrueContext extends PrimaryContext { public TerminalNode TRUE() { return getToken(PainlessParser.TRUE, 0); @@ -3707,6 +3801,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class FalseContext extends PrimaryContext { public TerminalNode FALSE() { return getToken(PainlessParser.FALSE, 0); @@ -3723,6 +3818,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class VariableContext extends PrimaryContext { public TerminalNode ID() { return getToken(PainlessParser.ID, 0); @@ -3739,6 +3835,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class NumericContext extends PrimaryContext { public TerminalNode OCTAL() { return getToken(PainlessParser.OCTAL, 0); @@ -3767,6 +3864,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class NewobjectContext extends PrimaryContext { public TerminalNode NEW() { return getToken(PainlessParser.NEW, 0); @@ -3791,6 +3889,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class PrecedenceContext extends PrimaryContext { public TerminalNode LP() { return getToken(PainlessParser.LP, 0); @@ -3839,11 +3938,11 @@ public final PrimaryContext primary() throws RecognitionException { enterOuterAlt(_localctx, 2); { setState(400); _la = _input.LA(1); - if (!(((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)))) != 0))) { + if (!((((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 15L) != 0)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } } @@ -3935,6 +4034,7 @@ public final PrimaryContext primary() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class PostfixContext extends ParserRuleContext { public CallinvokeContext callinvoke() { return getRuleContext(CallinvokeContext.class, 0); @@ -4000,6 +4100,7 @@ public final PostfixContext postfix() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class PostdotContext extends ParserRuleContext { public CallinvokeContext callinvoke() { return getRuleContext(CallinvokeContext.class, 0); @@ -4055,6 +4156,7 @@ public final PostdotContext postdot() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class CallinvokeContext extends ParserRuleContext { public TerminalNode DOTID() { return getToken(PainlessParser.DOTID, 0); @@ -4100,6 +4202,8 @@ public final CallinvokeContext callinvoke() throws RecognitionException { if (!(_la == DOT || _la == NSDOT)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(427); @@ -4117,6 +4221,7 @@ public final CallinvokeContext callinvoke() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class FieldaccessContext extends ParserRuleContext { public TerminalNode DOT() { return getToken(PainlessParser.DOT, 0); @@ -4162,6 +4267,8 @@ public final FieldaccessContext fieldaccess() throws RecognitionException { if (!(_la == DOT || _la == NSDOT)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } setState(431); @@ -4169,6 +4276,8 @@ public final FieldaccessContext fieldaccess() throws RecognitionException { if (!(_la == DOTINTEGER || _la == DOTID)) { _errHandler.recoverInline(this); } else { + if (_input.LA(1) == Token.EOF) matchedEOF = true; + _errHandler.reportMatch(this); consume(); } } @@ -4182,6 +4291,7 @@ public final FieldaccessContext fieldaccess() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class BraceaccessContext extends ParserRuleContext { public TerminalNode LBRACE() { return getToken(PainlessParser.LBRACE, 0); @@ -4234,6 +4344,7 @@ public final BraceaccessContext braceaccess() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class ArrayinitializerContext extends ParserRuleContext { public ArrayinitializerContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -4251,6 +4362,7 @@ public void copyFrom(ArrayinitializerContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class NewstandardarrayContext extends ArrayinitializerContext { public TerminalNode NEW() { return getToken(PainlessParser.NEW, 0); @@ -4307,6 +4419,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class NewinitializedarrayContext extends ArrayinitializerContext { public TerminalNode NEW() { return getToken(PainlessParser.NEW, 0); @@ -4449,14 +4562,10 @@ public final ArrayinitializerContext arrayinitializer() throws RecognitionExcept setState(460); match(LBRACK); setState(469); + _errHandler.sync(this); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << NEW) | (1L << BOOLNOT) | (1L << BWNOT) | (1L << ADD) | (1L - << SUB) | (1L << INCR) | (1L << DECR))) != 0) - || ((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)) | (1L << (STRING - 72)) | (1L << (REGEX - 72)) | (1L << (TRUE - 72)) | (1L << (FALSE - 72)) | (1L - << (NULL - 72)) | (1L << (ID - 72)))) != 0)) { + if (((_la) & ~0x3f) == 0 && ((1L << _la) & 864691155034439840L) != 0 + || (((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 2559L) != 0) { { setState(461); expression(); @@ -4510,6 +4619,7 @@ public final ArrayinitializerContext arrayinitializer() throws RecognitionExcept return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class ListinitializerContext extends ParserRuleContext { public TerminalNode LBRACE() { return getToken(PainlessParser.LBRACE, 0); @@ -4604,6 +4714,7 @@ public final ListinitializerContext listinitializer() throws RecognitionExceptio return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class MapinitializerContext extends ParserRuleContext { public TerminalNode LBRACE() { return getToken(PainlessParser.LBRACE, 0); @@ -4704,6 +4815,7 @@ public final MapinitializerContext mapinitializer() throws RecognitionException return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class MaptokenContext extends ParserRuleContext { public List expression() { return getRuleContexts(ExpressionContext.class); @@ -4756,6 +4868,7 @@ public final MaptokenContext maptoken() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class ArgumentsContext extends ParserRuleContext { public TerminalNode LP() { return getToken(PainlessParser.LP, 0); @@ -4808,14 +4921,10 @@ public final ArgumentsContext arguments() throws RecognitionException { setState(515); match(LP); setState(524); + _errHandler.sync(this); _la = _input.LA(1); - if ((((_la) & ~0x3f) == 0 - && ((1L << _la) & ((1L << LBRACE) | (1L << LP) | (1L << NEW) | (1L << THIS) | (1L << BOOLNOT) | (1L << BWNOT) | (1L - << ADD) | (1L << SUB) | (1L << INCR) | (1L << DECR))) != 0) - || ((((_la - 72)) & ~0x3f) == 0 - && ((1L << (_la - 72)) & ((1L << (OCTAL - 72)) | (1L << (HEX - 72)) | (1L << (INTEGER - 72)) | (1L << (DECIMAL - - 72)) | (1L << (STRING - 72)) | (1L << (REGEX - 72)) | (1L << (TRUE - 72)) | (1L << (FALSE - 72)) | (1L - << (NULL - 72)) | (1L << (PRIMITIVE - 72)) | (1L << (DEF - 72)) | (1L << (ID - 72)))) != 0)) { + if (((_la) & ~0x3f) == 0 && ((1L << _la) & 864691155101548704L) != 0 + || (((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 4095L) != 0) { { setState(516); argument(); @@ -4852,6 +4961,7 @@ public final ArgumentsContext arguments() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class ArgumentContext extends ParserRuleContext { public ExpressionContext expression() { return getRuleContext(ExpressionContext.class, 0); @@ -4917,6 +5027,7 @@ public final ArgumentContext argument() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class LambdaContext extends ParserRuleContext { public TerminalNode ARROW() { return getToken(PainlessParser.ARROW, 0); @@ -4978,6 +5089,7 @@ public final LambdaContext lambda() throws RecognitionException { enterOuterAlt(_localctx, 1); { setState(546); + _errHandler.sync(this); switch (_input.LA(1)) { case PRIMITIVE: case DEF: @@ -4990,9 +5102,9 @@ public final LambdaContext lambda() throws RecognitionException { setState(534); match(LP); setState(543); + _errHandler.sync(this); _la = _input.LA(1); - if (((((_la - 81)) & ~0x3f) == 0 - && ((1L << (_la - 81)) & ((1L << (PRIMITIVE - 81)) | (1L << (DEF - 81)) | (1L << (ID - 81)))) != 0)) { + if ((((_la - 81)) & ~0x3f) == 0 && ((1L << (_la - 81)) & 7L) != 0) { { setState(535); lamtype(); @@ -5025,6 +5137,7 @@ public final LambdaContext lambda() throws RecognitionException { setState(548); match(ARROW); setState(551); + _errHandler.sync(this); switch (_input.LA(1)) { case LBRACK: { setState(549); @@ -5068,6 +5181,7 @@ public final LambdaContext lambda() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class LamtypeContext extends ParserRuleContext { public TerminalNode ID() { return getToken(PainlessParser.ID, 0); @@ -5121,6 +5235,7 @@ public final LamtypeContext lamtype() throws RecognitionException { return _localctx; } + @SuppressWarnings("CheckReturnValue") public static class FuncrefContext extends ParserRuleContext { public FuncrefContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -5138,6 +5253,7 @@ public void copyFrom(FuncrefContext ctx) { } } + @SuppressWarnings("CheckReturnValue") public static class ClassfuncrefContext extends FuncrefContext { public DecltypeContext decltype() { return getRuleContext(DecltypeContext.class, 0); @@ -5162,6 +5278,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class ConstructorfuncrefContext extends FuncrefContext { public DecltypeContext decltype() { return getRuleContext(DecltypeContext.class, 0); @@ -5188,6 +5305,7 @@ public T accept(ParseTreeVisitor visitor) { } } + @SuppressWarnings("CheckReturnValue") public static class LocalfuncrefContext extends FuncrefContext { public TerminalNode THIS() { return getToken(PainlessParser.THIS, 0); @@ -5313,224 +5431,395 @@ private boolean noncondexpression_sempred(NoncondexpressionContext _localctx, in return true; } - public static final String _serializedATN = "\3\u0430\ud6d1\u8206\uad2d\u4417\uaef1\u8d80\uaadd\3W\u023e\4\2\t\2\4" - + "\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t" - + "\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22" - + "\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31" - + "\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t \4!" - + "\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\3\2\7\2R\n\2\f\2\16" - + "\2U\13\2\3\2\7\2X\n\2\f\2\16\2[\13\2\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\4\3" - + "\4\3\4\3\4\3\4\3\4\3\4\7\4k\n\4\f\4\16\4n\13\4\5\4p\n\4\3\4\3\4\3\5\3" - + "\5\3\5\3\5\5\5x\n\5\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\5\6\u0082\n\6\3\6" - + "\3\6\3\6\3\6\3\6\3\6\5\6\u008a\n\6\3\6\3\6\3\6\5\6\u008f\n\6\3\6\3\6\5" - + "\6\u0093\n\6\3\6\3\6\5\6\u0097\n\6\3\6\3\6\3\6\5\6\u009c\n\6\3\6\3\6\3" - + "\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6" - + "\6\6\u00b2\n\6\r\6\16\6\u00b3\5\6\u00b6\n\6\3\7\3\7\3\7\3\7\3\7\3\7\3" - + "\7\3\7\3\7\3\7\3\7\3\7\5\7\u00c4\n\7\3\7\3\7\3\7\5\7\u00c9\n\7\3\b\3\b" - + "\5\b\u00cd\n\b\3\t\3\t\7\t\u00d1\n\t\f\t\16\t\u00d4\13\t\3\t\5\t\u00d7" - + "\n\t\3\t\3\t\3\n\3\n\3\13\3\13\5\13\u00df\n\13\3\f\3\f\3\r\3\r\3\r\3\r" - + "\7\r\u00e7\n\r\f\r\16\r\u00ea\13\r\3\16\3\16\3\16\7\16\u00ef\n\16\f\16" - + "\16\16\u00f2\13\16\3\17\3\17\3\17\3\17\3\17\7\17\u00f9\n\17\f\17\16\17" - + "\u00fc\13\17\5\17\u00fe\n\17\3\20\3\20\3\20\5\20\u0103\n\20\3\21\3\21" - + "\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22" - + "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22" - + "\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22\3\22" - + "\3\22\3\22\3\22\3\22\3\22\7\22\u0136\n\22\f\22\16\22\u0139\13\22\3\23" - + "\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\23\5\23\u0146\n\23\3\24" - + "\3\24\3\24\3\24\3\24\5\24\u014d\n\24\3\25\3\25\3\25\3\25\3\25\3\25\3\25" - + "\5\25\u0156\n\25\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\3\26\5\26" - + "\u0162\n\26\3\27\3\27\3\30\3\30\3\30\6\30\u0169\n\30\r\30\16\30\u016a" - + "\3\30\3\30\3\30\6\30\u0170\n\30\r\30\16\30\u0171\3\30\3\30\3\30\7\30\u0177" - + "\n\30\f\30\16\30\u017a\13\30\3\30\3\30\7\30\u017e\n\30\f\30\16\30\u0181" - + "\13\30\5\30\u0183\n\30\3\31\3\31\7\31\u0187\n\31\f\31\16\31\u018a\13\31" - + "\3\31\5\31\u018d\n\31\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\32" - + "\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\32\3\32\5\32\u01a2\n\32\3\33\3\33" - + "\3\33\5\33\u01a7\n\33\3\34\3\34\5\34\u01ab\n\34\3\35\3\35\3\35\3\35\3" - + "\36\3\36\3\36\3\37\3\37\3\37\3\37\3 \3 \3 \3 \3 \3 \6 \u01be\n \r \16" - + " \u01bf\3 \3 \7 \u01c4\n \f \16 \u01c7\13 \5 \u01c9\n \3 \3 \3 \3 \3 " - + "\3 \3 \3 \7 \u01d3\n \f \16 \u01d6\13 \5 \u01d8\n \3 \3 \7 \u01dc\n \f" - + " \16 \u01df\13 \5 \u01e1\n \3!\3!\3!\3!\7!\u01e7\n!\f!\16!\u01ea\13!\3" - + "!\3!\3!\3!\5!\u01f0\n!\3\"\3\"\3\"\3\"\7\"\u01f6\n\"\f\"\16\"\u01f9\13" - + "\"\3\"\3\"\3\"\3\"\3\"\5\"\u0200\n\"\3#\3#\3#\3#\3$\3$\3$\3$\7$\u020a" - + "\n$\f$\16$\u020d\13$\5$\u020f\n$\3$\3$\3%\3%\3%\5%\u0216\n%\3&\3&\3&\3" - + "&\3&\7&\u021d\n&\f&\16&\u0220\13&\5&\u0222\n&\3&\5&\u0225\n&\3&\3&\3&" - + "\5&\u022a\n&\3\'\5\'\u022d\n\'\3\'\3\'\3(\3(\3(\3(\3(\3(\3(\3(\3(\3(\3" - + "(\5(\u023c\n(\3(\2\3\")\2\4\6\b\n\f\16\20\22\24\26\30\32\34\36 \"$&(*" - + ",.\60\62\64\668:<>@BDFHJLN\2\20\3\3\16\16\3\2 \"\3\2#$\3\2:;\3\2%\'\3" - + "\2(+\3\2,/\3\2>I\3\2<=\3\2\36\37\3\2ST\3\2JM\3\2\13\f\3\2VW\u0279\2S\3" - + "\2\2\2\4^\3\2\2\2\6c\3\2\2\2\bw\3\2\2\2\n\u00b5\3\2\2\2\f\u00c8\3\2\2" - + "\2\16\u00cc\3\2\2\2\20\u00ce\3\2\2\2\22\u00da\3\2\2\2\24\u00de\3\2\2\2" - + "\26\u00e0\3\2\2\2\30\u00e2\3\2\2\2\32\u00eb\3\2\2\2\34\u00fd\3\2\2\2\36" - + "\u00ff\3\2\2\2 \u0104\3\2\2\2\"\u010b\3\2\2\2$\u0145\3\2\2\2&\u014c\3" - + "\2\2\2(\u0155\3\2\2\2*\u0161\3\2\2\2,\u0163\3\2\2\2.\u0182\3\2\2\2\60" - + "\u018c\3\2\2\2\62\u01a1\3\2\2\2\64\u01a6\3\2\2\2\66\u01aa\3\2\2\28\u01ac" - + "\3\2\2\2:\u01b0\3\2\2\2<\u01b3\3\2\2\2>\u01e0\3\2\2\2@\u01ef\3\2\2\2B" - + "\u01ff\3\2\2\2D\u0201\3\2\2\2F\u0205\3\2\2\2H\u0215\3\2\2\2J\u0224\3\2" - + "\2\2L\u022c\3\2\2\2N\u023b\3\2\2\2PR\5\4\3\2QP\3\2\2\2RU\3\2\2\2SQ\3\2" - + "\2\2ST\3\2\2\2TY\3\2\2\2US\3\2\2\2VX\5\b\5\2WV\3\2\2\2X[\3\2\2\2YW\3\2" - + "\2\2YZ\3\2\2\2Z\\\3\2\2\2[Y\3\2\2\2\\]\7\2\2\3]\3\3\2\2\2^_\5\32\16\2" - + "_`\7U\2\2`a\5\6\4\2ab\5\20\t\2b\5\3\2\2\2co\7\t\2\2de\5\32\16\2el\7U\2" - + "\2fg\7\r\2\2gh\5\32\16\2hi\7U\2\2ik\3\2\2\2jf\3\2\2\2kn\3\2\2\2lj\3\2" - + "\2\2lm\3\2\2\2mp\3\2\2\2nl\3\2\2\2od\3\2\2\2op\3\2\2\2pq\3\2\2\2qr\7\n" - + "\2\2r\7\3\2\2\2sx\5\n\6\2tu\5\f\7\2uv\t\2\2\2vx\3\2\2\2ws\3\2\2\2wt\3" - + "\2\2\2x\t\3\2\2\2yz\7\17\2\2z{\7\t\2\2{|\5$\23\2|}\7\n\2\2}\u0081\5\16" - + "\b\2~\177\7\21\2\2\177\u0082\5\16\b\2\u0080\u0082\6\6\2\2\u0081~\3\2\2" - + "\2\u0081\u0080\3\2\2\2\u0082\u00b6\3\2\2\2\u0083\u0084\7\22\2\2\u0084" - + "\u0085\7\t\2\2\u0085\u0086\5$\23\2\u0086\u0089\7\n\2\2\u0087\u008a\5\16" - + "\b\2\u0088\u008a\5\22\n\2\u0089\u0087\3\2\2\2\u0089\u0088\3\2\2\2\u008a" - + "\u00b6\3\2\2\2\u008b\u008c\7\24\2\2\u008c\u008e\7\t\2\2\u008d\u008f\5" - + "\24\13\2\u008e\u008d\3\2\2\2\u008e\u008f\3\2\2\2\u008f\u0090\3\2\2\2\u0090" - + "\u0092\7\16\2\2\u0091\u0093\5$\23\2\u0092\u0091\3\2\2\2\u0092\u0093\3" - + "\2\2\2\u0093\u0094\3\2\2\2\u0094\u0096\7\16\2\2\u0095\u0097\5\26\f\2\u0096" - + "\u0095\3\2\2\2\u0096\u0097\3\2\2\2\u0097\u0098\3\2\2\2\u0098\u009b\7\n" - + "\2\2\u0099\u009c\5\16\b\2\u009a\u009c\5\22\n\2\u009b\u0099\3\2\2\2\u009b" - + "\u009a\3\2\2\2\u009c\u00b6\3\2\2\2\u009d\u009e\7\24\2\2\u009e\u009f\7" - + "\t\2\2\u009f\u00a0\5\32\16\2\u00a0\u00a1\7U\2\2\u00a1\u00a2\7\66\2\2\u00a2" - + "\u00a3\5$\23\2\u00a3\u00a4\7\n\2\2\u00a4\u00a5\5\16\b\2\u00a5\u00b6\3" - + "\2\2\2\u00a6\u00a7\7\24\2\2\u00a7\u00a8\7\t\2\2\u00a8\u00a9\7U\2\2\u00a9" - + "\u00aa\7\20\2\2\u00aa\u00ab\5$\23\2\u00ab\u00ac\7\n\2\2\u00ac\u00ad\5" - + "\16\b\2\u00ad\u00b6\3\2\2\2\u00ae\u00af\7\31\2\2\u00af\u00b1\5\20\t\2" - + "\u00b0\u00b2\5 \21\2\u00b1\u00b0\3\2\2\2\u00b2\u00b3\3\2\2\2\u00b3\u00b1" - + "\3\2\2\2\u00b3\u00b4\3\2\2\2\u00b4\u00b6\3\2\2\2\u00b5y\3\2\2\2\u00b5" - + "\u0083\3\2\2\2\u00b5\u008b\3\2\2\2\u00b5\u009d\3\2\2\2\u00b5\u00a6\3\2" - + "\2\2\u00b5\u00ae\3\2\2\2\u00b6\13\3\2\2\2\u00b7\u00b8\7\23\2\2\u00b8\u00b9" - + "\5\20\t\2\u00b9\u00ba\7\22\2\2\u00ba\u00bb\7\t\2\2\u00bb\u00bc\5$\23\2" - + "\u00bc\u00bd\7\n\2\2\u00bd\u00c9\3\2\2\2\u00be\u00c9\5\30\r\2\u00bf\u00c9" - + "\7\25\2\2\u00c0\u00c9\7\26\2\2\u00c1\u00c3\7\27\2\2\u00c2\u00c4\5$\23" - + "\2\u00c3\u00c2\3\2\2\2\u00c3\u00c4\3\2\2\2\u00c4\u00c9\3\2\2\2\u00c5\u00c6" - + "\7\33\2\2\u00c6\u00c9\5$\23\2\u00c7\u00c9\5$\23\2\u00c8\u00b7\3\2\2\2" - + "\u00c8\u00be\3\2\2\2\u00c8\u00bf\3\2\2\2\u00c8\u00c0\3\2\2\2\u00c8\u00c1" - + "\3\2\2\2\u00c8\u00c5\3\2\2\2\u00c8\u00c7\3\2\2\2\u00c9\r\3\2\2\2\u00ca" - + "\u00cd\5\20\t\2\u00cb\u00cd\5\b\5\2\u00cc\u00ca\3\2\2\2\u00cc\u00cb\3" - + "\2\2\2\u00cd\17\3\2\2\2\u00ce\u00d2\7\5\2\2\u00cf\u00d1\5\b\5\2\u00d0" - + "\u00cf\3\2\2\2\u00d1\u00d4\3\2\2\2\u00d2\u00d0\3\2\2\2\u00d2\u00d3\3\2" - + "\2\2\u00d3\u00d6\3\2\2\2\u00d4\u00d2\3\2\2\2\u00d5\u00d7\5\f\7\2\u00d6" - + "\u00d5\3\2\2\2\u00d6\u00d7\3\2\2\2\u00d7\u00d8\3\2\2\2\u00d8\u00d9\7\6" - + "\2\2\u00d9\21\3\2\2\2\u00da\u00db\7\16\2\2\u00db\23\3\2\2\2\u00dc\u00df" - + "\5\30\r\2\u00dd\u00df\5$\23\2\u00de\u00dc\3\2\2\2\u00de\u00dd\3\2\2\2" - + "\u00df\25\3\2\2\2\u00e0\u00e1\5$\23\2\u00e1\27\3\2\2\2\u00e2\u00e3\5\32" - + "\16\2\u00e3\u00e8\5\36\20\2\u00e4\u00e5\7\r\2\2\u00e5\u00e7\5\36\20\2" - + "\u00e6\u00e4\3\2\2\2\u00e7\u00ea\3\2\2\2\u00e8\u00e6\3\2\2\2\u00e8\u00e9" - + "\3\2\2\2\u00e9\31\3\2\2\2\u00ea\u00e8\3\2\2\2\u00eb\u00f0\5\34\17\2\u00ec" - + "\u00ed\7\7\2\2\u00ed\u00ef\7\b\2\2\u00ee\u00ec\3\2\2\2\u00ef\u00f2\3\2" - + "\2\2\u00f0\u00ee\3\2\2\2\u00f0\u00f1\3\2\2\2\u00f1\33\3\2\2\2\u00f2\u00f0" - + "\3\2\2\2\u00f3\u00fe\7T\2\2\u00f4\u00fe\7S\2\2\u00f5\u00fa\7U\2\2\u00f6" - + "\u00f7\7\13\2\2\u00f7\u00f9\7W\2\2\u00f8\u00f6\3\2\2\2\u00f9\u00fc\3\2" - + "\2\2\u00fa\u00f8\3\2\2\2\u00fa\u00fb\3\2\2\2\u00fb\u00fe\3\2\2\2\u00fc" - + "\u00fa\3\2\2\2\u00fd\u00f3\3\2\2\2\u00fd\u00f4\3\2\2\2\u00fd\u00f5\3\2" - + "\2\2\u00fe\35\3\2\2\2\u00ff\u0102\7U\2\2\u0100\u0101\7>\2\2\u0101\u0103" - + "\5$\23\2\u0102\u0100\3\2\2\2\u0102\u0103\3\2\2\2\u0103\37\3\2\2\2\u0104" - + "\u0105\7\32\2\2\u0105\u0106\7\t\2\2\u0106\u0107\5\34\17\2\u0107\u0108" - + "\7U\2\2\u0108\u0109\7\n\2\2\u0109\u010a\5\20\t\2\u010a!\3\2\2\2\u010b" - + "\u010c\b\22\1\2\u010c\u010d\5&\24\2\u010d\u0137\3\2\2\2\u010e\u010f\f" - + "\17\2\2\u010f\u0110\t\3\2\2\u0110\u0136\5\"\22\20\u0111\u0112\f\16\2\2" - + "\u0112\u0113\t\4\2\2\u0113\u0136\5\"\22\17\u0114\u0115\f\r\2\2\u0115\u0116" - + "\t\5\2\2\u0116\u0136\5\"\22\16\u0117\u0118\f\f\2\2\u0118\u0119\t\6\2\2" - + "\u0119\u0136\5\"\22\r\u011a\u011b\f\13\2\2\u011b\u011c\t\7\2\2\u011c\u0136" - + "\5\"\22\f\u011d\u011e\f\t\2\2\u011e\u011f\t\b\2\2\u011f\u0136\5\"\22\n" - + "\u0120\u0121\f\b\2\2\u0121\u0122\7\60\2\2\u0122\u0136\5\"\22\t\u0123\u0124" - + "\f\7\2\2\u0124\u0125\7\61\2\2\u0125\u0136\5\"\22\b\u0126\u0127\f\6\2\2" - + "\u0127\u0128\7\62\2\2\u0128\u0136\5\"\22\7\u0129\u012a\f\5\2\2\u012a\u012b" - + "\7\63\2\2\u012b\u0136\5\"\22\6\u012c\u012d\f\4\2\2\u012d\u012e\7\64\2" - + "\2\u012e\u0136\5\"\22\5\u012f\u0130\f\3\2\2\u0130\u0131\7\67\2\2\u0131" - + "\u0136\5\"\22\3\u0132\u0133\f\n\2\2\u0133\u0134\7\35\2\2\u0134\u0136\5" - + "\32\16\2\u0135\u010e\3\2\2\2\u0135\u0111\3\2\2\2\u0135\u0114\3\2\2\2\u0135" - + "\u0117\3\2\2\2\u0135\u011a\3\2\2\2\u0135\u011d\3\2\2\2\u0135\u0120\3\2" - + "\2\2\u0135\u0123\3\2\2\2\u0135\u0126\3\2\2\2\u0135\u0129\3\2\2\2\u0135" - + "\u012c\3\2\2\2\u0135\u012f\3\2\2\2\u0135\u0132\3\2\2\2\u0136\u0139\3\2" - + "\2\2\u0137\u0135\3\2\2\2\u0137\u0138\3\2\2\2\u0138#\3\2\2\2\u0139\u0137" - + "\3\2\2\2\u013a\u0146\5\"\22\2\u013b\u013c\5\"\22\2\u013c\u013d\7\65\2" - + "\2\u013d\u013e\5$\23\2\u013e\u013f\7\66\2\2\u013f\u0140\5$\23\2\u0140" - + "\u0146\3\2\2\2\u0141\u0142\5\"\22\2\u0142\u0143\t\t\2\2\u0143\u0144\5" - + "$\23\2\u0144\u0146\3\2\2\2\u0145\u013a\3\2\2\2\u0145\u013b\3\2\2\2\u0145" - + "\u0141\3\2\2\2\u0146%\3\2\2\2\u0147\u0148\t\n\2\2\u0148\u014d\5\60\31" - + "\2\u0149\u014a\t\4\2\2\u014a\u014d\5&\24\2\u014b\u014d\5(\25\2\u014c\u0147" - + "\3\2\2\2\u014c\u0149\3\2\2\2\u014c\u014b\3\2\2\2\u014d\'\3\2\2\2\u014e" - + "\u0156\5\60\31\2\u014f\u0150\5\60\31\2\u0150\u0151\t\n\2\2\u0151\u0156" - + "\3\2\2\2\u0152\u0153\t\13\2\2\u0153\u0156\5&\24\2\u0154\u0156\5*\26\2" - + "\u0155\u014e\3\2\2\2\u0155\u014f\3\2\2\2\u0155\u0152\3\2\2\2\u0155\u0154" - + "\3\2\2\2\u0156)\3\2\2\2\u0157\u0158\7\t\2\2\u0158\u0159\5,\27\2\u0159" - + "\u015a\7\n\2\2\u015a\u015b\5&\24\2\u015b\u0162\3\2\2\2\u015c\u015d\7\t" - + "\2\2\u015d\u015e\5.\30\2\u015e\u015f\7\n\2\2\u015f\u0160\5(\25\2\u0160" - + "\u0162\3\2\2\2\u0161\u0157\3\2\2\2\u0161\u015c\3\2\2\2\u0162+\3\2\2\2" - + "\u0163\u0164\t\f\2\2\u0164-\3\2\2\2\u0165\u0168\7T\2\2\u0166\u0167\7\7" - + "\2\2\u0167\u0169\7\b\2\2\u0168\u0166\3\2\2\2\u0169\u016a\3\2\2\2\u016a" - + "\u0168\3\2\2\2\u016a\u016b\3\2\2\2\u016b\u0183\3\2\2\2\u016c\u016f\7S" - + "\2\2\u016d\u016e\7\7\2\2\u016e\u0170\7\b\2\2\u016f\u016d\3\2\2\2\u0170" - + "\u0171\3\2\2\2\u0171\u016f\3\2\2\2\u0171\u0172\3\2\2\2\u0172\u0183\3\2" - + "\2\2\u0173\u0178\7U\2\2\u0174\u0175\7\13\2\2\u0175\u0177\7W\2\2\u0176" - + "\u0174\3\2\2\2\u0177\u017a\3\2\2\2\u0178\u0176\3\2\2\2\u0178\u0179\3\2" - + "\2\2\u0179\u017f\3\2\2\2\u017a\u0178\3\2\2\2\u017b\u017c\7\7\2\2\u017c" - + "\u017e\7\b\2\2\u017d\u017b\3\2\2\2\u017e\u0181\3\2\2\2\u017f\u017d\3\2" - + "\2\2\u017f\u0180\3\2\2\2\u0180\u0183\3\2\2\2\u0181\u017f\3\2\2\2\u0182" - + "\u0165\3\2\2\2\u0182\u016c\3\2\2\2\u0182\u0173\3\2\2\2\u0183/\3\2\2\2" - + "\u0184\u0188\5\62\32\2\u0185\u0187\5\64\33\2\u0186\u0185\3\2\2\2\u0187" - + "\u018a\3\2\2\2\u0188\u0186\3\2\2\2\u0188\u0189\3\2\2\2\u0189\u018d\3\2" - + "\2\2\u018a\u0188\3\2\2\2\u018b\u018d\5> \2\u018c\u0184\3\2\2\2\u018c\u018b" - + "\3\2\2\2\u018d\61\3\2\2\2\u018e\u018f\7\t\2\2\u018f\u0190\5$\23\2\u0190" - + "\u0191\7\n\2\2\u0191\u01a2\3\2\2\2\u0192\u01a2\t\r\2\2\u0193\u01a2\7P" - + "\2\2\u0194\u01a2\7Q\2\2\u0195\u01a2\7R\2\2\u0196\u01a2\7N\2\2\u0197\u01a2" - + "\7O\2\2\u0198\u01a2\5@!\2\u0199\u01a2\5B\"\2\u019a\u01a2\7U\2\2\u019b" - + "\u019c\7U\2\2\u019c\u01a2\5F$\2\u019d\u019e\7\30\2\2\u019e\u019f\5\34" - + "\17\2\u019f\u01a0\5F$\2\u01a0\u01a2\3\2\2\2\u01a1\u018e\3\2\2\2\u01a1" - + "\u0192\3\2\2\2\u01a1\u0193\3\2\2\2\u01a1\u0194\3\2\2\2\u01a1\u0195\3\2" - + "\2\2\u01a1\u0196\3\2\2\2\u01a1\u0197\3\2\2\2\u01a1\u0198\3\2\2\2\u01a1" - + "\u0199\3\2\2\2\u01a1\u019a\3\2\2\2\u01a1\u019b\3\2\2\2\u01a1\u019d\3\2" - + "\2\2\u01a2\63\3\2\2\2\u01a3\u01a7\58\35\2\u01a4\u01a7\5:\36\2\u01a5\u01a7" - + "\5<\37\2\u01a6\u01a3\3\2\2\2\u01a6\u01a4\3\2\2\2\u01a6\u01a5\3\2\2\2\u01a7" - + "\65\3\2\2\2\u01a8\u01ab\58\35\2\u01a9\u01ab\5:\36\2\u01aa\u01a8\3\2\2" - + "\2\u01aa\u01a9\3\2\2\2\u01ab\67\3\2\2\2\u01ac\u01ad\t\16\2\2\u01ad\u01ae" - + "\7W\2\2\u01ae\u01af\5F$\2\u01af9\3\2\2\2\u01b0\u01b1\t\16\2\2\u01b1\u01b2" - + "\t\17\2\2\u01b2;\3\2\2\2\u01b3\u01b4\7\7\2\2\u01b4\u01b5\5$\23\2\u01b5" - + "\u01b6\7\b\2\2\u01b6=\3\2\2\2\u01b7\u01b8\7\30\2\2\u01b8\u01bd\5\34\17" - + "\2\u01b9\u01ba\7\7\2\2\u01ba\u01bb\5$\23\2\u01bb\u01bc\7\b\2\2\u01bc\u01be" - + "\3\2\2\2\u01bd\u01b9\3\2\2\2\u01be\u01bf\3\2\2\2\u01bf\u01bd\3\2\2\2\u01bf" - + "\u01c0\3\2\2\2\u01c0\u01c8\3\2\2\2\u01c1\u01c5\5\66\34\2\u01c2\u01c4\5" - + "\64\33\2\u01c3\u01c2\3\2\2\2\u01c4\u01c7\3\2\2\2\u01c5\u01c3\3\2\2\2\u01c5" - + "\u01c6\3\2\2\2\u01c6\u01c9\3\2\2\2\u01c7\u01c5\3\2\2\2\u01c8\u01c1\3\2" - + "\2\2\u01c8\u01c9\3\2\2\2\u01c9\u01e1\3\2\2\2\u01ca\u01cb\7\30\2\2\u01cb" - + "\u01cc\5\34\17\2\u01cc\u01cd\7\7\2\2\u01cd\u01ce\7\b\2\2\u01ce\u01d7\7" - + "\5\2\2\u01cf\u01d4\5$\23\2\u01d0\u01d1\7\r\2\2\u01d1\u01d3\5$\23\2\u01d2" - + "\u01d0\3\2\2\2\u01d3\u01d6\3\2\2\2\u01d4\u01d2\3\2\2\2\u01d4\u01d5\3\2" - + "\2\2\u01d5\u01d8\3\2\2\2\u01d6\u01d4\3\2\2\2\u01d7\u01cf\3\2\2\2\u01d7" - + "\u01d8\3\2\2\2\u01d8\u01d9\3\2\2\2\u01d9\u01dd\7\6\2\2\u01da\u01dc\5\64" - + "\33\2\u01db\u01da\3\2\2\2\u01dc\u01df\3\2\2\2\u01dd\u01db\3\2\2\2\u01dd" - + "\u01de\3\2\2\2\u01de\u01e1\3\2\2\2\u01df\u01dd\3\2\2\2\u01e0\u01b7\3\2" - + "\2\2\u01e0\u01ca\3\2\2\2\u01e1?\3\2\2\2\u01e2\u01e3\7\7\2\2\u01e3\u01e8" - + "\5$\23\2\u01e4\u01e5\7\r\2\2\u01e5\u01e7\5$\23\2\u01e6\u01e4\3\2\2\2\u01e7" - + "\u01ea\3\2\2\2\u01e8\u01e6\3\2\2\2\u01e8\u01e9\3\2\2\2\u01e9\u01eb\3\2" - + "\2\2\u01ea\u01e8\3\2\2\2\u01eb\u01ec\7\b\2\2\u01ec\u01f0\3\2\2\2\u01ed" - + "\u01ee\7\7\2\2\u01ee\u01f0\7\b\2\2\u01ef\u01e2\3\2\2\2\u01ef\u01ed\3\2" - + "\2\2\u01f0A\3\2\2\2\u01f1\u01f2\7\7\2\2\u01f2\u01f7\5D#\2\u01f3\u01f4" - + "\7\r\2\2\u01f4\u01f6\5D#\2\u01f5\u01f3\3\2\2\2\u01f6\u01f9\3\2\2\2\u01f7" - + "\u01f5\3\2\2\2\u01f7\u01f8\3\2\2\2\u01f8\u01fa\3\2\2\2\u01f9\u01f7\3\2" - + "\2\2\u01fa\u01fb\7\b\2\2\u01fb\u0200\3\2\2\2\u01fc\u01fd\7\7\2\2\u01fd" - + "\u01fe\7\66\2\2\u01fe\u0200\7\b\2\2\u01ff\u01f1\3\2\2\2\u01ff\u01fc\3" - + "\2\2\2\u0200C\3\2\2\2\u0201\u0202\5$\23\2\u0202\u0203\7\66\2\2\u0203\u0204" - + "\5$\23\2\u0204E\3\2\2\2\u0205\u020e\7\t\2\2\u0206\u020b\5H%\2\u0207\u0208" - + "\7\r\2\2\u0208\u020a\5H%\2\u0209\u0207\3\2\2\2\u020a\u020d\3\2\2\2\u020b" - + "\u0209\3\2\2\2\u020b\u020c\3\2\2\2\u020c\u020f\3\2\2\2\u020d\u020b\3\2" - + "\2\2\u020e\u0206\3\2\2\2\u020e\u020f\3\2\2\2\u020f\u0210\3\2\2\2\u0210" - + "\u0211\7\n\2\2\u0211G\3\2\2\2\u0212\u0216\5$\23\2\u0213\u0216\5J&\2\u0214" - + "\u0216\5N(\2\u0215\u0212\3\2\2\2\u0215\u0213\3\2\2\2\u0215\u0214\3\2\2" - + "\2\u0216I\3\2\2\2\u0217\u0225\5L\'\2\u0218\u0221\7\t\2\2\u0219\u021e\5" - + "L\'\2\u021a\u021b\7\r\2\2\u021b\u021d\5L\'\2\u021c\u021a\3\2\2\2\u021d" - + "\u0220\3\2\2\2\u021e\u021c\3\2\2\2\u021e\u021f\3\2\2\2\u021f\u0222\3\2" - + "\2\2\u0220\u021e\3\2\2\2\u0221\u0219\3\2\2\2\u0221\u0222\3\2\2\2\u0222" - + "\u0223\3\2\2\2\u0223\u0225\7\n\2\2\u0224\u0217\3\2\2\2\u0224\u0218\3\2" - + "\2\2\u0225\u0226\3\2\2\2\u0226\u0229\79\2\2\u0227\u022a\5\20\t\2\u0228" - + "\u022a\5$\23\2\u0229\u0227\3\2\2\2\u0229\u0228\3\2\2\2\u022aK\3\2\2\2" - + "\u022b\u022d\5\32\16\2\u022c\u022b\3\2\2\2\u022c\u022d\3\2\2\2\u022d\u022e" - + "\3\2\2\2\u022e\u022f\7U\2\2\u022fM\3\2\2\2\u0230\u0231\5\32\16\2\u0231" - + "\u0232\78\2\2\u0232\u0233\7U\2\2\u0233\u023c\3\2\2\2\u0234\u0235\5\32" - + "\16\2\u0235\u0236\78\2\2\u0236\u0237\7\30\2\2\u0237\u023c\3\2\2\2\u0238" - + "\u0239\7\34\2\2\u0239\u023a\78\2\2\u023a\u023c\7U\2\2\u023b\u0230\3\2" - + "\2\2\u023b\u0234\3\2\2\2\u023b\u0238\3\2\2\2\u023cO\3\2\2\2>SYlow\u0081" - + "\u0089\u008e\u0092\u0096\u009b\u00b3\u00b5\u00c3\u00c8\u00cc\u00d2\u00d6" - + "\u00de\u00e8\u00f0\u00fa\u00fd\u0102\u0135\u0137\u0145\u014c\u0155\u0161" - + "\u016a\u0171\u0178\u017f\u0182\u0188\u018c\u01a1\u01a6\u01aa\u01bf\u01c5" - + "\u01c8\u01d4\u01d7\u01dd\u01e0\u01e8\u01ef\u01f7\u01ff\u020b\u020e\u0215" - + "\u021e\u0221\u0224\u0229\u022c\u023b"; + public static final String _serializedATN = "\u0004\u0001U\u023c\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002" + + "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002" + + "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002" + + "\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002" + + "\f\u0007\f\u0002\r\u0007\r\u0002\u000e\u0007\u000e\u0002\u000f\u0007\u000f" + + "\u0002\u0010\u0007\u0010\u0002\u0011\u0007\u0011\u0002\u0012\u0007\u0012" + + "\u0002\u0013\u0007\u0013\u0002\u0014\u0007\u0014\u0002\u0015\u0007\u0015" + + "\u0002\u0016\u0007\u0016\u0002\u0017\u0007\u0017\u0002\u0018\u0007\u0018" + + "\u0002\u0019\u0007\u0019\u0002\u001a\u0007\u001a\u0002\u001b\u0007\u001b" + + "\u0002\u001c\u0007\u001c\u0002\u001d\u0007\u001d\u0002\u001e\u0007\u001e" + + "\u0002\u001f\u0007\u001f\u0002 \u0007 \u0002!\u0007!\u0002\"\u0007\"\u0002" + + "#\u0007#\u0002$\u0007$\u0002%\u0007%\u0002&\u0007&\u0001\u0000\u0005\u0000" + + "P\b\u0000\n\u0000\f\u0000S\t\u0000\u0001\u0000\u0005\u0000V\b\u0000\n" + + "\u0000\f\u0000Y\t\u0000\u0001\u0000\u0001\u0000\u0001\u0001\u0001\u0001" + + "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0001\u0002\u0001\u0002" + + "\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0005\u0002i\b\u0002" + + "\n\u0002\f\u0002l\t\u0002\u0003\u0002n\b\u0002\u0001\u0002\u0001\u0002" + + "\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0003\u0003v\b\u0003" + + "\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004" + + "\u0001\u0004\u0001\u0004\u0003\u0004\u0080\b\u0004\u0001\u0004\u0001\u0004" + + "\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0003\u0004\u0088\b\u0004" + + "\u0001\u0004\u0001\u0004\u0001\u0004\u0003\u0004\u008d\b\u0004\u0001\u0004" + + "\u0001\u0004\u0003\u0004\u0091\b\u0004\u0001\u0004\u0001\u0004\u0003\u0004" + + "\u0095\b\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0003\u0004\u009a\b" + + "\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001" + + "\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001" + + "\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001\u0004\u0001" + + "\u0004\u0001\u0004\u0001\u0004\u0004\u0004\u00b0\b\u0004\u000b\u0004\f" + + "\u0004\u00b1\u0003\u0004\u00b4\b\u0004\u0001\u0005\u0001\u0005\u0001\u0005" + + "\u0001\u0005\u0001\u0005\u0001\u0005\u0001\u0005\u0001\u0005\u0001\u0005" + + "\u0001\u0005\u0001\u0005\u0001\u0005\u0003\u0005\u00c2\b\u0005\u0001\u0005" + + "\u0001\u0005\u0001\u0005\u0003\u0005\u00c7\b\u0005\u0001\u0006\u0001\u0006" + + "\u0003\u0006\u00cb\b\u0006\u0001\u0007\u0001\u0007\u0005\u0007\u00cf\b" + + "\u0007\n\u0007\f\u0007\u00d2\t\u0007\u0001\u0007\u0003\u0007\u00d5\b\u0007" + + "\u0001\u0007\u0001\u0007\u0001\b\u0001\b\u0001\t\u0001\t\u0003\t\u00dd" + + "\b\t\u0001\n\u0001\n\u0001\u000b\u0001\u000b\u0001\u000b\u0001\u000b\u0005" + + "\u000b\u00e5\b\u000b\n\u000b\f\u000b\u00e8\t\u000b\u0001\f\u0001\f\u0001" + + "\f\u0005\f\u00ed\b\f\n\f\f\f\u00f0\t\f\u0001\r\u0001\r\u0001\r\u0001\r" + + "\u0001\r\u0005\r\u00f7\b\r\n\r\f\r\u00fa\t\r\u0003\r\u00fc\b\r\u0001\u000e" + + "\u0001\u000e\u0001\u000e\u0003\u000e\u0101\b\u000e\u0001\u000f\u0001\u000f" + + "\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u000f\u0001\u0010" + + "\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010" + + "\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010" + + "\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010" + + "\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010" + + "\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010" + + "\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010" + + "\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0001\u0010\u0005\u0010" + + "\u0134\b\u0010\n\u0010\f\u0010\u0137\t\u0010\u0001\u0011\u0001\u0011\u0001" + + "\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001\u0011\u0001" + + "\u0011\u0001\u0011\u0001\u0011\u0003\u0011\u0144\b\u0011\u0001\u0012\u0001" + + "\u0012\u0001\u0012\u0001\u0012\u0001\u0012\u0003\u0012\u014b\b\u0012\u0001" + + "\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001\u0013\u0001" + + "\u0013\u0003\u0013\u0154\b\u0013\u0001\u0014\u0001\u0014\u0001\u0014\u0001" + + "\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001\u0014\u0001" + + "\u0014\u0003\u0014\u0160\b\u0014\u0001\u0015\u0001\u0015\u0001\u0016\u0001" + + "\u0016\u0001\u0016\u0004\u0016\u0167\b\u0016\u000b\u0016\f\u0016\u0168" + + "\u0001\u0016\u0001\u0016\u0001\u0016\u0004\u0016\u016e\b\u0016\u000b\u0016" + + "\f\u0016\u016f\u0001\u0016\u0001\u0016\u0001\u0016\u0005\u0016\u0175\b" + + "\u0016\n\u0016\f\u0016\u0178\t\u0016\u0001\u0016\u0001\u0016\u0005\u0016" + + "\u017c\b\u0016\n\u0016\f\u0016\u017f\t\u0016\u0003\u0016\u0181\b\u0016" + + "\u0001\u0017\u0001\u0017\u0005\u0017\u0185\b\u0017\n\u0017\f\u0017\u0188" + + "\t\u0017\u0001\u0017\u0003\u0017\u018b\b\u0017\u0001\u0018\u0001\u0018" + + "\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018" + + "\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018" + + "\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0001\u0018\u0003\u0018" + + "\u01a0\b\u0018\u0001\u0019\u0001\u0019\u0001\u0019\u0003\u0019\u01a5\b" + + "\u0019\u0001\u001a\u0001\u001a\u0003\u001a\u01a9\b\u001a\u0001\u001b\u0001" + + "\u001b\u0001\u001b\u0001\u001b\u0001\u001c\u0001\u001c\u0001\u001c\u0001" + + "\u001d\u0001\u001d\u0001\u001d\u0001\u001d\u0001\u001e\u0001\u001e\u0001" + + "\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0004\u001e\u01bc\b\u001e\u000b" + + "\u001e\f\u001e\u01bd\u0001\u001e\u0001\u001e\u0005\u001e\u01c2\b\u001e" + + "\n\u001e\f\u001e\u01c5\t\u001e\u0003\u001e\u01c7\b\u001e\u0001\u001e\u0001" + + "\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001\u001e\u0001" + + "\u001e\u0005\u001e\u01d1\b\u001e\n\u001e\f\u001e\u01d4\t\u001e\u0003\u001e" + + "\u01d6\b\u001e\u0001\u001e\u0001\u001e\u0005\u001e\u01da\b\u001e\n\u001e" + + "\f\u001e\u01dd\t\u001e\u0003\u001e\u01df\b\u001e\u0001\u001f\u0001\u001f" + + "\u0001\u001f\u0001\u001f\u0005\u001f\u01e5\b\u001f\n\u001f\f\u001f\u01e8" + + "\t\u001f\u0001\u001f\u0001\u001f\u0001\u001f\u0001\u001f\u0003\u001f\u01ee" + + "\b\u001f\u0001 \u0001 \u0001 \u0001 \u0005 \u01f4\b \n \f \u01f7\t \u0001" + + " \u0001 \u0001 \u0001 \u0001 \u0003 \u01fe\b \u0001!\u0001!\u0001!\u0001" + + "!\u0001\"\u0001\"\u0001\"\u0001\"\u0005\"\u0208\b\"\n\"\f\"\u020b\t\"" + + "\u0003\"\u020d\b\"\u0001\"\u0001\"\u0001#\u0001#\u0001#\u0003#\u0214\b" + + "#\u0001$\u0001$\u0001$\u0001$\u0001$\u0005$\u021b\b$\n$\f$\u021e\t$\u0003" + + "$\u0220\b$\u0001$\u0003$\u0223\b$\u0001$\u0001$\u0001$\u0003$\u0228\b" + + "$\u0001%\u0003%\u022b\b%\u0001%\u0001%\u0001&\u0001&\u0001&\u0001&\u0001" + + "&\u0001&\u0001&\u0001&\u0001&\u0001&\u0001&\u0003&\u023a\b&\u0001&\u0000" + + "\u0001 \'\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010\u0012\u0014\u0016" + + "\u0018\u001a\u001c\u001e \"$&(*,.02468:<>@BDFHJL\u0000\u000e\u0001\u0001" + + "\f\f\u0001\u0000\u001e \u0001\u0000!\"\u0001\u000089\u0001\u0000#%\u0001" + + "\u0000&)\u0001\u0000*-\u0001\u0000\u01ed\u0001\u0000\u0000\u0000@\u01fd\u0001\u0000\u0000\u0000B\u01ff" + + "\u0001\u0000\u0000\u0000D\u0203\u0001\u0000\u0000\u0000F\u0213\u0001\u0000" + + "\u0000\u0000H\u0222\u0001\u0000\u0000\u0000J\u022a\u0001\u0000\u0000\u0000" + + "L\u0239\u0001\u0000\u0000\u0000NP\u0003\u0002\u0001\u0000ON\u0001\u0000" + + "\u0000\u0000PS\u0001\u0000\u0000\u0000QO\u0001\u0000\u0000\u0000QR\u0001" + + "\u0000\u0000\u0000RW\u0001\u0000\u0000\u0000SQ\u0001\u0000\u0000\u0000" + + "TV\u0003\u0006\u0003\u0000UT\u0001\u0000\u0000\u0000VY\u0001\u0000\u0000" + + "\u0000WU\u0001\u0000\u0000\u0000WX\u0001\u0000\u0000\u0000XZ\u0001\u0000" + + "\u0000\u0000YW\u0001\u0000\u0000\u0000Z[\u0005\u0000\u0000\u0001[\u0001" + + "\u0001\u0000\u0000\u0000\\]\u0003\u0018\f\u0000]^\u0005S\u0000\u0000^" + + "_\u0003\u0004\u0002\u0000_`\u0003\u000e\u0007\u0000`\u0003\u0001\u0000" + + "\u0000\u0000am\u0005\u0007\u0000\u0000bc\u0003\u0018\f\u0000cj\u0005S" + + "\u0000\u0000de\u0005\u000b\u0000\u0000ef\u0003\u0018\f\u0000fg\u0005S" + + "\u0000\u0000gi\u0001\u0000\u0000\u0000hd\u0001\u0000\u0000\u0000il\u0001" + + "\u0000\u0000\u0000jh\u0001\u0000\u0000\u0000jk\u0001\u0000\u0000\u0000" + + "kn\u0001\u0000\u0000\u0000lj\u0001\u0000\u0000\u0000mb\u0001\u0000\u0000" + + "\u0000mn\u0001\u0000\u0000\u0000no\u0001\u0000\u0000\u0000op\u0005\b\u0000" + + "\u0000p\u0005\u0001\u0000\u0000\u0000qv\u0003\b\u0004\u0000rs\u0003\n" + + "\u0005\u0000st\u0007\u0000\u0000\u0000tv\u0001\u0000\u0000\u0000uq\u0001" + + "\u0000\u0000\u0000ur\u0001\u0000\u0000\u0000v\u0007\u0001\u0000\u0000" + + "\u0000wx\u0005\r\u0000\u0000xy\u0005\u0007\u0000\u0000yz\u0003\"\u0011" + + "\u0000z{\u0005\b\u0000\u0000{\u007f\u0003\f\u0006\u0000|}\u0005\u000f" + + "\u0000\u0000}\u0080\u0003\f\u0006\u0000~\u0080\u0004\u0004\u0000\u0000" + + "\u007f|\u0001\u0000\u0000\u0000\u007f~\u0001\u0000\u0000\u0000\u0080\u00b4" + + "\u0001\u0000\u0000\u0000\u0081\u0082\u0005\u0010\u0000\u0000\u0082\u0083" + + "\u0005\u0007\u0000\u0000\u0083\u0084\u0003\"\u0011\u0000\u0084\u0087\u0005" + + "\b\u0000\u0000\u0085\u0088\u0003\f\u0006\u0000\u0086\u0088\u0003\u0010" + + "\b\u0000\u0087\u0085\u0001\u0000\u0000\u0000\u0087\u0086\u0001\u0000\u0000" + + "\u0000\u0088\u00b4\u0001\u0000\u0000\u0000\u0089\u008a\u0005\u0012\u0000" + + "\u0000\u008a\u008c\u0005\u0007\u0000\u0000\u008b\u008d\u0003\u0012\t\u0000" + + "\u008c\u008b\u0001\u0000\u0000\u0000\u008c\u008d\u0001\u0000\u0000\u0000" + + "\u008d\u008e\u0001\u0000\u0000\u0000\u008e\u0090\u0005\f\u0000\u0000\u008f" + + "\u0091\u0003\"\u0011\u0000\u0090\u008f\u0001\u0000\u0000\u0000\u0090\u0091" + + "\u0001\u0000\u0000\u0000\u0091\u0092\u0001\u0000\u0000\u0000\u0092\u0094" + + "\u0005\f\u0000\u0000\u0093\u0095\u0003\u0014\n\u0000\u0094\u0093\u0001" + + "\u0000\u0000\u0000\u0094\u0095\u0001\u0000\u0000\u0000\u0095\u0096\u0001" + + "\u0000\u0000\u0000\u0096\u0099\u0005\b\u0000\u0000\u0097\u009a\u0003\f" + + "\u0006\u0000\u0098\u009a\u0003\u0010\b\u0000\u0099\u0097\u0001\u0000\u0000" + + "\u0000\u0099\u0098\u0001\u0000\u0000\u0000\u009a\u00b4\u0001\u0000\u0000" + + "\u0000\u009b\u009c\u0005\u0012\u0000\u0000\u009c\u009d\u0005\u0007\u0000" + + "\u0000\u009d\u009e\u0003\u0018\f\u0000\u009e\u009f\u0005S\u0000\u0000" + + "\u009f\u00a0\u00054\u0000\u0000\u00a0\u00a1\u0003\"\u0011\u0000\u00a1" + + "\u00a2\u0005\b\u0000\u0000\u00a2\u00a3\u0003\f\u0006\u0000\u00a3\u00b4" + + "\u0001\u0000\u0000\u0000\u00a4\u00a5\u0005\u0012\u0000\u0000\u00a5\u00a6" + + "\u0005\u0007\u0000\u0000\u00a6\u00a7\u0005S\u0000\u0000\u00a7\u00a8\u0005" + + "\u000e\u0000\u0000\u00a8\u00a9\u0003\"\u0011\u0000\u00a9\u00aa\u0005\b" + + "\u0000\u0000\u00aa\u00ab\u0003\f\u0006\u0000\u00ab\u00b4\u0001\u0000\u0000" + + "\u0000\u00ac\u00ad\u0005\u0017\u0000\u0000\u00ad\u00af\u0003\u000e\u0007" + + "\u0000\u00ae\u00b0\u0003\u001e\u000f\u0000\u00af\u00ae\u0001\u0000\u0000" + + "\u0000\u00b0\u00b1\u0001\u0000\u0000\u0000\u00b1\u00af\u0001\u0000\u0000" + + "\u0000\u00b1\u00b2\u0001\u0000\u0000\u0000\u00b2\u00b4\u0001\u0000\u0000" + + "\u0000\u00b3w\u0001\u0000\u0000\u0000\u00b3\u0081\u0001\u0000\u0000\u0000" + + "\u00b3\u0089\u0001\u0000\u0000\u0000\u00b3\u009b\u0001\u0000\u0000\u0000" + + "\u00b3\u00a4\u0001\u0000\u0000\u0000\u00b3\u00ac\u0001\u0000\u0000\u0000" + + "\u00b4\t\u0001\u0000\u0000\u0000\u00b5\u00b6\u0005\u0011\u0000\u0000\u00b6" + + "\u00b7\u0003\u000e\u0007\u0000\u00b7\u00b8\u0005\u0010\u0000\u0000\u00b8" + + "\u00b9\u0005\u0007\u0000\u0000\u00b9\u00ba\u0003\"\u0011\u0000\u00ba\u00bb" + + "\u0005\b\u0000\u0000\u00bb\u00c7\u0001\u0000\u0000\u0000\u00bc\u00c7\u0003" + + "\u0016\u000b\u0000\u00bd\u00c7\u0005\u0013\u0000\u0000\u00be\u00c7\u0005" + + "\u0014\u0000\u0000\u00bf\u00c1\u0005\u0015\u0000\u0000\u00c0\u00c2\u0003" + + "\"\u0011\u0000\u00c1\u00c0\u0001\u0000\u0000\u0000\u00c1\u00c2\u0001\u0000" + + "\u0000\u0000\u00c2\u00c7\u0001\u0000\u0000\u0000\u00c3\u00c4\u0005\u0019" + + "\u0000\u0000\u00c4\u00c7\u0003\"\u0011\u0000\u00c5\u00c7\u0003\"\u0011" + + "\u0000\u00c6\u00b5\u0001\u0000\u0000\u0000\u00c6\u00bc\u0001\u0000\u0000" + + "\u0000\u00c6\u00bd\u0001\u0000\u0000\u0000\u00c6\u00be\u0001\u0000\u0000" + + "\u0000\u00c6\u00bf\u0001\u0000\u0000\u0000\u00c6\u00c3\u0001\u0000\u0000" + + "\u0000\u00c6\u00c5\u0001\u0000\u0000\u0000\u00c7\u000b\u0001\u0000\u0000" + + "\u0000\u00c8\u00cb\u0003\u000e\u0007\u0000\u00c9\u00cb\u0003\u0006\u0003" + + "\u0000\u00ca\u00c8\u0001\u0000\u0000\u0000\u00ca\u00c9\u0001\u0000\u0000" + + "\u0000\u00cb\r\u0001\u0000\u0000\u0000\u00cc\u00d0\u0005\u0003\u0000\u0000" + + "\u00cd\u00cf\u0003\u0006\u0003\u0000\u00ce\u00cd\u0001\u0000\u0000\u0000" + + "\u00cf\u00d2\u0001\u0000\u0000\u0000\u00d0\u00ce\u0001\u0000\u0000\u0000" + + "\u00d0\u00d1\u0001\u0000\u0000\u0000\u00d1\u00d4\u0001\u0000\u0000\u0000" + + "\u00d2\u00d0\u0001\u0000\u0000\u0000\u00d3\u00d5\u0003\n\u0005\u0000\u00d4" + + "\u00d3\u0001\u0000\u0000\u0000\u00d4\u00d5\u0001\u0000\u0000\u0000\u00d5" + + "\u00d6\u0001\u0000\u0000\u0000\u00d6\u00d7\u0005\u0004\u0000\u0000\u00d7" + + "\u000f\u0001\u0000\u0000\u0000\u00d8\u00d9\u0005\f\u0000\u0000\u00d9\u0011" + + "\u0001\u0000\u0000\u0000\u00da\u00dd\u0003\u0016\u000b\u0000\u00db\u00dd" + + "\u0003\"\u0011\u0000\u00dc\u00da\u0001\u0000\u0000\u0000\u00dc\u00db\u0001" + + "\u0000\u0000\u0000\u00dd\u0013\u0001\u0000\u0000\u0000\u00de\u00df\u0003" + + "\"\u0011\u0000\u00df\u0015\u0001\u0000\u0000\u0000\u00e0\u00e1\u0003\u0018" + + "\f\u0000\u00e1\u00e6\u0003\u001c\u000e\u0000\u00e2\u00e3\u0005\u000b\u0000" + + "\u0000\u00e3\u00e5\u0003\u001c\u000e\u0000\u00e4\u00e2\u0001\u0000\u0000" + + "\u0000\u00e5\u00e8\u0001\u0000\u0000\u0000\u00e6\u00e4\u0001\u0000\u0000" + + "\u0000\u00e6\u00e7\u0001\u0000\u0000\u0000\u00e7\u0017\u0001\u0000\u0000" + + "\u0000\u00e8\u00e6\u0001\u0000\u0000\u0000\u00e9\u00ee\u0003\u001a\r\u0000" + + "\u00ea\u00eb\u0005\u0005\u0000\u0000\u00eb\u00ed\u0005\u0006\u0000\u0000" + + "\u00ec\u00ea\u0001\u0000\u0000\u0000\u00ed\u00f0\u0001\u0000\u0000\u0000" + + "\u00ee\u00ec\u0001\u0000\u0000\u0000\u00ee\u00ef\u0001\u0000\u0000\u0000" + + "\u00ef\u0019\u0001\u0000\u0000\u0000\u00f0\u00ee\u0001\u0000\u0000\u0000" + + "\u00f1\u00fc\u0005R\u0000\u0000\u00f2\u00fc\u0005Q\u0000\u0000\u00f3\u00f8" + + "\u0005S\u0000\u0000\u00f4\u00f5\u0005\t\u0000\u0000\u00f5\u00f7\u0005" + + "U\u0000\u0000\u00f6\u00f4\u0001\u0000\u0000\u0000\u00f7\u00fa\u0001\u0000" + + "\u0000\u0000\u00f8\u00f6\u0001\u0000\u0000\u0000\u00f8\u00f9\u0001\u0000" + + "\u0000\u0000\u00f9\u00fc\u0001\u0000\u0000\u0000\u00fa\u00f8\u0001\u0000" + + "\u0000\u0000\u00fb\u00f1\u0001\u0000\u0000\u0000\u00fb\u00f2\u0001\u0000" + + "\u0000\u0000\u00fb\u00f3\u0001\u0000\u0000\u0000\u00fc\u001b\u0001\u0000" + + "\u0000\u0000\u00fd\u0100\u0005S\u0000\u0000\u00fe\u00ff\u0005<\u0000\u0000" + + "\u00ff\u0101\u0003\"\u0011\u0000\u0100\u00fe\u0001\u0000\u0000\u0000\u0100" + + "\u0101\u0001\u0000\u0000\u0000\u0101\u001d\u0001\u0000\u0000\u0000\u0102" + + "\u0103\u0005\u0018\u0000\u0000\u0103\u0104\u0005\u0007\u0000\u0000\u0104" + + "\u0105\u0003\u001a\r\u0000\u0105\u0106\u0005S\u0000\u0000\u0106\u0107" + + "\u0005\b\u0000\u0000\u0107\u0108\u0003\u000e\u0007\u0000\u0108\u001f\u0001" + + "\u0000\u0000\u0000\u0109\u010a\u0006\u0010\uffff\uffff\u0000\u010a\u010b" + + "\u0003$\u0012\u0000\u010b\u0135\u0001\u0000\u0000\u0000\u010c\u010d\n" + + "\r\u0000\u0000\u010d\u010e\u0007\u0001\u0000\u0000\u010e\u0134\u0003 " + + "\u0010\u000e\u010f\u0110\n\f\u0000\u0000\u0110\u0111\u0007\u0002\u0000" + + "\u0000\u0111\u0134\u0003 \u0010\r\u0112\u0113\n\u000b\u0000\u0000\u0113" + + "\u0114\u0007\u0003\u0000\u0000\u0114\u0134\u0003 \u0010\f\u0115\u0116" + + "\n\n\u0000\u0000\u0116\u0117\u0007\u0004\u0000\u0000\u0117\u0134\u0003" + + " \u0010\u000b\u0118\u0119\n\t\u0000\u0000\u0119\u011a\u0007\u0005\u0000" + + "\u0000\u011a\u0134\u0003 \u0010\n\u011b\u011c\n\u0007\u0000\u0000\u011c" + + "\u011d\u0007\u0006\u0000\u0000\u011d\u0134\u0003 \u0010\b\u011e\u011f" + + "\n\u0006\u0000\u0000\u011f\u0120\u0005.\u0000\u0000\u0120\u0134\u0003" + + " \u0010\u0007\u0121\u0122\n\u0005\u0000\u0000\u0122\u0123\u0005/\u0000" + + "\u0000\u0123\u0134\u0003 \u0010\u0006\u0124\u0125\n\u0004\u0000\u0000" + + "\u0125\u0126\u00050\u0000\u0000\u0126\u0134\u0003 \u0010\u0005\u0127\u0128" + + "\n\u0003\u0000\u0000\u0128\u0129\u00051\u0000\u0000\u0129\u0134\u0003" + + " \u0010\u0004\u012a\u012b\n\u0002\u0000\u0000\u012b\u012c\u00052\u0000" + + "\u0000\u012c\u0134\u0003 \u0010\u0003\u012d\u012e\n\u0001\u0000\u0000" + + "\u012e\u012f\u00055\u0000\u0000\u012f\u0134\u0003 \u0010\u0001\u0130\u0131" + + "\n\b\u0000\u0000\u0131\u0132\u0005\u001b\u0000\u0000\u0132\u0134\u0003" + + "\u0018\f\u0000\u0133\u010c\u0001\u0000\u0000\u0000\u0133\u010f\u0001\u0000" + + "\u0000\u0000\u0133\u0112\u0001\u0000\u0000\u0000\u0133\u0115\u0001\u0000" + + "\u0000\u0000\u0133\u0118\u0001\u0000\u0000\u0000\u0133\u011b\u0001\u0000" + + "\u0000\u0000\u0133\u011e\u0001\u0000\u0000\u0000\u0133\u0121\u0001\u0000" + + "\u0000\u0000\u0133\u0124\u0001\u0000\u0000\u0000\u0133\u0127\u0001\u0000" + + "\u0000\u0000\u0133\u012a\u0001\u0000\u0000\u0000\u0133\u012d\u0001\u0000" + + "\u0000\u0000\u0133\u0130\u0001\u0000\u0000\u0000\u0134\u0137\u0001\u0000" + + "\u0000\u0000\u0135\u0133\u0001\u0000\u0000\u0000\u0135\u0136\u0001\u0000" + + "\u0000\u0000\u0136!\u0001\u0000\u0000\u0000\u0137\u0135\u0001\u0000\u0000" + + "\u0000\u0138\u0144\u0003 \u0010\u0000\u0139\u013a\u0003 \u0010\u0000\u013a" + + "\u013b\u00053\u0000\u0000\u013b\u013c\u0003\"\u0011\u0000\u013c\u013d" + + "\u00054\u0000\u0000\u013d\u013e\u0003\"\u0011\u0000\u013e\u0144\u0001" + + "\u0000\u0000\u0000\u013f\u0140\u0003 \u0010\u0000\u0140\u0141\u0007\u0007" + + "\u0000\u0000\u0141\u0142\u0003\"\u0011\u0000\u0142\u0144\u0001\u0000\u0000" + + "\u0000\u0143\u0138\u0001\u0000\u0000\u0000\u0143\u0139\u0001\u0000\u0000" + + "\u0000\u0143\u013f\u0001\u0000\u0000\u0000\u0144#\u0001\u0000\u0000\u0000" + + "\u0145\u0146\u0007\b\u0000\u0000\u0146\u014b\u0003.\u0017\u0000\u0147" + + "\u0148\u0007\u0002\u0000\u0000\u0148\u014b\u0003$\u0012\u0000\u0149\u014b" + + "\u0003&\u0013\u0000\u014a\u0145\u0001\u0000\u0000\u0000\u014a\u0147\u0001" + + "\u0000\u0000\u0000\u014a\u0149\u0001\u0000\u0000\u0000\u014b%\u0001\u0000" + + "\u0000\u0000\u014c\u0154\u0003.\u0017\u0000\u014d\u014e\u0003.\u0017\u0000" + + "\u014e\u014f\u0007\b\u0000\u0000\u014f\u0154\u0001\u0000\u0000\u0000\u0150" + + "\u0151\u0007\t\u0000\u0000\u0151\u0154\u0003$\u0012\u0000\u0152\u0154" + + "\u0003(\u0014\u0000\u0153\u014c\u0001\u0000\u0000\u0000\u0153\u014d\u0001" + + "\u0000\u0000\u0000\u0153\u0150\u0001\u0000\u0000\u0000\u0153\u0152\u0001" + + "\u0000\u0000\u0000\u0154\'\u0001\u0000\u0000\u0000\u0155\u0156\u0005\u0007" + + "\u0000\u0000\u0156\u0157\u0003*\u0015\u0000\u0157\u0158\u0005\b\u0000" + + "\u0000\u0158\u0159\u0003$\u0012\u0000\u0159\u0160\u0001\u0000\u0000\u0000" + + "\u015a\u015b\u0005\u0007\u0000\u0000\u015b\u015c\u0003,\u0016\u0000\u015c" + + "\u015d\u0005\b\u0000\u0000\u015d\u015e\u0003&\u0013\u0000\u015e\u0160" + + "\u0001\u0000\u0000\u0000\u015f\u0155\u0001\u0000\u0000\u0000\u015f\u015a" + + "\u0001\u0000\u0000\u0000\u0160)\u0001\u0000\u0000\u0000\u0161\u0162\u0007" + + "\n\u0000\u0000\u0162+\u0001\u0000\u0000\u0000\u0163\u0166\u0005R\u0000" + + "\u0000\u0164\u0165\u0005\u0005\u0000\u0000\u0165\u0167\u0005\u0006\u0000" + + "\u0000\u0166\u0164\u0001\u0000\u0000\u0000\u0167\u0168\u0001\u0000\u0000" + + "\u0000\u0168\u0166\u0001\u0000\u0000\u0000\u0168\u0169\u0001\u0000\u0000" + + "\u0000\u0169\u0181\u0001\u0000\u0000\u0000\u016a\u016d\u0005Q\u0000\u0000" + + "\u016b\u016c\u0005\u0005\u0000\u0000\u016c\u016e\u0005\u0006\u0000\u0000" + + "\u016d\u016b\u0001\u0000\u0000\u0000\u016e\u016f\u0001\u0000\u0000\u0000" + + "\u016f\u016d\u0001\u0000\u0000\u0000\u016f\u0170\u0001\u0000\u0000\u0000" + + "\u0170\u0181\u0001\u0000\u0000\u0000\u0171\u0176\u0005S\u0000\u0000\u0172" + + "\u0173\u0005\t\u0000\u0000\u0173\u0175\u0005U\u0000\u0000\u0174\u0172" + + "\u0001\u0000\u0000\u0000\u0175\u0178\u0001\u0000\u0000\u0000\u0176\u0174" + + "\u0001\u0000\u0000\u0000\u0176\u0177\u0001\u0000\u0000\u0000\u0177\u017d" + + "\u0001\u0000\u0000\u0000\u0178\u0176\u0001\u0000\u0000\u0000\u0179\u017a" + + "\u0005\u0005\u0000\u0000\u017a\u017c\u0005\u0006\u0000\u0000\u017b\u0179" + + "\u0001\u0000\u0000\u0000\u017c\u017f\u0001\u0000\u0000\u0000\u017d\u017b" + + "\u0001\u0000\u0000\u0000\u017d\u017e\u0001\u0000\u0000\u0000\u017e\u0181" + + "\u0001\u0000\u0000\u0000\u017f\u017d\u0001\u0000\u0000\u0000\u0180\u0163" + + "\u0001\u0000\u0000\u0000\u0180\u016a\u0001\u0000\u0000\u0000\u0180\u0171" + + "\u0001\u0000\u0000\u0000\u0181-\u0001\u0000\u0000\u0000\u0182\u0186\u0003" + + "0\u0018\u0000\u0183\u0185\u00032\u0019\u0000\u0184\u0183\u0001\u0000\u0000" + + "\u0000\u0185\u0188\u0001\u0000\u0000\u0000\u0186\u0184\u0001\u0000\u0000" + + "\u0000\u0186\u0187\u0001\u0000\u0000\u0000\u0187\u018b\u0001\u0000\u0000" + + "\u0000\u0188\u0186\u0001\u0000\u0000\u0000\u0189\u018b\u0003<\u001e\u0000" + + "\u018a\u0182\u0001\u0000\u0000\u0000\u018a\u0189\u0001\u0000\u0000\u0000" + + "\u018b/\u0001\u0000\u0000\u0000\u018c\u018d\u0005\u0007\u0000\u0000\u018d" + + "\u018e\u0003\"\u0011\u0000\u018e\u018f\u0005\b\u0000\u0000\u018f\u01a0" + + "\u0001\u0000\u0000\u0000\u0190\u01a0\u0007\u000b\u0000\u0000\u0191\u01a0" + + "\u0005N\u0000\u0000\u0192\u01a0\u0005O\u0000\u0000\u0193\u01a0\u0005P" + + "\u0000\u0000\u0194\u01a0\u0005L\u0000\u0000\u0195\u01a0\u0005M\u0000\u0000" + + "\u0196\u01a0\u0003>\u001f\u0000\u0197\u01a0\u0003@ \u0000\u0198\u01a0" + + "\u0005S\u0000\u0000\u0199\u019a\u0005S\u0000\u0000\u019a\u01a0\u0003D" + + "\"\u0000\u019b\u019c\u0005\u0016\u0000\u0000\u019c\u019d\u0003\u001a\r" + + "\u0000\u019d\u019e\u0003D\"\u0000\u019e\u01a0\u0001\u0000\u0000\u0000" + + "\u019f\u018c\u0001\u0000\u0000\u0000\u019f\u0190\u0001\u0000\u0000\u0000" + + "\u019f\u0191\u0001\u0000\u0000\u0000\u019f\u0192\u0001\u0000\u0000\u0000" + + "\u019f\u0193\u0001\u0000\u0000\u0000\u019f\u0194\u0001\u0000\u0000\u0000" + + "\u019f\u0195\u0001\u0000\u0000\u0000\u019f\u0196\u0001\u0000\u0000\u0000" + + "\u019f\u0197\u0001\u0000\u0000\u0000\u019f\u0198\u0001\u0000\u0000\u0000" + + "\u019f\u0199\u0001\u0000\u0000\u0000\u019f\u019b\u0001\u0000\u0000\u0000" + + "\u01a01\u0001\u0000\u0000\u0000\u01a1\u01a5\u00036\u001b\u0000\u01a2\u01a5" + + "\u00038\u001c\u0000\u01a3\u01a5\u0003:\u001d\u0000\u01a4\u01a1\u0001\u0000" + + "\u0000\u0000\u01a4\u01a2\u0001\u0000\u0000\u0000\u01a4\u01a3\u0001\u0000" + + "\u0000\u0000\u01a53\u0001\u0000\u0000\u0000\u01a6\u01a9\u00036\u001b\u0000" + + "\u01a7\u01a9\u00038\u001c\u0000\u01a8\u01a6\u0001\u0000\u0000\u0000\u01a8" + + "\u01a7\u0001\u0000\u0000\u0000\u01a95\u0001\u0000\u0000\u0000\u01aa\u01ab" + + "\u0007\f\u0000\u0000\u01ab\u01ac\u0005U\u0000\u0000\u01ac\u01ad\u0003" + + "D\"\u0000\u01ad7\u0001\u0000\u0000\u0000\u01ae\u01af\u0007\f\u0000\u0000" + + "\u01af\u01b0\u0007\r\u0000\u0000\u01b09\u0001\u0000\u0000\u0000\u01b1" + + "\u01b2\u0005\u0005\u0000\u0000\u01b2\u01b3\u0003\"\u0011\u0000\u01b3\u01b4" + + "\u0005\u0006\u0000\u0000\u01b4;\u0001\u0000\u0000\u0000\u01b5\u01b6\u0005" + + "\u0016\u0000\u0000\u01b6\u01bb\u0003\u001a\r\u0000\u01b7\u01b8\u0005\u0005" + + "\u0000\u0000\u01b8\u01b9\u0003\"\u0011\u0000\u01b9\u01ba\u0005\u0006\u0000" + + "\u0000\u01ba\u01bc\u0001\u0000\u0000\u0000\u01bb\u01b7\u0001\u0000\u0000" + + "\u0000\u01bc\u01bd\u0001\u0000\u0000\u0000\u01bd\u01bb\u0001\u0000\u0000" + + "\u0000\u01bd\u01be\u0001\u0000\u0000\u0000\u01be\u01c6\u0001\u0000\u0000" + + "\u0000\u01bf\u01c3\u00034\u001a\u0000\u01c0\u01c2\u00032\u0019\u0000\u01c1" + + "\u01c0\u0001\u0000\u0000\u0000\u01c2\u01c5\u0001\u0000\u0000\u0000\u01c3" + + "\u01c1\u0001\u0000\u0000\u0000\u01c3\u01c4\u0001\u0000\u0000\u0000\u01c4" + + "\u01c7\u0001\u0000\u0000\u0000\u01c5\u01c3\u0001\u0000\u0000\u0000\u01c6" + + "\u01bf\u0001\u0000\u0000\u0000\u01c6\u01c7\u0001\u0000\u0000\u0000\u01c7" + + "\u01df\u0001\u0000\u0000\u0000\u01c8\u01c9\u0005\u0016\u0000\u0000\u01c9" + + "\u01ca\u0003\u001a\r\u0000\u01ca\u01cb\u0005\u0005\u0000\u0000\u01cb\u01cc" + + "\u0005\u0006\u0000\u0000\u01cc\u01d5\u0005\u0003\u0000\u0000\u01cd\u01d2" + + "\u0003\"\u0011\u0000\u01ce\u01cf\u0005\u000b\u0000\u0000\u01cf\u01d1\u0003" + + "\"\u0011\u0000\u01d0\u01ce\u0001\u0000\u0000\u0000\u01d1\u01d4\u0001\u0000" + + "\u0000\u0000\u01d2\u01d0\u0001\u0000\u0000\u0000\u01d2\u01d3\u0001\u0000" + + "\u0000\u0000\u01d3\u01d6\u0001\u0000\u0000\u0000\u01d4\u01d2\u0001\u0000" + + "\u0000\u0000\u01d5\u01cd\u0001\u0000\u0000\u0000\u01d5\u01d6\u0001\u0000" + + "\u0000\u0000\u01d6\u01d7\u0001\u0000\u0000\u0000\u01d7\u01db\u0005\u0004" + + "\u0000\u0000\u01d8\u01da\u00032\u0019\u0000\u01d9\u01d8\u0001\u0000\u0000" + + "\u0000\u01da\u01dd\u0001\u0000\u0000\u0000\u01db\u01d9\u0001\u0000\u0000" + + "\u0000\u01db\u01dc\u0001\u0000\u0000\u0000\u01dc\u01df\u0001\u0000\u0000" + + "\u0000\u01dd\u01db\u0001\u0000\u0000\u0000\u01de\u01b5\u0001\u0000\u0000" + + "\u0000\u01de\u01c8\u0001\u0000\u0000\u0000\u01df=\u0001\u0000\u0000\u0000" + + "\u01e0\u01e1\u0005\u0005\u0000\u0000\u01e1\u01e6\u0003\"\u0011\u0000\u01e2" + + "\u01e3\u0005\u000b\u0000\u0000\u01e3\u01e5\u0003\"\u0011\u0000\u01e4\u01e2" + + "\u0001\u0000\u0000\u0000\u01e5\u01e8\u0001\u0000\u0000\u0000\u01e6\u01e4" + + "\u0001\u0000\u0000\u0000\u01e6\u01e7\u0001\u0000\u0000\u0000\u01e7\u01e9" + + "\u0001\u0000\u0000\u0000\u01e8\u01e6\u0001\u0000\u0000\u0000\u01e9\u01ea" + + "\u0005\u0006\u0000\u0000\u01ea\u01ee\u0001\u0000\u0000\u0000\u01eb\u01ec" + + "\u0005\u0005\u0000\u0000\u01ec\u01ee\u0005\u0006\u0000\u0000\u01ed\u01e0" + + "\u0001\u0000\u0000\u0000\u01ed\u01eb\u0001\u0000\u0000\u0000\u01ee?\u0001" + + "\u0000\u0000\u0000\u01ef\u01f0\u0005\u0005\u0000\u0000\u01f0\u01f5\u0003" + + "B!\u0000\u01f1\u01f2\u0005\u000b\u0000\u0000\u01f2\u01f4\u0003B!\u0000" + + "\u01f3\u01f1\u0001\u0000\u0000\u0000\u01f4\u01f7\u0001\u0000\u0000\u0000" + + "\u01f5\u01f3\u0001\u0000\u0000\u0000\u01f5\u01f6\u0001\u0000\u0000\u0000" + + "\u01f6\u01f8\u0001\u0000\u0000\u0000\u01f7\u01f5\u0001\u0000\u0000\u0000" + + "\u01f8\u01f9\u0005\u0006\u0000\u0000\u01f9\u01fe\u0001\u0000\u0000\u0000" + + "\u01fa\u01fb\u0005\u0005\u0000\u0000\u01fb\u01fc\u00054\u0000\u0000\u01fc" + + "\u01fe\u0005\u0006\u0000\u0000\u01fd\u01ef\u0001\u0000\u0000\u0000\u01fd" + + "\u01fa\u0001\u0000\u0000\u0000\u01feA\u0001\u0000\u0000\u0000\u01ff\u0200" + + "\u0003\"\u0011\u0000\u0200\u0201\u00054\u0000\u0000\u0201\u0202\u0003" + + "\"\u0011\u0000\u0202C\u0001\u0000\u0000\u0000\u0203\u020c\u0005\u0007" + + "\u0000\u0000\u0204\u0209\u0003F#\u0000\u0205\u0206\u0005\u000b\u0000\u0000" + + "\u0206\u0208\u0003F#\u0000\u0207\u0205\u0001\u0000\u0000\u0000\u0208\u020b" + + "\u0001\u0000\u0000\u0000\u0209\u0207\u0001\u0000\u0000\u0000\u0209\u020a" + + "\u0001\u0000\u0000\u0000\u020a\u020d\u0001\u0000\u0000\u0000\u020b\u0209" + + "\u0001\u0000\u0000\u0000\u020c\u0204\u0001\u0000\u0000\u0000\u020c\u020d" + + "\u0001\u0000\u0000\u0000\u020d\u020e\u0001\u0000\u0000\u0000\u020e\u020f" + + "\u0005\b\u0000\u0000\u020fE\u0001\u0000\u0000\u0000\u0210\u0214\u0003" + + "\"\u0011\u0000\u0211\u0214\u0003H$\u0000\u0212\u0214\u0003L&\u0000\u0213" + + "\u0210\u0001\u0000\u0000\u0000\u0213\u0211\u0001\u0000\u0000\u0000\u0213" + + "\u0212\u0001\u0000\u0000\u0000\u0214G\u0001\u0000\u0000\u0000\u0215\u0223" + + "\u0003J%\u0000\u0216\u021f\u0005\u0007\u0000\u0000\u0217\u021c\u0003J" + + "%\u0000\u0218\u0219\u0005\u000b\u0000\u0000\u0219\u021b\u0003J%\u0000" + + "\u021a\u0218\u0001\u0000\u0000\u0000\u021b\u021e\u0001\u0000\u0000\u0000" + + "\u021c\u021a\u0001\u0000\u0000\u0000\u021c\u021d\u0001\u0000\u0000\u0000" + + "\u021d\u0220\u0001\u0000\u0000\u0000\u021e\u021c\u0001\u0000\u0000\u0000" + + "\u021f\u0217\u0001\u0000\u0000\u0000\u021f\u0220\u0001\u0000\u0000\u0000" + + "\u0220\u0221\u0001\u0000\u0000\u0000\u0221\u0223\u0005\b\u0000\u0000\u0222" + + "\u0215\u0001\u0000\u0000\u0000\u0222\u0216\u0001\u0000\u0000\u0000\u0223" + + "\u0224\u0001\u0000\u0000\u0000\u0224\u0227\u00057\u0000\u0000\u0225\u0228" + + "\u0003\u000e\u0007\u0000\u0226\u0228\u0003\"\u0011\u0000\u0227\u0225\u0001" + + "\u0000\u0000\u0000\u0227\u0226\u0001\u0000\u0000\u0000\u0228I\u0001\u0000" + + "\u0000\u0000\u0229\u022b\u0003\u0018\f\u0000\u022a\u0229\u0001\u0000\u0000" + + "\u0000\u022a\u022b\u0001\u0000\u0000\u0000\u022b\u022c\u0001\u0000\u0000" + + "\u0000\u022c\u022d\u0005S\u0000\u0000\u022dK\u0001\u0000\u0000\u0000\u022e" + + "\u022f\u0003\u0018\f\u0000\u022f\u0230\u00056\u0000\u0000\u0230\u0231" + + "\u0005S\u0000\u0000\u0231\u023a\u0001\u0000\u0000\u0000\u0232\u0233\u0003" + + "\u0018\f\u0000\u0233\u0234\u00056\u0000\u0000\u0234\u0235\u0005\u0016" + + "\u0000\u0000\u0235\u023a\u0001\u0000\u0000\u0000\u0236\u0237\u0005\u001a" + + "\u0000\u0000\u0237\u0238\u00056\u0000\u0000\u0238\u023a\u0005S\u0000\u0000" + + "\u0239\u022e\u0001\u0000\u0000\u0000\u0239\u0232\u0001\u0000\u0000\u0000" + + "\u0239\u0236\u0001\u0000\u0000\u0000\u023aM\u0001\u0000\u0000\u0000 The return type of the visit operation. Use {@link Void} for * operations with no return type. */ +@SuppressWarnings("CheckReturnValue") class PainlessParserBaseVisitor extends AbstractParseTreeVisitor implements PainlessParserVisitor { /** * {@inheritDoc} diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/ParserErrorStrategy.java b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/ParserErrorStrategy.java index 7ddf9cd54fe83..6928b28a746f6 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/ParserErrorStrategy.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/ParserErrorStrategy.java @@ -38,6 +38,7 @@ import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Token; + import org.opensearch.painless.Location; /** diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/Walker.java b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/Walker.java index 719a69a9977e7..927c75d8ee9d2 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/Walker.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/antlr/Walker.java @@ -41,6 +41,7 @@ import org.antlr.v4.runtime.Recognizer; import org.antlr.v4.runtime.atn.PredictionMode; import org.antlr.v4.runtime.tree.TerminalNode; + import org.opensearch.painless.CompilerSettings; import org.opensearch.painless.Location; import org.opensearch.painless.Operation; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/api/Json.java b/modules/lang-painless/src/main/java/org/opensearch/painless/api/Json.java index 89f756ef8b76a..e3dd1b50736fa 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/api/Json.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/api/Json.java @@ -32,11 +32,11 @@ package org.opensearch.painless.api; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.io.OutputStream; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/api/LimitedCharSequence.java b/modules/lang-painless/src/main/java/org/opensearch/painless/api/LimitedCharSequence.java index 8084420295280..c8a28158ad8db 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/api/LimitedCharSequence.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/api/LimitedCharSequence.java @@ -32,8 +32,8 @@ package org.opensearch.painless.api; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.breaker.CircuitBreakingException; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.CircuitBreakingException; import org.opensearch.painless.CompilerSettings; import java.util.regex.Pattern; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessClass.java b/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessClass.java index efa2d51524557..fdf7df94252b6 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessClass.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessClass.java @@ -32,7 +32,7 @@ package org.opensearch.painless.lookup; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.util.CollectionUtils; import java.lang.invoke.MethodHandle; import java.util.Map; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookup.java b/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookup.java index 1249a9cffecb2..9a3b8bf9e2eee 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookup.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookup.java @@ -32,7 +32,7 @@ package org.opensearch.painless.lookup; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.util.CollectionUtils; import java.lang.invoke.MethodHandle; import java.util.Map; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookupBuilder.java b/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookupBuilder.java index ff3fbc640e990..54484e6685996 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookupBuilder.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/lookup/PainlessLookupBuilder.java @@ -2168,9 +2168,10 @@ private void generateBridgeMethod(PainlessClassBuilder painlessClassBuilder, Pai bridgeMethodWriter.loadArg(0); } - for (int typeParameterCount = 0; typeParameterCount < javaMethod.getParameterTypes().length; ++typeParameterCount) { + final Class[] typeParameters = javaMethod.getParameterTypes(); + for (int typeParameterCount = 0; typeParameterCount < typeParameters.length; ++typeParameterCount) { bridgeMethodWriter.loadArg(typeParameterCount + bridgeTypeParameterOffset); - Class typeParameter = javaMethod.getParameterTypes()[typeParameterCount]; + Class typeParameter = typeParameters[typeParameterCount]; if (typeParameter == Byte.class) bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_BYTE_IMPLICIT); else if (typeParameter == Short.class) bridgeMethodWriter.invokeStatic(DEF_UTIL_TYPE, DEF_TO_B_SHORT_IMPLICIT); diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/phase/DefaultConstantFoldingOptimizationPhase.java b/modules/lang-painless/src/main/java/org/opensearch/painless/phase/DefaultConstantFoldingOptimizationPhase.java index 8b55e70e8ea7c..06607b639e07c 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/phase/DefaultConstantFoldingOptimizationPhase.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/phase/DefaultConstantFoldingOptimizationPhase.java @@ -34,8 +34,8 @@ import org.opensearch.painless.AnalyzerCaster; import org.opensearch.painless.Operation; -import org.opensearch.painless.ir.BinaryMathNode; import org.opensearch.painless.ir.BinaryImplNode; +import org.opensearch.painless.ir.BinaryMathNode; import org.opensearch.painless.ir.BooleanNode; import org.opensearch.painless.ir.CastNode; import org.opensearch.painless.ir.ComparisonNode; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/phase/IRTreeBaseVisitor.java b/modules/lang-painless/src/main/java/org/opensearch/painless/phase/IRTreeBaseVisitor.java index 0b947decd7dad..deff3201a2fd5 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/phase/IRTreeBaseVisitor.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/phase/IRTreeBaseVisitor.java @@ -32,8 +32,8 @@ package org.opensearch.painless.phase; -import org.opensearch.painless.ir.BinaryMathNode; import org.opensearch.painless.ir.BinaryImplNode; +import org.opensearch.painless.ir.BinaryMathNode; import org.opensearch.painless.ir.BlockNode; import org.opensearch.painless.ir.BooleanNode; import org.opensearch.painless.ir.BreakNode; diff --git a/modules/lang-painless/src/main/java/org/opensearch/painless/phase/IRTreeVisitor.java b/modules/lang-painless/src/main/java/org/opensearch/painless/phase/IRTreeVisitor.java index babb9b14bcfb1..17bd1889a11bc 100644 --- a/modules/lang-painless/src/main/java/org/opensearch/painless/phase/IRTreeVisitor.java +++ b/modules/lang-painless/src/main/java/org/opensearch/painless/phase/IRTreeVisitor.java @@ -32,8 +32,8 @@ package org.opensearch.painless.phase; -import org.opensearch.painless.ir.BinaryMathNode; import org.opensearch.painless.ir.BinaryImplNode; +import org.opensearch.painless.ir.BinaryMathNode; import org.opensearch.painless.ir.BlockNode; import org.opensearch.painless.ir.BooleanNode; import org.opensearch.painless.ir.BreakNode; diff --git a/modules/lang-painless/src/main/resources/org/opensearch/painless/spi/org.opensearch.score.txt b/modules/lang-painless/src/main/resources/org/opensearch/painless/spi/org.opensearch.score.txt index cca7e07a95388..b9268421c7ef3 100644 --- a/modules/lang-painless/src/main/resources/org/opensearch/painless/spi/org.opensearch.score.txt +++ b/modules/lang-painless/src/main/resources/org/opensearch/painless/spi/org.opensearch.score.txt @@ -23,6 +23,9 @@ class org.opensearch.script.ScoreScript @no_import { } static_import { + int termFreq(org.opensearch.script.ScoreScript, String, String) bound_to org.opensearch.script.ScoreScriptUtils$TermFreq + long totalTermFreq(org.opensearch.script.ScoreScript, String, String) bound_to org.opensearch.script.ScoreScriptUtils$TotalTermFreq + long sumTotalTermFreq(org.opensearch.script.ScoreScript, String) bound_to org.opensearch.script.ScoreScriptUtils$SumTotalTermFreq double saturation(double, double) from_class org.opensearch.script.ScoreScriptUtils double sigmoid(double, double, double) from_class org.opensearch.script.ScoreScriptUtils double randomScore(org.opensearch.script.ScoreScript, int, String) bound_to org.opensearch.script.ScoreScriptUtils$RandomScoreField diff --git a/modules/lang-painless/src/main/resources/org/opensearch/painless/spi/org.opensearch.txt b/modules/lang-painless/src/main/resources/org/opensearch/painless/spi/org.opensearch.txt index debccf8ed4b9b..8009280857a63 100644 --- a/modules/lang-painless/src/main/resources/org/opensearch/painless/spi/org.opensearch.txt +++ b/modules/lang-painless/src/main/resources/org/opensearch/painless/spi/org.opensearch.txt @@ -74,6 +74,11 @@ class org.opensearch.index.fielddata.ScriptDocValues$Longs { long getValue() } +class org.opensearch.index.fielddata.ScriptDocValues$UnsignedLongs { + BigInteger get(int) + BigInteger getValue() +} + class org.opensearch.script.JodaCompatibleZonedDateTime { ##### ZonedDateTime methods int getDayOfMonth() diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/ArrayTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/ArrayTests.java index 7563ab87fd5e6..0b83a4c558ef6 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/ArrayTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/ArrayTests.java @@ -33,7 +33,6 @@ package org.opensearch.painless; import org.apache.lucene.util.Constants; -import org.opensearch.bootstrap.JavaVersion; import org.hamcrest.Matcher; import java.lang.invoke.MethodHandle; @@ -55,11 +54,7 @@ protected String valueCtorCall(String valueType, int size) { @Override protected Matcher outOfBoundsExceptionMessageMatcher(int index, int size) { - if (JavaVersion.current().compareTo(JavaVersion.parse("11")) < 0) { - return equalTo(Integer.toString(index)); - } else { - return equalTo("Index " + Integer.toString(index) + " out of bounds for length " + Integer.toString(size)); - } + return equalTo("Index " + Integer.toString(index) + " out of bounds for length " + Integer.toString(size)); } public void testArrayLengthHelper() throws Throwable { diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/AugmentationTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/AugmentationTests.java index 98b0cad9960f8..d1b4f90a0f309 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/AugmentationTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/AugmentationTests.java @@ -32,12 +32,12 @@ package org.opensearch.painless; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.opensearch.common.settings.Settings; import org.opensearch.painless.spi.Whitelist; import org.opensearch.painless.spi.WhitelistLoader; import org.opensearch.script.ScriptContext; +import org.junit.AfterClass; +import org.junit.BeforeClass; import java.util.ArrayList; import java.util.Arrays; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/BaseClassTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/BaseClassTests.java index 6892974b6ca7d..599e4047396be 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/BaseClassTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/BaseClassTests.java @@ -32,11 +32,11 @@ package org.opensearch.painless; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.opensearch.common.settings.Settings; import org.opensearch.painless.spi.Whitelist; import org.opensearch.script.ScriptContext; +import org.junit.AfterClass; +import org.junit.BeforeClass; import java.util.Collections; import java.util.HashMap; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/BasicStatementTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/BasicStatementTests.java index 403bd12147cfe..fddf069f85c0c 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/BasicStatementTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/BasicStatementTests.java @@ -32,11 +32,11 @@ package org.opensearch.painless; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.opensearch.common.settings.Settings; import org.opensearch.painless.spi.Whitelist; import org.opensearch.script.ScriptContext; +import org.junit.AfterClass; +import org.junit.BeforeClass; import java.util.ArrayList; import java.util.Collections; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/BindingsTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/BindingsTests.java index e5113d93677ab..895ff42b9a527 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/BindingsTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/BindingsTests.java @@ -32,13 +32,13 @@ package org.opensearch.painless; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.opensearch.common.settings.Settings; import org.opensearch.painless.spi.Whitelist; import org.opensearch.painless.spi.WhitelistInstanceBinding; import org.opensearch.painless.spi.WhitelistLoader; import org.opensearch.script.ScriptContext; +import org.junit.AfterClass; +import org.junit.BeforeClass; import java.util.ArrayList; import java.util.Collections; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/DebugTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/DebugTests.java index 285de018f1db2..abd6bd91b917e 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/DebugTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/DebugTests.java @@ -34,7 +34,7 @@ import org.opensearch.OpenSearchException; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.painless.lookup.PainlessLookup; import org.opensearch.painless.lookup.PainlessLookupBuilder; import org.opensearch.painless.spi.Whitelist; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/DefBootstrapTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/DefBootstrapTests.java index 9134d7a3c062b..d063a3fd5c0a6 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/DefBootstrapTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/DefBootstrapTests.java @@ -157,21 +157,13 @@ public void testMegamorphic() throws Throwable { map.put("a", "b"); assertEquals(2, (int) handle.invokeExact((Object) map)); - final IllegalArgumentException iae = expectThrows( - IllegalArgumentException.class, - () -> { Integer.toString((int) handle.invokeExact(new Object())); } - ); + final IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> { + Integer.toString((int) handle.invokeExact(new Object())); + }); assertEquals("dynamic method [java.lang.Object, size/0] not found", iae.getMessage()); - assertTrue( - "Does not fail inside ClassValue.computeValue()", - Arrays.stream(iae.getStackTrace()) - .anyMatch( - e -> { - return e.getMethodName().equals("computeValue") - && e.getClassName().startsWith("org.opensearch.painless.DefBootstrap$PIC$"); - } - ) - ); + assertTrue("Does not fail inside ClassValue.computeValue()", Arrays.stream(iae.getStackTrace()).anyMatch(e -> { + return e.getMethodName().equals("computeValue") && e.getClassName().startsWith("org.opensearch.painless.DefBootstrap$PIC$"); + })); } // test operators with null guards diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/FactoryTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/FactoryTests.java index 7ee0bd6fc096d..2c2c9b8a7c6d8 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/FactoryTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/FactoryTests.java @@ -32,14 +32,14 @@ package org.opensearch.painless; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.opensearch.common.settings.Settings; import org.opensearch.painless.spi.Whitelist; import org.opensearch.script.ScriptContext; import org.opensearch.script.ScriptException; import org.opensearch.script.ScriptFactory; import org.opensearch.script.TemplateScript; +import org.junit.AfterClass; +import org.junit.BeforeClass; import java.util.Arrays; import java.util.Collections; @@ -360,6 +360,14 @@ public void testVoidReturn() { assertEquals(iae.getMessage(), "not a statement: result not used from addition operation [+]"); } + public void testDefToVoidReturnThrowsException() { + ClassCastException exception = expectScriptThrows( + ClassCastException.class, + () -> getEngine().compile("def_return_in_void", "def x=1;return x;", VoidReturnTestScript.CONTEXT, Collections.emptyMap()) + ); + assertEquals(exception.getMessage(), "Cannot cast from [def] to [void]."); + } + public abstract static class FactoryTestConverterScript { private final Map params; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/FunctionRefTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/FunctionRefTests.java index 2ed1fa49d0bda..065e0fbd1f4e2 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/FunctionRefTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/FunctionRefTests.java @@ -268,18 +268,16 @@ public void testInterfaceStaticMethod() { } public void testMethodMissing() { - Exception e = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("List l = [2, 1]; l.sort(Integer::bogus); return l.get(0);"); } - ); + Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("List l = [2, 1]; l.sort(Integer::bogus); return l.get(0);"); + }); assertThat(e.getMessage(), containsString("function reference [Integer::bogus/2] matching [java.util.Comparator")); } public void testQualifiedMethodMissing() { - Exception e = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("List l = [2, 1]; l.sort(java.time.Instant::bogus); return l.get(0);", false); } - ); + Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("List l = [2, 1]; l.sort(java.time.Instant::bogus); return l.get(0);", false); + }); assertThat( e.getMessage(), containsString("function reference [java.time.Instant::bogus/2] matching [java.util.Comparator, compare/2") @@ -287,26 +285,23 @@ public void testQualifiedMethodMissing() { } public void testClassMissing() { - Exception e = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("List l = [2, 1]; l.sort(Bogus::bogus); return l.get(0);", false); } - ); + Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("List l = [2, 1]; l.sort(Bogus::bogus); return l.get(0);", false); + }); assertThat(e.getMessage(), endsWith("variable [Bogus] is not defined")); } public void testQualifiedClassMissing() { - Exception e = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("List l = [2, 1]; l.sort(org.joda.time.BogusDateTime::bogus); return l.get(0);", false); } - ); + Exception e = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("List l = [2, 1]; l.sort(org.joda.time.BogusDateTime::bogus); return l.get(0);", false); + }); assertEquals("variable [org.joda.time.BogusDateTime] is not defined", e.getMessage()); } public void testNotFunctionalInterface() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.add(Integer::bogus); return l.get(0);"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("List l = new ArrayList(); l.add(2); l.add(1); l.add(Integer::bogus); return l.get(0);"); + }); assertThat( expected.getMessage(), containsString("cannot convert function reference [Integer::bogus] to a non-functional interface [def]") @@ -314,17 +309,15 @@ public void testNotFunctionalInterface() { } public void testIncompatible() { - expectScriptThrows( - ClassCastException.class, - () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::startsWith); return l.get(0);"); } - ); + expectScriptThrows(ClassCastException.class, () -> { + exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::startsWith); return l.get(0);"); + }); } public void testWrongArity() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("Optional.empty().orElseGet(String::startsWith);"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("Optional.empty().orElseGet(String::startsWith);"); + }); assertThat( expected.getMessage(), containsString("function reference [String::startsWith/0] matching [java.util.function.Supplier") @@ -332,18 +325,16 @@ public void testWrongArity() { } public void testWrongArityNotEnough() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("List l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);"); + }); assertThat(expected.getMessage(), containsString("function reference [String::isEmpty/2] matching [java.util.Comparator")); } public void testWrongArityDef() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("def y = Optional.empty(); return y.orElseGet(String::startsWith);"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("def y = Optional.empty(); return y.orElseGet(String::startsWith);"); + }); assertThat( expected.getMessage(), containsString("function reference [String::startsWith/0] matching [java.util.function.Supplier") @@ -351,38 +342,33 @@ public void testWrongArityDef() { } public void testWrongArityNotEnoughDef() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("def l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("def l = new ArrayList(); l.add(2); l.add(1); l.sort(String::isEmpty);"); + }); assertThat(expected.getMessage(), containsString("function reference [String::isEmpty/2] matching [java.util.Comparator")); } public void testReturnVoid() { - Throwable expected = expectScriptThrows( - ClassCastException.class, - () -> { exec("StringBuilder b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(b::setLength).sum();"); } - ); + Throwable expected = expectScriptThrows(ClassCastException.class, () -> { + exec("StringBuilder b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(b::setLength).sum();"); + }); assertThat(expected.getMessage(), containsString("Cannot cast from [void] to [long].")); } public void testReturnVoidDef() { - Exception expected = expectScriptThrows( - LambdaConversionException.class, - () -> { exec("StringBuilder b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(b::setLength);"); } - ); + Exception expected = expectScriptThrows(LambdaConversionException.class, () -> { + exec("StringBuilder b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(b::setLength);"); + }); assertThat(expected.getMessage(), containsString("lambda expects return type [long], but found return type [void]")); - expected = expectScriptThrows( - LambdaConversionException.class, - () -> { exec("def b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(b::setLength);"); } - ); + expected = expectScriptThrows(LambdaConversionException.class, () -> { + exec("def b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(b::setLength);"); + }); assertThat(expected.getMessage(), containsString("lambda expects return type [long], but found return type [void]")); - expected = expectScriptThrows( - LambdaConversionException.class, - () -> { exec("def b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(b::setLength);"); } - ); + expected = expectScriptThrows(LambdaConversionException.class, () -> { + exec("def b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(b::setLength);"); + }); assertThat(expected.getMessage(), containsString("lambda expects return type [long], but found return type [void]")); } } diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/FunctionTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/FunctionTests.java index 77b6e8f0b3d5c..45efaa2c8a5a4 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/FunctionTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/FunctionTests.java @@ -82,10 +82,9 @@ public void testReturnsAreUnboxedIfNeeded() { } public void testDuplicates() { - Exception expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("void test(int x) {x = 2;} void test(def y) {y = 3;} test()"); } - ); + Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("void test(int x) {x = 2;} void test(def y) {y = 3;} test()"); + }); assertThat(expected.getMessage(), containsString("found duplicate function")); } @@ -108,23 +107,20 @@ public void testInfiniteLoop() { public void testReturnVoid() { assertEquals(null, exec("void test(StringBuilder b, int i) {b.setLength(i)} test(new StringBuilder(), 1)")); - Exception expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("int test(StringBuilder b, int i) {b.setLength(i)} test(new StringBuilder(), 1)"); } - ); + Exception expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("int test(StringBuilder b, int i) {b.setLength(i)} test(new StringBuilder(), 1)"); + }); assertEquals( "invalid function definition: " + "not all paths provide a return value for function [test] with [2] parameters", expected.getMessage() ); - expected = expectScriptThrows( - ClassCastException.class, - () -> { exec("int test(StringBuilder b, int i) {return b.setLength(i)} test(new StringBuilder(), 1)"); } - ); + expected = expectScriptThrows(ClassCastException.class, () -> { + exec("int test(StringBuilder b, int i) {return b.setLength(i)} test(new StringBuilder(), 1)"); + }); assertEquals("Cannot cast from [void] to [int].", expected.getMessage()); - expected = expectScriptThrows( - ClassCastException.class, - () -> { exec("def test(StringBuilder b, int i) {return b.setLength(i)} test(new StringBuilder(), 1)"); } - ); + expected = expectScriptThrows(ClassCastException.class, () -> { + exec("def test(StringBuilder b, int i) {return b.setLength(i)} test(new StringBuilder(), 1)"); + }); assertEquals("Cannot cast from [void] to [def].", expected.getMessage()); } } diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/GeneralCastTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/GeneralCastTests.java index 225dbd51819de..27930e2cb43d8 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/GeneralCastTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/GeneralCastTests.java @@ -293,14 +293,12 @@ public void testIllegalExplicitConversionsDef() { } public void testIllegalVoidCasts() { - expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("def map = ['a': 1,'b': 2,'c': 3]; map.c = Collections.sort(new ArrayList(map.keySet()));"); } - ); - expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("Map map = ['a': 1,'b': 2,'c': 3]; def x = new HashMap(); x.put(1, map.clear());"); } - ); + expectScriptThrows(IllegalArgumentException.class, () -> { + exec("def map = ['a': 1,'b': 2,'c': 3]; map.c = Collections.sort(new ArrayList(map.keySet()));"); + }); + expectScriptThrows(IllegalArgumentException.class, () -> { + exec("Map map = ['a': 1,'b': 2,'c': 3]; def x = new HashMap(); x.put(1, map.clear());"); + }); } public void testBoxedDefCalls() { diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/LambdaTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/LambdaTests.java index c1b19522021b8..8d0cbb4018801 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/LambdaTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/LambdaTests.java @@ -173,28 +173,19 @@ public void testTwoCaptures() { } public void testCapturesAreReadOnly() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { - exec( - "List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(x -> { l = null; return x + 1 }).sum();" - ); - } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(x -> { l = null; return x + 1 }).sum();"); + }); assertTrue(expected.getMessage().contains("is read-only")); } /** Lambda parameters shouldn't be able to mask a variable already in scope */ public void testNoParamMasking() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { - exec( - "int x = 0; List l = new ArrayList(); l.add(1); l.add(1); " - + "return l.stream().mapToInt(x -> { x += 1; return x }).sum();" - ); - } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec( + "int x = 0; List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(x -> { x += 1; return x }).sum();" + ); + }); assertTrue(expected.getMessage().contains("already defined")); } @@ -214,36 +205,30 @@ public void testNestedCaptureParams() { } public void testWrongArity() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - false, - () -> { exec("Optional.empty().orElseGet(x -> x);"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, false, () -> { + exec("Optional.empty().orElseGet(x -> x);"); + }); assertTrue(expected.getMessage().contains("Incorrect number of parameters")); } public void testWrongArityDef() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("def y = Optional.empty(); return y.orElseGet(x -> x);"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("def y = Optional.empty(); return y.orElseGet(x -> x);"); + }); assertTrue(expected.getMessage(), expected.getMessage().contains("due to an incorrect number of arguments")); } public void testWrongArityNotEnough() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - false, - () -> { exec("List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(() -> 5).sum();"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, false, () -> { + exec("List l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(() -> 5).sum();"); + }); assertTrue(expected.getMessage().contains("Incorrect number of parameters")); } public void testWrongArityNotEnoughDef() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("def l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(() -> 5).sum();"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("def l = new ArrayList(); l.add(1); l.add(1); " + "return l.stream().mapToInt(() -> 5).sum();"); + }); assertTrue(expected.getMessage(), expected.getMessage().contains("due to an incorrect number of arguments")); } @@ -308,19 +293,17 @@ public void testReservedCapture() { } public void testReturnVoid() { - Throwable expected = expectScriptThrows( - ClassCastException.class, - () -> { exec("StringBuilder b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(i -> b.setLength(i))"); } - ); + Throwable expected = expectScriptThrows(ClassCastException.class, () -> { + exec("StringBuilder b = new StringBuilder(); List l = [1, 2]; l.stream().mapToLong(i -> b.setLength(i))"); + }); assertThat(expected.getMessage(), containsString("Cannot cast from [void] to [long].")); } public void testReturnVoidDef() { // If we can catch the error at compile time we do - Exception expected = expectScriptThrows( - ClassCastException.class, - () -> { exec("StringBuilder b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(i -> b.setLength(i))"); } - ); + Exception expected = expectScriptThrows(ClassCastException.class, () -> { + exec("StringBuilder b = new StringBuilder(); def l = [1, 2]; l.stream().mapToLong(i -> b.setLength(i))"); + }); assertThat(expected.getMessage(), containsString("Cannot cast from [void] to [def].")); // Otherwise we convert the void into a null diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/OverloadTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/OverloadTests.java index 16f3c16a59548..02fa18ec07178 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/OverloadTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/OverloadTests.java @@ -38,20 +38,18 @@ public class OverloadTests extends ScriptTestCase { public void testMethod() { // assertEquals(2, exec("return 'abc123abc'.indexOf('c');")); // assertEquals(8, exec("return 'abc123abc'.indexOf('c', 3);")); - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("return 'abc123abc'.indexOf('c', 3, 'bogus');"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("return 'abc123abc'.indexOf('c', 3, 'bogus');"); + }); assertTrue(expected.getMessage().contains("[java.lang.String, indexOf/3]")); } public void testMethodDynamic() { assertEquals(2, exec("def x = 'abc123abc'; return x.indexOf('c');")); assertEquals(8, exec("def x = 'abc123abc'; return x.indexOf('c', 3);")); - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("def x = 'abc123abc'; return x.indexOf('c', 3, 'bogus');"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("def x = 'abc123abc'; return x.indexOf('c', 3, 'bogus');"); + }); assertTrue(expected.getMessage().contains("dynamic method [java.lang.String, indexOf/3] not found")); } diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/RegexLimit2Tests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/RegexLimit2Tests.java index 1d08fdac9c58c..b860c783a5c0d 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/RegexLimit2Tests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/RegexLimit2Tests.java @@ -32,9 +32,9 @@ package org.opensearch.painless; +import org.opensearch.common.settings.Settings; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.opensearch.common.settings.Settings; public class RegexLimit2Tests extends ScriptTestCase { // This regex has backtracking due to .*? diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/RegexLimitTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/RegexLimitTests.java index c3233bc0d924a..8a965e715f810 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/RegexLimitTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/RegexLimitTests.java @@ -32,10 +32,10 @@ package org.opensearch.painless; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.breaker.CircuitBreakingException; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.opensearch.common.breaker.CircuitBreakingException; -import org.opensearch.common.settings.Settings; import java.util.Collections; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/RegexTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/RegexTests.java index 8c1f545efcf7a..628e15b9bb544 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/RegexTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/RegexTests.java @@ -32,10 +32,10 @@ package org.opensearch.painless; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.opensearch.common.settings.Settings; import org.opensearch.script.ScriptException; +import org.junit.AfterClass; +import org.junit.BeforeClass; import java.nio.CharBuffer; import java.util.Arrays; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/ScriptTestCase.java b/modules/lang-painless/src/test/java/org/opensearch/painless/ScriptTestCase.java index a30aa97d33461..0bf7bf527bcd1 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/ScriptTestCase.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/ScriptTestCase.java @@ -32,8 +32,6 @@ package org.opensearch.painless; -import junit.framework.AssertionFailedError; - import org.opensearch.common.settings.Settings; import org.opensearch.painless.antlr.Walker; import org.opensearch.painless.spi.Whitelist; @@ -48,8 +46,10 @@ import java.util.List; import java.util.Map; -import static org.hamcrest.Matchers.hasSize; +import junit.framework.AssertionFailedError; + import static org.opensearch.painless.action.PainlessExecuteAction.PainlessTestScript; +import static org.hamcrest.Matchers.hasSize; /** * Base test case for scripting unit tests. diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/ScriptedMetricAggContextsTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/ScriptedMetricAggContextsTests.java index af28a3f850d0a..5a7e672da6bd6 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/ScriptedMetricAggContextsTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/ScriptedMetricAggContextsTests.java @@ -35,8 +35,6 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.memory.MemoryIndex; import org.apache.lucene.search.Scorable; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.opensearch.common.settings.Settings; import org.opensearch.painless.spi.Whitelist; import org.opensearch.script.ScriptContext; @@ -44,6 +42,8 @@ import org.opensearch.search.lookup.LeafSearchLookup; import org.opensearch.search.lookup.SearchLookup; import org.opensearch.search.lookup.SourceLookup; +import org.junit.AfterClass; +import org.junit.BeforeClass; import java.io.IOException; import java.util.ArrayList; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/SimilarityScriptTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/SimilarityScriptTests.java index 9b404853cf7b5..3c28183d05171 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/SimilarityScriptTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/SimilarityScriptTests.java @@ -49,14 +49,14 @@ import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.ByteBuffersDirectory; import org.apache.lucene.store.Directory; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.opensearch.common.settings.Settings; import org.opensearch.index.similarity.ScriptedSimilarity; import org.opensearch.painless.spi.Whitelist; import org.opensearch.script.ScriptContext; import org.opensearch.script.SimilarityScript; import org.opensearch.script.SimilarityWeightScript; +import org.junit.AfterClass; +import org.junit.BeforeClass; import java.io.IOException; import java.util.Collections; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/StringTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/StringTests.java index d344a9c2a31f1..b9586924d4fcd 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/StringTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/StringTests.java @@ -182,11 +182,9 @@ public void testStringAndCharacter() { assertEquals('c', exec("String s = \"c\"; (char)s")); assertEquals('c', exec("String s = 'c'; (char)s")); - ClassCastException expected = expectScriptThrows( - ClassCastException.class, - false, - () -> { assertEquals("cc", exec("return (String)(char)\"cc\"")); } - ); + ClassCastException expected = expectScriptThrows(ClassCastException.class, false, () -> { + assertEquals("cc", exec("return (String)(char)\"cc\"")); + }); assertTrue(expected.getMessage().contains("cannot cast java.lang.String with length not equal to one to char")); expected = expectScriptThrows(ClassCastException.class, false, () -> { assertEquals("cc", exec("return (String)(char)'cc'")); }); diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/WhenThingsGoWrongTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/WhenThingsGoWrongTests.java index fb8d2eccfa043..0d498e16154c8 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/WhenThingsGoWrongTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/WhenThingsGoWrongTests.java @@ -32,13 +32,14 @@ package org.opensearch.painless; -import junit.framework.AssertionFailedError; import org.apache.lucene.util.Constants; import org.opensearch.script.ScriptException; import java.lang.invoke.WrongMethodTypeException; import java.util.Collections; +import junit.framework.AssertionFailedError; + import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.instanceOf; @@ -57,10 +58,9 @@ public void testNullPointer() { public void testScriptStack() { for (String type : new String[] { "String", "def " }) { // trigger NPE at line 1 of the script - ScriptException exception = expectThrows( - ScriptException.class, - () -> { exec(type + " x = null; boolean y = x.isEmpty();\n" + "return y;"); } - ); + ScriptException exception = expectThrows(ScriptException.class, () -> { + exec(type + " x = null; boolean y = x.isEmpty();\n" + "return y;"); + }); // null deref at x.isEmpty(), the '.' is offset 30 assertScriptElementColumn(30, exception); assertScriptStack(exception, "y = x.isEmpty();\n", " ^---- HERE"); @@ -84,12 +84,9 @@ public void testScriptStack() { assertThat(exception.getCause(), instanceOf(NullPointerException.class)); // trigger NPE at line 4 in script (inside conditional) - exception = expectThrows( - ScriptException.class, - () -> { - exec(type + " x = null;\n" + "boolean y = false;\n" + "if (!y) {\n" + " y = x.isEmpty();\n" + "}\n" + "return y;"); - } - ); + exception = expectThrows(ScriptException.class, () -> { + exec(type + " x = null;\n" + "boolean y = false;\n" + "if (!y) {\n" + " y = x.isEmpty();\n" + "}\n" + "return y;"); + }); // null deref at x.isEmpty(), the '.' is offset 53 assertScriptElementColumn(53, exception); assertScriptStack(exception, "y = x.isEmpty();\n}\n", " ^---- HERE"); @@ -121,10 +118,9 @@ public void testInvalidShift() { } public void testBogusParameter() { - IllegalArgumentException expected = expectThrows( - IllegalArgumentException.class, - () -> { exec("return 5;", null, Collections.singletonMap("bogusParameterKey", "bogusParameterValue"), true); } - ); + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + exec("return 5;", null, Collections.singletonMap("bogusParameterKey", "bogusParameterValue"), true); + }); assertTrue(expected.getMessage().contains("Unrecognized compile-time parameter")); } @@ -178,10 +174,9 @@ public void testLoopLimits() { } public void testIllegalDynamicMethod() { - IllegalArgumentException expected = expectScriptThrows( - IllegalArgumentException.class, - () -> { exec("def x = 'test'; return x.getClass().toString()"); } - ); + IllegalArgumentException expected = expectScriptThrows(IllegalArgumentException.class, () -> { + exec("def x = 'test'; return x.getClass().toString()"); + }); assertTrue(expected.getMessage().contains("dynamic method [java.lang.String, getClass/0] not found")); } diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/WhitelistLoaderTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/WhitelistLoaderTests.java index e4e754a541414..b7ba3040894e3 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/WhitelistLoaderTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/WhitelistLoaderTests.java @@ -47,19 +47,15 @@ public class WhitelistLoaderTests extends ScriptTestCase { public void testUnknownAnnotations() { Map parsers = new HashMap<>(WhitelistAnnotationParser.BASE_ANNOTATION_PARSERS); - RuntimeException expected = expectThrows( - RuntimeException.class, - () -> { WhitelistLoader.loadFromResourceFiles(Whitelist.class, parsers, "org.opensearch.painless.annotation.unknown"); } - ); + RuntimeException expected = expectThrows(RuntimeException.class, () -> { + WhitelistLoader.loadFromResourceFiles(Whitelist.class, parsers, "org.opensearch.painless.annotation.unknown"); + }); assertEquals("invalid annotation: parser not found for [unknownAnnotation] [@unknownAnnotation]", expected.getCause().getMessage()); assertEquals(IllegalArgumentException.class, expected.getCause().getClass()); - expected = expectThrows( - RuntimeException.class, - () -> { - WhitelistLoader.loadFromResourceFiles(Whitelist.class, parsers, "org.opensearch.painless.annotation.unknown_with_options"); - } - ); + expected = expectThrows(RuntimeException.class, () -> { + WhitelistLoader.loadFromResourceFiles(Whitelist.class, parsers, "org.opensearch.painless.annotation.unknown_with_options"); + }); assertEquals( "invalid annotation: parser not found for [unknownAnootationWithMessage] [@unknownAnootationWithMessage[arg=\"arg value\"]]", expected.getCause().getMessage() diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/action/ContextInfoTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/action/ContextInfoTests.java index 9e3f8794b9cbc..b576a65db61df 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/action/ContextInfoTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/action/ContextInfoTests.java @@ -32,8 +32,8 @@ package org.opensearch.painless.action; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.AbstractSerializingTestCase; import java.util.ArrayList; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteApiTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteApiTests.java index 424d88cbc9e00..7840f7b010abc 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteApiTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteApiTests.java @@ -31,9 +31,9 @@ package org.opensearch.painless.action; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.painless.PainlessPlugin; @@ -92,13 +92,13 @@ public void testFilterExecutionContext() throws IOException { IndexService indexService = createIndex("index", Settings.EMPTY, "doc", "field", "type=long"); Request.ContextSetup contextSetup = new Request.ContextSetup("index", new BytesArray("{\"field\": 3}"), null); - contextSetup.setXContentType(XContentType.JSON); + contextSetup.setXContentType(MediaTypeRegistry.JSON); Request request = new Request(new Script("doc['field'].value >= 3"), "filter", contextSetup); Response response = innerShardOperation(request, scriptService, indexService); assertThat(response.getResult(), equalTo(true)); contextSetup = new Request.ContextSetup("index", new BytesArray("{\"field\": 3}"), null); - contextSetup.setXContentType(XContentType.JSON); + contextSetup.setXContentType(MediaTypeRegistry.JSON); request = new Request( new Script(ScriptType.INLINE, "painless", "doc['field'].value >= params.max", singletonMap("max", 3)), "filter", @@ -108,7 +108,7 @@ public void testFilterExecutionContext() throws IOException { assertThat(response.getResult(), equalTo(true)); contextSetup = new Request.ContextSetup("index", new BytesArray("{\"field\": 2}"), null); - contextSetup.setXContentType(XContentType.JSON); + contextSetup.setXContentType(MediaTypeRegistry.JSON); request = new Request( new Script(ScriptType.INLINE, "painless", "doc['field'].value >= params.max", singletonMap("max", 3)), "filter", @@ -127,7 +127,7 @@ public void testScoreExecutionContext() throws IOException { new BytesArray("{\"rank\": 4.0, \"text\": \"quick brown fox\"}"), new MatchQueryBuilder("text", "fox") ); - contextSetup.setXContentType(XContentType.JSON); + contextSetup.setXContentType(MediaTypeRegistry.JSON); Request request = new Request( new Script( ScriptType.INLINE, diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteRequestTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteRequestTests.java index 0c3bb95bdadc3..661c12a111eb7 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteRequestTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteRequestTests.java @@ -31,17 +31,17 @@ package org.opensearch.painless.action; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.painless.action.PainlessExecuteAction.Request.ContextSetup; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteResponseTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteResponseTests.java index db2da58894e70..9c9ba090685ec 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteResponseTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/action/PainlessExecuteResponseTests.java @@ -31,8 +31,8 @@ package org.opensearch.painless.action; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.AbstractSerializingTestCase; import java.io.IOException; diff --git a/modules/lang-painless/src/test/java/org/opensearch/painless/api/LimitedCharSequenceTests.java b/modules/lang-painless/src/test/java/org/opensearch/painless/api/LimitedCharSequenceTests.java index a7787f4bc3c29..4117eb331197f 100644 --- a/modules/lang-painless/src/test/java/org/opensearch/painless/api/LimitedCharSequenceTests.java +++ b/modules/lang-painless/src/test/java/org/opensearch/painless/api/LimitedCharSequenceTests.java @@ -32,7 +32,7 @@ package org.opensearch.painless.api; -import org.opensearch.common.breaker.CircuitBreakingException; +import org.opensearch.core.common.breaker.CircuitBreakingException; import org.opensearch.test.OpenSearchTestCase; import java.util.regex.Pattern; diff --git a/modules/lang-painless/src/yamlRestTest/java/org/opensearch/painless/LangPainlessClientYamlTestSuiteIT.java b/modules/lang-painless/src/yamlRestTest/java/org/opensearch/painless/LangPainlessClientYamlTestSuiteIT.java index 520c656723177..f83a8929d979d 100644 --- a/modules/lang-painless/src/yamlRestTest/java/org/opensearch/painless/LangPainlessClientYamlTestSuiteIT.java +++ b/modules/lang-painless/src/yamlRestTest/java/org/opensearch/painless/LangPainlessClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/100_terms_agg.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/100_terms_agg.yml index 000e1af694d7d..aa01647811c83 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/100_terms_agg.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/100_terms_agg.yml @@ -139,4 +139,3 @@ setup: - is_false: aggregations.placeholder.buckets.0.str_terms.buckets.1.key_as_string - match: { aggregations.placeholder.buckets.0.str_terms.buckets.1.doc_count: 1 } - match: { aggregations.placeholder.buckets.0.the_bucket_script.value: 2.0 } - diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/10_basic.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/10_basic.yml index e442b40ffb845..a2458269b7aa1 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/10_basic.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: lang-painless } } + - contains: { nodes.$cluster_manager.modules: { name: lang-painless } } diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/120_script_score_term_frequency.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/120_script_score_term_frequency.yml new file mode 100644 index 0000000000000..b3ff66251938d --- /dev/null +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/120_script_score_term_frequency.yml @@ -0,0 +1,95 @@ +--- +setup: + - skip: + version: " - 2.9.99" + reason: "termFreq functions for script_score was introduced in 2.10.0" + - do: + indices.create: + index: test + body: + settings: + number_of_shards: 1 + mappings: + properties: + f1: + type: keyword + f2: + type: text + - do: + bulk: + refresh: true + body: + - '{"index": {"_index": "test", "_id": "doc1"}}' + - '{"f1": "v0", "f2": "v1"}' + - '{"index": {"_index": "test", "_id": "doc2"}}' + - '{"f2": "v2"}' + +--- +"Script score function using the termFreq function": + - do: + search: + index: test + rest_total_hits_as_int: true + body: + query: + function_score: + query: + match_all: {} + script_score: + script: + source: "termFreq(params.field, params.term)" + params: + field: "f1" + term: "v0" + - match: { hits.total: 2 } + - match: { hits.hits.0._id: "doc1" } + - match: { hits.hits.1._id: "doc2" } + - match: { hits.hits.0._score: 1.0 } + - match: { hits.hits.1._score: 0.0 } + +--- +"Script score function using the totalTermFreq function": + - do: + search: + index: test + rest_total_hits_as_int: true + body: + query: + function_score: + query: + match_all: {} + script_score: + script: + source: "if (doc[params.field].size() == 0) return params.default_value; else { return totalTermFreq(params.field, params.term); }" + params: + default_value: 0.5 + field: "f1" + term: "v0" + - match: { hits.total: 2 } + - match: { hits.hits.0._id: "doc1" } + - match: { hits.hits.1._id: "doc2" } + - match: { hits.hits.0._score: 1.0 } + - match: { hits.hits.1._score: 0.5 } + +--- +"Script score function using the sumTotalTermFreq function": + - do: + search: + index: test + rest_total_hits_as_int: true + body: + query: + function_score: + query: + match_all: {} + script_score: + script: + source: "if (doc[params.field].size() == 0) return params.default_value; else { return sumTotalTermFreq(params.field); }" + params: + default_value: 0.5 + field: "f1" + - match: { hits.total: 2 } + - match: { hits.hits.0._id: "doc1" } + - match: { hits.hits.1._id: "doc2" } + - match: { hits.hits.0._score: 1.0 } + - match: { hits.hits.1._score: 0.5 } diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/71_context_api.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/71_context_api.yml index 0413661fc586c..478ca9ae8abf4 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/71_context_api.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/71_context_api.yml @@ -2,7 +2,7 @@ - do: scripts_painless_context: {} - match: { contexts.0: aggregation_selector} - - match: { contexts.22: update} + - match: { contexts.23: update} --- "Action to get all API values for score context": diff --git a/modules/mapper-extras/src/javaRestTest/java/org/opensearch/index/mapper/TokenCountFieldMapperIntegrationIT.java b/modules/mapper-extras/src/javaRestTest/java/org/opensearch/index/mapper/TokenCountFieldMapperIntegrationIT.java index e25344a4bb4e3..bd0795f07139b 100644 --- a/modules/mapper-extras/src/javaRestTest/java/org/opensearch/index/mapper/TokenCountFieldMapperIntegrationIT.java +++ b/modules/mapper-extras/src/javaRestTest/java/org/opensearch/index/mapper/TokenCountFieldMapperIntegrationIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.bulk.BulkResponse; diff --git a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/RankFeatureFieldMapper.java b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/RankFeatureFieldMapper.java index 31fef7301477a..42e7d9933dd61 100644 --- a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/RankFeatureFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/RankFeatureFieldMapper.java @@ -39,7 +39,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.opensearch.common.lucene.Lucene; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.lookup.SearchLookup; diff --git a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/RankFeaturesFieldMapper.java b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/RankFeaturesFieldMapper.java index 43853eb40f432..8c2fc705c4b52 100644 --- a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/RankFeaturesFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/RankFeaturesFieldMapper.java @@ -36,13 +36,12 @@ import org.apache.lucene.index.IndexOptions; import org.apache.lucene.search.Query; import org.opensearch.common.lucene.Lucene; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.lookup.SearchLookup; import java.io.IOException; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -55,8 +54,18 @@ public class RankFeaturesFieldMapper extends ParametrizedFieldMapper { public static final String CONTENT_TYPE = "rank_features"; + private static RankFeaturesFieldType ft(FieldMapper in) { + return ((RankFeaturesFieldMapper) in).fieldType(); + } + public static class Builder extends ParametrizedFieldMapper.Builder { + private final Parameter positiveScoreImpact = Parameter.boolParam( + "positive_score_impact", + false, + m -> ft(m).positiveScoreImpact, + true + ); private final Parameter> meta = Parameter.metaParam(); public Builder(String name) { @@ -66,16 +75,17 @@ public Builder(String name) { @Override protected List> getParameters() { - return Collections.singletonList(meta); + return List.of(meta, positiveScoreImpact); } @Override public RankFeaturesFieldMapper build(BuilderContext context) { return new RankFeaturesFieldMapper( name, - new RankFeaturesFieldType(buildFullName(context), meta.getValue()), + new RankFeaturesFieldType(buildFullName(context), meta.getValue(), positiveScoreImpact.getValue()), multiFieldsBuilder.build(this, context), - copyTo.build() + copyTo.build(), + positiveScoreImpact.getValue() ); } } @@ -84,9 +94,12 @@ public RankFeaturesFieldMapper build(BuilderContext context) { public static final class RankFeaturesFieldType extends MappedFieldType { - public RankFeaturesFieldType(String name, Map meta) { + private final boolean positiveScoreImpact; + + public RankFeaturesFieldType(String name, Map meta, boolean positiveScoreImpact) { super(name, false, false, false, TextSearchInfo.NONE, meta); setIndexAnalyzer(Lucene.KEYWORD_ANALYZER); + this.positiveScoreImpact = positiveScoreImpact; } @Override @@ -94,6 +107,10 @@ public String typeName() { return CONTENT_TYPE; } + public boolean positiveScoreImpact() { + return positiveScoreImpact; + } + @Override public Query existsQuery(QueryShardContext context) { throw new IllegalArgumentException("[rank_features] fields do not support [exists] queries"); @@ -115,9 +132,18 @@ public Query termQuery(Object value, QueryShardContext context) { } } - private RankFeaturesFieldMapper(String simpleName, MappedFieldType mappedFieldType, MultiFields multiFields, CopyTo copyTo) { + private final boolean positiveScoreImpact; + + private RankFeaturesFieldMapper( + String simpleName, + MappedFieldType mappedFieldType, + MultiFields multiFields, + CopyTo copyTo, + boolean positiveScoreImpact + ) { super(simpleName, mappedFieldType, multiFields, copyTo); assert fieldType.indexOptions().compareTo(IndexOptions.DOCS_AND_FREQS) <= 0; + this.positiveScoreImpact = positiveScoreImpact; } @Override @@ -164,6 +190,9 @@ public void parse(ParseContext context) throws IOException { + "] in the same document" ); } + if (positiveScoreImpact == false) { + value = 1 / value; + } context.doc().addWithKey(key, new FeatureField(name(), feature, value)); } else { throw new IllegalArgumentException( diff --git a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/ScaledFloatFieldMapper.java b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/ScaledFloatFieldMapper.java index 78a9e389eb63f..7be241017f683 100644 --- a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/ScaledFloatFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/ScaledFloatFieldMapper.java @@ -32,6 +32,8 @@ package org.opensearch.index.mapper; +import com.fasterxml.jackson.core.JsonParseException; + import org.apache.lucene.document.Field; import org.apache.lucene.index.DocValues; import org.apache.lucene.index.LeafReaderContext; @@ -43,9 +45,9 @@ import org.opensearch.common.Explicit; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.index.fielddata.FieldData; import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.index.fielddata.IndexNumericFieldData; @@ -392,7 +394,7 @@ protected void parseCreateField(ParseContext context) throws IOException { } else { try { numericValue = parse(parser, coerce.value()); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException | JsonParseException e) { if (ignoreMalformed.value()) { return; } else { diff --git a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/SearchAsYouTypeFieldMapper.java b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/SearchAsYouTypeFieldMapper.java index 7394993448bbf..397a7b48b472a 100644 --- a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/SearchAsYouTypeFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/SearchAsYouTypeFieldMapper.java @@ -60,6 +60,7 @@ import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.Operations; import org.opensearch.common.collect.Iterators; +import org.opensearch.common.lucene.search.AutomatonQueries; import org.opensearch.index.analysis.AnalyzerScope; import org.opensearch.index.analysis.IndexAnalyzers; import org.opensearch.index.analysis.NamedAnalyzer; @@ -204,8 +205,8 @@ public SearchAsYouTypeFieldMapper build(Mapper.BuilderContext context) { ft.setIndexAnalyzer(analyzers.getIndexAnalyzer()); // set up the prefix field - FieldType prefixft = new FieldType(fieldType); - prefixft.setStoreTermVectors(false); + FieldType prefixft = new FieldType(); + prefixft.setIndexOptions(fieldType.indexOptions()); prefixft.setOmitNorms(true); prefixft.setStored(false); final String fullName = buildFullName(context); @@ -431,8 +432,7 @@ public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, bool automata.add(Automata.makeAnyChar()); } Automaton automaton = Operations.concatenate(automata); - AutomatonQuery query = new AutomatonQuery(new Term(name(), value + "*"), automaton); - query.setRewriteMethod(method); + AutomatonQuery query = AutomatonQueries.createAutomatonQuery(new Term(name(), value + "*"), automaton, method); return new BooleanQuery.Builder().add(query, BooleanClause.Occur.SHOULD) .add(new TermQuery(new Term(parentField, value)), BooleanClause.Occur.SHOULD) .build(); @@ -618,7 +618,7 @@ public SpanQuery spanPrefixQuery(String value, SpanMultiTermQueryWrapper.SpanRew private final PrefixFieldMapper prefixField; private final ShingleFieldMapper[] shingleFields; - private final Builder builder; + private final IndexAnalyzers indexAnalyzers; public SearchAsYouTypeFieldMapper( String simpleName, @@ -636,7 +636,7 @@ public SearchAsYouTypeFieldMapper( this.store = builder.store.getValue(); this.indexOptions = builder.indexOptions.getValue(); this.termVectors = builder.termVectors.getValue(); - this.builder = builder; + this.indexAnalyzers = builder.analyzers.indexAnalyzers; } @Override @@ -663,7 +663,7 @@ protected String contentType() { @Override public ParametrizedFieldMapper.Builder getMergeBuilder() { - return new Builder(simpleName(), builder.analyzers.indexAnalyzers).init(this); + return new Builder(simpleName(), this.indexAnalyzers).init(this); } public static String getShingleFieldName(String parentField, int shingleSize) { diff --git a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/TokenCountFieldMapper.java b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/TokenCountFieldMapper.java index fd029503e9a7b..1851afcf0af85 100644 --- a/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/TokenCountFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/opensearch/index/mapper/TokenCountFieldMapper.java @@ -122,7 +122,7 @@ static class TokenCountFieldType extends NumberFieldMapper.NumberFieldType { @Override public ValueFetcher valueFetcher(QueryShardContext context, SearchLookup searchLookup, String format) { if (hasDocValues() == false) { - return lookup -> org.opensearch.common.collect.List.of(); + return lookup -> List.of(); } return new DocValueFetcher(docValueFormat(format, null), searchLookup.doc().getForField(this)); } diff --git a/modules/mapper-extras/src/main/java/org/opensearch/index/query/RankFeatureQueryBuilder.java b/modules/mapper-extras/src/main/java/org/opensearch/index/query/RankFeatureQueryBuilder.java index 9ad52c805da9d..13591d0782ea2 100644 --- a/modules/mapper-extras/src/main/java/org/opensearch/index/query/RankFeatureQueryBuilder.java +++ b/modules/mapper-extras/src/main/java/org/opensearch/index/query/RankFeatureQueryBuilder.java @@ -35,16 +35,16 @@ import org.apache.lucene.document.FeatureField; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.mapper.RankFeatureFieldMapper.RankFeatureFieldType; import org.opensearch.index.mapper.RankFeatureMetaFieldMapper; import org.opensearch.index.mapper.RankFeaturesFieldMapper.RankFeaturesFieldType; -import org.opensearch.index.mapper.MappedFieldType; import java.io.IOException; import java.util.Arrays; diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/BWCTemplateTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/BWCTemplateTests.java index d9e40fac1ad0f..bb944de2468aa 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/BWCTemplateTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/BWCTemplateTests.java @@ -32,7 +32,7 @@ package org.opensearch.index.mapper; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchSingleNodeTestCase; @@ -54,9 +54,9 @@ public void testBeatsTemplatesBWC() throws Exception { byte[] metricBeat = copyToBytesFromClasspath("/org/opensearch/index/mapper/metricbeat-6.0.template.json"); byte[] packetBeat = copyToBytesFromClasspath("/org/opensearch/index/mapper/packetbeat-6.0.template.json"); byte[] fileBeat = copyToBytesFromClasspath("/org/opensearch/index/mapper/filebeat-6.0.template.json"); - client().admin().indices().preparePutTemplate("metricbeat").setSource(metricBeat, XContentType.JSON).get(); - client().admin().indices().preparePutTemplate("packetbeat").setSource(packetBeat, XContentType.JSON).get(); - client().admin().indices().preparePutTemplate("filebeat").setSource(fileBeat, XContentType.JSON).get(); + client().admin().indices().preparePutTemplate("metricbeat").setSource(metricBeat, MediaTypeRegistry.JSON).get(); + client().admin().indices().preparePutTemplate("packetbeat").setSource(packetBeat, MediaTypeRegistry.JSON).get(); + client().admin().indices().preparePutTemplate("filebeat").setSource(fileBeat, MediaTypeRegistry.JSON).get(); client().prepareIndex("metricbeat-foo").setId("1").setSource("message", "foo").get(); client().prepareIndex("packetbeat-foo").setId("1").setSource("message", "foo").get(); diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeatureFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeatureFieldMapperTests.java index 908e5db6196c3..f0bd69df0e35e 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeatureFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeatureFieldMapperTests.java @@ -38,14 +38,13 @@ import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.List; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.Plugin; import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.List; import static org.hamcrest.Matchers.instanceOf; @@ -91,7 +90,7 @@ protected void minimalMapping(XContentBuilder b) throws IOException { public void testDefaults() throws Exception { DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping)); - assertEquals(Strings.toString(fieldMapping(this::minimalMapping)), mapper.mappingSource().toString()); + assertEquals(fieldMapping(this::minimalMapping).toString(), mapper.mappingSource().toString()); ParsedDocument doc1 = mapper.parse(source(b -> b.field("field", 10))); IndexableField[] fields = doc1.rootDoc().getFields("_feature"); diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeatureMetaFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeatureMetaFieldMapperTests.java index 3161e7462d2a0..0ae261936a225 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeatureMetaFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeatureMetaFieldMapperTests.java @@ -32,11 +32,9 @@ package org.opensearch.index.mapper; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchSingleNodeTestCase; @@ -61,18 +59,17 @@ protected Collection> getPlugins() { } public void testBasics() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("type") - .startObject("properties") - .startObject("field") - .field("type", "rank_feature") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = MediaTypeRegistry.JSON.contentBuilder() + .startObject() + .startObject("type") + .startObject("properties") + .startObject("field") + .field("type", "rank_feature") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping)); @@ -85,13 +82,15 @@ public void testBasics() throws Exception { * and parsing of a document fails if the document contains these meta-fields. */ public void testDocumentParsingFailsOnMetaField() throws Exception { - String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("_doc").endObject().endObject()); + String mapping = MediaTypeRegistry.JSON.contentBuilder().startObject().startObject("_doc").endObject().endObject().toString(); DocumentMapper mapper = parser.parse("_doc", new CompressedXContent(mapping)); String rfMetaField = RankFeatureMetaFieldMapper.CONTENT_TYPE; - BytesReference bytes = BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(rfMetaField, 0).endObject()); + BytesReference bytes = BytesReference.bytes( + MediaTypeRegistry.JSON.contentBuilder().startObject().field(rfMetaField, 0).endObject() + ); MapperParsingException e = expectThrows( MapperParsingException.class, - () -> mapper.parse(new SourceToParse("test", "1", bytes, XContentType.JSON)) + () -> mapper.parse(new SourceToParse("test", "1", bytes, MediaTypeRegistry.JSON)) ); assertTrue( e.getCause().getMessage().contains("Field [" + rfMetaField + "] is a metadata field and cannot be added inside a document.") diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeaturesFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeaturesFieldMapperTests.java index 129ba6b126237..0773bdedf806a 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeaturesFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeaturesFieldMapperTests.java @@ -34,14 +34,14 @@ import org.apache.lucene.document.FeatureField; import org.apache.lucene.index.IndexableField; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.Plugin; import org.hamcrest.Matchers; import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.List; public class RankFeaturesFieldMapperTests extends MapperTestCase { @@ -58,7 +58,7 @@ protected void assertExistsQuery(MapperService mapperService) { @Override protected Collection getPlugins() { - return org.opensearch.common.collect.List.of(new MapperExtrasPlugin()); + return List.of(new MapperExtrasPlugin()); } @Override @@ -67,8 +67,8 @@ protected void minimalMapping(XContentBuilder b) throws IOException { } @Override - protected void registerParameters(ParameterChecker checker) { - // no parameters to configure + protected void registerParameters(ParameterChecker checker) throws IOException { + checker.registerConflictCheck("positive_score_impact", b -> b.field("positive_score_impact", false)); } @Override @@ -78,7 +78,7 @@ protected boolean supportsMeta() { public void testDefaults() throws Exception { DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping)); - assertEquals(Strings.toString(fieldMapping(this::minimalMapping)), mapper.mappingSource().toString()); + assertEquals(fieldMapping(this::minimalMapping).toString(), mapper.mappingSource().toString()); ParsedDocument doc1 = mapper.parse(source(this::writeField)); @@ -95,6 +95,33 @@ public void testDefaults() throws Exception { assertTrue(freq1 < freq2); } + public void testNegativeScoreImpact() throws Exception { + DocumentMapper mapper = createDocumentMapper( + fieldMapping(b -> b.field("type", "rank_features").field("positive_score_impact", false)) + ); + + ParsedDocument doc1 = mapper.parse(source(this::writeField)); + + IndexableField[] fields = doc1.rootDoc().getFields("field"); + assertEquals(2, fields.length); + assertThat(fields[0], Matchers.instanceOf(FeatureField.class)); + FeatureField featureField1 = null; + FeatureField featureField2 = null; + for (IndexableField field : fields) { + if (field.stringValue().equals("foo")) { + featureField1 = (FeatureField) field; + } else if (field.stringValue().equals("bar")) { + featureField2 = (FeatureField) field; + } else { + throw new UnsupportedOperationException(); + } + } + + int freq1 = RankFeatureFieldMapperTests.getFrequency(featureField1.tokenStream(null, null)); + int freq2 = RankFeatureFieldMapperTests.getFrequency(featureField2.tokenStream(null, null)); + assertTrue(freq1 > freq2); + } + public void testRejectMultiValuedFields() throws MapperParsingException, IOException { DocumentMapper mapper = createDocumentMapper(mapping(b -> { b.startObject("field").field("type", "rank_features").endObject(); diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeaturesFieldTypeTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeaturesFieldTypeTests.java index b8c653bc97ce7..8ece0d63f05ba 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeaturesFieldTypeTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/RankFeaturesFieldTypeTests.java @@ -37,7 +37,7 @@ public class RankFeaturesFieldTypeTests extends FieldTypeTestCase { public void testIsNotAggregatable() { - MappedFieldType fieldType = new RankFeaturesFieldMapper.RankFeaturesFieldType("field", Collections.emptyMap()); + MappedFieldType fieldType = new RankFeaturesFieldMapper.RankFeaturesFieldType("field", Collections.emptyMap(), true); assertFalse(fieldType.isAggregatable()); } } diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldMapperTests.java index 3de322b286183..7cb951ae73844 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldMapperTests.java @@ -34,11 +34,10 @@ import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.IndexableField; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.Plugin; import java.io.IOException; @@ -95,7 +94,7 @@ public void testExistsQueryDocValuesDisabled() throws IOException { public void testDefaults() throws Exception { XContentBuilder mapping = fieldMapping(b -> b.field("type", "scaled_float").field("scaling_factor", 10.0)); DocumentMapper mapper = createDocumentMapper(mapping); - assertEquals(Strings.toString(mapping), mapper.mappingSource().toString()); + assertEquals(mapping.toString(), mapper.mappingSource().toString()); ParsedDocument doc = mapper.parse(source(b -> b.field("field", 123))); IndexableField[] fields = doc.rootDoc().getFields("field"); @@ -136,7 +135,7 @@ public void testNotIndexed() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", 123).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -157,7 +156,7 @@ public void testNoDocValues() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", 123).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -178,7 +177,7 @@ public void testStore() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", 123).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -201,7 +200,7 @@ public void testCoerce() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", "123").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); IndexableField[] fields = doc.rootDoc().getFields("field"); @@ -220,7 +219,7 @@ public void testCoerce() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", "123").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); MapperParsingException e = expectThrows(MapperParsingException.class, runnable); @@ -229,6 +228,7 @@ public void testCoerce() throws Exception { public void testIgnoreMalformed() throws Exception { doTestIgnoreMalformed("a", "For input string: \"a\""); + doTestIgnoreMalformed(true, "Current token (VALUE_TRUE) not numeric"); List values = Arrays.asList("NaN", "Infinity", "-Infinity"); for (String value : values) { @@ -236,14 +236,14 @@ public void testIgnoreMalformed() throws Exception { } } - private void doTestIgnoreMalformed(String value, String exceptionMessageContains) throws Exception { + private void doTestIgnoreMalformed(Object value, String exceptionMessageContains) throws Exception { DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping)); ThrowingRunnable runnable = () -> mapper.parse( new SourceToParse( "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", value).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); MapperParsingException e = expectThrows(MapperParsingException.class, runnable); @@ -257,7 +257,7 @@ private void doTestIgnoreMalformed(String value, String exceptionMessageContains "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", value).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -272,7 +272,7 @@ public void testNullValue() throws IOException { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().nullField("field").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertArrayEquals(new IndexableField[0], doc.rootDoc().getFields("field")); @@ -285,7 +285,7 @@ public void testNullValue() throws IOException { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().nullField("field").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); IndexableField[] fields = doc.rootDoc().getFields("field"); diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldTypeTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldTypeTests.java index 0baf3db32a4d8..be12c49321b87 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldTypeTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/ScaledFloatFieldTypeTests.java @@ -45,7 +45,7 @@ import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.index.fielddata.IndexNumericFieldData; import org.opensearch.index.fielddata.LeafNumericFieldData; import org.opensearch.index.fielddata.SortedNumericDoubleValues; diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/SearchAsYouTypeFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/SearchAsYouTypeFieldMapperTests.java index 786791314692d..26803c8548e24 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/SearchAsYouTypeFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/SearchAsYouTypeFieldMapperTests.java @@ -50,9 +50,10 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.SynonymQuery; import org.apache.lucene.search.TermQuery; -import org.opensearch.common.Strings; import org.opensearch.common.lucene.search.MultiPhrasePrefixQuery; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AnalyzerScope; import org.opensearch.index.analysis.IndexAnalyzers; @@ -75,6 +76,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -133,7 +135,7 @@ protected void writeFieldValue(XContentBuilder builder) throws IOException { @Override protected Collection getPlugins() { - return org.opensearch.common.collect.List.of(new MapperExtrasPlugin()); + return List.of(new MapperExtrasPlugin()); } @Override @@ -149,20 +151,9 @@ protected IndexAnalyzers createIndexAnalyzers(IndexSettings indexSettings) { NamedAnalyzer simple = new NamedAnalyzer("simple", AnalyzerScope.INDEX, new SimpleAnalyzer()); NamedAnalyzer whitespace = new NamedAnalyzer("whitespace", AnalyzerScope.INDEX, new WhitespaceAnalyzer()); return new IndexAnalyzers( - org.opensearch.common.collect.Map.of( - "default", - dflt, - "standard", - standard, - "keyword", - keyword, - "simple", - simple, - "whitespace", - whitespace - ), - org.opensearch.common.collect.Map.of(), - org.opensearch.common.collect.Map.of() + Map.of("default", dflt, "standard", standard, "keyword", keyword, "simple", simple, "whitespace", whitespace), + Map.of(), + Map.of() ); } @@ -352,15 +343,30 @@ public void testIndex() throws IOException { } public void testTermVectors() throws IOException { - DocumentMapper mapper = createDocumentMapper(fieldMapping(b -> b.field("type", "search_as_you_type").field("term_vector", "yes"))); + String[] termVectors = { + "yes", + "with_positions", + "with_offsets", + "with_positions_offsets", + "with_positions_payloads", + "with_positions_offsets_payloads" }; + + for (String termVector : termVectors) { + DocumentMapper mapper = createDocumentMapper( + fieldMapping(b -> b.field("type", "search_as_you_type").field("term_vector", termVector)) + ); - assertTrue(getRootFieldMapper(mapper, "field").fieldType().fieldType.storeTermVectors()); + assertTrue(getRootFieldMapper(mapper, "field").fieldType().fieldType.storeTermVectors()); - Stream.of(getShingleFieldMapper(mapper, "field._2gram"), getShingleFieldMapper(mapper, "field._3gram")) - .forEach(m -> assertTrue("for " + m.name(), m.fieldType.storeTermVectors())); + Stream.of(getShingleFieldMapper(mapper, "field._2gram"), getShingleFieldMapper(mapper, "field._3gram")) + .forEach(m -> assertTrue("for " + m.name(), m.fieldType.storeTermVectors())); - PrefixFieldMapper prefixFieldMapper = getPrefixFieldMapper(mapper, "field._index_prefix"); - assertFalse(prefixFieldMapper.fieldType.storeTermVectors()); + PrefixFieldMapper prefixFieldMapper = getPrefixFieldMapper(mapper, "field._index_prefix"); + assertFalse(prefixFieldMapper.fieldType.storeTermVectors()); + assertFalse(prefixFieldMapper.fieldType.storeTermVectorOffsets()); + assertFalse(prefixFieldMapper.fieldType.storeTermVectorPositions()); + assertFalse(prefixFieldMapper.fieldType.storeTermVectorPayloads()); + } } public void testNorms() throws IOException { @@ -594,7 +600,7 @@ public void testAnalyzerSerialization() throws IOException { b.field("type", "search_as_you_type"); b.field("analyzer", "simple"); })); - String serialized = Strings.toString(ms.documentMapper()); + String serialized = Strings.toString(MediaTypeRegistry.JSON, ms.documentMapper()); assertEquals( serialized, "{\"_doc\":{\"properties\":{\"field\":" @@ -602,7 +608,7 @@ public void testAnalyzerSerialization() throws IOException { ); merge(ms, mapping(b -> {})); - assertEquals(serialized, Strings.toString(ms.documentMapper())); + assertEquals(serialized, Strings.toString(MediaTypeRegistry.JSON, ms.documentMapper())); } private void documentParsingTestCase(Collection values) throws IOException { diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/SearchAsYouTypeFieldTypeTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/SearchAsYouTypeFieldTypeTests.java index 1998465318c2b..62b906d7442f6 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/SearchAsYouTypeFieldTypeTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/SearchAsYouTypeFieldTypeTests.java @@ -49,10 +49,11 @@ import java.io.IOException; import java.util.Collections; +import java.util.List; import static java.util.Arrays.asList; -import static org.apache.lucene.search.MultiTermQuery.CONSTANT_SCORE_REWRITE; import static org.hamcrest.Matchers.equalTo; +import static org.apache.lucene.search.MultiTermQuery.CONSTANT_SCORE_REWRITE; public class SearchAsYouTypeFieldTypeTests extends FieldTypeTestCase { @@ -137,7 +138,10 @@ public void testPrefixQuery() { // this term should be too long to be rewriteable to a term query on the prefix field final String longTerm = "toolongforourprefixfieldthistermis"; - assertThat(fieldType.prefixQuery(longTerm, CONSTANT_SCORE_REWRITE, MOCK_QSC), equalTo(new PrefixQuery(new Term(NAME, longTerm)))); + assertThat( + fieldType.prefixQuery(longTerm, CONSTANT_SCORE_REWRITE, MOCK_QSC), + equalTo(new PrefixQuery(new Term(NAME, longTerm), CONSTANT_SCORE_REWRITE)) + ); OpenSearchException ee = expectThrows( OpenSearchException.class, @@ -154,9 +158,9 @@ public void testFetchSourceValue() throws IOException { SearchAsYouTypeFieldType fieldType = createFieldType(); fieldType.setIndexAnalyzer(Lucene.STANDARD_ANALYZER); - assertEquals(org.opensearch.common.collect.List.of("value"), fetchSourceValue(fieldType, "value")); - assertEquals(org.opensearch.common.collect.List.of("42"), fetchSourceValue(fieldType, 42L)); - assertEquals(org.opensearch.common.collect.List.of("true"), fetchSourceValue(fieldType, true)); + assertEquals(List.of("value"), fetchSourceValue(fieldType, "value")); + assertEquals(List.of("42"), fetchSourceValue(fieldType, 42L)); + assertEquals(List.of("true"), fetchSourceValue(fieldType, true)); SearchAsYouTypeFieldMapper.PrefixFieldType prefixFieldType = new SearchAsYouTypeFieldMapper.PrefixFieldType( fieldType.name(), @@ -164,17 +168,17 @@ public void testFetchSourceValue() throws IOException { 2, 10 ); - assertEquals(org.opensearch.common.collect.List.of("value"), fetchSourceValue(prefixFieldType, "value")); - assertEquals(org.opensearch.common.collect.List.of("42"), fetchSourceValue(prefixFieldType, 42L)); - assertEquals(org.opensearch.common.collect.List.of("true"), fetchSourceValue(prefixFieldType, true)); + assertEquals(List.of("value"), fetchSourceValue(prefixFieldType, "value")); + assertEquals(List.of("42"), fetchSourceValue(prefixFieldType, 42L)); + assertEquals(List.of("true"), fetchSourceValue(prefixFieldType, true)); SearchAsYouTypeFieldMapper.ShingleFieldType shingleFieldType = new SearchAsYouTypeFieldMapper.ShingleFieldType( fieldType.name(), 5, fieldType.getTextSearchInfo() ); - assertEquals(org.opensearch.common.collect.List.of("value"), fetchSourceValue(shingleFieldType, "value")); - assertEquals(org.opensearch.common.collect.List.of("42"), fetchSourceValue(shingleFieldType, 42L)); - assertEquals(org.opensearch.common.collect.List.of("true"), fetchSourceValue(shingleFieldType, true)); + assertEquals(List.of("value"), fetchSourceValue(shingleFieldType, "value")); + assertEquals(List.of("42"), fetchSourceValue(shingleFieldType, 42L)); + assertEquals(List.of("true"), fetchSourceValue(shingleFieldType, true)); } } diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/TokenCountFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/TokenCountFieldMapperTests.java index e9d3767373b95..a82128e5b190c 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/TokenCountFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/mapper/TokenCountFieldMapperTests.java @@ -33,13 +33,13 @@ package org.opensearch.index.mapper; import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.tests.analysis.CannedTokenStream; -import org.apache.lucene.tests.analysis.MockTokenizer; -import org.apache.lucene.tests.analysis.Token; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.core.KeywordAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.opensearch.common.xcontent.XContentBuilder; +import org.apache.lucene.tests.analysis.CannedTokenStream; +import org.apache.lucene.tests.analysis.MockTokenizer; +import org.apache.lucene.tests.analysis.Token; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AnalyzerScope; import org.opensearch.index.analysis.IndexAnalyzers; diff --git a/modules/mapper-extras/src/test/java/org/opensearch/index/query/RankFeatureQueryBuilderTests.java b/modules/mapper-extras/src/test/java/org/opensearch/index/query/RankFeatureQueryBuilderTests.java index e183ba6f6735c..015075d730984 100644 --- a/modules/mapper-extras/src/test/java/org/opensearch/index/query/RankFeatureQueryBuilderTests.java +++ b/modules/mapper-extras/src/test/java/org/opensearch/index/query/RankFeatureQueryBuilderTests.java @@ -36,7 +36,6 @@ import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest; -import org.opensearch.common.Strings; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.index.mapper.MapperExtrasPlugin; import org.opensearch.index.mapper.MapperService; @@ -61,16 +60,14 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws mapperService.merge( "_doc", new CompressedXContent( - Strings.toString( - PutMappingRequest.simpleMapping( - "my_feature_field", - "type=rank_feature", - "my_negative_feature_field", - "type=rank_feature,positive_score_impact=false", - "my_feature_vector_field", - "type=rank_features" - ) - ) + PutMappingRequest.simpleMapping( + "my_feature_field", + "type=rank_feature", + "my_negative_feature_field", + "type=rank_feature,positive_score_impact=false", + "my_feature_vector_field", + "type=rank_features" + ).toString() ), MapperService.MergeReason.MAPPING_UPDATE ); diff --git a/modules/mapper-extras/src/test/resources/org/opensearch/index/mapper/metricbeat-6.0.template.json b/modules/mapper-extras/src/test/resources/org/opensearch/index/mapper/metricbeat-6.0.template.json index f456755feeb93..5a83dbd2128c4 100644 --- a/modules/mapper-extras/src/test/resources/org/opensearch/index/mapper/metricbeat-6.0.template.json +++ b/modules/mapper-extras/src/test/resources/org/opensearch/index/mapper/metricbeat-6.0.template.json @@ -229,7 +229,7 @@ }, "objects": { "properties": { - "master": { + "primary": { "type": "long" }, "total": { @@ -4601,7 +4601,7 @@ "connected_replicas": { "type": "long" }, - "master_offset": { + "primary_offset": { "type": "long" }, "role": { diff --git a/modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/rank_feature/10_basic.yml b/modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/rank_feature/10_basic.yml index 6fea35eb21f4e..ac951263ca299 100644 --- a/modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/rank_feature/10_basic.yml +++ b/modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/rank_feature/10_basic.yml @@ -157,3 +157,24 @@ setup: - match: hits.hits.1._id: "1" + +--- +"Negative linear": + + - do: + search: + index: test + body: + query: + rank_feature: + field: url_length + linear: {} + + - match: + hits.total.value: 2 + + - match: + hits.hits.0._id: "2" + + - match: + hits.hits.1._id: "1" diff --git a/modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/rank_features/10_basic.yml b/modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/rank_features/10_basic.yml index d4d5d2a360406..2644b9e777f6a 100644 --- a/modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/rank_features/10_basic.yml +++ b/modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/rank_features/10_basic.yml @@ -9,6 +9,9 @@ setup: properties: tags: type: rank_features + negative_reviews: + type: rank_features + positive_score_impact: false - do: index: @@ -18,6 +21,9 @@ setup: tags: foo: 3 bar: 5 + negative_reviews: + 1star: 10 + 2star: 1 - do: index: @@ -27,6 +33,9 @@ setup: tags: bar: 6 quux: 10 + negative_reviews: + 1star: 1 + 2star: 10 - do: indices.refresh: {} @@ -97,3 +106,34 @@ setup: - match: hits.hits.1._id: "1" + +--- +"Linear negative impact": + + - do: + search: + index: test + body: + query: + rank_feature: + field: negative_reviews.1star + linear: {} + + - match: + hits.hits.0._id: "2" + - match: + hits.hits.1._id: "1" + + - do: + search: + index: test + body: + query: + rank_feature: + field: negative_reviews.2star + linear: {} + + - match: + hits.hits.0._id: "1" + - match: + hits.hits.1._id: "2" diff --git a/modules/opensearch-dashboards/build.gradle b/modules/opensearch-dashboards/build.gradle index 9bda17243bdb4..f76ca739faf81 100644 --- a/modules/opensearch-dashboards/build.gradle +++ b/modules/opensearch-dashboards/build.gradle @@ -41,4 +41,3 @@ dependencies { testClusters.all { module ':modules:reindex' } - diff --git a/modules/opensearch-dashboards/src/javaRestTest/java/org/opensearch/dashboards/OpenSearchDashboardsSystemIndexIT.java b/modules/opensearch-dashboards/src/javaRestTest/java/org/opensearch/dashboards/OpenSearchDashboardsSystemIndexIT.java index 2584a9b41f14d..0a9133ed5bca0 100644 --- a/modules/opensearch-dashboards/src/javaRestTest/java/org/opensearch/dashboards/OpenSearchDashboardsSystemIndexIT.java +++ b/modules/opensearch-dashboards/src/javaRestTest/java/org/opensearch/dashboards/OpenSearchDashboardsSystemIndexIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.http.util.EntityUtils; import org.opensearch.client.Request; import org.opensearch.client.Response; diff --git a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/AbstractParentChildTestCase.java b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/AbstractParentChildTestCase.java index 5566c688aefbf..e049edf843069 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/AbstractParentChildTestCase.java +++ b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/AbstractParentChildTestCase.java @@ -32,7 +32,14 @@ package org.opensearch.join.aggregations; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.SearchRequestBuilder; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.client.Requests; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.join.query.ParentChildTestCase; +import org.junit.Before; import java.util.ArrayList; import java.util.HashMap; @@ -41,9 +48,8 @@ import java.util.Map; import java.util.Set; -import org.opensearch.action.index.IndexRequestBuilder; -import org.opensearch.join.query.ParentChildTestCase; -import org.junit.Before; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; /** * Small base test-class which combines stuff used for Children and Parent aggregation tests @@ -52,6 +58,10 @@ public abstract class AbstractParentChildTestCase extends ParentChildTestCase { protected final Map categoryToControl = new HashMap<>(); protected final Map articleToControl = new HashMap<>(); + public AbstractParentChildTestCase(Settings dynamicSettings) { + super(dynamicSettings); + } + @Before public void setupCluster() throws Exception { assertAcked( @@ -154,4 +164,38 @@ private ParentControl(String category) { this.category = category; } } + + // Test when there is 1 child document and 1 parent document per segment. + public void testSparseSegments() throws InterruptedException { + assertAcked( + prepareCreate("sparse").setMapping( + addFieldMappings( + buildParentJoinFieldMappingFromSimplifiedDef("join_field", true, "article", "comment"), + "commenter", + "keyword", + "category", + "keyword" + ) + ) + .setSettings( + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + ) + ); + + List requests = new ArrayList<>(); + requests.add(createIndexRequest("sparse", "article", "article-0", null, "category", List.of("0"))); + indexRandom(true, false, requests); + client().admin().indices().refresh(Requests.refreshRequest("sparse")).actionGet(); + requests = new ArrayList<>(); + requests.add(createIndexRequest("sparse", "comment", "comment-0", "article-0", "commenter", "0")); + indexRandom(true, false, requests); + + SearchResponse searchResponse = getSearchRequest().get(); + assertSearchResponse(searchResponse); + validateSpareSegmentsSearchResponse(searchResponse); + } + + abstract SearchRequestBuilder getSearchRequest(); + + abstract void validateSpareSegmentsSearchResponse(SearchResponse searchResponse); } diff --git a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/ChildrenIT.java b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/ChildrenIT.java index 08354cbaaf93b..5fc0a202ae45e 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/ChildrenIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/ChildrenIT.java @@ -31,13 +31,17 @@ package org.opensearch.join.aggregations; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.join.ScoreMode; import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.update.UpdateResponse; import org.opensearch.client.Requests; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.SearchHit; import org.opensearch.search.aggregations.AggregationBuilders; import org.opensearch.search.aggregations.InternalAggregation; @@ -47,14 +51,18 @@ import org.opensearch.search.sort.SortOrder; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; +import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.matchQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; import static org.opensearch.join.aggregations.JoinAggregationBuilders.children; import static org.opensearch.join.query.JoinQueryBuilders.hasChildQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.search.aggregations.AggregationBuilders.topHits; @@ -69,6 +77,23 @@ public class ChildrenIT extends AbstractParentChildTestCase { + public ChildrenIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testChildrenAggs() throws Exception { SearchResponse searchResponse = client().prepareSearch("test") .setQuery(matchQuery("randomized", true)) @@ -227,7 +252,7 @@ public void testNonExistingChildType() throws Exception { public void testPostCollection() throws Exception { String indexName = "prodcatalog"; - String masterType = "masterprod"; + String mainType = "mainprod"; String childType = "variantsku"; assertAcked( prepareCreate(indexName).setSettings( @@ -235,7 +260,7 @@ public void testPostCollection() throws Exception { ) .setMapping( addFieldMappings( - buildParentJoinFieldMappingFromSimplifiedDef("join_field", true, masterType, childType), + buildParentJoinFieldMappingFromSimplifiedDef("join_field", true, mainType, childType), "brand", "text", "name", @@ -251,7 +276,7 @@ public void testPostCollection() throws Exception { ); List requests = new ArrayList<>(); - requests.add(createIndexRequest(indexName, masterType, "1", null, "brand", "Levis", "name", "Style 501", "material", "Denim")); + requests.add(createIndexRequest(indexName, mainType, "1", null, "brand", "Levis", "name", "Style 501", "material", "Denim")); requests.add(createIndexRequest(indexName, childType, "3", "1", "color", "blue", "size", "32")); requests.add(createIndexRequest(indexName, childType, "4", "1", "color", "blue", "size", "34")); requests.add(createIndexRequest(indexName, childType, "5", "1", "color", "blue", "size", "36")); @@ -259,9 +284,7 @@ public void testPostCollection() throws Exception { requests.add(createIndexRequest(indexName, childType, "7", "1", "color", "black", "size", "40")); requests.add(createIndexRequest(indexName, childType, "8", "1", "color", "gray", "size", "36")); - requests.add( - createIndexRequest(indexName, masterType, "2", null, "brand", "Wrangler", "name", "Regular Cut", "material", "Leather") - ); + requests.add(createIndexRequest(indexName, mainType, "2", null, "brand", "Wrangler", "name", "Regular Cut", "material", "Leather")); requests.add(createIndexRequest(indexName, childType, "9", "2", "color", "blue", "size", "32")); requests.add(createIndexRequest(indexName, childType, "10", "2", "color", "blue", "size", "34")); requests.add(createIndexRequest(indexName, childType, "12", "2", "color", "black", "size", "36")); @@ -409,4 +432,18 @@ public void testPostCollectAllLeafReaders() throws Exception { children = parents.getBuckets().get(0).getAggregations().get("child_docs"); assertThat(children.getDocCount(), equalTo(2L)); } + + @Override + SearchRequestBuilder getSearchRequest() { + return client().prepareSearch("sparse") + .setSize(10000) + .setQuery(matchAllQuery()) + .addAggregation(children("to_comment", "comment").subAggregation(terms("commenters").field("commenter").size(10000))); + } + + @Override + void validateSpareSegmentsSearchResponse(SearchResponse searchResponse) { + Children children = searchResponse.getAggregations().get("to_comment"); + assertEquals(children.getDocCount(), 1); + } } diff --git a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/ParentIT.java b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/ParentIT.java index 351b0beec481b..04703a65aa19d 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/ParentIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/aggregations/ParentIT.java @@ -32,12 +32,18 @@ package org.opensearch.join.aggregations; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.bucket.MultiBucketsAggregation; import org.opensearch.search.aggregations.bucket.terms.Terms; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -47,8 +53,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.matchQuery; import static org.opensearch.join.aggregations.JoinAggregationBuilders.parent; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.search.aggregations.AggregationBuilders.topHits; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; @@ -56,6 +64,23 @@ public class ParentIT extends AbstractParentChildTestCase { + public ParentIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testSimpleParentAgg() throws Exception { final SearchRequestBuilder searchRequest = client().prepareSearch("test") .setSize(10000) @@ -264,4 +289,18 @@ public void testTermsParentAggTerms() throws Exception { } } } + + @Override + SearchRequestBuilder getSearchRequest() { + return client().prepareSearch("sparse") + .setSize(10000) + .setQuery(matchAllQuery()) + .addAggregation(parent("to_article", "comment").subAggregation(terms("category").field("category").size(10000))); + } + + @Override + void validateSpareSegmentsSearchResponse(SearchResponse searchResponse) { + Parent parentAgg = searchResponse.getAggregations().get("to_article"); + assertEquals(parentAgg.getDocCount(), 1); + } } diff --git a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/ChildQuerySearchIT.java b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/ChildQuerySearchIT.java index d684f0bfebcfb..f7b8b47d2f3a2 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/ChildQuerySearchIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/ChildQuerySearchIT.java @@ -31,6 +31,8 @@ package org.opensearch.join.query; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.join.ScoreMode; import org.opensearch.action.explain.ExplainResponse; import org.opensearch.action.index.IndexRequestBuilder; @@ -42,6 +44,8 @@ import org.opensearch.common.lucene.search.function.FunctionScoreQuery; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.rest.RestStatus; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.IdsQueryBuilder; import org.opensearch.index.query.InnerHitBuilder; @@ -50,7 +54,6 @@ import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.functionscore.FunctionScoreQueryBuilder; -import org.opensearch.rest.RestStatus; import org.opensearch.search.SearchHit; import org.opensearch.search.aggregations.AggregationBuilders; import org.opensearch.search.aggregations.bucket.filter.Filter; @@ -65,6 +68,8 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -87,6 +92,7 @@ import static org.opensearch.join.query.JoinQueryBuilders.hasChildQuery; import static org.opensearch.join.query.JoinQueryBuilders.hasParentQuery; import static org.opensearch.join.query.JoinQueryBuilders.parentId; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; @@ -100,6 +106,23 @@ public class ChildQuerySearchIT extends ParentChildTestCase { + public ChildQuerySearchIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testMultiLevelChild() throws Exception { assertAcked( prepareCreate("test").setMapping( diff --git a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/InnerHitsIT.java b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/InnerHitsIT.java index ffcc9cf38545f..39da86c7fd726 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/InnerHitsIT.java +++ b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/InnerHitsIT.java @@ -32,11 +32,15 @@ package org.opensearch.join.query; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.util.ArrayUtil; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.InnerHitBuilder; @@ -54,6 +58,7 @@ import org.opensearch.search.sort.SortOrder; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -73,6 +78,7 @@ import static org.opensearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO; import static org.opensearch.join.query.JoinQueryBuilders.hasChildQuery; import static org.opensearch.join.query.JoinQueryBuilders.hasParentQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; @@ -87,6 +93,23 @@ public class InnerHitsIT extends ParentChildTestCase { + public InnerHitsIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { ArrayList> plugins = new ArrayList<>(super.nodePlugins()); diff --git a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/ParentChildTestCase.java b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/ParentChildTestCase.java index 5d6d4fb333d49..9b87b340eb0a8 100644 --- a/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/ParentChildTestCase.java +++ b/modules/parent-join/src/internalClusterTest/java/org/opensearch/join/query/ParentChildTestCase.java @@ -32,16 +32,16 @@ package org.opensearch.join.query; import org.opensearch.action.index.IndexRequestBuilder; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexModule; import org.opensearch.join.ParentJoinPlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.Arrays; @@ -51,7 +51,11 @@ import java.util.Map; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) -public abstract class ParentChildTestCase extends OpenSearchIntegTestCase { +public abstract class ParentChildTestCase extends ParameterizedOpenSearchIntegTestCase { + + public ParentChildTestCase(Settings dynamicSettings) { + super(dynamicSettings); + } @Override protected boolean ignoreExternalCluster() { @@ -85,7 +89,7 @@ protected IndexRequestBuilder createIndexRequest(String index, String type, Stri protected IndexRequestBuilder createIndexRequest(String index, String type, String id, String parentId, XContentBuilder builder) throws IOException { - Map source = XContentHelper.convertToMap(JsonXContent.jsonXContent, Strings.toString(builder), false); + Map source = XContentHelper.convertToMap(JsonXContent.jsonXContent, builder.toString(), false); return createIndexRequest(index, type, id, parentId, source); } diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregationBuilder.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregationBuilder.java index bff730878d1f3..b58e278133d95 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregationBuilder.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregationBuilder.java @@ -33,11 +33,11 @@ package org.opensearch.join.aggregations; import org.apache.lucene.search.Query; -import org.opensearch.common.ParsingException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.query.QueryShardContext; import org.opensearch.join.mapper.ParentIdFieldMapper; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregatorFactory.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregatorFactory.java index 793b35111cfe2..bbca89fc56820 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregatorFactory.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenAggregatorFactory.java @@ -118,4 +118,9 @@ public String getStatsSubtype() { // Child Aggregation is registered in non-standard way, so it might return child's values type return OTHER_SUBTYPE; } + + @Override + protected boolean supportsConcurrentSegmentSearch() { + return true; + } } diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenToParentAggregator.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenToParentAggregator.java index d8fce3d72edb6..b6b1dff932be8 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenToParentAggregator.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ChildrenToParentAggregator.java @@ -32,7 +32,7 @@ package org.opensearch.join.aggregations; import org.apache.lucene.search.Query; -import org.opensearch.common.ParseField; +import org.opensearch.core.ParseField; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.CardinalityUpperBound; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalChildren.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalChildren.java index d8221d2d2afb1..dfe34d16d016b 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalChildren.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalChildren.java @@ -32,7 +32,7 @@ package org.opensearch.join.aggregations; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.bucket.InternalSingleBucketAggregation; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalParent.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalParent.java index d512d62526306..cd4522b3b063b 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalParent.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/InternalParent.java @@ -32,7 +32,7 @@ package org.opensearch.join.aggregations; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.aggregations.bucket.InternalSingleBucketAggregation; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregationBuilder.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregationBuilder.java index f180981db126e..7d0aaee71583e 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregationBuilder.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregationBuilder.java @@ -33,11 +33,11 @@ package org.opensearch.join.aggregations; import org.apache.lucene.search.Query; -import org.opensearch.common.ParsingException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.query.QueryShardContext; import org.opensearch.join.mapper.ParentIdFieldMapper; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregatorFactory.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregatorFactory.java index 40c07c8f53e20..9a21cd1db3200 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregatorFactory.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentAggregatorFactory.java @@ -118,4 +118,10 @@ public String getStatsSubtype() { // Parent Aggregation is registered in non-standard way return OTHER_SUBTYPE; } + + @Override + protected boolean supportsConcurrentSegmentSearch() { + // See https://github.com/opensearch-project/OpenSearch/issues/9316 + return false; + } } diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentToChildrenAggregator.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentToChildrenAggregator.java index 8d342eada4d54..537034dbce4db 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentToChildrenAggregator.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParentToChildrenAggregator.java @@ -32,7 +32,7 @@ package org.opensearch.join.aggregations; import org.apache.lucene.search.Query; -import org.opensearch.common.ParseField; +import org.opensearch.core.ParseField; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.AggregatorFactories; import org.opensearch.search.aggregations.CardinalityUpperBound; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParsedChildren.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParsedChildren.java index cbbf359943304..2704a30d45111 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParsedChildren.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParsedChildren.java @@ -31,7 +31,7 @@ package org.opensearch.join.aggregations; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import java.io.IOException; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParsedParent.java b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParsedParent.java index 26c993403b667..f14ee81ad1b19 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParsedParent.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/aggregations/ParsedParent.java @@ -31,7 +31,7 @@ package org.opensearch.join.aggregations; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.aggregations.bucket.ParsedSingleBucketAggregation; import java.io.IOException; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/mapper/ParentJoinFieldMapper.java b/modules/parent-join/src/main/java/org/opensearch/join/mapper/ParentJoinFieldMapper.java index 662bacc0fb08a..a229d050f3b1d 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/mapper/ParentJoinFieldMapper.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/mapper/ParentJoinFieldMapper.java @@ -38,9 +38,9 @@ import org.apache.lucene.index.IndexOptions; import org.apache.lucene.util.BytesRef; import org.opensearch.common.lucene.Lucene; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.IndexSettings; import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/query/HasChildQueryBuilder.java b/modules/parent-join/src/main/java/org/opensearch/join/query/HasChildQueryBuilder.java index b25c1fd45e56e..1a51259e9e4e4 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/query/HasChildQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/query/HasChildQueryBuilder.java @@ -42,14 +42,14 @@ import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.similarities.Similarity; import org.opensearch.OpenSearchException; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.lucene.search.Queries; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.fielddata.IndexOrdinalsFieldData; import org.opensearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData; import org.opensearch.index.mapper.MappedFieldType; @@ -416,11 +416,12 @@ public void visit(QueryVisitor visitor) { } @Override - public Query rewrite(IndexReader reader) throws IOException { - Query rewritten = super.rewrite(reader); + public Query rewrite(IndexSearcher searcher) throws IOException { + Query rewritten = super.rewrite(searcher); if (rewritten != this) { return rewritten; } + IndexReader reader = searcher.getIndexReader(); if (reader instanceof DirectoryReader) { IndexSearcher indexSearcher = new IndexSearcher(reader); indexSearcher.setQueryCache(null); diff --git a/modules/parent-join/src/main/java/org/opensearch/join/query/HasParentQueryBuilder.java b/modules/parent-join/src/main/java/org/opensearch/join/query/HasParentQueryBuilder.java index 32cb8cf8174f0..d296a7b0141ff 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/query/HasParentQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/query/HasParentQueryBuilder.java @@ -35,13 +35,13 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.join.ScoreMode; import org.opensearch.OpenSearchException; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.search.Queries; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.query.AbstractQueryBuilder; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/query/ParentIdQueryBuilder.java b/modules/parent-join/src/main/java/org/opensearch/join/query/ParentIdQueryBuilder.java index 8964cdefc8eb2..bfc01bf151a9c 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/query/ParentIdQueryBuilder.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/query/ParentIdQueryBuilder.java @@ -37,12 +37,12 @@ import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.opensearch.OpenSearchException; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.AbstractQueryBuilder; import org.opensearch.index.query.QueryShardContext; import org.opensearch.index.query.QueryShardException; diff --git a/modules/parent-join/src/main/java/org/opensearch/join/spi/ParentJoinNamedXContentProvider.java b/modules/parent-join/src/main/java/org/opensearch/join/spi/ParentJoinNamedXContentProvider.java index 2aee61dc8d6a4..95cd1e60f03cc 100644 --- a/modules/parent-join/src/main/java/org/opensearch/join/spi/ParentJoinNamedXContentProvider.java +++ b/modules/parent-join/src/main/java/org/opensearch/join/spi/ParentJoinNamedXContentProvider.java @@ -32,9 +32,9 @@ package org.opensearch.join.spi; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ContextParser; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.join.aggregations.ChildrenAggregationBuilder; import org.opensearch.join.aggregations.ParentAggregationBuilder; import org.opensearch.join.aggregations.ParsedChildren; diff --git a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ChildrenToParentAggregatorTests.java b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ChildrenToParentAggregatorTests.java index 0d134592fa678..21bdcd5f01fcc 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ChildrenToParentAggregatorTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ChildrenToParentAggregatorTests.java @@ -38,19 +38,20 @@ import org.apache.lucene.document.StringField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.collect.Tuple; import org.opensearch.common.lucene.index.OpenSearchDirectoryReader; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.mapper.ContentPath; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.IdFieldMapper; @@ -60,7 +61,6 @@ import org.opensearch.index.mapper.MappingLookup; import org.opensearch.index.mapper.NumberFieldMapper; import org.opensearch.index.mapper.Uid; -import org.opensearch.index.shard.ShardId; import org.opensearch.join.ParentJoinPlugin; import org.opensearch.join.mapper.MetaJoinFieldMapper; import org.opensearch.join.mapper.ParentJoinFieldMapper; diff --git a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/InternalChildrenTests.java b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/InternalChildrenTests.java index 8eaedc4aa15b0..2ad9afc75f285 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/InternalChildrenTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/InternalChildrenTests.java @@ -32,9 +32,9 @@ package org.opensearch.join.aggregations; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.NamedXContentRegistry.Entry; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry.Entry; import org.opensearch.join.ParentJoinPlugin; import org.opensearch.plugins.SearchPlugin; import org.opensearch.search.aggregations.Aggregation; diff --git a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/InternalParentTests.java b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/InternalParentTests.java index cd5236ab49a39..26536ebd5c15b 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/InternalParentTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/InternalParentTests.java @@ -32,8 +32,8 @@ package org.opensearch.join.aggregations; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.NamedXContentRegistry.Entry; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.NamedXContentRegistry.Entry; import org.opensearch.join.ParentJoinPlugin; import org.opensearch.plugins.SearchPlugin; import org.opensearch.search.aggregations.Aggregation; diff --git a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ParentTests.java b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ParentTests.java index abb258f37198b..20fde444e2a77 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ParentTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ParentTests.java @@ -32,14 +32,14 @@ package org.opensearch.join.aggregations; -import java.util.Arrays; -import java.util.Collection; - import org.opensearch.join.ParentJoinPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.search.aggregations.BaseAggregationTestCase; import org.opensearch.test.TestGeoShapeFieldMapperPlugin; +import java.util.Arrays; +import java.util.Collection; + public class ParentTests extends BaseAggregationTestCase { @Override diff --git a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ParentToChildrenAggregatorTests.java b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ParentToChildrenAggregatorTests.java index 2ed06ee0c0ea9..6b10389430fef 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ParentToChildrenAggregatorTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/aggregations/ParentToChildrenAggregatorTests.java @@ -39,29 +39,29 @@ import org.apache.lucene.document.StringField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; -import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.collect.Tuple; import org.opensearch.common.lucene.index.OpenSearchDirectoryReader; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.mapper.ContentPath; -import org.opensearch.index.mapper.MappingLookup; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.IdFieldMapper; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.mapper.Mapper; import org.opensearch.index.mapper.MapperService; +import org.opensearch.index.mapper.MappingLookup; import org.opensearch.index.mapper.NumberFieldMapper; import org.opensearch.index.mapper.Uid; -import org.opensearch.index.shard.ShardId; import org.opensearch.join.ParentJoinPlugin; import org.opensearch.join.mapper.MetaJoinFieldMapper; import org.opensearch.join.mapper.ParentJoinFieldMapper; diff --git a/modules/parent-join/src/test/java/org/opensearch/join/mapper/ParentJoinFieldMapperTests.java b/modules/parent-join/src/test/java/org/opensearch/join/mapper/ParentJoinFieldMapperTests.java index a9ac151dd3806..96e8287e8e75f 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/mapper/ParentJoinFieldMapperTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/mapper/ParentJoinFieldMapperTests.java @@ -32,11 +32,10 @@ package org.opensearch.join.mapper; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.MapperException; @@ -60,19 +59,18 @@ protected Collection> getPlugins() { } public void testSingleLevel() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IndexService service = createIndex("test"); DocumentMapper docMapper = service.mapperService() .merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); @@ -80,7 +78,12 @@ public void testSingleLevel() throws Exception { // Doc without join ParsedDocument doc = docMapper.parse( - new SourceToParse("test", "0", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().endObject()), XContentType.JSON) + new SourceToParse( + "test", + "0", + BytesReference.bytes(MediaTypeRegistry.JSON.contentBuilder().startObject().endObject()), + MediaTypeRegistry.JSON + ) ); assertNull(doc.rootDoc().getBinaryValue("join_field")); @@ -90,7 +93,7 @@ public void testSingleLevel() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("join_field", "parent").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertEquals("1", doc.rootDoc().getBinaryValue("join_field#parent").utf8ToString()); @@ -110,7 +113,7 @@ public void testSingleLevel() throws Exception { .endObject() .endObject() ), - XContentType.JSON, + MediaTypeRegistry.JSON, "1" ) ); @@ -125,7 +128,7 @@ public void testSingleLevel() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("join_field", "unknown").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) ); @@ -133,19 +136,18 @@ public void testSingleLevel() throws Exception { } public void testParentIdSpecifiedAsNumber() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IndexService service = createIndex("test"); DocumentMapper docMapper = service.mapperService() .merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); @@ -162,7 +164,7 @@ public void testParentIdSpecifiedAsNumber() throws Exception { .endObject() .endObject() ), - XContentType.JSON, + MediaTypeRegistry.JSON, "1" ) ); @@ -181,7 +183,7 @@ public void testParentIdSpecifiedAsNumber() throws Exception { .endObject() .endObject() ), - XContentType.JSON, + MediaTypeRegistry.JSON, "1" ) ); @@ -190,20 +192,19 @@ public void testParentIdSpecifiedAsNumber() throws Exception { } public void testMultipleLevels() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .field("child", "grand_child") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .field("child", "grand_child") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IndexService service = createIndex("test"); DocumentMapper docMapper = service.mapperService() .merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); @@ -211,7 +212,12 @@ public void testMultipleLevels() throws Exception { // Doc without join ParsedDocument doc = docMapper.parse( - new SourceToParse("test", "0", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().endObject()), XContentType.JSON) + new SourceToParse( + "test", + "0", + BytesReference.bytes(XContentFactory.jsonBuilder().startObject().endObject()), + MediaTypeRegistry.JSON + ) ); assertNull(doc.rootDoc().getBinaryValue("join_field")); @@ -221,7 +227,7 @@ public void testMultipleLevels() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("join_field", "parent").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertEquals("1", doc.rootDoc().getBinaryValue("join_field#parent").utf8ToString()); @@ -241,7 +247,7 @@ public void testMultipleLevels() throws Exception { .endObject() .endObject() ), - XContentType.JSON, + MediaTypeRegistry.JSON, "1" ) ); @@ -257,7 +263,7 @@ public void testMultipleLevels() throws Exception { "test", "2", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("join_field", "child").endObject()), - XContentType.JSON, + MediaTypeRegistry.JSON, "1" ) ) @@ -280,7 +286,7 @@ public void testMultipleLevels() throws Exception { .endObject() .endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ) ); @@ -300,7 +306,7 @@ public void testMultipleLevels() throws Exception { .endObject() .endObject() ), - XContentType.JSON, + MediaTypeRegistry.JSON, "1" ) ); @@ -315,7 +321,7 @@ public void testMultipleLevels() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("join_field", "unknown").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) ); @@ -323,39 +329,37 @@ public void testMultipleLevels() throws Exception { } public void testUpdateRelations() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .array("child", "grand_child1", "grand_child2") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); + IndexService indexService = createIndex("test"); + DocumentMapper docMapper = indexService.mapperService() + .merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); + assertTrue(docMapper.mappers().getMapper("join_field") == ParentJoinFieldMapper.getMapper(indexService.mapperService())); + + { + final String updateMapping = XContentFactory.jsonBuilder() .startObject() .startObject("properties") .startObject("join_field") .field("type", "join") .startObject("relations") - .field("parent", "child") .array("child", "grand_child1", "grand_child2") .endObject() .endObject() .endObject() .endObject() - ); - IndexService indexService = createIndex("test"); - DocumentMapper docMapper = indexService.mapperService() - .merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); - assertTrue(docMapper.mappers().getMapper("join_field") == ParentJoinFieldMapper.getMapper(indexService.mapperService())); - - { - final String updateMapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .array("child", "grand_child1", "grand_child2") - .endObject() - .endObject() - .endObject() - .endObject() - ); + .toString(); IllegalArgumentException exc = expectThrows( IllegalArgumentException.class, () -> indexService.mapperService() @@ -365,20 +369,19 @@ public void testUpdateRelations() throws Exception { } { - final String updateMapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .field("child", "grand_child1") - .endObject() - .endObject() - .endObject() - .endObject() - ); + final String updateMapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .field("child", "grand_child1") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IllegalArgumentException exc = expectThrows( IllegalArgumentException.class, () -> indexService.mapperService() @@ -388,21 +391,20 @@ public void testUpdateRelations() throws Exception { } { - final String updateMapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("uber_parent", "parent") - .field("parent", "child") - .array("child", "grand_child1", "grand_child2") - .endObject() - .endObject() - .endObject() - .endObject() - ); + final String updateMapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("uber_parent", "parent") + .field("parent", "child") + .array("child", "grand_child1", "grand_child2") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IllegalArgumentException exc = expectThrows( IllegalArgumentException.class, () -> indexService.mapperService() @@ -412,21 +414,20 @@ public void testUpdateRelations() throws Exception { } { - final String updateMapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .array("child", "grand_child1", "grand_child2") - .field("grand_child2", "grand_grand_child") - .endObject() - .endObject() - .endObject() - .endObject() - ); + final String updateMapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .array("child", "grand_child1", "grand_child2") + .field("grand_child2", "grand_grand_child") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IllegalArgumentException exc = expectThrows( IllegalArgumentException.class, () -> indexService.mapperService() @@ -436,20 +437,19 @@ public void testUpdateRelations() throws Exception { } { - final String updateMapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .array("parent", "child", "child2") - .array("child", "grand_child1", "grand_child2") - .endObject() - .endObject() - .endObject() - .endObject() - ); + final String updateMapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .array("parent", "child", "child2") + .array("child", "grand_child1", "grand_child2") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); docMapper = indexService.mapperService() .merge("type", new CompressedXContent(updateMapping), MapperService.MergeReason.MAPPING_UPDATE); ParentJoinFieldMapper mapper = ParentJoinFieldMapper.getMapper(indexService.mapperService()); @@ -462,21 +462,20 @@ public void testUpdateRelations() throws Exception { } { - final String updateMapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .array("parent", "child", "child2") - .array("child", "grand_child1", "grand_child2") - .array("other", "child_other1", "child_other2") - .endObject() - .endObject() - .endObject() - .endObject() - ); + final String updateMapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .array("parent", "child", "child2") + .array("child", "grand_child1", "grand_child2") + .array("other", "child_other1", "child_other2") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); docMapper = indexService.mapperService() .merge("type", new CompressedXContent(updateMapping), MapperService.MergeReason.MAPPING_UPDATE); ParentJoinFieldMapper mapper = ParentJoinFieldMapper.getMapper(indexService.mapperService()); @@ -492,23 +491,22 @@ public void testUpdateRelations() throws Exception { } public void testInvalidJoinFieldInsideObject() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("object") - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .endObject() - .endObject() - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("object") + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IndexService indexService = createIndex("test"); MapperParsingException exc = expectThrows( MapperParsingException.class, @@ -521,24 +519,23 @@ public void testInvalidJoinFieldInsideObject() throws Exception { } public void testInvalidJoinFieldInsideMultiFields() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("number") - .field("type", "integer") - .startObject("fields") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .endObject() - .endObject() - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("number") + .field("type", "integer") + .startObject("fields") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IndexService indexService = createIndex("test"); MapperParsingException exc = expectThrows( MapperParsingException.class, @@ -553,26 +550,25 @@ public void testInvalidJoinFieldInsideMultiFields() throws Exception { public void testMultipleJoinFields() throws Exception { IndexService indexService = createIndex("test"); { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .field("child", "grand_child") - .endObject() - .endObject() - .startObject("another_join_field") - .field("type", "join") - .startObject("relations") - .field("product", "item") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .field("child", "grand_child") + .endObject() + .endObject() + .startObject("another_join_field") + .field("type", "join") + .startObject("relations") + .field("product", "item") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); MapperParsingException exc = expectThrows( MapperParsingException.class, () -> indexService.mapperService().merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE) @@ -581,43 +577,7 @@ public void testMultipleJoinFields() throws Exception { } { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .startObject("relations") - .field("parent", "child") - .field("child", "grand_child") - .endObject() - .endObject() - .endObject() - .endObject() - ); - indexService.mapperService().merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); - String updateMapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("another_join_field") - .field("type", "join") - .endObject() - .endObject() - .endObject() - ); - MapperParsingException exc = expectThrows( - MapperParsingException.class, - () -> indexService.mapperService() - .merge("type", new CompressedXContent(updateMapping), MapperService.MergeReason.MAPPING_UPDATE) - ); - assertThat(exc.getMessage(), containsString("Field [_parent_join] is defined more than once")); - } - } - - public void testEagerGlobalOrdinals() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() + String mapping = XContentFactory.jsonBuilder() .startObject() .startObject("properties") .startObject("join_field") @@ -629,7 +589,40 @@ public void testEagerGlobalOrdinals() throws Exception { .endObject() .endObject() .endObject() - ); + .toString(); + indexService.mapperService().merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); + String updateMapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("another_join_field") + .field("type", "join") + .endObject() + .endObject() + .endObject() + .toString(); + MapperParsingException exc = expectThrows( + MapperParsingException.class, + () -> indexService.mapperService() + .merge("type", new CompressedXContent(updateMapping), MapperService.MergeReason.MAPPING_UPDATE) + ); + assertThat(exc.getMessage(), containsString("Field [_parent_join] is defined more than once")); + } + } + + public void testEagerGlobalOrdinals() throws Exception { + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .startObject("relations") + .field("parent", "child") + .field("child", "grand_child") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); IndexService service = createIndex("test"); DocumentMapper docMapper = service.mapperService() .merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); @@ -640,21 +633,20 @@ public void testEagerGlobalOrdinals() throws Exception { assertNotNull(service.mapperService().fieldType("join_field#child")); assertTrue(service.mapperService().fieldType("join_field#child").eagerGlobalOrdinals()); - mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("join_field") - .field("type", "join") - .field("eager_global_ordinals", false) - .startObject("relations") - .field("parent", "child") - .field("child", "grand_child") - .endObject() - .endObject() - .endObject() - .endObject() - ); + mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("join_field") + .field("type", "join") + .field("eager_global_ordinals", false) + .startObject("relations") + .field("parent", "child") + .field("child", "grand_child") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); service.mapperService().merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); assertFalse(service.mapperService().fieldType("join_field").eagerGlobalOrdinals()); assertNotNull(service.mapperService().fieldType("join_field#parent")); diff --git a/modules/parent-join/src/test/java/org/opensearch/join/query/HasChildQueryBuilderTests.java b/modules/parent-join/src/test/java/org/opensearch/join/query/HasChildQueryBuilderTests.java index 5595c98a439bf..096b87ec9e3f4 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/query/HasChildQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/query/HasChildQueryBuilderTests.java @@ -33,10 +33,10 @@ package org.opensearch.join.query; import com.carrotsearch.randomizedtesting.generators.RandomPicks; + import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermInSetQuery; @@ -46,13 +46,10 @@ import org.apache.lucene.search.similarities.Similarity; import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.common.Strings; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.mapper.IdFieldMapper; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; -import org.opensearch.index.mapper.Uid; import org.opensearch.index.query.IdsQueryBuilder; import org.opensearch.index.query.InnerHitBuilder; import org.opensearch.index.query.InnerHitContextBuilder; @@ -143,7 +140,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws .endObject() .endObject(); - mapperService.merge(TYPE, new CompressedXContent(Strings.toString(mapping)), MapperService.MergeReason.MAPPING_UPDATE); + mapperService.merge(TYPE, new CompressedXContent(mapping.toString()), MapperService.MergeReason.MAPPING_UPDATE); } /** @@ -306,17 +303,9 @@ static void assertLateParsingQuery(Query query, String type, String id) throws I assertThat(booleanQuery.clauses().get(0).getOccur(), equalTo(BooleanClause.Occur.MUST)); assertThat(booleanQuery.clauses().get(0).getQuery(), instanceOf(TermInSetQuery.class)); TermInSetQuery termsQuery = (TermInSetQuery) booleanQuery.clauses().get(0).getQuery(); - Query rewrittenTermsQuery = termsQuery.rewrite(null); - assertThat(rewrittenTermsQuery, instanceOf(ConstantScoreQuery.class)); - ConstantScoreQuery constantScoreQuery = (ConstantScoreQuery) rewrittenTermsQuery; - assertThat(constantScoreQuery.getQuery(), instanceOf(BooleanQuery.class)); - BooleanQuery booleanTermsQuery = (BooleanQuery) constantScoreQuery.getQuery(); - assertThat(booleanTermsQuery.clauses().toString(), booleanTermsQuery.clauses().size(), equalTo(1)); - assertThat(booleanTermsQuery.clauses().get(0).getOccur(), equalTo(BooleanClause.Occur.SHOULD)); - assertThat(booleanTermsQuery.clauses().get(0).getQuery(), instanceOf(TermQuery.class)); - TermQuery termQuery = (TermQuery) booleanTermsQuery.clauses().get(0).getQuery(); - assertThat(termQuery.getTerm().field(), equalTo(IdFieldMapper.NAME)); - assertThat(termQuery.getTerm().bytes(), equalTo(Uid.encodeId(id))); + // The query is of type MultiTermQueryConstantScoreBlendedWrapper and is sealed inside Apache Lucene, + // no access to inner queries without using the reflection, falling back to stringified query comparison + assertThat(termsQuery.toString(), equalTo("_id:([ff 69 64])")); // check the type filter assertThat(booleanQuery.clauses().get(1).getOccur(), equalTo(BooleanClause.Occur.FILTER)); assertEquals(new TermQuery(new Term("join_field", type)), booleanQuery.clauses().get(1).getQuery()); diff --git a/modules/parent-join/src/test/java/org/opensearch/join/query/HasParentQueryBuilderTests.java b/modules/parent-join/src/test/java/org/opensearch/join/query/HasParentQueryBuilderTests.java index 0f983799a6d25..135f815b5441c 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/query/HasParentQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/query/HasParentQueryBuilderTests.java @@ -37,9 +37,8 @@ import org.apache.lucene.search.join.ScoreMode; import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.common.Strings; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.IdsQueryBuilder; import org.opensearch.index.query.InnerHitBuilder; @@ -122,7 +121,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws .endObject() .endObject(); - mapperService.merge(TYPE, new CompressedXContent(Strings.toString(mapping)), MapperService.MergeReason.MAPPING_UPDATE); + mapperService.merge(TYPE, new CompressedXContent(mapping.toString()), MapperService.MergeReason.MAPPING_UPDATE); } /** diff --git a/modules/parent-join/src/test/java/org/opensearch/join/query/ParentIdQueryBuilderTests.java b/modules/parent-join/src/test/java/org/opensearch/join/query/ParentIdQueryBuilderTests.java index 41bc717db1fc8..558c94741b63d 100644 --- a/modules/parent-join/src/test/java/org/opensearch/join/query/ParentIdQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/opensearch/join/query/ParentIdQueryBuilderTests.java @@ -39,9 +39,8 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.opensearch.OpenSearchException; -import org.opensearch.common.Strings; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.QueryShardContext; import org.opensearch.index.query.QueryShardException; @@ -111,7 +110,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws .endObject() .endObject(); - mapperService.merge(TYPE, new CompressedXContent(Strings.toString(mapping)), MapperService.MergeReason.MAPPING_UPDATE); + mapperService.merge(TYPE, new CompressedXContent(mapping.toString()), MapperService.MergeReason.MAPPING_UPDATE); } @Override diff --git a/modules/percolator/src/internalClusterTest/java/org/opensearch/percolator/PercolatorQuerySearchIT.java b/modules/percolator/src/internalClusterTest/java/org/opensearch/percolator/PercolatorQuerySearchIT.java index 8d3c37bc9b039..516d8c8581de7 100644 --- a/modules/percolator/src/internalClusterTest/java/org/opensearch/percolator/PercolatorQuerySearchIT.java +++ b/modules/percolator/src/internalClusterTest/java/org/opensearch/percolator/PercolatorQuerySearchIT.java @@ -31,20 +31,24 @@ package org.opensearch.percolator; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.join.ScoreMode; import org.opensearch.OpenSearchException; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.DistanceUnit; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentType; -import org.opensearch.geo.GeoPlugin; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.geo.GeoModulePlugin; import org.opensearch.index.mapper.MapperParsingException; import org.opensearch.index.query.MatchPhraseQueryBuilder; import org.opensearch.index.query.MultiMatchQueryBuilder; @@ -53,7 +57,8 @@ import org.opensearch.plugins.Plugin; import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -75,6 +80,7 @@ import static org.opensearch.index.query.QueryBuilders.spanNotQuery; import static org.opensearch.index.query.QueryBuilders.spanTermQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchHits; @@ -84,7 +90,24 @@ import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.IsNull.notNullValue; -public class PercolatorQuerySearchIT extends OpenSearchIntegTestCase { +public class PercolatorQuerySearchIT extends ParameterizedOpenSearchIntegTestCase { + + public PercolatorQuerySearchIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected boolean addMockGeoShapeFieldMapper() { @@ -93,7 +116,7 @@ protected boolean addMockGeoShapeFieldMapper() { @Override protected Collection> nodePlugins() { - return Arrays.asList(PercolatorPlugin.class, GeoPlugin.class); + return Arrays.asList(PercolatorPlugin.class, GeoModulePlugin.class); } public void testPercolatorQuery() throws Exception { @@ -125,14 +148,16 @@ public void testPercolatorQuery() throws Exception { BytesReference source = BytesReference.bytes(jsonBuilder().startObject().endObject()); logger.info("percolating empty doc"); - SearchResponse response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + SearchResponse response = client().prepareSearch() + .setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)) + .get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject()); logger.info("percolating doc with 1 field"); response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)) .addSort("id", SortOrder.ASC) .get(); assertHitCount(response, 2); @@ -144,7 +169,7 @@ public void testPercolatorQuery() throws Exception { source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").field("field2", "value").endObject()); logger.info("percolating doc with 2 fields"); response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)) .addSort("id", SortOrder.ASC) .get(); assertHitCount(response, 3); @@ -164,7 +189,7 @@ public void testPercolatorQuery() throws Exception { BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject()), BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").field("field2", "value").endObject()) ), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .addSort("id", SortOrder.ASC) @@ -267,44 +292,46 @@ public void testPercolatorRangeQueries() throws Exception { // Test long range: BytesReference source = BytesReference.bytes(jsonBuilder().startObject().field("field1", 12).endObject()); - SearchResponse response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + SearchResponse response = client().prepareSearch() + .setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)) + .get(); logger.info("response={}", response); assertHitCount(response, 2); assertThat(response.getHits().getAt(0).getId(), equalTo("3")); assertThat(response.getHits().getAt(1).getId(), equalTo("1")); source = BytesReference.bytes(jsonBuilder().startObject().field("field1", 11).endObject()); - response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)).get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); // Test double range: source = BytesReference.bytes(jsonBuilder().startObject().field("field2", 12).endObject()); - response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)).get(); assertHitCount(response, 2); assertThat(response.getHits().getAt(0).getId(), equalTo("6")); assertThat(response.getHits().getAt(1).getId(), equalTo("4")); source = BytesReference.bytes(jsonBuilder().startObject().field("field2", 11).endObject()); - response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)).get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("4")); // Test IP range: source = BytesReference.bytes(jsonBuilder().startObject().field("field3", "192.168.1.5").endObject()); - response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)).get(); assertHitCount(response, 2); assertThat(response.getHits().getAt(0).getId(), equalTo("9")); assertThat(response.getHits().getAt(1).getId(), equalTo("7")); source = BytesReference.bytes(jsonBuilder().startObject().field("field3", "192.168.1.4").endObject()); - response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)).get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("7")); // Test date range: source = BytesReference.bytes(jsonBuilder().startObject().field("field4", "2016-05-15").endObject()); - response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)).get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("10")); } @@ -355,7 +382,7 @@ public void testPercolatorGeoQueries() throws Exception { jsonBuilder().startObject().startObject("field1").field("lat", 52.20).field("lon", 4.51).endObject().endObject() ); SearchResponse response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)) .addSort("id", SortOrder.ASC) .get(); assertHitCount(response, 3); @@ -390,9 +417,9 @@ public void testPercolatorQueryExistingDocument() throws Exception { ) .get(); - client().prepareIndex("test").setId("4").setSource("{\"id\": \"4\"}", XContentType.JSON).get(); - client().prepareIndex("test").setId("5").setSource(XContentType.JSON, "id", "5", "field1", "value").get(); - client().prepareIndex("test").setId("6").setSource(XContentType.JSON, "id", "6", "field1", "value", "field2", "value").get(); + client().prepareIndex("test").setId("4").setSource("{\"id\": \"4\"}", MediaTypeRegistry.JSON).get(); + client().prepareIndex("test").setId("5").setSource(MediaTypeRegistry.JSON, "id", "5", "field1", "value").get(); + client().prepareIndex("test").setId("6").setSource(MediaTypeRegistry.JSON, "id", "6", "field1", "value", "field2", "value").get(); client().admin().indices().prepareRefresh().get(); logger.info("percolating empty doc"); @@ -432,14 +459,13 @@ public void testPercolatorQueryExistingDocumentSourceDisabled() throws Exception client().prepareIndex("test").setId("1").setSource(jsonBuilder().startObject().field("query", matchAllQuery()).endObject()).get(); - client().prepareIndex("test").setId("2").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId("2").setSource("{}", MediaTypeRegistry.JSON).get(); client().admin().indices().prepareRefresh().get(); logger.info("percolating empty doc with source disabled"); - IllegalArgumentException e = expectThrows( - IllegalArgumentException.class, - () -> { client().prepareSearch().setQuery(new PercolateQueryBuilder("query", "test", "1", null, null, null)).get(); } - ); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> { + client().prepareSearch().setQuery(new PercolateQueryBuilder("query", "test", "1", null, null, null)).get(); + }); assertThat(e.getMessage(), containsString("source disabled")); } @@ -529,7 +555,7 @@ public void testPercolatorSpecificQueries() throws Exception { .endObject() ); SearchResponse response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)) .addSort("id", SortOrder.ASC) .get(); assertHitCount(response, 4); @@ -587,7 +613,7 @@ public void testPercolatorQueryWithHighlighting() throws Exception { jsonBuilder().startObject().field("field1", "The quick brown fox jumps over the lazy dog").endObject() ); SearchResponse searchResponse = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", document, XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("query", document, MediaTypeRegistry.JSON)) .highlighter(new HighlightBuilder().field("field1")) .addSort("id", SortOrder.ASC) .get(); @@ -620,8 +646,8 @@ public void testPercolatorQueryWithHighlighting() throws Exception { BytesReference document2 = BytesReference.bytes(jsonBuilder().startObject().field("field1", "over the lazy dog").endObject()); searchResponse = client().prepareSearch() .setQuery( - boolQuery().should(new PercolateQueryBuilder("query", document1, XContentType.JSON).setName("query1")) - .should(new PercolateQueryBuilder("query", document2, XContentType.JSON).setName("query2")) + boolQuery().should(new PercolateQueryBuilder("query", document1, MediaTypeRegistry.JSON).setName("query1")) + .should(new PercolateQueryBuilder("query", document2, MediaTypeRegistry.JSON).setName("query2")) ) .highlighter(new HighlightBuilder().field("field1")) .addSort("id", SortOrder.ASC) @@ -660,7 +686,7 @@ public void testPercolatorQueryWithHighlighting() throws Exception { BytesReference.bytes(jsonBuilder().startObject().field("field1", "jumps").endObject()), BytesReference.bytes(jsonBuilder().startObject().field("field1", "brown fox").endObject()) ), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .highlighter(new HighlightBuilder().field("field1")) @@ -713,7 +739,7 @@ public void testPercolatorQueryWithHighlighting() throws Exception { BytesReference.bytes(jsonBuilder().startObject().field("field1", "dog").endObject()), BytesReference.bytes(jsonBuilder().startObject().field("field1", "fox").endObject()) ), - XContentType.JSON + MediaTypeRegistry.JSON ).setName("query1") ) .should( @@ -723,7 +749,7 @@ public void testPercolatorQueryWithHighlighting() throws Exception { BytesReference.bytes(jsonBuilder().startObject().field("field1", "jumps").endObject()), BytesReference.bytes(jsonBuilder().startObject().field("field1", "brown fox").endObject()) ), - XContentType.JSON + MediaTypeRegistry.JSON ).setName("query2") ) ) @@ -812,7 +838,7 @@ public void testTakePositionOffsetGapIntoAccount() throws Exception { client().admin().indices().prepareRefresh().get(); SearchResponse response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", new BytesArray("{\"field\" : [\"brown\", \"fox\"]}"), XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("query", new BytesArray("{\"field\" : [\"brown\", \"fox\"]}"), MediaTypeRegistry.JSON)) .get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("2")); @@ -900,7 +926,7 @@ public void testWithMultiplePercolatorFields() throws Exception { BytesReference source = BytesReference.bytes(jsonBuilder().startObject().field("field", "value").endObject()); SearchResponse response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder(queryFieldName, source, XContentType.JSON)) + .setQuery(new PercolateQueryBuilder(queryFieldName, source, MediaTypeRegistry.JSON)) .setIndices("test1") .get(); assertHitCount(response, 1); @@ -908,7 +934,7 @@ public void testWithMultiplePercolatorFields() throws Exception { assertThat(response.getHits().getAt(0).getIndex(), equalTo("test1")); response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("object_field." + queryFieldName, source, XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("object_field." + queryFieldName, source, MediaTypeRegistry.JSON)) .setIndices("test2") .get(); assertHitCount(response, 1); @@ -1013,7 +1039,7 @@ public void testPercolateQueryWithNestedDocuments() throws Exception { .endArray() .endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .addSort("id", SortOrder.ASC) @@ -1040,7 +1066,7 @@ public void testPercolateQueryWithNestedDocuments() throws Exception { .endArray() .endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .addSort("id", SortOrder.ASC) @@ -1053,7 +1079,7 @@ public void testPercolateQueryWithNestedDocuments() throws Exception { new PercolateQueryBuilder( "query", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("companyname", "notstark").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .addSort("id", SortOrder.ASC) @@ -1106,7 +1132,7 @@ public void testPercolateQueryWithNestedDocuments() throws Exception { .endObject() ) ), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .addSort("id", SortOrder.ASC) @@ -1159,7 +1185,7 @@ public void testPercolatorQueryViaMultiSearch() throws Exception { new PercolateQueryBuilder( "query", BytesReference.bytes(jsonBuilder().startObject().field("field1", "b").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) ) @@ -1179,7 +1205,7 @@ public void testPercolatorQueryViaMultiSearch() throws Exception { new PercolateQueryBuilder( "query", BytesReference.bytes(jsonBuilder().startObject().field("field1", "b c").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) ) @@ -1189,7 +1215,7 @@ public void testPercolatorQueryViaMultiSearch() throws Exception { new PercolateQueryBuilder( "query", BytesReference.bytes(jsonBuilder().startObject().field("field1", "d").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) ) @@ -1249,7 +1275,7 @@ public void testDisallowExpensiveQueries() throws IOException { // Execute with search.allow_expensive_queries = null => default value = false => success BytesReference source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject()); SearchResponse response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)) .get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); @@ -1262,7 +1288,7 @@ public void testDisallowExpensiveQueries() throws IOException { OpenSearchException e = expectThrows( OpenSearchException.class, - () -> client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get() + () -> client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)).get() ); assertEquals( "[percolate] queries cannot be executed when 'search.allow_expensive_queries' is set to false.", @@ -1274,7 +1300,7 @@ public void testDisallowExpensiveQueries() throws IOException { updateSettingsRequest.persistentSettings(Settings.builder().put("search.allow_expensive_queries", true)); assertAcked(client().admin().cluster().updateSettings(updateSettingsRequest).actionGet()); - response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, XContentType.JSON)).get(); + response = client().prepareSearch().setQuery(new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON)).get(); assertHitCount(response, 1); assertThat(response.getHits().getAt(0).getId(), equalTo("1")); assertThat(response.getHits().getAt(0).getFields().get("_percolator_document_slot").getValue(), equalTo(0)); @@ -1308,7 +1334,7 @@ public void testWrappedWithConstantScore() throws Exception { new PercolateQueryBuilder( "q", BytesReference.bytes(jsonBuilder().startObject().field("d", "2020-02-01T15:00:00.000+11:00").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .get(); @@ -1319,7 +1345,7 @@ public void testWrappedWithConstantScore() throws Exception { new PercolateQueryBuilder( "q", BytesReference.bytes(jsonBuilder().startObject().field("d", "2020-02-01T15:00:00.000+11:00").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .addSort("_doc", SortOrder.ASC) @@ -1332,7 +1358,7 @@ public void testWrappedWithConstantScore() throws Exception { new PercolateQueryBuilder( "q", BytesReference.bytes(jsonBuilder().startObject().field("d", "2020-02-01T15:00:00.000+11:00").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) ) diff --git a/modules/percolator/src/main/java/org/opensearch/percolator/PercolateQuery.java b/modules/percolator/src/main/java/org/opensearch/percolator/PercolateQuery.java index 0aa8318e7c191..99907b48d4fdd 100644 --- a/modules/percolator/src/main/java/org/opensearch/percolator/PercolateQuery.java +++ b/modules/percolator/src/main/java/org/opensearch/percolator/PercolateQuery.java @@ -32,8 +32,9 @@ package org.opensearch.percolator; -import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; @@ -45,13 +46,11 @@ import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TwoPhaseIterator; import org.apache.lucene.search.Weight; -import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.util.Accountable; import org.apache.lucene.util.Bits; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.lucene.Lucene; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; import java.util.List; @@ -89,8 +88,8 @@ final class PercolateQuery extends Query implements Accountable { } @Override - public Query rewrite(IndexReader reader) throws IOException { - Query rewritten = candidateMatchesQuery.rewrite(reader); + public Query rewrite(IndexSearcher searcher) throws IOException { + Query rewritten = candidateMatchesQuery.rewrite(searcher); if (rewritten != candidateMatchesQuery) { return new PercolateQuery( name, diff --git a/modules/percolator/src/main/java/org/opensearch/percolator/PercolateQueryBuilder.java b/modules/percolator/src/main/java/org/opensearch/percolator/PercolateQueryBuilder.java index b2130eca3bb02..6933bfbef4666 100644 --- a/modules/percolator/src/main/java/org/opensearch/percolator/PercolateQueryBuilder.java +++ b/modules/percolator/src/main/java/org/opensearch/percolator/PercolateQueryBuilder.java @@ -54,27 +54,31 @@ import org.apache.lucene.util.BitDocIdSet; import org.apache.lucene.util.BitSet; import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.SetOnce; import org.opensearch.OpenSearchException; import org.opensearch.ResourceNotFoundException; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.get.GetRequest; -import org.opensearch.common.ParseField; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.InputStreamStreamInput; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; +import org.opensearch.common.SetOnce; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.InputStreamStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.indices.breaker.CircuitBreakerService; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.analysis.FieldNameAnalyzer; import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.index.fielddata.IndexFieldDataCache; @@ -90,8 +94,6 @@ import org.opensearch.index.query.QueryShardContext; import org.opensearch.index.query.QueryShardException; import org.opensearch.index.query.Rewriteable; -import org.opensearch.indices.breaker.CircuitBreakerService; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -103,8 +105,8 @@ import java.util.Objects; import java.util.function.Supplier; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.opensearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES; public class PercolateQueryBuilder extends AbstractQueryBuilder { @@ -123,7 +125,7 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder documents; - private final XContentType documentXContentType; + private final MediaType documentXContentType; private final String indexedDocumentIndex; private final String indexedDocumentId; @@ -137,10 +139,10 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder documents, XContentType documentXContentType) { + public PercolateQueryBuilder(String field, List documents, MediaType documentXContentType) { if (field == null) { throw new IllegalArgumentException("[field] is a required argument"); } @@ -252,7 +254,11 @@ protected PercolateQueryBuilder(String field, Supplier documentS } documents = in.readList(StreamInput::readBytesReference); if (documents.isEmpty() == false) { - documentXContentType = in.readEnum(XContentType.class); + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + documentXContentType = in.readMediaType(); + } else { + documentXContentType = in.readEnum(XContentType.class); + } } else { documentXContentType = null; } @@ -298,7 +304,11 @@ protected void doWriteTo(StreamOutput out) throws IOException { out.writeBytesReference(document); } if (documents.isEmpty() == false) { - out.writeEnum(documentXContentType); + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + documentXContentType.writeTo(out); + } else { + out.writeEnum((XContentType) documentXContentType); + } } } @@ -359,9 +369,9 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep if (indexedDocId != null) { return new PercolateQueryBuilder(field, indexedDocIndex, indexedDocId, indexDocRouting, indexDocPreference, indexedDocVersion); } else if (document != null) { - return new PercolateQueryBuilder(field, Collections.singletonList(document), XContentType.JSON); + return new PercolateQueryBuilder(field, Collections.singletonList(document), MediaTypeRegistry.JSON); } else { - return new PercolateQueryBuilder(field, documents, XContentType.JSON); + return new PercolateQueryBuilder(field, documents, MediaTypeRegistry.JSON); } }); static { @@ -432,7 +442,7 @@ protected QueryBuilder doRewrite(QueryRewriteContext queryShardContext) { PercolateQueryBuilder rewritten = new PercolateQueryBuilder( field, Collections.singletonList(source), - XContentHelper.xContentType(source) + MediaTypeRegistry.xContentType(source) ); if (name != null) { rewritten.setName(name); @@ -546,7 +556,6 @@ protected Analyzer getWrappedAnalyzer(String fieldName) { String name = this.name != null ? this.name : pft.name(); QueryShardContext percolateShardContext = wrap(context); PercolatorFieldMapper.configureContext(percolateShardContext, pft.mapUnmappedFieldsAsText); - ; PercolateQuery.QueryStore queryStore = createStore(pft.queryBuilderField, percolateShardContext); return pft.percolateQuery(name, queryStore, documents, docSearcher, excludeNestedDocuments, context.indexVersionCreated()); @@ -561,7 +570,7 @@ public List getDocuments() { } // pkg-private for testing - XContentType getXContentType() { + MediaType getXContentType() { return documentXContentType; } diff --git a/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorFieldMapper.java b/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorFieldMapper.java index fec38207582e7..e30ce218ed5ff 100644 --- a/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorFieldMapper.java +++ b/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorFieldMapper.java @@ -56,16 +56,16 @@ import org.apache.lucene.util.BytesRefBuilder; import org.opensearch.Version; import org.opensearch.action.support.PlainActionFuture; -import org.opensearch.common.ParsingException; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.hash.MurmurHash3; -import org.opensearch.common.io.stream.OutputStreamStreamOutput; import org.opensearch.common.lucene.search.Queries; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentLocation; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.OutputStreamStreamOutput; +import org.opensearch.core.xcontent.XContentLocation; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.BinaryFieldMapper; import org.opensearch.index.mapper.FieldMapper; import org.opensearch.index.mapper.KeywordFieldMapper; diff --git a/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorHighlightSubFetchPhase.java b/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorHighlightSubFetchPhase.java index 9b4e42d239750..78e5980e88d5c 100644 --- a/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorHighlightSubFetchPhase.java +++ b/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorHighlightSubFetchPhase.java @@ -36,8 +36,8 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryVisitor; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.document.DocumentField; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.search.SearchHit; import org.opensearch.search.fetch.FetchContext; import org.opensearch.search.fetch.FetchSubPhase; diff --git a/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorMatchedSlotSubFetchPhase.java b/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorMatchedSlotSubFetchPhase.java index a157a20f5f2c4..0884d534849da 100644 --- a/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorMatchedSlotSubFetchPhase.java +++ b/modules/percolator/src/main/java/org/opensearch/percolator/PercolatorMatchedSlotSubFetchPhase.java @@ -57,8 +57,8 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import static org.apache.lucene.search.DocIdSetIterator.NO_MORE_DOCS; import static org.opensearch.percolator.PercolatorHighlightSubFetchPhase.locatePercolatorQuery; +import static org.apache.lucene.search.DocIdSetIterator.NO_MORE_DOCS; /** * Adds a special field to a percolator query hit to indicate which documents matched with the percolator query. diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/CandidateQueryTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/CandidateQueryTests.java index e59aa227e3dc7..64c0c01be6314 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/CandidateQueryTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/CandidateQueryTests.java @@ -94,14 +94,13 @@ import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.lucene.search.function.FunctionScoreQuery; import org.opensearch.common.geo.ShapeRelation; +import org.opensearch.common.lucene.search.function.FunctionScoreQuery; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.mapper.DocumentMapper; @@ -112,9 +111,9 @@ import org.opensearch.index.query.QueryShardContext; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchSingleNodeTestCase; +import org.opensearch.test.VersionUtils; import org.junit.After; import org.junit.Before; -import org.opensearch.test.VersionUtils; import java.io.IOException; import java.util.ArrayList; @@ -163,51 +162,49 @@ public void init() throws Exception { indexService = createIndex(indexName, Settings.EMPTY); mapperService = indexService.mapperService(); - String mapper = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("type") - .startObject("properties") - .startObject("int_field") - .field("type", "integer") - .endObject() - .startObject("long_field") - .field("type", "long") - .endObject() - .startObject("half_float_field") - .field("type", "half_float") - .endObject() - .startObject("float_field") - .field("type", "float") - .endObject() - .startObject("double_field") - .field("type", "double") - .endObject() - .startObject("ip_field") - .field("type", "ip") - .endObject() - .startObject("field") - .field("type", "keyword") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapper = XContentFactory.jsonBuilder() + .startObject() + .startObject("type") + .startObject("properties") + .startObject("int_field") + .field("type", "integer") + .endObject() + .startObject("long_field") + .field("type", "long") + .endObject() + .startObject("half_float_field") + .field("type", "half_float") + .endObject() + .startObject("float_field") + .field("type", "float") + .endObject() + .startObject("double_field") + .field("type", "double") + .endObject() + .startObject("ip_field") + .field("type", "ip") + .endObject() + .startObject("field") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); documentMapper = mapperService.merge("type", new CompressedXContent(mapper), MapperService.MergeReason.MAPPING_UPDATE); String queryField = "query_field"; - String percolatorMapper = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("type") - .startObject("properties") - .startObject(queryField) - .field("type", "percolator") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String percolatorMapper = XContentFactory.jsonBuilder() + .startObject() + .startObject("type") + .startObject("properties") + .startObject(queryField) + .field("type", "percolator") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); mapperService.merge("type", new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE); fieldMapper = (PercolatorFieldMapper) mapperService.documentMapper().mappers().getMapper(queryField); fieldType = (PercolatorFieldMapper.PercolatorFieldType) fieldMapper.fieldType(); @@ -1275,7 +1272,7 @@ private CustomQuery(Term term) { } @Override - public Query rewrite(IndexReader reader) throws IOException { + public Query rewrite(IndexSearcher searcher) throws IOException { return new TermQuery(term); } diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/PercolateQueryBuilderTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/PercolateQueryBuilderTests.java index 87aa28a3346bc..9891ec8527267 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/PercolateQueryBuilderTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/PercolateQueryBuilderTests.java @@ -38,15 +38,14 @@ import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest; import org.opensearch.action.get.GetRequest; import org.opensearch.action.get.GetResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.io.stream.BytesStreamOutput; import org.opensearch.common.lucene.uid.Versions; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.get.GetResult; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.QueryBuilder; @@ -109,15 +108,13 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws mapperService.merge( docType, new CompressedXContent( - Strings.toString( - PutMappingRequest.simpleMapping(queryField, "type=percolator", aliasField, "type=alias,path=" + queryField) - ) + PutMappingRequest.simpleMapping(queryField, "type=percolator", aliasField, "type=alias,path=" + queryField).toString() ), MapperService.MergeReason.MAPPING_UPDATE ); mapperService.merge( docType, - new CompressedXContent(Strings.toString(PutMappingRequest.simpleMapping(TEXT_FIELD_NAME, "type=text"))), + new CompressedXContent(PutMappingRequest.simpleMapping(TEXT_FIELD_NAME, "type=text").toString()), MapperService.MergeReason.MAPPING_UPDATE ); } @@ -155,7 +152,7 @@ private PercolateQueryBuilder doCreateTestQueryBuilder(boolean indexedDocument) indexedDocumentVersion ); } else { - queryBuilder = new PercolateQueryBuilder(queryField, documentSource, XContentType.JSON); + queryBuilder = new PercolateQueryBuilder(queryField, documentSource, MediaTypeRegistry.JSON); } if (randomBoolean()) { queryBuilder.setName(randomAlphaOfLength(4)); @@ -224,7 +221,7 @@ public void testMustRewrite() throws IOException { IllegalStateException e = expectThrows(IllegalStateException.class, () -> pqb.toQuery(createShardContext())); assertThat(e.getMessage(), equalTo("query builder must be rewritten first")); QueryBuilder rewrite = rewriteAndFetch(pqb, createShardContext()); - PercolateQueryBuilder geoShapeQueryBuilder = new PercolateQueryBuilder(pqb.getField(), documentSource, XContentType.JSON); + PercolateQueryBuilder geoShapeQueryBuilder = new PercolateQueryBuilder(pqb.getField(), documentSource, MediaTypeRegistry.JSON); assertEquals(geoShapeQueryBuilder, rewrite); } @@ -245,15 +242,14 @@ protected Set getObjectsHoldingArbitraryContent() { } public void testRequiredParameters() { - IllegalArgumentException e = expectThrows( - IllegalArgumentException.class, - () -> { new PercolateQueryBuilder(null, new BytesArray("{}"), XContentType.JSON); } - ); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> { + new PercolateQueryBuilder(null, new BytesArray("{}"), MediaTypeRegistry.JSON); + }); assertThat(e.getMessage(), equalTo("[field] is a required argument")); e = expectThrows( IllegalArgumentException.class, - () -> new PercolateQueryBuilder("_field", (List) null, XContentType.JSON) + () -> new PercolateQueryBuilder("_field", (List) null, MediaTypeRegistry.JSON) ); assertThat(e.getMessage(), equalTo("[document] is a required argument")); diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/PercolateQueryTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/PercolateQueryTests.java index c5049e21acc0c..f75367fc054c0 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/PercolateQueryTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/PercolateQueryTests.java @@ -56,7 +56,7 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; -import org.opensearch.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesArray; import org.opensearch.test.OpenSearchTestCase; import org.junit.After; import org.junit.Before; diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/PercolateWithNestedQueryBuilderTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/PercolateWithNestedQueryBuilderTests.java index 0ab9eff731bff..a5682928863cc 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/PercolateWithNestedQueryBuilderTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/PercolateWithNestedQueryBuilderTests.java @@ -33,10 +33,9 @@ package org.opensearch.percolator; import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryShardContext; @@ -50,7 +49,7 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws super.initializeAdditionalMappings(mapperService); mapperService.merge( "_doc", - new CompressedXContent(Strings.toString(PutMappingRequest.simpleMapping("some_nested_object", "type=nested"))), + new CompressedXContent(PutMappingRequest.simpleMapping("some_nested_object", "type=nested").toString()), MapperService.MergeReason.MAPPING_UPDATE ); } @@ -58,7 +57,11 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws public void testDetectsNestedDocuments() throws IOException { QueryShardContext shardContext = createShardContext(); - PercolateQueryBuilder builder = new PercolateQueryBuilder(queryField, new BytesArray("{ \"foo\": \"bar\" }"), XContentType.JSON); + PercolateQueryBuilder builder = new PercolateQueryBuilder( + queryField, + new BytesArray("{ \"foo\": \"bar\" }"), + MediaTypeRegistry.JSON + ); QueryBuilder rewrittenBuilder = rewriteAndFetch(builder, shardContext); PercolateQuery query = (PercolateQuery) rewrittenBuilder.toQuery(shardContext); assertFalse(query.excludesNestedDocs()); @@ -66,7 +69,7 @@ public void testDetectsNestedDocuments() throws IOException { builder = new PercolateQueryBuilder( queryField, new BytesArray("{ \"foo\": \"bar\", \"some_nested_object\": [ { \"baz\": 42 } ] }"), - XContentType.JSON + MediaTypeRegistry.JSON ); rewrittenBuilder = rewriteAndFetch(builder, shardContext); query = (PercolateQuery) rewrittenBuilder.toQuery(shardContext); diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorFieldMapperTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorFieldMapperTests.java index ca6f3a78b27d7..95da3c54ec61e 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorFieldMapperTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorFieldMapperTests.java @@ -56,21 +56,20 @@ import org.opensearch.Version; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.hash.MurmurHash3; -import org.opensearch.common.io.stream.InputStreamStreamInput; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.network.InetAddresses; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.InputStreamStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.mapper.DocumentMapper; @@ -101,8 +100,8 @@ import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; -import org.opensearch.test.OpenSearchSingleNodeTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchSingleNodeTestCase; import org.junit.Before; import java.io.ByteArrayInputStream; @@ -158,67 +157,65 @@ public void init() throws Exception { indexService = createIndex("test"); mapperService = indexService.mapperService(); - String mapper = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("field") - .field("type", "text") - .endObject() - .startObject("field1") - .field("type", "text") - .endObject() - .startObject("field2") - .field("type", "text") - .endObject() - .startObject("_field3") - .field("type", "text") - .endObject() - .startObject("field4") - .field("type", "text") - .endObject() - .startObject("number_field1") - .field("type", "integer") - .endObject() - .startObject("number_field2") - .field("type", "long") - .endObject() - .startObject("number_field3") - .field("type", "long") - .endObject() - .startObject("number_field4") - .field("type", "half_float") - .endObject() - .startObject("number_field5") - .field("type", "float") - .endObject() - .startObject("number_field6") - .field("type", "double") - .endObject() - .startObject("number_field7") - .field("type", "ip") - .endObject() - .startObject("date_field") - .field("type", "date") - .endObject() - .endObject() - .endObject() - ); + String mapper = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("field") + .field("type", "text") + .endObject() + .startObject("field1") + .field("type", "text") + .endObject() + .startObject("field2") + .field("type", "text") + .endObject() + .startObject("_field3") + .field("type", "text") + .endObject() + .startObject("field4") + .field("type", "text") + .endObject() + .startObject("number_field1") + .field("type", "integer") + .endObject() + .startObject("number_field2") + .field("type", "long") + .endObject() + .startObject("number_field3") + .field("type", "long") + .endObject() + .startObject("number_field4") + .field("type", "half_float") + .endObject() + .startObject("number_field5") + .field("type", "float") + .endObject() + .startObject("number_field6") + .field("type", "double") + .endObject() + .startObject("number_field7") + .field("type", "ip") + .endObject() + .startObject("date_field") + .field("type", "date") + .endObject() + .endObject() + .endObject() + .toString(); mapperService.merge(MapperService.SINGLE_MAPPING_NAME, new CompressedXContent(mapper), MapperService.MergeReason.MAPPING_UPDATE); } private void addQueryFieldMappings() throws Exception { fieldName = randomAlphaOfLength(4); - String percolatorMapper = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject(fieldName) - .field("type", "percolator") - .endObject() - .endObject() - .endObject() - ); + String percolatorMapper = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject(fieldName) + .field("type", "percolator") + .endObject() + .endObject() + .endObject() + .toString(); mapperService.merge( MapperService.SINGLE_MAPPING_NAME, new CompressedXContent(percolatorMapper), @@ -555,7 +552,7 @@ public void testPercolatorFieldMapper() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, queryBuilder).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -575,7 +572,7 @@ public void testPercolatorFieldMapper() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, queryBuilder).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertThat(doc.rootDoc().getFields(fieldType.extractionResultField.name()).length, equalTo(1)); @@ -592,7 +589,7 @@ public void testPercolatorFieldMapper() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, queryBuilder).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertThat(doc.rootDoc().getFields(fieldType.extractionResultField.name()).length, equalTo(1)); @@ -620,7 +617,7 @@ public void testStoringQueries() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, query).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); BytesRef qbSource = doc.rootDoc().getFields(fieldType.queryBuilderField.name())[0].binaryValue(); @@ -638,7 +635,7 @@ public void testQueryWithRewrite() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, queryBuilder).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); BytesRef qbSource = doc.rootDoc().getFields(fieldType.queryBuilderField.name())[0].binaryValue(); @@ -664,7 +661,7 @@ public void testPercolatorFieldMapperUnMappedField() throws Exception { BytesReference.bytes( XContentFactory.jsonBuilder().startObject().field(fieldName, termQuery("unmapped_field", "value")).endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); }); @@ -680,7 +677,7 @@ public void testPercolatorFieldMapper_noQuery() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertThat(doc.rootDoc().getFields(fieldType.queryBuilderField.name()).length, equalTo(0)); @@ -692,7 +689,7 @@ public void testPercolatorFieldMapper_noQuery() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().nullField(fieldName).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); } catch (MapperParsingException e) { @@ -705,17 +702,16 @@ public void testAllowNoAdditionalSettings() throws Exception { IndexService indexService = createIndex("test1", Settings.EMPTY); MapperService mapperService = indexService.mapperService(); - String percolatorMapper = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject(fieldName) - .field("type", "percolator") - .field("index", "no") - .endObject() - .endObject() - .endObject() - ); + String percolatorMapper = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject(fieldName) + .field("type", "percolator") + .field("index", "no") + .endObject() + .endObject() + .endObject() + .toString(); MapperParsingException e = expectThrows( MapperParsingException.class, () -> mapperService.merge( @@ -730,21 +726,20 @@ public void testAllowNoAdditionalSettings() throws Exception { // multiple percolator fields are allowed in the mapping, but only one field can be used at index time. public void testMultiplePercolatorFields() throws Exception { String typeName = MapperService.SINGLE_MAPPING_NAME; - String percolatorMapper = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject(typeName) - .startObject("properties") - .startObject("query_field1") - .field("type", "percolator") - .endObject() - .startObject("query_field2") - .field("type", "percolator") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String percolatorMapper = XContentFactory.jsonBuilder() + .startObject() + .startObject(typeName) + .startObject("properties") + .startObject("query_field1") + .field("type", "percolator") + .endObject() + .startObject("query_field2") + .field("type", "percolator") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); mapperService.merge(typeName, new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE); QueryBuilder queryBuilder = matchQuery("field", "value"); @@ -756,7 +751,7 @@ public void testMultiplePercolatorFields() throws Exception { BytesReference.bytes( jsonBuilder().startObject().field("query_field1", queryBuilder).field("query_field2", queryBuilder).endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertThat(doc.rootDoc().getFields().size(), equalTo(16)); // also includes all other meta fields @@ -770,23 +765,22 @@ public void testMultiplePercolatorFields() throws Exception { // percolator field can be nested under an object field, but only one query can be specified per document public void testNestedPercolatorField() throws Exception { String typeName = MapperService.SINGLE_MAPPING_NAME; - String percolatorMapper = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject(typeName) - .startObject("properties") - .startObject("object_field") - .field("type", "object") - .startObject("properties") - .startObject("query_field") - .field("type", "percolator") - .endObject() - .endObject() - .endObject() - .endObject() - .endObject() - .endObject() - ); + String percolatorMapper = XContentFactory.jsonBuilder() + .startObject() + .startObject(typeName) + .startObject("properties") + .startObject("object_field") + .field("type", "object") + .startObject("properties") + .startObject("query_field") + .field("type", "percolator") + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); mapperService.merge(typeName, new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE); QueryBuilder queryBuilder = matchQuery("field", "value"); @@ -798,7 +792,7 @@ public void testNestedPercolatorField() throws Exception { BytesReference.bytes( jsonBuilder().startObject().startObject("object_field").field("query_field", queryBuilder).endObject().endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertThat(doc.rootDoc().getFields().size(), equalTo(12)); // also includes all other meta fields @@ -823,7 +817,7 @@ public void testNestedPercolatorField() throws Exception { .endArray() .endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); assertThat(doc.rootDoc().getFields().size(), equalTo(12)); // also includes all other meta fields @@ -848,7 +842,7 @@ public void testNestedPercolatorField() throws Exception { .endArray() .endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); }); @@ -862,7 +856,7 @@ public void testUnsupportedQueries() { PercolatorFieldMapper.verifyQuery(rangeQuery1); PercolatorFieldMapper.verifyQuery(rangeQuery2); - HasChildQueryBuilder hasChildQuery = new HasChildQueryBuilder("_type", new MatchAllQueryBuilder(), ScoreMode.None); + HasChildQueryBuilder hasChildQuery = new HasChildQueryBuilder("parent", new MatchAllQueryBuilder(), ScoreMode.None); expectThrows(IllegalArgumentException.class, () -> PercolatorFieldMapper.verifyQuery(new BoolQueryBuilder().must(hasChildQuery))); expectThrows(IllegalArgumentException.class, () -> PercolatorFieldMapper.verifyQuery(new DisMaxQueryBuilder().add(hasChildQuery))); PercolatorFieldMapper.verifyQuery(new ConstantScoreQueryBuilder((rangeQuery1))); @@ -881,7 +875,7 @@ public void testUnsupportedQueries() { expectThrows(IllegalArgumentException.class, () -> PercolatorFieldMapper.verifyQuery(hasChildQuery)); expectThrows(IllegalArgumentException.class, () -> PercolatorFieldMapper.verifyQuery(new BoolQueryBuilder().must(hasChildQuery))); - HasParentQueryBuilder hasParentQuery = new HasParentQueryBuilder("_type", new MatchAllQueryBuilder(), false); + HasParentQueryBuilder hasParentQuery = new HasParentQueryBuilder("parent", new MatchAllQueryBuilder(), false); expectThrows(IllegalArgumentException.class, () -> PercolatorFieldMapper.verifyQuery(hasParentQuery)); expectThrows(IllegalArgumentException.class, () -> PercolatorFieldMapper.verifyQuery(new BoolQueryBuilder().must(hasParentQuery))); } @@ -902,18 +896,17 @@ private void assertQueryBuilder(BytesRef actual, QueryBuilder expected) throws I } public void testEmptyName() throws Exception { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("type1") - .startObject("properties") - .startObject("") - .field("type", "percolator") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("type1") + .startObject("properties") + .startObject("") + .field("type", "percolator") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); DocumentMapperParser parser = mapperService.documentMapperParser(); IllegalArgumentException e = expectThrows( @@ -946,10 +939,10 @@ public void testImplicitlySetDefaultScriptLang() throws Exception { BytesReference.bytes( XContentFactory.jsonBuilder() .startObject() - .rawField(fieldName, new BytesArray(Strings.toString(query)).streamInput(), query.contentType()) + .rawField(fieldName, new BytesArray(query.toString()).streamInput(), query.contentType()) .endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); BytesRef querySource = doc.rootDoc().getFields(fieldType.queryBuilderField.name())[0].binaryValue(); @@ -993,10 +986,10 @@ public void testImplicitlySetDefaultScriptLang() throws Exception { BytesReference.bytes( XContentFactory.jsonBuilder() .startObject() - .rawField(fieldName, new BytesArray(Strings.toString(query)).streamInput(), query.contentType()) + .rawField(fieldName, new BytesArray(query.toString()).streamInput(), query.contentType()) .endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); querySource = doc.rootDoc().getFields(fieldType.queryBuilderField.name())[0].binaryValue(); @@ -1085,7 +1078,7 @@ public void testDuplicatedClauses() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, qb).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -1110,7 +1103,7 @@ public void testDuplicatedClauses() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, qb).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -1138,7 +1131,7 @@ public void testDuplicatedClauses() throws Exception { "test", "1", BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(fieldName, qb).endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ); diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorHighlightSubFetchPhaseTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorHighlightSubFetchPhaseTests.java index 83ca9037658b5..18ab11864397f 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorHighlightSubFetchPhaseTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorHighlightSubFetchPhaseTests.java @@ -38,17 +38,18 @@ import org.apache.lucene.search.DisjunctionMaxQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.lucene.search.function.FunctionScoreQuery; import org.opensearch.common.lucene.search.function.RandomScoreFunction; +import org.opensearch.core.common.bytes.BytesArray; import org.opensearch.search.fetch.FetchContext; import org.opensearch.search.fetch.subphase.highlight.SearchHighlightContext; import org.opensearch.test.OpenSearchTestCase; -import org.mockito.Mockito; import java.util.Arrays; import java.util.Collections; +import org.mockito.Mockito; + import static java.util.Collections.emptyMap; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorMatchedSlotSubFetchPhaseTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorMatchedSlotSubFetchPhaseTests.java index efa5a7a3d5095..ffb1764d7f3d6 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorMatchedSlotSubFetchPhaseTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorMatchedSlotSubFetchPhaseTests.java @@ -36,7 +36,6 @@ import org.apache.lucene.document.NumericDocValuesField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.index.memory.MemoryIndex; import org.apache.lucene.search.MatchAllDocsQuery; @@ -46,6 +45,7 @@ import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TotalHits; import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.FixedBitSet; import org.opensearch.index.mapper.SeqNoFieldMapper; import org.opensearch.search.SearchHit; diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorQuerySearchTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorQuerySearchTests.java index 1d77c9d472864..07c7234dff587 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorQuerySearchTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/PercolatorQuerySearchTests.java @@ -35,11 +35,11 @@ import org.apache.lucene.search.join.ScoreMode; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.WriteRequest; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexService; import org.opensearch.index.cache.bitset.BitsetFilterCache; import org.opensearch.index.engine.Engine; @@ -115,7 +115,7 @@ public void testPercolateScriptQuery() throws IOException { new PercolateQueryBuilder( "query", BytesReference.bytes(jsonBuilder().startObject().field("field1", "b").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .get(); @@ -188,7 +188,7 @@ public void testPercolateQueryWithNestedDocuments_doNotLeakBitsetCacheEntries() .endArray() .endObject() ), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .addSort("_doc", SortOrder.ASC) @@ -269,7 +269,7 @@ public void testPercolateQueryWithNestedDocuments_doLeakFieldDataCacheEntries() doc.endObject(); for (int i = 0; i < 32; i++) { SearchResponse response = client().prepareSearch() - .setQuery(new PercolateQueryBuilder("query", BytesReference.bytes(doc), XContentType.JSON)) + .setQuery(new PercolateQueryBuilder("query", BytesReference.bytes(doc), MediaTypeRegistry.JSON)) .addSort("_doc", SortOrder.ASC) .get(); assertHitCount(response, 1); @@ -293,7 +293,7 @@ public void testMapUnmappedFieldAsText() throws IOException { new PercolateQueryBuilder( "query", BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").endObject()), - XContentType.JSON + MediaTypeRegistry.JSON ) ) .get(); @@ -348,13 +348,13 @@ public void testRangeQueriesWithNow() throws Exception { BytesReference source = BytesReference.bytes( jsonBuilder().startObject().field("field1", "value").field("field2", currentTime[0]).endObject() ); - QueryBuilder queryBuilder = new PercolateQueryBuilder("query", source, XContentType.JSON); + QueryBuilder queryBuilder = new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON); Query query = queryBuilder.toQuery(queryShardContext); assertThat(searcher.count(query), equalTo(3)); currentTime[0] = currentTime[0] + 10800000; // + 3 hours source = BytesReference.bytes(jsonBuilder().startObject().field("field1", "value").field("field2", currentTime[0]).endObject()); - queryBuilder = new PercolateQueryBuilder("query", source, XContentType.JSON); + queryBuilder = new PercolateQueryBuilder("query", source, MediaTypeRegistry.JSON); query = queryBuilder.toQuery(queryShardContext); assertThat(searcher.count(query), equalTo(3)); } diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/QueryAnalyzerTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/QueryAnalyzerTests.java index 509f483bcd253..9699fb741a678 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/QueryAnalyzerTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/QueryAnalyzerTests.java @@ -51,6 +51,7 @@ import org.apache.lucene.queries.spans.SpanTermQuery; import org.apache.lucene.sandbox.document.HalfFloatPoint; import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.ConstantScoreQuery; @@ -65,7 +66,6 @@ import org.apache.lucene.search.TermInSetQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermRangeQuery; -import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.join.QueryBitSetProducer; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.util.BytesRef; diff --git a/modules/percolator/src/test/java/org/opensearch/percolator/QueryBuilderStoreTests.java b/modules/percolator/src/test/java/org/opensearch/percolator/QueryBuilderStoreTests.java index 965b64d94ef96..340d359f85523 100644 --- a/modules/percolator/src/test/java/org/opensearch/percolator/QueryBuilderStoreTests.java +++ b/modules/percolator/src/test/java/org/opensearch/percolator/QueryBuilderStoreTests.java @@ -41,13 +41,12 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.store.Directory; -import org.mockito.Mockito; import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.index.fielddata.plain.BytesBinaryIndexFieldData; import org.opensearch.index.mapper.BinaryFieldMapper; import org.opensearch.index.mapper.ContentPath; @@ -63,6 +62,8 @@ import java.io.IOException; import java.util.Collections; +import org.mockito.Mockito; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/modules/rank-eval/src/internalClusterTest/java/org/opensearch/index/rankeval/RankEvalRequestIT.java b/modules/rank-eval/src/internalClusterTest/java/org/opensearch/index/rankeval/RankEvalRequestIT.java index ea80b59711b8a..5b6dcbe482d04 100644 --- a/modules/rank-eval/src/internalClusterTest/java/org/opensearch/index/rankeval/RankEvalRequestIT.java +++ b/modules/rank-eval/src/internalClusterTest/java/org/opensearch/index/rankeval/RankEvalRequestIT.java @@ -32,10 +32,14 @@ package org.opensearch.index.rankeval; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.support.IndicesOptions; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -43,7 +47,7 @@ import org.opensearch.indices.IndexClosedException; import org.opensearch.plugins.Plugin; import org.opensearch.search.builder.SearchSourceBuilder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.Before; import java.util.ArrayList; @@ -54,15 +58,33 @@ import java.util.Set; import static org.opensearch.index.rankeval.EvaluationMetric.filterUnratedDocuments; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.instanceOf; -public class RankEvalRequestIT extends OpenSearchIntegTestCase { +public class RankEvalRequestIT extends ParameterizedOpenSearchIntegTestCase { private static final String TEST_INDEX = "test"; private static final String INDEX_ALIAS = "alias0"; private static final int RELEVANT_RATING_1 = 1; + public RankEvalRequestIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Arrays.asList(RankEvalPlugin.class); diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/DiscountedCumulativeGain.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/DiscountedCumulativeGain.java index 35db325b70ca5..31c52cab404c6 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/DiscountedCumulativeGain.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/DiscountedCumulativeGain.java @@ -32,12 +32,12 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import java.io.IOException; @@ -49,8 +49,8 @@ import java.util.OptionalInt; import java.util.stream.Collectors; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.opensearch.index.rankeval.EvaluationMetric.joinHitsWithRatings; /** @@ -282,11 +282,9 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t return builder; } - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - NAME, - true, - args -> { return new Detail((Double) args[0], (Double) args[1] != null ? (Double) args[1] : 0.0d, (Integer) args[2]); } - ); + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(NAME, true, args -> { + return new Detail((Double) args[0], (Double) args[1] != null ? (Double) args[1] : 0.0d, (Integer) args[2]); + }); static { PARSER.declareDouble(constructorArg(), DCG_FIELD); diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvalQueryQuality.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvalQueryQuality.java index 28f0a2f334ee0..b08f6bee571d5 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvalQueryQuality.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvalQueryQuality.java @@ -32,15 +32,15 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import org.opensearch.index.rankeval.RatedDocument.DocumentKey; import java.io.IOException; diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvaluationMetric.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvaluationMetric.java index 70a6e24d27478..5330121cd1295 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvaluationMetric.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/EvaluationMetric.java @@ -32,8 +32,8 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.io.stream.NamedWriteable; -import org.opensearch.common.xcontent.ToXContentObject; +import org.opensearch.core.common.io.stream.NamedWriteable; +import org.opensearch.core.xcontent.ToXContentObject; import org.opensearch.index.rankeval.RatedDocument.DocumentKey; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/ExpectedReciprocalRank.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/ExpectedReciprocalRank.java index 3557e0576bb05..6d7f90fb63659 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/ExpectedReciprocalRank.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/ExpectedReciprocalRank.java @@ -33,12 +33,12 @@ package org.opensearch.index.rankeval; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import java.io.IOException; @@ -47,8 +47,8 @@ import java.util.Objects; import java.util.OptionalInt; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.opensearch.index.rankeval.EvaluationMetric.joinHitsWithRatings; /** @@ -262,11 +262,9 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t return builder.field(UNRATED_FIELD.getPreferredName(), this.unratedDocs); } - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - NAME, - true, - args -> { return new Detail((Integer) args[0]); } - ); + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(NAME, true, args -> { + return new Detail((Integer) args[0]); + }); static { PARSER.declareInt(constructorArg(), UNRATED_FIELD); diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MeanReciprocalRank.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MeanReciprocalRank.java index 59792537e89e5..bc455f30f092b 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MeanReciprocalRank.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MeanReciprocalRank.java @@ -32,12 +32,12 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import java.io.IOException; @@ -45,8 +45,8 @@ import java.util.Objects; import java.util.OptionalInt; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.opensearch.index.rankeval.EvaluationMetric.joinHitsWithRatings; /** @@ -219,11 +219,9 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t return builder.field(FIRST_RELEVANT_RANK_FIELD.getPreferredName(), firstRelevantRank); } - private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - NAME, - true, - args -> { return new Detail((Integer) args[0]); } - ); + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>(NAME, true, args -> { + return new Detail((Integer) args[0]); + }); static { PARSER.declareInt(constructorArg(), FIRST_RELEVANT_RANK_FIELD); diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MetricDetail.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MetricDetail.java index 788c2e2993724..5627ac37a47fc 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MetricDetail.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/MetricDetail.java @@ -32,9 +32,9 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.io.stream.NamedWriteable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.NamedWriteable; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/PrecisionAtK.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/PrecisionAtK.java index a5a8dcab34945..531db06ef3cfb 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/PrecisionAtK.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/PrecisionAtK.java @@ -32,23 +32,23 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; +import javax.naming.directory.SearchResult; + import java.io.IOException; import java.util.List; import java.util.Objects; import java.util.OptionalInt; -import javax.naming.directory.SearchResult; - -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.opensearch.index.rankeval.EvaluationMetric.joinHitsWithRatings; /** diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalNamedXContentProvider.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalNamedXContentProvider.java index da59a73858957..c6b589ee77515 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalNamedXContentProvider.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalNamedXContentProvider.java @@ -32,8 +32,8 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.plugins.spi.NamedXContentProvider; import java.util.ArrayList; diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalPlugin.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalPlugin.java index a1eaa0f62f0f1..9cfc122473673 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalPlugin.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalPlugin.java @@ -33,15 +33,15 @@ package org.opensearch.index.rankeval; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; -import org.opensearch.common.xcontent.NamedXContentRegistry.Entry; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry.Entry; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.rest.RestController; diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequest.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequest.java index 66db397865a0b..5fd1826ff8215 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequest.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequest.java @@ -39,9 +39,9 @@ import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchType; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Arrays; diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequestBuilder.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequestBuilder.java index 84fc45527ec27..f3fdb486bd070 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequestBuilder.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalRequestBuilder.java @@ -32,8 +32,8 @@ package org.opensearch.index.rankeval; -import org.opensearch.action.ActionType; import org.opensearch.action.ActionRequestBuilder; +import org.opensearch.action.ActionType; import org.opensearch.client.OpenSearchClient; public class RankEvalRequestBuilder extends ActionRequestBuilder { diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalResponse.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalResponse.java index 23b42846458a2..880032ede01a2 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalResponse.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalResponse.java @@ -33,17 +33,18 @@ package org.opensearch.index.rankeval; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import java.io.IOException; import java.util.Collections; @@ -105,7 +106,7 @@ public Map getFailures() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalSpec.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalSpec.java index 8708c94c353e4..229782b5fb5d1 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalSpec.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RankEvalSpec.java @@ -32,17 +32,18 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import org.opensearch.script.Script; import java.io.IOException; @@ -249,7 +250,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedDocument.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedDocument.java index 5f9a71cd25e7b..afb454025dd01 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedDocument.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedDocument.java @@ -32,15 +32,16 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; @@ -128,7 +129,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedRequest.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedRequest.java index 5289a3945db52..bb05da0682aed 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedRequest.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedRequest.java @@ -33,15 +33,16 @@ package org.opensearch.index.rankeval; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.rankeval.RatedDocument.DocumentKey; import org.opensearch.search.builder.SearchSourceBuilder; @@ -340,7 +341,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedSearchHit.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedSearchHit.java index 01c1c81f2bf2a..f7b2b7a890334 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedSearchHit.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RatedSearchHit.java @@ -32,16 +32,16 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser.ValueType; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser.ValueType; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import java.io.IOException; diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RecallAtK.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RecallAtK.java index 4edd23133d2ec..90c1f4951269e 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RecallAtK.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RecallAtK.java @@ -32,23 +32,23 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; +import javax.naming.directory.SearchResult; + import java.io.IOException; import java.util.List; import java.util.Objects; import java.util.OptionalInt; -import javax.naming.directory.SearchResult; - -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.opensearch.index.rankeval.EvaluationMetric.joinHitsWithRatings; /** diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RestRankEvalAction.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RestRankEvalAction.java index 3c8748d13da0d..cb5138117d0e1 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RestRankEvalAction.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/RestRankEvalAction.java @@ -35,8 +35,8 @@ import org.opensearch.action.search.SearchType; import org.opensearch.action.support.IndicesOptions; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.RestToXContentListener; diff --git a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/TransportRankEvalAction.java b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/TransportRankEvalAction.java index 8cfde2d2b412e..1fe5a2840fae6 100644 --- a/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/TransportRankEvalAction.java +++ b/modules/rank-eval/src/main/java/org/opensearch/index/rankeval/TransportRankEvalAction.java @@ -32,7 +32,6 @@ package org.opensearch.index.rankeval; -import org.opensearch.action.ActionListener; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.MultiSearchResponse.Item; @@ -40,13 +39,14 @@ import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.client.Client; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.script.Script; import org.opensearch.script.ScriptService; import org.opensearch.script.TemplateScript; @@ -126,7 +126,7 @@ protected void doExecute(Task task, RankEvalRequest request, ActionListener { return new DiscountedCumulativeGain(original.getNormalize(), original.getUnknownDocRating(), original.getK()); }, - DiscountedCumulativeGainTests::mutateTestItem - ); + checkEqualsAndHashCode(createTestItem(), original -> { + return new DiscountedCumulativeGain(original.getNormalize(), original.getUnknownDocRating(), original.getK()); + }, DiscountedCumulativeGainTests::mutateTestItem); } private static DiscountedCumulativeGain mutateTestItem(DiscountedCumulativeGain original) { diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/EvalQueryQualityTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/EvalQueryQualityTests.java index 56d23d8ca3184..ad58f1909c811 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/EvalQueryQualityTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/EvalQueryQualityTests.java @@ -33,13 +33,13 @@ package org.opensearch.index.rankeval; import org.opensearch.action.OriginalIndices; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchShardTarget; import org.opensearch.test.OpenSearchTestCase; @@ -48,8 +48,8 @@ import java.util.List; import java.util.function.Predicate; -import static org.opensearch.common.xcontent.XContentHelper.toXContent; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentHelper.toXContent; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; import static org.opensearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode; import static org.opensearch.test.XContentTestUtils.insertRandomFields; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertToXContentEquivalent; diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/ExpectedReciprocalRankTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/ExpectedReciprocalRankTests.java index 723a1e2202e2b..d10c6c285e0d7 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/ExpectedReciprocalRankTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/ExpectedReciprocalRankTests.java @@ -33,16 +33,16 @@ package org.opensearch.index.rankeval; import org.opensearch.action.OriginalIndices; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchShardTarget; import org.opensearch.test.OpenSearchTestCase; @@ -166,7 +166,7 @@ public static ExpectedReciprocalRank createTestItem() { public void testXContentRoundtrip() throws IOException { ExpectedReciprocalRank testItem = createTestItem(); - XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(randomFrom(XContentType.values())); XContentBuilder shuffled = shuffleXContent(testItem.toXContent(builder, ToXContent.EMPTY_PARAMS)); try (XContentParser itemParser = createParser(shuffled)) { itemParser.nextToken(); @@ -213,11 +213,9 @@ public void testSerialization() throws IOException { } public void testEqualsAndHash() throws IOException { - checkEqualsAndHashCode( - createTestItem(), - original -> { return new ExpectedReciprocalRank(original.getMaxRelevance(), original.getUnknownDocRating(), original.getK()); }, - ExpectedReciprocalRankTests::mutateTestItem - ); + checkEqualsAndHashCode(createTestItem(), original -> { + return new ExpectedReciprocalRank(original.getMaxRelevance(), original.getUnknownDocRating(), original.getK()); + }, ExpectedReciprocalRankTests::mutateTestItem); } private static ExpectedReciprocalRank mutateTestItem(ExpectedReciprocalRank original) { diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/MeanReciprocalRankTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/MeanReciprocalRankTests.java index 2cd16c05f2a20..2304cf74d307f 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/MeanReciprocalRankTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/MeanReciprocalRankTests.java @@ -33,16 +33,16 @@ package org.opensearch.index.rankeval; import org.opensearch.action.OriginalIndices; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchShardTarget; import org.opensearch.test.OpenSearchTestCase; @@ -183,7 +183,7 @@ public void testNoResults() throws Exception { public void testXContentRoundtrip() throws IOException { MeanReciprocalRank testItem = createTestItem(); - XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(randomFrom(XContentType.values())); XContentBuilder shuffled = shuffleXContent(testItem.toXContent(builder, ToXContent.EMPTY_PARAMS)); try (XContentParser itemParser = createParser(shuffled)) { itemParser.nextToken(); diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/PrecisionAtKTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/PrecisionAtKTests.java index 1c7a02dc27cf7..16e74d928c2b4 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/PrecisionAtKTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/PrecisionAtKTests.java @@ -33,16 +33,16 @@ package org.opensearch.index.rankeval; import org.opensearch.action.OriginalIndices; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchShardTarget; import org.opensearch.test.OpenSearchTestCase; @@ -199,7 +199,7 @@ public static PrecisionAtK createTestItem() { public void testXContentRoundtrip() throws IOException { PrecisionAtK testItem = createTestItem(); - XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(randomFrom(XContentType.values())); XContentBuilder shuffled = shuffleXContent(testItem.toXContent(builder, ToXContent.EMPTY_PARAMS)); try (XContentParser itemParser = createParser(shuffled)) { itemParser.nextToken(); diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalRequestTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalRequestTests.java index 401bb1b2a7bb7..023edc7827dc5 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalRequestTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalRequestTests.java @@ -34,10 +34,10 @@ import org.opensearch.action.search.SearchType; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.Writeable.Reader; import org.opensearch.common.util.ArrayUtils; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.Writeable.Reader; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.test.AbstractWireSerializingTestCase; import org.junit.AfterClass; diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalResponseTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalResponseTests.java index 3d883b373d705..db792130ca016 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalResponseTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalResponseTests.java @@ -37,20 +37,20 @@ import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.cluster.block.ClusterBlockException; -import org.opensearch.cluster.coordination.NoMasterBlockService; -import org.opensearch.common.ParsingException; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.breaker.CircuitBreakingException; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.cluster.coordination.NoClusterManagerBlockService; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentLocation; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.CircuitBreakingException; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentLocation; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchParseException; import org.opensearch.search.SearchShardTarget; @@ -67,7 +67,7 @@ import java.util.function.Predicate; import static java.util.Collections.singleton; -import static org.opensearch.common.xcontent.XContentHelper.toXContent; +import static org.opensearch.core.xcontent.XContentHelper.toXContent; import static org.opensearch.test.TestSearchContext.SHARD_TARGET; import static org.opensearch.test.XContentTestUtils.insertRandomFields; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertToXContentEquivalent; @@ -76,7 +76,7 @@ public class RankEvalResponseTests extends OpenSearchTestCase { private static final Exception[] RANDOM_EXCEPTIONS = new Exception[] { - new ClusterBlockException(singleton(NoMasterBlockService.NO_MASTER_BLOCK_WRITES)), + new ClusterBlockException(singleton(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_WRITES)), new CircuitBreakingException("Data too large", 123, 456, CircuitBreaker.Durability.PERMANENT), new SearchParseException(SHARD_TARGET, "Parse failure", new XContentLocation(12, 98)), new IllegalArgumentException("Closed resource", new RuntimeException("Resource")), @@ -177,7 +177,7 @@ public void testToXContent() throws IOException { Collections.singletonMap("coffee_query", coffeeQueryQuality), Collections.singletonMap("beer_query", new ParsingException(new XContentLocation(0, 0), "someMsg")) ); - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(MediaTypeRegistry.JSON); String xContent = BytesReference.bytes(response.toXContent(builder, ToXContent.EMPTY_PARAMS)).utf8ToString(); assertEquals( ("{" diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalSpecTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalSpecTests.java index b79b26eb0af2e..c665fe4045cef 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalSpecTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RankEvalSpecTests.java @@ -32,16 +32,15 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.rankeval.RankEvalSpec.ScriptWithId; @@ -103,7 +102,7 @@ static RankEvalSpec createTestItem() { builder.startObject(); builder.field("field", randomAlphaOfLengthBetween(1, 5)); builder.endObject(); - script = Strings.toString(builder); + script = builder.toString(); } catch (IOException e) { // this shouldn't happen in tests, re-throw just not to swallow it throw new RuntimeException(e); diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedDocumentTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedDocumentTests.java index f3fb01b153c55..390412674e05e 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedDocumentTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedDocumentTests.java @@ -32,14 +32,14 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; @@ -57,7 +57,7 @@ public static RatedDocument createRatedDocument() { public void testXContentParsing() throws IOException { RatedDocument testItem = createRatedDocument(); - XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(randomFrom(XContentType.values())); XContentBuilder shuffled = shuffleXContent(testItem.toXContent(builder, ToXContent.EMPTY_PARAMS)); try (XContentParser itemParser = createParser(shuffled)) { RatedDocument parsedItem = RatedDocument.fromXContent(itemParser); @@ -91,11 +91,9 @@ public void testSerialization() throws IOException { } public void testEqualsAndHash() throws IOException { - checkEqualsAndHashCode( - createRatedDocument(), - original -> { return new RatedDocument(original.getIndex(), original.getDocID(), original.getRating()); }, - RatedDocumentTests::mutateTestItem - ); + checkEqualsAndHashCode(createRatedDocument(), original -> { + return new RatedDocument(original.getIndex(), original.getDocID(), original.getRating()); + }, RatedDocumentTests::mutateTestItem); } private static RatedDocument mutateTestItem(RatedDocument original) { diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedRequestsTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedRequestsTests.java index a2555c2e1e392..588be3d94c2c6 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedRequestsTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedRequestsTests.java @@ -32,17 +32,17 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.search.SearchModule; @@ -134,7 +134,7 @@ public static RatedRequest createTestItem(boolean forceRequest) { public void testXContentRoundtrip() throws IOException { RatedRequest testItem = createTestItem(randomBoolean()); - XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(randomFrom(XContentType.values())); XContentBuilder shuffled = shuffleXContent(testItem.toXContent(builder, ToXContent.EMPTY_PARAMS)); try (XContentParser itemParser = createParser(shuffled)) { itemParser.nextToken(); diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedSearchHitTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedSearchHitTests.java index 555a0c95a3456..69cf329401918 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedSearchHitTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RatedSearchHitTests.java @@ -32,11 +32,11 @@ package org.opensearch.index.rankeval; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import org.opensearch.test.OpenSearchTestCase; diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RecallAtKTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RecallAtKTests.java index 6efb44a3875e1..e89d0bcb20eef 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RecallAtKTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/RecallAtKTests.java @@ -33,16 +33,16 @@ package org.opensearch.index.rankeval; import org.opensearch.action.OriginalIndices; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchShardTarget; import org.opensearch.test.OpenSearchTestCase; @@ -185,7 +185,7 @@ public static RecallAtK createTestItem() { public void testXContentRoundtrip() throws IOException { RecallAtK testItem = createTestItem(); - XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(randomFrom(XContentType.values())); XContentBuilder shuffled = shuffleXContent(testItem.toXContent(builder, ToXContent.EMPTY_PARAMS)); try (XContentParser itemParser = createParser(shuffled)) { itemParser.nextToken(); diff --git a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/TransportRankEvalActionTests.java b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/TransportRankEvalActionTests.java index b2052bcb79255..bd42289015bf4 100644 --- a/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/TransportRankEvalActionTests.java +++ b/modules/rank-eval/src/test/java/org/opensearch/index/rankeval/TransportRankEvalActionTests.java @@ -32,7 +32,6 @@ package org.opensearch.index.rankeval; -import org.opensearch.action.ActionListener; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.SearchType; @@ -40,7 +39,8 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.client.node.NodeClient; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.script.ScriptService; import org.opensearch.search.builder.SearchSourceBuilder; diff --git a/modules/reindex/src/internalClusterTest/java/org/opensearch/client/documentation/ReindexDocumentationIT.java b/modules/reindex/src/internalClusterTest/java/org/opensearch/client/documentation/ReindexDocumentationIT.java index 6d313e06263b3..59d079535f58b 100644 --- a/modules/reindex/src/internalClusterTest/java/org/opensearch/client/documentation/ReindexDocumentationIT.java +++ b/modules/reindex/src/internalClusterTest/java/org/opensearch/client/documentation/ReindexDocumentationIT.java @@ -32,10 +32,12 @@ package org.opensearch.client.documentation; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.tasks.get.GetTaskResponse; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.client.Client; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.IndexModule; import org.opensearch.index.engine.Engine; import org.opensearch.index.query.QueryBuilders; @@ -53,12 +55,10 @@ import org.opensearch.index.reindex.UpdateByQueryAction; import org.opensearch.index.reindex.UpdateByQueryRequestBuilder; import org.opensearch.index.shard.IndexingOperationListener; -import org.opensearch.index.shard.ShardId; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.sort.SortOrder; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.test.OpenSearchIntegTestCase; import org.hamcrest.Matcher; diff --git a/modules/reindex/src/internalClusterTest/java/org/opensearch/index/codec/MultiCodecReindexIT.java b/modules/reindex/src/internalClusterTest/java/org/opensearch/index/codec/MultiCodecReindexIT.java new file mode 100644 index 0000000000000..85c652bab1cb0 --- /dev/null +++ b/modules/reindex/src/internalClusterTest/java/org/opensearch/index/codec/MultiCodecReindexIT.java @@ -0,0 +1,198 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.index.codec; + +import org.opensearch.action.admin.indices.flush.FlushResponse; +import org.opensearch.action.admin.indices.refresh.RefreshResponse; +import org.opensearch.action.admin.indices.segments.IndicesSegmentsRequest; +import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.opensearch.action.support.ActiveShardCount; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.engine.Segment; +import org.opensearch.index.reindex.BulkByScrollResponse; +import org.opensearch.index.reindex.ReindexAction; +import org.opensearch.index.reindex.ReindexPlugin; +import org.opensearch.index.reindex.ReindexRequestBuilder; +import org.opensearch.index.reindex.ReindexTestCase; +import org.opensearch.plugins.Plugin; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static java.util.stream.Collectors.toList; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_BLOCKS_METADATA; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_BLOCKS_READ; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_BLOCKS_WRITE; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY_ALLOW_DELETE; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; + +public class MultiCodecReindexIT extends ReindexTestCase { + + @Override + protected Collection> nodePlugins() { + return List.of(ReindexPlugin.class); + } + + public void testReindexingMultipleCodecs() throws InterruptedException, ExecutionException { + internalCluster().ensureAtLeastNumDataNodes(1); + Map codecMap = Map.of( + "best_compression", + "BEST_COMPRESSION", + "zlib", + "BEST_COMPRESSION", + "default", + "BEST_SPEED", + "lz4", + "BEST_SPEED" + ); + + for (Map.Entry codec : codecMap.entrySet()) { + assertReindexingWithMultipleCodecs(codec.getKey(), codec.getValue(), codecMap); + } + + } + + private void assertReindexingWithMultipleCodecs(String destCodec, String destCodecMode, Map codecMap) + throws ExecutionException, InterruptedException { + + final String index = "test-index" + destCodec; + final String destIndex = "dest-index" + destCodec; + + // creating source index + createIndex( + index, + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put("index.codec", "default") + .put("index.merge.policy.max_merged_segment", "1b") + .build() + ); + ensureGreen(index); + + final int nbDocs = randomIntBetween(2, 5); + + // indexing with all 4 codecs + for (Map.Entry codec : codecMap.entrySet()) { + useCodec(index, codec.getKey()); + ingestDocs(index, nbDocs); + } + + assertTrue( + getSegments(index).stream() + .flatMap(s -> s.getAttributes().values().stream()) + .collect(Collectors.toSet()) + .containsAll(codecMap.values()) + ); + + // creating destination index with destination codec + createIndex( + destIndex, + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put("index.codec", destCodec) + .build() + ); + + BulkByScrollResponse bulkResponse = new ReindexRequestBuilder(client(), ReindexAction.INSTANCE).source(index) + .destination(destIndex) + .refresh(true) + .waitForActiveShards(ActiveShardCount.ONE) + .get(); + + assertEquals(codecMap.size() * nbDocs, bulkResponse.getCreated()); + assertEquals(codecMap.size() * nbDocs, bulkResponse.getTotal()); + assertEquals(0, bulkResponse.getDeleted()); + assertEquals(0, bulkResponse.getNoops()); + assertEquals(0, bulkResponse.getVersionConflicts()); + assertEquals(1, bulkResponse.getBatches()); + assertTrue(bulkResponse.getTook().getMillis() > 0); + assertEquals(0, bulkResponse.getBulkFailures().size()); + assertEquals(0, bulkResponse.getSearchFailures().size()); + assertTrue(getSegments(destIndex).stream().allMatch(segment -> segment.attributes.containsValue(destCodecMode))); + } + + private void useCodec(String index, String codec) throws ExecutionException, InterruptedException { + assertAcked(client().admin().indices().prepareClose(index).setWaitForActiveShards(1)); + + assertAcked( + client().admin() + .indices() + .updateSettings(new UpdateSettingsRequest(index).settings(Settings.builder().put("index.codec", codec))) + .get() + ); + + assertAcked(client().admin().indices().prepareOpen(index).setWaitForActiveShards(1)); + } + + private void flushAndRefreshIndex(String index) { + + // Request is not blocked + for (String blockSetting : Arrays.asList( + SETTING_BLOCKS_READ, + SETTING_BLOCKS_WRITE, + SETTING_READ_ONLY, + SETTING_BLOCKS_METADATA, + SETTING_READ_ONLY_ALLOW_DELETE + )) { + try { + enableIndexBlock(index, blockSetting); + // flush + FlushResponse flushResponse = client().admin().indices().prepareFlush(index).setForce(true).execute().actionGet(); + assertNoFailures(flushResponse); + + // refresh + RefreshResponse refreshResponse = client().admin().indices().prepareRefresh(index).execute().actionGet(); + assertNoFailures(refreshResponse); + } finally { + disableIndexBlock(index, blockSetting); + } + } + } + + private void ingestDocs(String index, int nbDocs) throws InterruptedException { + + indexRandom( + randomBoolean(), + false, + randomBoolean(), + IntStream.range(0, nbDocs) + .mapToObj(i -> client().prepareIndex(index).setId(UUID.randomUUID().toString()).setSource("num", i)) + .collect(toList()) + ); + flushAndRefreshIndex(index); + } + + private ArrayList getSegments(String index) { + + return new ArrayList<>( + client().admin() + .indices() + .segments(new IndicesSegmentsRequest(index)) + .actionGet() + .getIndices() + .get(index) + .getShards() + .get(0) + .getShards()[0].getSegments() + ); + } + +} diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollAction.java index 43adffc6f7671..e6c1cde5c0e98 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollAction.java @@ -32,11 +32,9 @@ package org.opensearch.index.reindex; -import java.util.Optional; import org.apache.http.HttpRequestInterceptor; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.admin.indices.refresh.RefreshRequest; @@ -52,9 +50,10 @@ import org.opensearch.action.support.TransportAction; import org.opensearch.client.ParentTaskAssigningClient; import org.opensearch.common.Nullable; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.IdFieldMapper; import org.opensearch.index.mapper.IndexFieldMapper; @@ -79,6 +78,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; @@ -90,8 +90,8 @@ import static java.util.Collections.unmodifiableList; import static org.opensearch.action.bulk.BackoffPolicy.exponentialBackoff; import static org.opensearch.common.unit.TimeValue.timeValueNanos; +import static org.opensearch.core.rest.RestStatus.CONFLICT; import static org.opensearch.index.reindex.AbstractBulkByScrollRequest.MAX_DOCS_ALL_MATCHES; -import static org.opensearch.rest.RestStatus.CONFLICT; import static org.opensearch.search.sort.SortBuilders.fieldSort; /** diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBaseReindexRestHandler.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBaseReindexRestHandler.java index a0696ea1b72ea..f034e92551ede 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBaseReindexRestHandler.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBaseReindexRestHandler.java @@ -36,14 +36,10 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.rest.BaseRestHandler; -import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; import org.opensearch.tasks.LoggingTaskListener; -import org.opensearch.tasks.Task; import java.io.IOException; import java.util.HashMap; @@ -124,17 +120,6 @@ protected Request setCommonOptions(RestRequest restRequest, Request request) { return request; } - private RestChannelConsumer sendTask(String localNodeId, Task task) { - return channel -> { - try (XContentBuilder builder = channel.newBuilder()) { - builder.startObject(); - builder.field("task", localNodeId + ":" + task.getId()); - builder.endObject(); - channel.sendResponse(new BytesRestResponse(RestStatus.OK, builder)); - } - }; - } - private static Integer parseSlices(RestRequest request) { String slicesString = request.param("slices"); if (slicesString == null) { diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBulkByQueryRestHandler.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBulkByQueryRestHandler.java index 512e7f430b7c5..96e15a3899383 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBulkByQueryRestHandler.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/AbstractBulkByQueryRestHandler.java @@ -34,12 +34,12 @@ import org.opensearch.action.ActionType; import org.opensearch.action.search.SearchRequest; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.rest.RestRequest; import org.opensearch.rest.action.search.RestSearchAction; @@ -106,7 +106,7 @@ private XContentParser extractRequestSpecificFields(RestRequest restRequest, Map } try ( XContentParser parser = restRequest.contentOrSourceParamParser(); - XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType()) + XContentBuilder builder = MediaTypeRegistry.contentBuilder(parser.contentType()) ) { Map body = parser.map(); diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/AsyncDeleteByQueryAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/AsyncDeleteByQueryAction.java index 1a9ce16acc255..fefd7fd280082 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/AsyncDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/AsyncDeleteByQueryAction.java @@ -33,9 +33,9 @@ package org.opensearch.index.reindex; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.delete.DeleteRequest; import org.opensearch.client.ParentTaskAssigningClient; +import org.opensearch.core.action.ActionListener; import org.opensearch.script.ScriptService; import org.opensearch.threadpool.ThreadPool; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/BulkByScrollParallelizationHelper.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/BulkByScrollParallelizationHelper.java index 334390ca730cf..d5a6e392f2019 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/BulkByScrollParallelizationHelper.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/BulkByScrollParallelizationHelper.java @@ -32,18 +32,18 @@ package org.opensearch.index.reindex; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionType; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsRequest; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsResponse; import org.opensearch.action.search.SearchRequest; import org.opensearch.client.Client; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.Index; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.mapper.IdFieldMapper; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.slice.SliceBuilder; -import org.opensearch.tasks.TaskId; import java.util.Arrays; import java.util.Collections; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/BulkIndexByScrollResponseContentListener.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/BulkIndexByScrollResponseContentListener.java index a41eb12cdd12b..b9367d352fdb1 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/BulkIndexByScrollResponseContentListener.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/BulkIndexByScrollResponseContentListener.java @@ -33,13 +33,13 @@ package org.opensearch.index.reindex; import org.opensearch.action.bulk.BulkItemResponse.Failure; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.reindex.ScrollableHitSource.SearchFailure; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestResponse; -import org.opensearch.rest.RestStatus; import org.opensearch.rest.action.RestBuilderListener; import java.util.Map; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexPlugin.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexPlugin.java index 865ae26f6f54d..cab1a1dedb3b8 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexPlugin.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexPlugin.java @@ -32,29 +32,27 @@ package org.opensearch.index.reindex; -import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.index.reindex.spi.RemoteReindexExtension; -import org.opensearch.plugins.ExtensiblePlugin; -import org.opensearch.plugins.ExtensiblePlugin.ExtensionLoader; -import org.opensearch.watcher.ResourceWatcherService; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; +import org.opensearch.index.reindex.spi.RemoteReindexExtension; import org.opensearch.plugins.ActionPlugin; +import org.opensearch.plugins.ExtensiblePlugin; +import org.opensearch.plugins.ExtensiblePlugin.ExtensionLoader; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.RepositoriesService; import org.opensearch.rest.RestController; @@ -69,6 +67,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.function.Supplier; import static java.util.Collections.singletonList; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexSslConfig.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexSslConfig.java index f48422d41ea9e..4c2ec4a7f02b5 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexSslConfig.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexSslConfig.java @@ -35,14 +35,14 @@ import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; -import org.opensearch.common.Strings; import org.opensearch.common.settings.SecureSetting; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.ssl.SslConfiguration; import org.opensearch.common.ssl.SslConfigurationKeys; import org.opensearch.common.ssl.SslConfigurationLoader; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; import org.opensearch.watcher.FileChangesListener; import org.opensearch.watcher.FileWatcher; @@ -50,6 +50,7 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; + import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexValidator.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexValidator.java index 71c3aad8713e1..d065afceab503 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexValidator.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/ReindexValidator.java @@ -45,10 +45,10 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.index.IndexNotFoundException; import org.opensearch.search.builder.SearchSourceBuilder; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/Reindexer.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/Reindexer.java index 8ade055d10f60..8e338dedc4ec8 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/Reindexer.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/Reindexer.java @@ -32,7 +32,6 @@ package org.opensearch.index.reindex; -import java.util.Optional; import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.HttpRequestInterceptor; @@ -44,7 +43,6 @@ import org.apache.http.message.BasicHeader; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.bulk.BackoffPolicy; import org.opensearch.action.bulk.BulkItemResponse; @@ -54,14 +52,15 @@ import org.opensearch.client.RestClient; import org.opensearch.client.RestClientBuilder; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.lucene.uid.Versions; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.VersionFieldMapper; import org.opensearch.index.reindex.remote.RemoteScrollableHitSource; @@ -77,6 +76,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; @@ -368,27 +368,24 @@ protected RequestWrapper buildRequest(ScrollableHitSource.Hit doc) index.id(doc.getId()); // the source xcontent type and destination could be different - final XContentType sourceXContentType = doc.getXContentType(); - final XContentType mainRequestXContentType = mainRequest.getDestination().getContentType(); - if (mainRequestXContentType != null && doc.getXContentType() != mainRequestXContentType) { + final MediaType sourceMediaType = doc.getMediaType(); + final MediaType mainRequestMediaType = mainRequest.getDestination().getContentType(); + if (mainRequestMediaType != null && doc.getMediaType() != mainRequestMediaType) { // we need to convert try ( InputStream stream = doc.getSource().streamInput(); - XContentParser parser = sourceXContentType.xContent() + XContentParser parser = sourceMediaType.xContent() .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, stream); - XContentBuilder builder = XContentBuilder.builder(mainRequestXContentType.xContent()) + XContentBuilder builder = XContentBuilder.builder(mainRequestMediaType.xContent()) ) { parser.nextToken(); builder.copyCurrentStructure(parser); index.source(BytesReference.bytes(builder), builder.contentType()); } catch (IOException e) { - throw new UncheckedIOException( - "failed to convert hit from " + sourceXContentType + " to " + mainRequestXContentType, - e - ); + throw new UncheckedIOException("failed to convert hit from " + sourceMediaType + " to " + mainRequestMediaType, e); } } else { - index.source(doc.getSource(), doc.getXContentType()); + index.source(doc.getSource(), doc.getMediaType()); } /* diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/RestDeleteByQueryAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/RestDeleteByQueryAction.java index 6f2e5d8e71edb..4f68884b194e9 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/RestDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/RestDeleteByQueryAction.java @@ -33,7 +33,7 @@ package org.opensearch.index.reindex; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.rest.RestRequest; import java.io.IOException; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/RestReindexAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/RestReindexAction.java index 9fb30876c3e3c..c03eba10b41ba 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/RestReindexAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/RestReindexAction.java @@ -34,8 +34,8 @@ import org.opensearch.action.DocWriteRequest; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestRequestFilter; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/RestRethrottleAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/RestRethrottleAction.java index 89864cf56c71d..925cada3e40b5 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/RestRethrottleAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/RestRethrottleAction.java @@ -34,9 +34,9 @@ import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.core.tasks.TaskId; import org.opensearch.rest.BaseRestHandler; import org.opensearch.rest.RestRequest; -import org.opensearch.tasks.TaskId; import java.util.List; import java.util.function.Supplier; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/RestUpdateByQueryAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/RestUpdateByQueryAction.java index 9be1687a09432..aeb339d49e1bb 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/RestUpdateByQueryAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/RestUpdateByQueryAction.java @@ -33,7 +33,7 @@ package org.opensearch.index.reindex; import org.opensearch.client.node.NodeClient; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.rest.RestRequest; import org.opensearch.script.Script; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/RethrottleRequest.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/RethrottleRequest.java index f174aa6d6fda3..2036479c73bc4 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/RethrottleRequest.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/RethrottleRequest.java @@ -34,8 +34,8 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.tasks.BaseTasksRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportDeleteByQueryAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportDeleteByQueryAction.java index 51e388227eb10..299626bd7fd22 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportDeleteByQueryAction.java @@ -32,14 +32,14 @@ package org.opensearch.index.reindex; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.client.Client; import org.opensearch.client.ParentTaskAssigningClient; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.script.ScriptService; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportReindexAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportReindexAction.java index c84d103a2ef6f..e624b0619a26e 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportReindexAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportReindexAction.java @@ -32,8 +32,6 @@ package org.opensearch.index.reindex; -import java.util.Optional; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.AutoCreateIndex; import org.opensearch.action.support.HandledTransportAction; @@ -44,6 +42,7 @@ import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; import org.opensearch.index.reindex.spi.RemoteReindexExtension; import org.opensearch.script.ScriptService; import org.opensearch.tasks.Task; @@ -51,6 +50,7 @@ import org.opensearch.transport.TransportService; import java.util.List; +import java.util.Optional; import java.util.function.Function; import static java.util.Collections.emptyList; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportRethrottleAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportRethrottleAction.java index 2ee869ce2b465..21ae8fd722629 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportRethrottleAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportRethrottleAction.java @@ -33,7 +33,6 @@ package org.opensearch.index.reindex; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.TaskOperationFailure; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; @@ -42,7 +41,8 @@ import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportUpdateByQueryAction.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportUpdateByQueryAction.java index f07915b9d9e76..1af9b62d72fd4 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportUpdateByQueryAction.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/TransportUpdateByQueryAction.java @@ -33,7 +33,6 @@ package org.opensearch.index.reindex; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; @@ -42,7 +41,8 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.index.mapper.IdFieldMapper; import org.opensearch.index.mapper.IndexFieldMapper; import org.opensearch.index.mapper.RoutingFieldMapper; @@ -138,7 +138,7 @@ protected RequestWrapper buildRequest(ScrollableHitSource.Hit doc) IndexRequest index = new IndexRequest(); index.index(doc.getIndex()); index.id(doc.getId()); - index.source(doc.getSource(), doc.getXContentType()); + index.source(doc.getSource(), doc.getMediaType()); index.setIfSeqNo(doc.getSeqNo()); index.setIfPrimaryTerm(doc.getPrimaryTerm()); index.setPipeline(mainRequest.getPipeline()); diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteRequestBuilders.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteRequestBuilders.java index 8467fbdeacd0e..6886ca9420091 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteRequestBuilders.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteRequestBuilders.java @@ -38,16 +38,15 @@ import org.opensearch.Version; import org.opensearch.action.search.SearchRequest; import org.opensearch.client.Request; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.sort.FieldSortBuilder; import org.opensearch.search.sort.SortBuilder; @@ -181,7 +180,7 @@ static Request initialSearch(SearchRequest searchRequest, BytesReference query, } entity.endObject(); - request.setJsonEntity(Strings.toString(entity)); + request.setJsonEntity(entity.toString()); } catch (IOException e) { throw new OpenSearchException("unexpected error building entity", e); } @@ -246,7 +245,7 @@ static Request scroll(String scroll, TimeValue keepAlive, Version remoteVersion) try (XContentBuilder entity = JsonXContent.contentBuilder()) { entity.startObject().field("scroll_id", scroll).endObject(); - request.setJsonEntity(Strings.toString(entity)); + request.setJsonEntity(entity.toString()); } catch (IOException e) { throw new OpenSearchException("failed to build scroll entity", e); } @@ -263,7 +262,7 @@ static Request clearScroll(String scroll, Version remoteVersion) { } try (XContentBuilder entity = JsonXContent.contentBuilder()) { entity.startObject().array("scroll_id", scroll).endObject(); - request.setJsonEntity(Strings.toString(entity)); + request.setJsonEntity(entity.toString()); } catch (IOException e) { throw new OpenSearchException("failed to build clear scroll entity", e); } diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteResponseParsers.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteResponseParsers.java index d22b995036e90..981a22d4e7945 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteResponseParsers.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteResponseParsers.java @@ -35,18 +35,18 @@ import org.apache.lucene.search.TotalHits; import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ObjectParser.ValueType; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentLocation; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ObjectParser.ValueType; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentLocation; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.reindex.ScrollableHitSource.BasicHit; import org.opensearch.index.reindex.ScrollableHitSource.Hit; import org.opensearch.index.reindex.ScrollableHitSource.Response; @@ -60,8 +60,8 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static java.util.Objects.requireNonNull; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Parsers to convert the response from the remote host into objects useful for {@link RemoteScrollableHitSource}. @@ -72,7 +72,7 @@ private RemoteResponseParsers() {} /** * Parser for an individual {@code hit} element. */ - public static final ConstructingObjectParser HIT_PARSER = new ConstructingObjectParser<>("hit", true, a -> { + public static final ConstructingObjectParser HIT_PARSER = new ConstructingObjectParser<>("hit", true, a -> { int i = 0; String index = (String) a[i++]; String id = (String) a[i++]; @@ -106,7 +106,7 @@ private RemoteResponseParsers() {} class Fields { String routing; } - ObjectParser fieldsParser = new ObjectParser<>("fields", Fields::new); + ObjectParser fieldsParser = new ObjectParser<>("fields", Fields::new); HIT_PARSER.declareObject((hit, fields) -> { hit.setRouting(fields.routing); }, fieldsParser, new ParseField("fields")); fieldsParser.declareString((fields, routing) -> fields.routing = routing, routingField); fieldsParser.declareLong((fields, ttl) -> {}, ttlField); // ignore ttls since they have been removed @@ -116,7 +116,7 @@ class Fields { /** * Parser for the {@code hits} element. Parsed to an array of {@code [total (Long), hits (List)]}. */ - public static final ConstructingObjectParser HITS_PARSER = new ConstructingObjectParser<>("hits", true, a -> a); + public static final ConstructingObjectParser HITS_PARSER = new ConstructingObjectParser<>("hits", true, a -> a); static { HITS_PARSER.declareField(constructorArg(), (p, c) -> { if (p.currentToken() == XContentParser.Token.START_OBJECT) { @@ -184,7 +184,7 @@ class Fields { SHARDS_PARSER.declareObjectArray(optionalConstructorArg(), SEARCH_FAILURE_PARSER, new ParseField("failures")); } - public static final ConstructingObjectParser RESPONSE_PARSER = new ConstructingObjectParser<>( + public static final ConstructingObjectParser RESPONSE_PARSER = new ConstructingObjectParser<>( "search_response", true, a -> { @@ -296,13 +296,13 @@ public void setCausedBy(Throwable causedBy) { /** * Parses the main action to return just the {@linkplain Version} that it returns. We throw everything else out. */ - public static final ConstructingObjectParser MAIN_ACTION_PARSER = new ConstructingObjectParser<>( + public static final ConstructingObjectParser MAIN_ACTION_PARSER = new ConstructingObjectParser<>( "/", true, a -> (Version) a[0] ); static { - ConstructingObjectParser versionParser = new ConstructingObjectParser<>( + ConstructingObjectParser versionParser = new ConstructingObjectParser<>( "version", true, a -> a[0] == null diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteScrollableHitSource.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteScrollableHitSource.java index be691243ecf84..dc1cddca4bd55 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteScrollableHitSource.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/remote/RemoteScrollableHitSource.java @@ -49,18 +49,18 @@ import org.opensearch.client.ResponseListener; import org.opensearch.client.RestClient; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.reindex.RejectAwareActionListener; import org.opensearch.index.reindex.ScrollableHitSource; -import org.opensearch.rest.RestStatus; import org.opensearch.threadpool.ThreadPool; import java.io.IOException; @@ -181,7 +181,7 @@ protected void cleanup(Runnable onCompletion) { private void execute( Request request, - BiFunction parser, + BiFunction parser, RejectAwareActionListener listener ) { // Preserve the thread context so headers survive after the call @@ -197,12 +197,12 @@ public void onSuccess(org.opensearch.client.Response response) { try { HttpEntity responseEntity = response.getEntity(); InputStream content = responseEntity.getContent(); - XContentType xContentType = null; + MediaType mediaType = null; if (responseEntity.getContentType() != null) { final String mimeType = ContentType.parse(responseEntity.getContentType().getValue()).getMimeType(); - xContentType = XContentType.fromMediaType(mimeType); + mediaType = MediaType.fromMediaType(mimeType); } - if (xContentType == null) { + if (mediaType == null) { try { logger.debug("Response didn't include Content-Type: " + bodyMessage(response.getEntity())); throw new OpenSearchException( @@ -216,10 +216,10 @@ public void onSuccess(org.opensearch.client.Response response) { } // EMPTY is safe here because we don't call namedObject try ( - XContentParser xContentParser = xContentType.xContent() + XContentParser xContentParser = mediaType.xContent() .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, content) ) { - parsedResponse = parser.apply(xContentParser, xContentType); + parsedResponse = parser.apply(xContentParser, mediaType); } catch (XContentParseException e) { /* Because we're streaming the response we can't get a copy of it here. The best we can do is hint that it * is totally wrong and we're probably not talking to Elasticsearch. */ diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/spi/ReindexRestInterceptorProvider.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/spi/ReindexRestInterceptorProvider.java index 034981c969b4b..5af3c2a704529 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/spi/ReindexRestInterceptorProvider.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/spi/ReindexRestInterceptorProvider.java @@ -5,11 +5,12 @@ package org.opensearch.index.reindex.spi; -import java.util.Optional; import org.apache.http.HttpRequestInterceptor; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.index.reindex.ReindexRequest; +import java.util.Optional; + public interface ReindexRestInterceptorProvider { /** * @param request Reindex request. diff --git a/modules/reindex/src/main/java/org/opensearch/index/reindex/spi/RemoteReindexExtension.java b/modules/reindex/src/main/java/org/opensearch/index/reindex/spi/RemoteReindexExtension.java index ce252de292d63..00da543877be2 100644 --- a/modules/reindex/src/main/java/org/opensearch/index/reindex/spi/RemoteReindexExtension.java +++ b/modules/reindex/src/main/java/org/opensearch/index/reindex/spi/RemoteReindexExtension.java @@ -5,7 +5,7 @@ package org.opensearch.index.reindex.spi; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; import org.opensearch.index.reindex.BulkByScrollResponse; import org.opensearch.index.reindex.ReindexRequest; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java index 671faef6c5545..1aa1e7e13ea97 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java @@ -32,7 +32,6 @@ package org.opensearch.index.reindex; -import org.junit.Before; import org.opensearch.action.ActionRequest; import org.opensearch.action.delete.DeleteRequest; import org.opensearch.action.index.IndexRequest; @@ -40,6 +39,7 @@ import org.opensearch.index.reindex.AbstractAsyncBulkByScrollAction.RequestWrapper; import org.opensearch.script.ScriptService; import org.opensearch.script.UpdateScript; +import org.junit.Before; import java.util.Collections; import java.util.Map; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/AsyncBulkByScrollActionTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/AsyncBulkByScrollActionTests.java index 145aaaf24fbee..8ddc1ff778982 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/AsyncBulkByScrollActionTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/AsyncBulkByScrollActionTests.java @@ -33,13 +33,11 @@ package org.opensearch.index.reindex; import org.apache.lucene.search.TotalHits; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; +import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.action.ActionType; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.DocWriteResponse.Result; @@ -71,23 +69,25 @@ import org.opensearch.client.ParentTaskAssigningClient; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.CheckedConsumer; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AbstractRunnable; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.engine.VersionConflictEngineException; import org.opensearch.index.reindex.ScrollableHitSource.Hit; import org.opensearch.index.reindex.ScrollableHitSource.SearchFailure; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.internal.InternalSearchResponse; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskManager; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.client.NoOpClient; @@ -121,7 +121,6 @@ import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.Collections.synchronizedSet; -import static org.apache.lucene.tests.util.TestUtil.randomSimpleString; import static org.opensearch.action.bulk.BackoffPolicy.constantBackoff; import static org.opensearch.common.unit.TimeValue.timeValueMillis; import static org.opensearch.common.unit.TimeValue.timeValueSeconds; @@ -135,6 +134,7 @@ import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.apache.lucene.tests.util.TestUtil.randomSimpleString; public class AsyncBulkByScrollActionTests extends OpenSearchTestCase { private MyMockClient client; @@ -448,7 +448,7 @@ protected AbstractAsyncBulkByScrollAction.RequestWrapper buildRequest(Hit doc } }; ScrollableHitSource.BasicHit hit = new ScrollableHitSource.BasicHit("index", "id", 0); - hit.setSource(new BytesArray("{}"), XContentType.JSON); + hit.setSource(new BytesArray("{}"), MediaTypeRegistry.JSON); ScrollableHitSource.Response response = new ScrollableHitSource.Response(false, emptyList(), 1, singletonList(hit), null); simulateScrollResponse(action, System.nanoTime(), 0, response); ExecutionException e = expectThrows(ExecutionException.class, () -> listener.get()); diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/CancelTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/CancelTests.java index bd43f05225f65..1ad5b385d5676 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/CancelTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/CancelTests.java @@ -35,22 +35,22 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.ingest.DeletePipelineRequest; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.tasks.TaskCancelledException; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexModule; import org.opensearch.index.engine.Engine; import org.opensearch.index.engine.Engine.Operation.Origin; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.shard.IndexingOperationListener; -import org.opensearch.index.shard.ShardId; import org.opensearch.ingest.IngestTestPlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.tasks.TaskCancelledException; import org.opensearch.tasks.TaskInfo; import org.hamcrest.Matcher; import org.junit.Before; @@ -263,7 +263,7 @@ public void testUpdateByQueryCancel() throws Exception { + " } ]\n" + "}" ); - assertAcked(client().admin().cluster().preparePutPipeline("set-processed", pipeline, XContentType.JSON).get()); + assertAcked(client().admin().cluster().preparePutPipeline("set-processed", pipeline, MediaTypeRegistry.JSON).get()); testCancel(UpdateByQueryAction.NAME, updateByQuery().setPipeline("set-processed").source(INDEX), (response, total, modified) -> { assertThat(response, matcher().updated(modified).reasonCancelled(equalTo("by user request"))); @@ -307,7 +307,7 @@ public void testUpdateByQueryCancelWithWorkers() throws Exception { + " } ]\n" + "}" ); - assertAcked(client().admin().cluster().preparePutPipeline("set-processed", pipeline, XContentType.JSON).get()); + assertAcked(client().admin().cluster().preparePutPipeline("set-processed", pipeline, MediaTypeRegistry.JSON).get()); testCancel( UpdateByQueryAction.NAME, diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/ClientScrollableHitSourceTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/ClientScrollableHitSourceTests.java index 9333e5bd3dcc9..9a6eb7e7fc2ae 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/ClientScrollableHitSourceTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/ClientScrollableHitSourceTests.java @@ -33,9 +33,7 @@ package org.opensearch.index.reindex; import org.apache.lucene.search.TotalHits; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.bulk.BackoffPolicy; import org.opensearch.action.search.SearchAction; @@ -45,14 +43,16 @@ import org.opensearch.action.search.SearchScrollRequest; import org.opensearch.client.ParentTaskAssigningClient; import org.opensearch.client.support.AbstractClient; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.tasks.TaskId; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.internal.InternalSearchResponse; -import org.opensearch.tasks.TaskId; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; @@ -69,9 +69,9 @@ import java.util.stream.IntStream; import static java.util.Collections.emptyMap; -import static org.apache.lucene.tests.util.TestUtil.randomSimpleString; import static org.opensearch.common.unit.TimeValue.timeValueSeconds; import static org.hamcrest.Matchers.instanceOf; +import static org.apache.lucene.tests.util.TestUtil.randomSimpleString; public class ClientScrollableHitSourceTests extends OpenSearchTestCase { @@ -105,10 +105,9 @@ private static class ExpectedException extends RuntimeException { public void testRetryFail() { int retries = randomInt(10); - ExpectedException ex = expectThrows( - ExpectedException.class, - () -> { dotestBasicsWithRetry(retries, retries + 1, retries + 1, e -> { throw new ExpectedException(e); }); } - ); + ExpectedException ex = expectThrows(ExpectedException.class, () -> { + dotestBasicsWithRetry(retries, retries + 1, retries + 1, e -> { throw new ExpectedException(e); }); + }); assertThat(ex.getCause(), instanceOf(OpenSearchRejectedExecutionException.class)); } diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/DeleteByQueryBasicTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/DeleteByQueryBasicTests.java index 21bbb02fb147c..baf3c83bd0050 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/DeleteByQueryBasicTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/DeleteByQueryBasicTests.java @@ -274,9 +274,9 @@ public void testDeleteByQueryOnReadOnlyAllowDeleteIndex() throws Exception { InternalTestCluster internalTestCluster = internalCluster(); InternalClusterInfoService infoService = (InternalClusterInfoService) internalTestCluster.getInstance( ClusterInfoService.class, - internalTestCluster.getMasterName() + internalTestCluster.getClusterManagerName() ); - ThreadPool threadPool = internalTestCluster.getInstance(ThreadPool.class, internalTestCluster.getMasterName()); + ThreadPool threadPool = internalTestCluster.getInstance(ThreadPool.class, internalTestCluster.getClusterManagerName()); // Refresh the cluster info after a random delay to check the disk threshold and release the block on the index threadPool.schedule(infoService::refresh, TimeValue.timeValueMillis(randomIntBetween(1, 100)), ThreadPool.Names.MANAGEMENT); // The delete by query request will be executed successfully because the block will be released diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java index bd2aae5b7a7ab..2e14df4628283 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java @@ -34,9 +34,9 @@ import org.opensearch.client.RestClient; import org.opensearch.client.RestClientBuilderTestCase; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.index.query.MatchAllQueryBuilder; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteWhitelistTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteWhitelistTests.java index 8012b67253cb6..0546f505c58cd 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteWhitelistTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteWhitelistTests.java @@ -32,8 +32,8 @@ package org.opensearch.index.reindex; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.test.OpenSearchTestCase; import java.net.UnknownHostException; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteWithAuthTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteWithAuthTests.java index 8ce850a936557..7c2f0e2c52a0e 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteWithAuthTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexFromRemoteWithAuthTests.java @@ -32,12 +32,9 @@ package org.opensearch.index.reindex; -import org.apache.lucene.util.SetOnce; import org.opensearch.OpenSearchSecurityException; import org.opensearch.OpenSearchStatusException; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.action.admin.cluster.node.info.NodeInfo; import org.opensearch.action.search.SearchAction; import org.opensearch.action.support.ActionFilter; @@ -46,13 +43,17 @@ import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.SetOnce; import org.opensearch.common.network.NetworkModule; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.concurrent.ThreadContext; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.http.HttpInfo; @@ -60,7 +61,6 @@ import org.opensearch.plugins.Plugin; import org.opensearch.repositories.RepositoriesService; import org.opensearch.rest.RestHeaderDefinition; -import org.opensearch.rest.RestStatus; import org.opensearch.script.ScriptService; import org.opensearch.tasks.Task; import org.opensearch.test.OpenSearchSingleNodeTestCase; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexMetadataTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexMetadataTests.java index 291325a3d8a5b..82619f1c3959b 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexMetadataTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexMetadataTests.java @@ -32,8 +32,8 @@ package org.opensearch.index.reindex; -import org.opensearch.index.reindex.ScrollableHitSource.Hit; import org.opensearch.action.index.IndexRequest; +import org.opensearch.index.reindex.ScrollableHitSource.Hit; /** * Index-by-search test for ttl, timestamp, and routing. diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexRestClientSslTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexRestClientSslTests.java index eb19454d8d7e8..1123ae4623300 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexRestClientSslTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexRestClientSslTests.java @@ -36,15 +36,16 @@ import com.sun.net.httpserver.HttpsExchange; import com.sun.net.httpserver.HttpsParameters; import com.sun.net.httpserver.HttpsServer; + import org.opensearch.client.Request; import org.opensearch.client.Response; import org.opensearch.client.RestClient; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.io.PathUtils; import org.opensearch.common.settings.Settings; import org.opensearch.common.ssl.PemKeyConfig; import org.opensearch.common.ssl.PemTrustConfig; +import org.opensearch.core.common.bytes.BytesArray; import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.test.OpenSearchTestCase; @@ -60,6 +61,7 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; + import java.io.IOException; import java.net.InetSocketAddress; import java.nio.file.Path; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexScriptTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexScriptTests.java index 85f0c3c24abee..1700a25330463 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexScriptTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexScriptTests.java @@ -35,10 +35,11 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.script.ScriptService; -import org.mockito.Mockito; import java.util.Map; +import org.mockito.Mockito; + import static org.hamcrest.Matchers.containsString; /** diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexSourceTargetValidationTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexSourceTargetValidationTests.java index e3bd1c1cba4a6..026017298fc5b 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexSourceTargetValidationTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/ReindexSourceTargetValidationTests.java @@ -44,12 +44,12 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.indices.SystemIndices; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.indices.SystemIndices; import org.opensearch.test.OpenSearchTestCase; import java.util.HashMap; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/RestReindexActionTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/RestReindexActionTests.java index aa8221b045d3f..19859fea86bd6 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/RestReindexActionTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/RestReindexActionTests.java @@ -32,12 +32,12 @@ package org.opensearch.index.reindex; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.rest.FakeRestRequest; import org.opensearch.test.rest.RestActionTestCase; import org.junit.Before; @@ -88,14 +88,14 @@ public void testPipelineQueryParameterIsError() throws IOException { public void testSetScrollTimeout() throws IOException { { FakeRestRequest.Builder requestBuilder = new FakeRestRequest.Builder(xContentRegistry()); - requestBuilder.withContent(new BytesArray("{}"), XContentType.JSON); + requestBuilder.withContent(new BytesArray("{}"), MediaTypeRegistry.JSON); ReindexRequest request = action.buildRequest(requestBuilder.build(), new NamedWriteableRegistry(Collections.emptyList())); assertEquals(AbstractBulkByScrollRequest.DEFAULT_SCROLL_TIMEOUT, request.getScrollTime()); } { FakeRestRequest.Builder requestBuilder = new FakeRestRequest.Builder(xContentRegistry()); requestBuilder.withParams(singletonMap("scroll", "10m")); - requestBuilder.withContent(new BytesArray("{}"), XContentType.JSON); + requestBuilder.withContent(new BytesArray("{}"), MediaTypeRegistry.JSON); ReindexRequest request = action.buildRequest(requestBuilder.build(), new NamedWriteableRegistry(Collections.emptyList())); assertEquals("10m", request.getScrollTime().toString()); } diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/RethrottleTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/RethrottleTests.java index 6bedd59515e45..c48f55a4ab08d 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/RethrottleTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/RethrottleTests.java @@ -32,14 +32,14 @@ package org.opensearch.index.reindex; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionFuture; +import org.opensearch.OpenSearchException; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.admin.cluster.node.tasks.list.TaskGroup; import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.tasks.TaskId; import java.util.ArrayList; import java.util.List; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/RetryTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/RetryTests.java index 124670dba9510..84b9a22d90680 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/RetryTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/RetryTests.java @@ -32,7 +32,6 @@ package org.opensearch.index.reindex; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.node.info.NodeInfo; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.bulk.BackoffPolicy; @@ -40,10 +39,11 @@ import org.opensearch.action.bulk.BulkResponse; import org.opensearch.action.bulk.Retry; import org.opensearch.client.Client; -import org.opensearch.common.bytes.BytesArray; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; import org.opensearch.http.HttpInfo; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.Plugin; @@ -118,18 +118,18 @@ public void testReindex() throws Exception { public void testReindexFromRemote() throws Exception { Function> function = client -> { /* - * Use the master node for the reindex from remote because that node + * Use the cluster-manager node for the reindex from remote because that node * doesn't have a copy of the data on it. */ - NodeInfo masterNode = null; + NodeInfo clusterManagerNode = null; for (NodeInfo candidate : client.admin().cluster().prepareNodesInfo().get().getNodes()) { - if (candidate.getNode().isMasterNode()) { - masterNode = candidate; + if (candidate.getNode().isClusterManagerNode()) { + clusterManagerNode = candidate; } } - assertNotNull(masterNode); + assertNotNull(clusterManagerNode); - TransportAddress address = masterNode.getInfo(HttpInfo.class).getAddress().publishAddress(); + TransportAddress address = clusterManagerNode.getInfo(HttpInfo.class).getAddress().publishAddress(); RemoteInfo remote = new RemoteInfo( "http", address.getAddress(), @@ -206,7 +206,7 @@ private void testCase( assertFalse(initialBulkResponse.buildFailureMessage(), initialBulkResponse.hasFailures()); client().admin().indices().prepareRefresh("source").get(); - AbstractBulkByScrollRequestBuilder builder = request.apply(internalCluster().masterClient()); + AbstractBulkByScrollRequestBuilder builder = request.apply(internalCluster().clusterManagerClient()); // Make sure we use more than one batch so we have to scroll builder.source().setSize(DOC_COUNT / randomIntBetween(2, 10)); @@ -262,8 +262,8 @@ private CyclicBarrier blockExecutor(String name, String node) throws Exception { */ private BulkByScrollTask.Status taskStatus(String action) { /* - * We always use the master client because we always start the test requests on the - * master. We do this simply to make sure that the test request is not started on the + * We always use the cluster-manager client because we always start the test requests on the + * cluster-manager. We do this simply to make sure that the test request is not started on the * node who's queue we're manipulating. */ ListTasksResponse response = client().admin().cluster().prepareListTasks().setActions(action).setDetailed(true).get(); diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/RoundTripTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/RoundTripTests.java index 6239946852cf8..811620837d978 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/RoundTripTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/RoundTripTests.java @@ -33,16 +33,16 @@ package org.opensearch.index.reindex; import org.opensearch.Version; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.tasks.TaskId; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; -import org.opensearch.tasks.TaskId; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; @@ -50,8 +50,8 @@ import java.util.HashMap; import java.util.Map; -import static org.apache.lucene.tests.util.TestUtil.randomSimpleString; import static org.opensearch.common.unit.TimeValue.parseTimeValue; +import static org.apache.lucene.tests.util.TestUtil.randomSimpleString; /** * Round trip tests for all {@link Writeable} things declared in this plugin. diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/TransportRethrottleActionTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/TransportRethrottleActionTests.java index a9e1a59b7e443..44b95244c9507 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/TransportRethrottleActionTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/TransportRethrottleActionTests.java @@ -32,31 +32,32 @@ package org.opensearch.index.reindex; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.TaskOperationFailure; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.client.Client; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.Matcher; import org.junit.Before; -import org.mockito.ArgumentCaptor; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.function.Consumer; +import org.mockito.ArgumentCaptor; + import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.opensearch.common.unit.TimeValue.timeValueMillis; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.theInstance; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -131,7 +132,8 @@ public void testRethrottleSuccessfulResponse() { true, false, new TaskId("test", task.getId()), - Collections.emptyMap() + Collections.emptyMap(), + null ) ); sliceStatuses.add(new BulkByScrollTask.StatusOrException(status)); @@ -167,7 +169,8 @@ public void testRethrottleWithSomeSucceeded() { true, false, new TaskId("test", task.getId()), - Collections.emptyMap() + Collections.emptyMap(), + null ) ); sliceStatuses.add(new BulkByScrollTask.StatusOrException(status)); diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/UpdateByQueryWhileModifyingTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/UpdateByQueryWhileModifyingTests.java index f203625dcfc44..b0fe9e55d3ba2 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/UpdateByQueryWhileModifyingTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/UpdateByQueryWhileModifyingTests.java @@ -40,10 +40,10 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import static org.apache.lucene.tests.util.TestUtil.randomSimpleString; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; +import static org.apache.lucene.tests.util.TestUtil.randomSimpleString; /** * Mutates a document while update-by-query-ing it and asserts that the mutation diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteInfoTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteInfoTests.java index 91558963d4343..640a09c33b3a6 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteInfoTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteInfoTests.java @@ -32,7 +32,7 @@ package org.opensearch.index.reindex.remote; -import org.opensearch.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesArray; import org.opensearch.index.reindex.RemoteInfo; import org.opensearch.test.OpenSearchTestCase; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteRequestBuildersTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteRequestBuildersTests.java index c349bc54bcbd9..e3fbac9958df8 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteRequestBuildersTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteRequestBuildersTests.java @@ -37,10 +37,10 @@ import org.opensearch.Version; import org.opensearch.action.search.SearchRequest; import org.opensearch.client.Request; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.io.Streams; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.test.OpenSearchTestCase; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteResponseParsersTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteResponseParsersTests.java index a6b0b0a67fc89..2a082e496b9e2 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteResponseParsersTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteResponseParsersTests.java @@ -33,10 +33,10 @@ package org.opensearch.index.reindex.remote; import org.opensearch.action.search.ShardSearchFailure; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.reindex.ScrollableHitSource; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.Matchers; diff --git a/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteScrollableHitSourceTests.java b/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteScrollableHitSourceTests.java index 337bc67796f8e..aabd1100859d7 100644 --- a/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteScrollableHitSourceTests.java +++ b/modules/reindex/src/test/java/org/opensearch/index/reindex/remote/RemoteScrollableHitSourceTests.java @@ -57,27 +57,25 @@ import org.opensearch.action.search.SearchRequest; import org.opensearch.client.HeapBufferedAsyncResponseConsumer; import org.opensearch.client.RestClient; -import org.opensearch.common.ParsingException; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.io.FileSystemUtils; import org.opensearch.common.io.Streams; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.OpenSearchExecutors; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.util.FileSystemUtils; import org.opensearch.index.reindex.RejectAwareActionListener; import org.opensearch.index.reindex.ScrollableHitSource; import org.opensearch.index.reindex.ScrollableHitSource.Response; -import org.opensearch.rest.RestStatus; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; import org.junit.After; import org.junit.Before; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import java.io.IOException; import java.io.InputStreamReader; @@ -92,6 +90,9 @@ import java.util.function.Consumer; import java.util.stream.Stream; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + import static org.opensearch.common.unit.TimeValue.timeValueMillis; import static org.opensearch.common.unit.TimeValue.timeValueMinutes; import static org.hamcrest.Matchers.empty; diff --git a/modules/reindex/src/test/resources/responses/failure_with_status.json b/modules/reindex/src/test/resources/responses/failure_with_status.json index 314de37a6793e..f9de958a571b3 100644 --- a/modules/reindex/src/test/resources/responses/failure_with_status.json +++ b/modules/reindex/src/test/resources/responses/failure_with_status.json @@ -16,7 +16,6 @@ "max_score": 0.0, "hits": [ { "_index": "test", - "_type": "test", "_id": "10000", "_version": 1, "_score": 0.0, diff --git a/modules/reindex/src/test/resources/responses/rejection.json b/modules/reindex/src/test/resources/responses/rejection.json index 0cc48bfca4fb5..e99cf02ee8023 100644 --- a/modules/reindex/src/test/resources/responses/rejection.json +++ b/modules/reindex/src/test/resources/responses/rejection.json @@ -21,7 +21,6 @@ "max_score" : null, "hits" : [ { "_index" : "test", - "_type" : "test", "_id" : "AVToMiC250DjIiBO3yJ_", "_version" : 1, "_score" : null, diff --git a/modules/reindex/src/test/resources/responses/scroll_fully_loaded.json b/modules/reindex/src/test/resources/responses/scroll_fully_loaded.json index a2c1be34e5ccd..7a6b1ff748b92 100644 --- a/modules/reindex/src/test/resources/responses/scroll_fully_loaded.json +++ b/modules/reindex/src/test/resources/responses/scroll_fully_loaded.json @@ -13,7 +13,6 @@ "max_score" : null, "hits" : [ { "_index" : "test", - "_type" : "test", "_id" : "AVToMiDL50DjIiBO3yKA", "_version" : 1, "_score" : null, diff --git a/modules/reindex/src/test/resources/responses/scroll_fully_loaded_1_7.json b/modules/reindex/src/test/resources/responses/scroll_fully_loaded_1_7.json index f8bebddecf3cf..2f5456704a6f0 100644 --- a/modules/reindex/src/test/resources/responses/scroll_fully_loaded_1_7.json +++ b/modules/reindex/src/test/resources/responses/scroll_fully_loaded_1_7.json @@ -13,7 +13,6 @@ "max_score" : null, "hits" : [ { "_index" : "test", - "_type" : "test", "_id" : "AVToMiDL50DjIiBO3yKA", "_version" : 1, "_score" : null, diff --git a/modules/reindex/src/test/resources/responses/scroll_ok.json b/modules/reindex/src/test/resources/responses/scroll_ok.json index 5cdc4a400cbf0..ea6b473ed3e0e 100644 --- a/modules/reindex/src/test/resources/responses/scroll_ok.json +++ b/modules/reindex/src/test/resources/responses/scroll_ok.json @@ -13,7 +13,6 @@ "max_score" : null, "hits" : [ { "_index" : "test", - "_type" : "test", "_id" : "AVToMiDL50DjIiBO3yKA", "_version" : 1, "_score" : null, diff --git a/modules/reindex/src/test/resources/responses/start_ok.json b/modules/reindex/src/test/resources/responses/start_ok.json index a2988341f8caf..97ef80f2dac06 100644 --- a/modules/reindex/src/test/resources/responses/start_ok.json +++ b/modules/reindex/src/test/resources/responses/start_ok.json @@ -12,7 +12,6 @@ "max_score" : null, "hits" : [ { "_index" : "test", - "_type" : "test", "_id" : "AVToMiC250DjIiBO3yJ_", "_version" : 1, "_score" : null, diff --git a/modules/reindex/src/yamlRestTest/resources/rest-api-spec/test/reindex/90_remote.yml b/modules/reindex/src/yamlRestTest/resources/rest-api-spec/test/reindex/90_remote.yml index ac8d0730ff283..7d43a54987225 100644 --- a/modules/reindex/src/yamlRestTest/resources/rest-api-spec/test/reindex/90_remote.yml +++ b/modules/reindex/src/yamlRestTest/resources/rest-api-spec/test/reindex/90_remote.yml @@ -7,15 +7,15 @@ body: { "text": "test" } refresh: true - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: refresh: true @@ -68,15 +68,15 @@ - do: indices.refresh: {} - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: refresh: true @@ -118,15 +118,15 @@ routing: foo refresh: true - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: refresh: true @@ -169,15 +169,15 @@ body: { "text": "test" } refresh: true - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: refresh: true @@ -236,15 +236,15 @@ body: { "text": "test" } refresh: true - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: refresh: true @@ -302,15 +302,15 @@ body: { "text": "test" } refresh: true - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: refresh: true @@ -358,15 +358,15 @@ body: { "text": "test" } refresh: true - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: catch: /query malformed, no start_object after query name/ reindex: @@ -390,14 +390,12 @@ refresh: true - do: - # sometimes IIS is listening on port 0. In that case we fail in other ways and this test isn't useful. - # make sure to stop any local webservers if running this test locally otherwise an s_s_l handshake exception may occur catch: /connect_exception|IIS Windows Server/ reindex: body: source: remote: - host: http://127.0.0.1:0 + host: http://127.0.0.1:54321 index: source dest: index: dest @@ -411,15 +409,15 @@ body: { "text": "test", "filtered": "removed" } refresh: true - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: refresh: true @@ -480,15 +478,15 @@ indices.refresh: {} - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: requests_per_second: .00000001 # About 9.5 years to complete the request diff --git a/modules/reindex/src/yamlRestTest/resources/rest-api-spec/test/reindex/95_parent_join.yml b/modules/reindex/src/yamlRestTest/resources/rest-api-spec/test/reindex/95_parent_join.yml index b36593b4b962c..a3c2e0505bda6 100644 --- a/modules/reindex/src/yamlRestTest/resources/rest-api-spec/test/reindex/95_parent_join.yml +++ b/modules/reindex/src/yamlRestTest/resources/rest-api-spec/test/reindex/95_parent_join.yml @@ -88,15 +88,15 @@ setup: --- "Reindex from remote with parent join field": - # Fetch the http host. We use the host of the master because we know there will always be a master. + # Fetch the http host. We use the host of the cluster-manager because we know there will always be a cluster-manager. - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: metric: [ http ] - - is_true: nodes.$master.http.publish_address - - set: {nodes.$master.http.publish_address: host} + - is_true: nodes.$cluster_manager.http.publish_address + - set: {nodes.$cluster_manager.http.publish_address: host} - do: reindex: refresh: true diff --git a/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 b/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/modules/repository-s3/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/modules/repository-url/build.gradle b/modules/repository-url/build.gradle index 24742416de6f2..702f0e9bb0f8b 100644 --- a/modules/repository-url/build.gradle +++ b/modules/repository-url/build.gradle @@ -75,4 +75,3 @@ testClusters.all { "http://snapshot.test*,http://${urlFixture.addressAndPort}" }, PropertyNormalization.IGNORE_VALUE } - diff --git a/modules/repository-url/src/internalClusterTest/java/org/opensearch/repositories/url/URLSnapshotRestoreIT.java b/modules/repository-url/src/internalClusterTest/java/org/opensearch/repositories/url/URLSnapshotRestoreIT.java index aa274549f3a9b..e073db7276119 100644 --- a/modules/repository-url/src/internalClusterTest/java/org/opensearch/repositories/url/URLSnapshotRestoreIT.java +++ b/modules/repository-url/src/internalClusterTest/java/org/opensearch/repositories/url/URLSnapshotRestoreIT.java @@ -38,7 +38,7 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.Client; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeUnit; import org.opensearch.plugin.repository.url.URLRepositoryPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.fs.FsRepository; diff --git a/modules/repository-url/src/main/java/org/opensearch/common/blobstore/url/URLBlobContainer.java b/modules/repository-url/src/main/java/org/opensearch/common/blobstore/url/URLBlobContainer.java index 1f966be98bdf7..b13a4d5a39a5b 100644 --- a/modules/repository-url/src/main/java/org/opensearch/common/blobstore/url/URLBlobContainer.java +++ b/modules/repository-url/src/main/java/org/opensearch/common/blobstore/url/URLBlobContainer.java @@ -83,12 +83,20 @@ public URL url() { } /** - * This operation is not supported by URLBlobContainer + * Tests whether a blob with the given blob name exists in the container. + * + * @param blobName + * The name of the blob whose existence is to be determined. + * @return {@code true} if a blob exists in the {@link BlobContainer} with the given name, and {@code false} otherwise. */ @Override - public boolean blobExists(String blobName) { - assert false : "should never be called for a read-only url repo"; - throw new UnsupportedOperationException("URL repository doesn't support this operation"); + public boolean blobExists(String blobName) throws IOException { + try { + readBlob(blobName); + return true; + } catch (FileNotFoundException e) { + return false; + } } /** diff --git a/modules/repository-url/src/main/java/org/opensearch/common/blobstore/url/URLBlobStore.java b/modules/repository-url/src/main/java/org/opensearch/common/blobstore/url/URLBlobStore.java index fbfbf5e006fee..0fad0cbe21033 100644 --- a/modules/repository-url/src/main/java/org/opensearch/common/blobstore/url/URLBlobStore.java +++ b/modules/repository-url/src/main/java/org/opensearch/common/blobstore/url/URLBlobStore.java @@ -37,8 +37,8 @@ import org.opensearch.common.blobstore.BlobStore; import org.opensearch.common.blobstore.BlobStoreException; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import java.net.MalformedURLException; import java.net.URL; diff --git a/modules/repository-url/src/main/java/org/opensearch/plugin/repository/url/URLRepositoryPlugin.java b/modules/repository-url/src/main/java/org/opensearch/plugin/repository/url/URLRepositoryPlugin.java index d1b08d443805f..5b26008d70f46 100644 --- a/modules/repository-url/src/main/java/org/opensearch/plugin/repository/url/URLRepositoryPlugin.java +++ b/modules/repository-url/src/main/java/org/opensearch/plugin/repository/url/URLRepositoryPlugin.java @@ -34,7 +34,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Setting; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; diff --git a/modules/repository-url/src/main/java/org/opensearch/repositories/url/URLRepository.java b/modules/repository-url/src/main/java/org/opensearch/repositories/url/URLRepository.java index 837a30555e127..4c8d8aab4532b 100644 --- a/modules/repository-url/src/main/java/org/opensearch/repositories/url/URLRepository.java +++ b/modules/repository-url/src/main/java/org/opensearch/repositories/url/URLRepository.java @@ -43,7 +43,7 @@ import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.util.URIPattern; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.repositories.RepositoryException; @@ -113,7 +113,7 @@ public URLRepository( ClusterService clusterService, RecoverySettings recoverySettings ) { - super(metadata, false, namedXContentRegistry, clusterService, recoverySettings); + super(metadata, namedXContentRegistry, clusterService, recoverySettings); if (URL_SETTING.exists(metadata.settings()) == false && REPOSITORIES_URL_SETTING.exists(environment.settings()) == false) { throw new RepositoryException(metadata.name(), "missing url"); diff --git a/modules/repository-url/src/test/java/org/opensearch/common/blobstore/url/URLBlobStoreTests.java b/modules/repository-url/src/test/java/org/opensearch/common/blobstore/url/URLBlobStoreTests.java index 90b75f50b16ad..0b62304270052 100644 --- a/modules/repository-url/src/test/java/org/opensearch/common/blobstore/url/URLBlobStoreTests.java +++ b/modules/repository-url/src/test/java/org/opensearch/common/blobstore/url/URLBlobStoreTests.java @@ -33,6 +33,7 @@ package org.opensearch.common.blobstore.url; import com.sun.net.httpserver.HttpServer; + import org.opensearch.common.SuppressForbidden; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; diff --git a/modules/repository-url/src/test/java/org/opensearch/repositories/url/URLFixture.java b/modules/repository-url/src/test/java/org/opensearch/repositories/url/URLFixture.java index 04c2d249be635..b2dab1314f66d 100644 --- a/modules/repository-url/src/test/java/org/opensearch/repositories/url/URLFixture.java +++ b/modules/repository-url/src/test/java/org/opensearch/repositories/url/URLFixture.java @@ -31,9 +31,9 @@ package org.opensearch.repositories.url; -import org.opensearch.test.fixture.AbstractHttpFixture; import org.opensearch.common.SuppressForbidden; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.test.fixture.AbstractHttpFixture; import java.io.IOException; import java.nio.file.Files; diff --git a/modules/repository-url/src/test/java/org/opensearch/repositories/url/URLRepositoryTests.java b/modules/repository-url/src/test/java/org/opensearch/repositories/url/URLRepositoryTests.java index 30d1c81fc9e9f..7fdfa2d6b3346 100644 --- a/modules/repository-url/src/test/java/org/opensearch/repositories/url/URLRepositoryTests.java +++ b/modules/repository-url/src/test/java/org/opensearch/repositories/url/URLRepositoryTests.java @@ -35,7 +35,7 @@ import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.TestEnvironment; import org.opensearch.indices.recovery.RecoverySettings; diff --git a/modules/repository-url/src/yamlRestTest/java/org/opensearch/repositories/url/RepositoryURLClientYamlTestSuiteIT.java b/modules/repository-url/src/yamlRestTest/java/org/opensearch/repositories/url/RepositoryURLClientYamlTestSuiteIT.java index 3d0c09fb2288c..02c890b2a8ee4 100644 --- a/modules/repository-url/src/yamlRestTest/java/org/opensearch/repositories/url/RepositoryURLClientYamlTestSuiteIT.java +++ b/modules/repository-url/src/yamlRestTest/java/org/opensearch/repositories/url/RepositoryURLClientYamlTestSuiteIT.java @@ -34,19 +34,19 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.nio.entity.NStringEntity; import org.opensearch.client.Request; import org.opensearch.client.Response; -import org.opensearch.common.Strings; import org.opensearch.common.io.PathUtils; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.repositories.fs.FsRepository; -import org.opensearch.rest.RestStatus; import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; import org.junit.Before; @@ -144,7 +144,7 @@ private static HttpEntity buildRepositorySettings(final String type, final Setti builder.endObject(); } builder.endObject(); - return new NStringEntity(Strings.toString(builder), ContentType.APPLICATION_JSON); + return new NStringEntity(builder.toString(), ContentType.APPLICATION_JSON); } } } diff --git a/modules/repository-url/src/yamlRestTest/resources/rest-api-spec/test/repository_url/10_basic.yml b/modules/repository-url/src/yamlRestTest/resources/rest-api-spec/test/repository_url/10_basic.yml index b932f0d53caad..0c7aed0ae7103 100644 --- a/modules/repository-url/src/yamlRestTest/resources/rest-api-spec/test/repository_url/10_basic.yml +++ b/modules/repository-url/src/yamlRestTest/resources/rest-api-spec/test/repository_url/10_basic.yml @@ -102,13 +102,13 @@ teardown: - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: repository-url } } + - contains: { nodes.$cluster_manager.modules: { name: repository-url } } --- "Restore with repository-url using http://": diff --git a/modules/repository-url/src/yamlRestTest/resources/rest-api-spec/test/repository_url/20_repository.yml b/modules/repository-url/src/yamlRestTest/resources/rest-api-spec/test/repository_url/20_repository.yml index f40ad16bb4265..cc6b3b5ce1f33 100644 --- a/modules/repository-url/src/yamlRestTest/resources/rest-api-spec/test/repository_url/20_repository.yml +++ b/modules/repository-url/src/yamlRestTest/resources/rest-api-spec/test/repository_url/20_repository.yml @@ -27,6 +27,3 @@ type: url settings: url: "http://snapshot.unknown" - - - diff --git a/modules/search-pipeline-common/build.gradle b/modules/search-pipeline-common/build.gradle new file mode 100644 index 0000000000000..657392d884e97 --- /dev/null +++ b/modules/search-pipeline-common/build.gradle @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +apply plugin: 'opensearch.yaml-rest-test' +apply plugin: 'opensearch.internal-cluster-test' + +opensearchplugin { + description 'Module for search pipeline processors that do not require additional security permissions or have large dependencies and resources' + classname 'org.opensearch.search.pipeline.common.SearchPipelineCommonModulePlugin' + extendedPlugins = ['lang-painless'] +} + +dependencies { + compileOnly project(':modules:lang-painless') +} + +restResources { + restApi { + includeCore '_common', 'search_pipeline', 'cluster', 'indices', 'index', 'nodes', 'search' + } +} + + +thirdPartyAudit.ignoreMissingClasses( + // from log4j + 'org.osgi.framework.AdaptPermission', + 'org.osgi.framework.AdminPermission', + 'org.osgi.framework.Bundle', + 'org.osgi.framework.BundleActivator', + 'org.osgi.framework.BundleContext', + 'org.osgi.framework.BundleEvent', + 'org.osgi.framework.SynchronousBundleListener', + 'org.osgi.framework.wiring.BundleWire', + 'org.osgi.framework.wiring.BundleWiring' +) diff --git a/modules/search-pipeline-common/src/internalClusterTest/java/org/opensearch/search/pipeline/common/SearchPipelineCommonIT.java b/modules/search-pipeline-common/src/internalClusterTest/java/org/opensearch/search/pipeline/common/SearchPipelineCommonIT.java new file mode 100644 index 0000000000000..b8b0798812df1 --- /dev/null +++ b/modules/search-pipeline-common/src/internalClusterTest/java/org/opensearch/search/pipeline/common/SearchPipelineCommonIT.java @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; +import org.opensearch.action.admin.indices.refresh.RefreshRequest; +import org.opensearch.action.admin.indices.refresh.RefreshResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.search.DeleteSearchPipelineRequest; +import org.opensearch.action.search.PutSearchPipelineRequest; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.index.query.MatchAllQueryBuilder; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class SearchPipelineCommonIT extends OpenSearchIntegTestCase { + + @Override + protected Collection> nodePlugins() { + return List.of(SearchPipelineCommonModulePlugin.class); + } + + public void testFilterQuery() { + // Create a pipeline with a filter_query processor. + String pipelineName = "foo"; + PutSearchPipelineRequest putSearchPipelineRequest = new PutSearchPipelineRequest( + pipelineName, + new BytesArray( + "{" + + "\"request_processors\": [" + + "{" + + "\"filter_query\" : {" + + "\"query\": {" + + "\"term\" : {" + + "\"field\" : \"value\"" + + "}" + + "}" + + "}" + + "}" + + "]" + + "}" + ), + MediaTypeRegistry.JSON + ); + AcknowledgedResponse ackRsp = client().admin().cluster().putSearchPipeline(putSearchPipelineRequest).actionGet(); + assertTrue(ackRsp.isAcknowledged()); + + // Index some documents. + String indexName = "myindex"; + IndexRequest doc1 = new IndexRequest(indexName).id("doc1").source(Map.of("field", "value")); + IndexRequest doc2 = new IndexRequest(indexName).id("doc2").source(Map.of("field", "something else")); + + IndexResponse ir = client().index(doc1).actionGet(); + assertSame(RestStatus.CREATED, ir.status()); + ir = client().index(doc2).actionGet(); + assertSame(RestStatus.CREATED, ir.status()); + + // Refresh so the documents are visible to search. + RefreshResponse refRsp = client().admin().indices().refresh(new RefreshRequest(indexName)).actionGet(); + assertSame(RestStatus.OK, refRsp.getStatus()); + + // Search without the pipeline. Should see both documents. + SearchRequest req = new SearchRequest(indexName).source(new SearchSourceBuilder().query(new MatchAllQueryBuilder())); + SearchResponse rsp = client().search(req).actionGet(); + assertEquals(2, rsp.getHits().getTotalHits().value); + + // Search with the pipeline. Should only see document with "field":"value". + req.pipeline(pipelineName); + rsp = client().search(req).actionGet(); + assertEquals(1, rsp.getHits().getTotalHits().value); + + // Clean up. + ackRsp = client().admin().cluster().deleteSearchPipeline(new DeleteSearchPipelineRequest(pipelineName)).actionGet(); + assertTrue(ackRsp.isAcknowledged()); + ackRsp = client().admin().indices().delete(new DeleteIndexRequest(indexName)).actionGet(); + assertTrue(ackRsp.isAcknowledged()); + } +} diff --git a/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/FilterQueryRequestProcessor.java b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/FilterQueryRequestProcessor.java new file mode 100644 index 0000000000000..3d04d6d5ed1e3 --- /dev/null +++ b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/FilterQueryRequestProcessor.java @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.common.xcontent.LoggingDeprecationHandler; +import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.index.query.BoolQueryBuilder; +import org.opensearch.index.query.QueryBuilder; +import org.opensearch.ingest.ConfigurationUtils; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.search.pipeline.AbstractProcessor; +import org.opensearch.search.pipeline.Processor; +import org.opensearch.search.pipeline.SearchRequestProcessor; + +import java.io.InputStream; +import java.util.Map; + +import static org.opensearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder; + +/** + * This is a {@link SearchRequestProcessor} that replaces the incoming query with a BooleanQuery + * that MUST match the incoming query with a FILTER clause based on the configured query. + */ +public class FilterQueryRequestProcessor extends AbstractProcessor implements SearchRequestProcessor { + /** + * Key to reference this processor type from a search pipeline. + */ + public static final String TYPE = "filter_query"; + + final QueryBuilder filterQuery; + + /** + * Returns the type of the processor. + * + * @return The processor type. + */ + @Override + public String getType() { + return TYPE; + } + + /** + * Constructor that takes a filter query. + * + * @param tag processor tag + * @param description processor description + * @param ignoreFailure option to ignore failure + * @param filterQuery the query that will be added as a filter to incoming queries + */ + FilterQueryRequestProcessor(String tag, String description, boolean ignoreFailure, QueryBuilder filterQuery) { + super(tag, description, ignoreFailure); + this.filterQuery = filterQuery; + } + + /** + * Modifies the search request by adding a filtered query to the existing query, if any, and sets it as the new query + * in the search request's SearchSourceBuilder. + * + * @param request The search request to be processed. + * @return The modified search request. + * @throws Exception if an error occurs while processing the request. + */ + @Override + public SearchRequest processRequest(SearchRequest request) throws Exception { + QueryBuilder originalQuery = null; + if (request.source() != null) { + originalQuery = request.source().query(); + } + + BoolQueryBuilder filteredQuery = new BoolQueryBuilder().filter(filterQuery); + if (originalQuery != null) { + filteredQuery.must(originalQuery); + } + if (request.source() == null) { + request.source(new SearchSourceBuilder()); + } + request.source().query(filteredQuery); + return request; + } + + static class Factory implements Processor.Factory { + private static final String QUERY_KEY = "query"; + private final NamedXContentRegistry namedXContentRegistry; + + Factory(NamedXContentRegistry namedXContentRegistry) { + this.namedXContentRegistry = namedXContentRegistry; + } + + @Override + public FilterQueryRequestProcessor create( + Map> processorFactories, + String tag, + String description, + boolean ignoreFailure, + Map config, + PipelineContext pipelineContext + ) throws Exception { + Map query = ConfigurationUtils.readOptionalMap(TYPE, tag, config, QUERY_KEY); + if (query == null) { + throw new IllegalArgumentException("Did not specify the " + QUERY_KEY + " property in processor of type " + TYPE); + } + try ( + XContentBuilder builder = XContentBuilder.builder(JsonXContent.jsonXContent).map(query); + InputStream stream = BytesReference.bytes(builder).streamInput(); + XContentParser parser = MediaTypeRegistry.JSON.xContent() + .createParser(namedXContentRegistry, LoggingDeprecationHandler.INSTANCE, stream) + ) { + return new FilterQueryRequestProcessor(tag, description, ignoreFailure, parseInnerQueryBuilder(parser)); + } + } + } +} diff --git a/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/RenameFieldResponseProcessor.java b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/RenameFieldResponseProcessor.java new file mode 100644 index 0000000000000..212fe844d97a1 --- /dev/null +++ b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/RenameFieldResponseProcessor.java @@ -0,0 +1,162 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.collect.Tuple; +import org.opensearch.common.document.DocumentField; +import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.ingest.ConfigurationUtils; +import org.opensearch.search.SearchHit; +import org.opensearch.search.pipeline.AbstractProcessor; +import org.opensearch.search.pipeline.Processor; +import org.opensearch.search.pipeline.SearchRequestProcessor; +import org.opensearch.search.pipeline.SearchResponseProcessor; + +import java.util.Map; + +/** + * This is a {@link SearchRequestProcessor} that renames a field before returning the search response + */ +public class RenameFieldResponseProcessor extends AbstractProcessor implements SearchResponseProcessor { + + private final String oldField; + private final String newField; + private final boolean ignoreMissing; + + /** + * Key to reference this processor type from a search pipeline. + */ + public static final String TYPE = "rename_field"; + + /** + * Constructor that takes a target field to rename and the new name + * + * @param tag processor tag + * @param description processor description + * @param ignoreFailure option to ignore failure + * @param oldField name of field to be renamed + * @param newField name of field that will replace the old field + * @param ignoreMissing if true, do not throw error if oldField does not exist within search response + */ + public RenameFieldResponseProcessor( + String tag, + String description, + boolean ignoreFailure, + String oldField, + String newField, + boolean ignoreMissing + ) { + super(tag, description, ignoreFailure); + this.oldField = oldField; + this.newField = newField; + this.ignoreMissing = ignoreMissing; + } + + @Override + public String getType() { + return TYPE; + } + + /** + * Getter function for oldField + * @return oldField + */ + public String getOldField() { + return oldField; + } + + /** + * Getter function for newField + * @return newField + */ + public String getNewField() { + return newField; + } + + /** + * Getter function for ignoreMissing + * @return ignoreMissing + */ + public boolean isIgnoreMissing() { + return ignoreMissing; + } + + @Override + public SearchResponse processResponse(SearchRequest request, SearchResponse response) throws Exception { + boolean foundField = false; + + SearchHit[] hits = response.getHits().getHits(); + for (SearchHit hit : hits) { + Map fields = hit.getFields(); + if (fields.containsKey(oldField)) { + foundField = true; + DocumentField old = hit.removeDocumentField(oldField); + DocumentField newDocField = new DocumentField(newField, old.getValues()); + hit.setDocumentField(newField, newDocField); + } + + if (hit.hasSource()) { + BytesReference sourceRef = hit.getSourceRef(); + Tuple> typeAndSourceMap = XContentHelper.convertToMap( + sourceRef, + false, + (MediaType) null + ); + + Map sourceAsMap = typeAndSourceMap.v2(); + if (sourceAsMap.containsKey(oldField)) { + foundField = true; + Object val = sourceAsMap.remove(oldField); + sourceAsMap.put(newField, val); + + XContentBuilder builder = XContentBuilder.builder(typeAndSourceMap.v1().xContent()); + builder.map(sourceAsMap); + hit.sourceRef(BytesReference.bytes(builder)); + } + } + + if (!foundField && !ignoreMissing) { + throw new IllegalArgumentException("Document with id " + hit.getId() + " is missing field " + oldField); + } + } + + return response; + } + + /** + * This is a factor that creates the RenameResponseProcessor + */ + public static final class Factory implements Processor.Factory { + + /** + * Constructor for factory + */ + Factory() {} + + @Override + public RenameFieldResponseProcessor create( + Map> processorFactories, + String tag, + String description, + boolean ignoreFailure, + Map config, + PipelineContext pipelineContext + ) throws Exception { + String oldField = ConfigurationUtils.readStringProperty(TYPE, tag, config, "field"); + String newField = ConfigurationUtils.readStringProperty(TYPE, tag, config, "target_field"); + boolean ignoreMissing = ConfigurationUtils.readBooleanProperty(TYPE, tag, config, "ignore_missing", false); + return new RenameFieldResponseProcessor(tag, description, ignoreFailure, oldField, newField, ignoreMissing); + } + } +} diff --git a/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/ScriptRequestProcessor.java b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/ScriptRequestProcessor.java new file mode 100644 index 0000000000000..90f71fd1754e4 --- /dev/null +++ b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/ScriptRequestProcessor.java @@ -0,0 +1,183 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.common.Nullable; +import org.opensearch.common.xcontent.LoggingDeprecationHandler; +import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.script.Script; +import org.opensearch.script.ScriptException; +import org.opensearch.script.ScriptService; +import org.opensearch.script.ScriptType; +import org.opensearch.script.SearchScript; +import org.opensearch.search.pipeline.AbstractProcessor; +import org.opensearch.search.pipeline.Processor; +import org.opensearch.search.pipeline.SearchRequestProcessor; +import org.opensearch.search.pipeline.common.helpers.SearchRequestMap; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.opensearch.ingest.ConfigurationUtils.newConfigurationException; + +/** + * Processor that evaluates a script with a search request in its context + * and then returns the modified search request. + */ +public final class ScriptRequestProcessor extends AbstractProcessor implements SearchRequestProcessor { + /** + * Key to reference this processor type from a search pipeline. + */ + public static final String TYPE = "script"; + + private final Script script; + private final ScriptService scriptService; + private final SearchScript precompiledSearchScript; + + /** + * Processor that evaluates a script with a search request in its context + * + * @param tag The processor's tag. + * @param description The processor's description. + * @param ignoreFailure The option to ignore failure + * @param script The {@link Script} to execute. + * @param precompiledSearchScript The {@link Script} precompiled + * @param scriptService The {@link ScriptService} used to execute the script. + */ + ScriptRequestProcessor( + String tag, + String description, + boolean ignoreFailure, + Script script, + @Nullable SearchScript precompiledSearchScript, + ScriptService scriptService + ) { + super(tag, description, ignoreFailure); + this.script = script; + this.precompiledSearchScript = precompiledSearchScript; + this.scriptService = scriptService; + } + + /** + * Executes the script with the search request in context. + * + * @param request The search request passed into the script context. + * @return The modified search request. + * @throws Exception if an error occurs while processing the request. + */ + @Override + public SearchRequest processRequest(SearchRequest request) throws Exception { + // assert request is not null and source is not null + if (request == null || request.source() == null) { + throw new IllegalArgumentException("search request must not be null"); + } + final SearchScript searchScript; + if (precompiledSearchScript == null) { + SearchScript.Factory factory = scriptService.compile(script, SearchScript.CONTEXT); + searchScript = factory.newInstance(script.getParams()); + } else { + searchScript = precompiledSearchScript; + } + // execute the script with the search request in context + searchScript.execute(Map.of("_source", new SearchRequestMap(request))); + return request; + } + + /** + * Returns the type of the processor. + * + * @return The processor type. + */ + @Override + public String getType() { + return TYPE; + } + + /** + * Returns the script used by the processor. + * + * @return The script. + */ + Script getScript() { + return script; + } + + /** + * Returns the precompiled search script used by the processor. + * + * @return The precompiled search script. + */ + SearchScript getPrecompiledSearchScript() { + return precompiledSearchScript; + } + + /** + * Factory class for creating {@link ScriptRequestProcessor}. + */ + public static final class Factory implements Processor.Factory { + private static final List SCRIPT_CONFIG_KEYS = List.of("id", "source", "inline", "lang", "params", "options"); + + private final ScriptService scriptService; + + /** + * Constructs a new Factory instance with the specified {@link ScriptService}. + * + * @param scriptService The {@link ScriptService} used to execute scripts. + */ + public Factory(ScriptService scriptService) { + this.scriptService = scriptService; + } + + @Override + public ScriptRequestProcessor create( + Map> registry, + String processorTag, + String description, + boolean ignoreFailure, + Map config, + PipelineContext pipelineContext + ) throws Exception { + Map scriptConfig = new HashMap<>(); + for (String key : SCRIPT_CONFIG_KEYS) { + Object val = config.remove(key); + if (val != null) { + scriptConfig.put(key, val); + } + } + try ( + XContentBuilder builder = XContentBuilder.builder(JsonXContent.jsonXContent).map(scriptConfig); + InputStream stream = BytesReference.bytes(builder).streamInput(); + XContentParser parser = MediaTypeRegistry.JSON.xContent() + .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, stream) + ) { + Script script = Script.parse(parser); + + // verify script is able to be compiled before successfully creating processor. + SearchScript searchScript = null; + try { + final SearchScript.Factory factory = scriptService.compile(script, SearchScript.CONTEXT); + if (ScriptType.INLINE.equals(script.getType())) { + searchScript = factory.newInstance(script.getParams()); + } + } catch (ScriptException e) { + throw newConfigurationException(TYPE, processorTag, null, e); + } + return new ScriptRequestProcessor(processorTag, description, ignoreFailure, script, searchScript, scriptService); + } + } + } +} diff --git a/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/SearchPipelineCommonModulePlugin.java b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/SearchPipelineCommonModulePlugin.java new file mode 100644 index 0000000000000..49681b80fdead --- /dev/null +++ b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/SearchPipelineCommonModulePlugin.java @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import org.opensearch.plugins.Plugin; +import org.opensearch.plugins.SearchPipelinePlugin; +import org.opensearch.search.pipeline.Processor; +import org.opensearch.search.pipeline.SearchRequestProcessor; +import org.opensearch.search.pipeline.SearchResponseProcessor; + +import java.util.Map; + +/** + * Plugin providing common search request/response processors for use in search pipelines. + */ +public class SearchPipelineCommonModulePlugin extends Plugin implements SearchPipelinePlugin { + + /** + * No constructor needed, but build complains if we don't have a constructor with JavaDoc. + */ + public SearchPipelineCommonModulePlugin() {} + + /** + * Returns a map of processor factories. + * + * @param parameters The parameters required for creating the processor factories. + * @return A map of processor factories, where the keys are the processor types and the values are the corresponding factory instances. + */ + @Override + public Map> getRequestProcessors(Parameters parameters) { + return Map.of( + FilterQueryRequestProcessor.TYPE, + new FilterQueryRequestProcessor.Factory(parameters.namedXContentRegistry), + ScriptRequestProcessor.TYPE, + new ScriptRequestProcessor.Factory(parameters.scriptService) + ); + } + + @Override + public Map> getResponseProcessors(Parameters parameters) { + return Map.of(RenameFieldResponseProcessor.TYPE, new RenameFieldResponseProcessor.Factory()); + } +} diff --git a/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMap.java b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMap.java new file mode 100644 index 0000000000000..7af3ac66be146 --- /dev/null +++ b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMap.java @@ -0,0 +1,395 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common.helpers; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.search.builder.SearchSourceBuilder; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * A custom implementation of {@link Map} that provides access to the properties of a {@link SearchRequest}'s + * {@link SearchSourceBuilder}. The class allows retrieving and modifying specific properties of the search request. + */ +public class SearchRequestMap implements Map { + private static final String UNSUPPORTED_OP_ERR = " Method not supported in Search pipeline script"; + + private final SearchSourceBuilder source; + + /** + * Constructs a new instance of the {@link SearchRequestMap} with the provided {@link SearchRequest}. + * + * @param searchRequest The SearchRequest containing the SearchSourceBuilder to be accessed. + */ + public SearchRequestMap(SearchRequest searchRequest) { + source = searchRequest.source(); + } + + /** + * Retrieves the number of properties in the SearchSourceBuilder. + * + * @return The number of properties in the SearchSourceBuilder. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public int size() { + throw new UnsupportedOperationException("size" + UNSUPPORTED_OP_ERR); + } + + /** + * Checks if the SearchSourceBuilder is empty. + * + * @return {@code true} if the SearchSourceBuilder is empty, {@code false} otherwise. + */ + @Override + public boolean isEmpty() { + return source == null; + } + + /** + * Checks if the SearchSourceBuilder contains the specified property. + * + * @param key The property to check for. + * @return {@code true} if the SearchSourceBuilder contains the specified property, {@code false} otherwise. + */ + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + /** + * Checks if the SearchSourceBuilder contains the specified value. + * + * @param value The value to check for. + * @return {@code true} if the SearchSourceBuilder contains the specified value, {@code false} otherwise. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public boolean containsValue(Object value) { + throw new UnsupportedOperationException("containsValue" + UNSUPPORTED_OP_ERR); + } + + /** + * Retrieves the value associated with the specified property from the SearchSourceBuilder. + * + * @param key The SearchSourceBuilder property whose value is to be retrieved. + * @return The value associated with the specified property or null if the property has not been initialized. + * @throws IllegalArgumentException if the property name is not a String. + * @throws SearchRequestMapProcessingException if the property is not supported. + */ + @Override + public Object get(Object key) { + if (!(key instanceof String)) { + throw new IllegalArgumentException("key must be a String"); + } + // This is the explicit implementation of fetch value from source + switch ((String) key) { + case "from": + return source.from(); + case "size": + return source.size(); + case "explain": + return source.explain(); + case "version": + return source.version(); + case "seq_no_primary_term": + return source.seqNoAndPrimaryTerm(); + case "track_scores": + return source.trackScores(); + case "track_total_hits": + return source.trackTotalHitsUpTo(); + case "min_score": + return source.minScore(); + case "terminate_after": + return source.terminateAfter(); + case "profile": + return source.profile(); + default: + throw new SearchRequestMapProcessingException("Unsupported key: " + key); + } + } + + /** + * Sets the value for the specified property in the SearchSourceBuilder. + * + * @param key The property whose value is to be set. + * @param value The value to be set for the specified property. + * @return The original value associated with the property, or null if none existed. + * @throws IllegalArgumentException if the property is not a String. + * @throws SearchRequestMapProcessingException if the property is not supported or an error occurs during the setting. + */ + @Override + public Object put(String key, Object value) { + Object originalValue = get(key); + try { + switch (key) { + case "from": + source.from((Integer) value); + break; + case "size": + source.size((Integer) value); + break; + case "explain": + source.explain((Boolean) value); + break; + case "version": + source.version((Boolean) value); + break; + case "seq_no_primary_term": + source.seqNoAndPrimaryTerm((Boolean) value); + break; + case "track_scores": + source.trackScores((Boolean) value); + break; + case "track_total_hits": + source.trackTotalHitsUpTo((Integer) value); + break; + case "min_score": + source.minScore((Float) value); + break; + case "terminate_after": + source.terminateAfter((Integer) value); + break; + case "profile": + source.profile((Boolean) value); + break; + case "stats": // Not modifying stats, sorts, docvalue_fields, etc. as they require more complex handling + case "sort": + case "timeout": + case "docvalue_fields": + case "indices_boost": + default: + throw new SearchRequestMapProcessingException("Unsupported SearchRequest source property: " + key); + } + } catch (Exception e) { + throw new SearchRequestMapProcessingException("Error while setting value for SearchRequest source property: " + key, e); + } + return originalValue; + } + + /** + * Removes the specified property from the SearchSourceBuilder. + * + * @param key The name of the property that will be removed. + * @return The value associated with the property before it was removed, or null if the property was not found. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Object remove(Object key) { + throw new UnsupportedOperationException("remove" + UNSUPPORTED_OP_ERR); + } + + /** + * Sets all the properties from the specified map to the SearchSourceBuilder. + * + * @param m The map containing the properties to be set. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException("putAll" + UNSUPPORTED_OP_ERR); + } + + /** + * Removes all properties from the SearchSourceBuilder. + * + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public void clear() { + throw new UnsupportedOperationException("clear" + UNSUPPORTED_OP_ERR); + } + + /** + * Returns a set view of the property names in the SearchSourceBuilder. + * + * @return A set view of the property names in the SearchSourceBuilder. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Set keySet() { + throw new UnsupportedOperationException("keySet" + UNSUPPORTED_OP_ERR); + } + + /** + * Returns a collection view of the property values in the SearchSourceBuilder. + * + * @return A collection view of the property values in the SearchSourceBuilder. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Collection values() { + throw new UnsupportedOperationException("values" + UNSUPPORTED_OP_ERR); + } + + /** + * Returns a set view of the properties in the SearchSourceBuilder. + * + * @return A set view of the properties in the SearchSourceBuilder. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Set> entrySet() { + throw new UnsupportedOperationException("entrySet" + UNSUPPORTED_OP_ERR); + } + + /** + * Returns the value to which the specified property has, or the defaultValue if the property is not present in the + * SearchSourceBuilder. + * + * @param key The property whose associated value is to be returned. + * @param defaultValue The default value to be returned if the property is not present. + * @return The value to which the specified property has, or the defaultValue if the property is not present. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Object getOrDefault(Object key, Object defaultValue) { + throw new UnsupportedOperationException("getOrDefault" + UNSUPPORTED_OP_ERR); + } + + /** + * Performs the given action for each property in the SearchSourceBuilder until all properties have been processed or the + * action throws an exception + * + * @param action The action to be performed for each property. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public void forEach(BiConsumer action) { + throw new UnsupportedOperationException("forEach" + UNSUPPORTED_OP_ERR); + } + + /** + * Replaces each property's value with the result of invoking the given function on that property until all properties have + * been processed or the function throws an exception. + * + * @param function The function to apply to each property. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public void replaceAll(BiFunction function) { + throw new UnsupportedOperationException("replaceAll" + UNSUPPORTED_OP_ERR); + } + + /** + * If the specified property is not already associated with a value, associates it with the given value and returns null, + * else returns the current value. + * + * @param key The property whose value is to be set if absent. + * @param value The value to be associated with the specified property. + * @return The current value associated with the property, or null if the property is not present. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Object putIfAbsent(String key, Object value) { + throw new UnsupportedOperationException("putIfAbsent" + UNSUPPORTED_OP_ERR); + } + + /** + * Removes the property only if it has the given value. + * + * @param key The property to be removed. + * @param value The value expected to be associated with the property. + * @return {@code true} if the entry was removed, {@code false} otherwise. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public boolean remove(Object key, Object value) { + throw new UnsupportedOperationException("remove" + UNSUPPORTED_OP_ERR); + } + + /** + * Replaces the specified property only if it has the given value. + * + * @param key The property to be replaced. + * @param oldValue The value expected to be associated with the property. + * @param newValue The value to be associated with the property. + * @return {@code true} if the property was replaced, {@code false} otherwise. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public boolean replace(String key, Object oldValue, Object newValue) { + throw new UnsupportedOperationException("replace" + UNSUPPORTED_OP_ERR); + } + + /** + * Replaces the specified property only if it has the given value. + * + * @param key The property to be replaced. + * @param value The value to be associated with the property. + * @return The previous value associated with the property, or null if the property was not found. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Object replace(String key, Object value) { + throw new UnsupportedOperationException("replace" + UNSUPPORTED_OP_ERR); + } + + /** + * The computed value associated with the property, or null if the property is not present. + * + * @param key The property whose value is to be computed if absent. + * @param mappingFunction The function to compute a value based on the property. + * @return The computed value associated with the property, or null if the property is not present. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Object computeIfAbsent(String key, Function mappingFunction) { + throw new UnsupportedOperationException("computeIfAbsent" + UNSUPPORTED_OP_ERR); + } + + /** + * If the value for the specified property is present, attempts to compute a new mapping given the property and its current + * mapped value. + * + * @param key The property for which the mapping is to be computed. + * @param remappingFunction The function to compute a new mapping. + * @return The new value associated with the property, or null if the property is not present. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Object computeIfPresent(String key, BiFunction remappingFunction) { + throw new UnsupportedOperationException("computeIfPresent" + UNSUPPORTED_OP_ERR); + } + + /** + * If the value for the specified property is present, attempts to compute a new mapping given the property and its current + * mapped value, or removes the property if the computed value is null. + * + * @param key The property for which the mapping is to be computed. + * @param remappingFunction The function to compute a new mapping. + * @return The new value associated with the property, or null if the property is not present. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Object compute(String key, BiFunction remappingFunction) { + throw new UnsupportedOperationException("compute" + UNSUPPORTED_OP_ERR); + } + + /** + * If the specified property is not already associated with a value or is associated with null, associates it with the + * given non-null value. Otherwise, replaces the associated value with the results of applying the given + * remapping function to the current and new values. + * + * @param key The property for which the mapping is to be merged. + * @param value The non-null value to be merged with the existing value. + * @param remappingFunction The function to merge the existing and new values. + * @return The new value associated with the property, or null if the property is not present. + * @throws UnsupportedOperationException always, as the method is not supported. + */ + @Override + public Object merge(String key, Object value, BiFunction remappingFunction) { + throw new UnsupportedOperationException("merge" + UNSUPPORTED_OP_ERR); + } +} diff --git a/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMapProcessingException.java b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMapProcessingException.java new file mode 100644 index 0000000000000..cb1e45a20b624 --- /dev/null +++ b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMapProcessingException.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common.helpers; + +import org.opensearch.OpenSearchException; +import org.opensearch.OpenSearchWrapperException; + +/** + * An exception that indicates an error occurred while processing a {@link SearchRequestMap}. + */ +public class SearchRequestMapProcessingException extends OpenSearchException implements OpenSearchWrapperException { + + /** + * Constructs a new SearchRequestMapProcessingException with the specified message. + * + * @param msg The error message. + * @param args Arguments to substitute in the error message. + */ + public SearchRequestMapProcessingException(String msg, Object... args) { + super(msg, args); + } + + /** + * Constructs a new SearchRequestMapProcessingException with the specified message and cause. + * + * @param msg The error message. + * @param cause The cause of the exception. + * @param args Arguments to substitute in the error message. + */ + public SearchRequestMapProcessingException(String msg, Throwable cause, Object... args) { + super(msg, cause, args); + } +} diff --git a/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/package-info.java b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/package-info.java new file mode 100644 index 0000000000000..b960ff72a9e2b --- /dev/null +++ b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/helpers/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Provides helper classes and utilities for working with search pipeline processors. + */ +package org.opensearch.search.pipeline.common.helpers; diff --git a/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/package-info.java b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/package-info.java new file mode 100644 index 0000000000000..9b065b4008021 --- /dev/null +++ b/modules/search-pipeline-common/src/main/java/org/opensearch/search/pipeline/common/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * A collection of commonly-useful request and response processors for use in search pipelines. + */ +package org.opensearch.search.pipeline.common; diff --git a/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/FilterQueryRequestProcessorTests.java b/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/FilterQueryRequestProcessorTests.java new file mode 100644 index 0000000000000..576660d05b96d --- /dev/null +++ b/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/FilterQueryRequestProcessorTests.java @@ -0,0 +1,51 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.index.query.BoolQueryBuilder; +import org.opensearch.index.query.QueryBuilder; +import org.opensearch.index.query.TermQueryBuilder; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.test.AbstractBuilderTestCase; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class FilterQueryRequestProcessorTests extends AbstractBuilderTestCase { + + public void testFilterQuery() throws Exception { + QueryBuilder filterQuery = new TermQueryBuilder("field", "value"); + FilterQueryRequestProcessor filterQueryRequestProcessor = new FilterQueryRequestProcessor(null, null, false, filterQuery); + QueryBuilder incomingQuery = new TermQueryBuilder("text", "foo"); + SearchSourceBuilder source = new SearchSourceBuilder().query(incomingQuery); + SearchRequest request = new SearchRequest().source(source); + SearchRequest transformedRequest = filterQueryRequestProcessor.processRequest(request); + assertEquals(new BoolQueryBuilder().must(incomingQuery).filter(filterQuery), transformedRequest.source().query()); + + // Test missing incoming query + request = new SearchRequest(); + transformedRequest = filterQueryRequestProcessor.processRequest(request); + assertEquals(new BoolQueryBuilder().filter(filterQuery), transformedRequest.source().query()); + } + + public void testFactory() throws Exception { + FilterQueryRequestProcessor.Factory factory = new FilterQueryRequestProcessor.Factory(this.xContentRegistry()); + Map configMap = new HashMap<>(Map.of("query", Map.of("term", Map.of("field", "value")))); + FilterQueryRequestProcessor processor = factory.create(Collections.emptyMap(), null, null, false, configMap, null); + assertEquals(new TermQueryBuilder("field", "value"), processor.filterQuery); + + // Missing "query" parameter: + expectThrows( + IllegalArgumentException.class, + () -> factory.create(Collections.emptyMap(), null, null, false, Collections.emptyMap(), null) + ); + } +} diff --git a/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/RenameFieldResponseProcessorTests.java b/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/RenameFieldResponseProcessorTests.java new file mode 100644 index 0000000000000..b051a8123b354 --- /dev/null +++ b/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/RenameFieldResponseProcessorTests.java @@ -0,0 +1,132 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a.java + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import org.apache.lucene.search.TotalHits; +import org.opensearch.OpenSearchParseException; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.search.SearchResponseSections; +import org.opensearch.common.document.DocumentField; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.index.query.QueryBuilder; +import org.opensearch.index.query.TermQueryBuilder; +import org.opensearch.ingest.RandomDocumentPicks; +import org.opensearch.search.SearchHit; +import org.opensearch.search.SearchHits; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.test.OpenSearchTestCase; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class RenameFieldResponseProcessorTests extends OpenSearchTestCase { + + private SearchRequest createDummyRequest() { + QueryBuilder query = new TermQueryBuilder("field", "value"); + SearchSourceBuilder source = new SearchSourceBuilder().query(query); + return new SearchRequest().source(source); + } + + private SearchResponse createTestResponse(int size, boolean includeMapping) { + SearchHit[] hits = new SearchHit[size]; + for (int i = 0; i < size; i++) { + Map searchHitFields = new HashMap<>(); + if (includeMapping) { + searchHitFields.put("field " + i, new DocumentField("value " + i, Collections.emptyList())); + } + searchHitFields.put("field " + i, new DocumentField("value " + i, Collections.emptyList())); + hits[i] = new SearchHit(i, "doc " + i, searchHitFields, Collections.emptyMap()); + hits[i].sourceRef(new BytesArray("{ \"field " + i + "\" : \"value " + i + "\" }")); + hits[i].score(i); + } + SearchHits searchHits = new SearchHits(hits, new TotalHits(size * 2L, TotalHits.Relation.EQUAL_TO), size); + SearchResponseSections searchResponseSections = new SearchResponseSections(searchHits, null, null, false, false, null, 0); + return new SearchResponse(searchResponseSections, null, 1, 1, 0, 10, null, null); + } + + public void testRenameResponse() throws Exception { + SearchRequest request = createDummyRequest(); + + RenameFieldResponseProcessor renameFieldResponseProcessor = new RenameFieldResponseProcessor( + null, + null, + false, + "field 0", + "new field", + false + ); + SearchResponse response = createTestResponse(2, false); + SearchResponse renameResponse = renameFieldResponseProcessor.processResponse(request, createTestResponse(5, false)); + + assertNotEquals(response.getHits(), renameResponse.getHits()); + } + + public void testRenameResponseWithMapping() throws Exception { + SearchRequest request = createDummyRequest(); + + RenameFieldResponseProcessor renameFieldResponseProcessor = new RenameFieldResponseProcessor( + null, + null, + false, + "field 0", + "new field", + true + ); + SearchResponse response = createTestResponse(5, true); + SearchResponse renameResponse = renameFieldResponseProcessor.processResponse(request, createTestResponse(5, true)); + + assertNotEquals(response.getHits(), renameResponse.getHits()); + + boolean foundField = false; + for (SearchHit hit : renameResponse.getHits().getHits()) { + if (hit.getFields().containsKey("new field")) { + foundField = true; + } + } + assertTrue(foundField); + } + + public void testMissingField() throws Exception { + SearchRequest request = createDummyRequest(); + RenameFieldResponseProcessor renameFieldResponseProcessor = new RenameFieldResponseProcessor( + null, + null, + false, + "field", + "new field", + false + ); + assertThrows( + IllegalArgumentException.class, + () -> renameFieldResponseProcessor.processResponse(request, createTestResponse(3, true)) + ); + } + + public void testFactory() throws Exception { + String oldField = RandomDocumentPicks.randomFieldName(random()); + String newField = RandomDocumentPicks.randomFieldName(random()); + Map config = new HashMap<>(); + config.put("field", oldField); + config.put("target_field", newField); + + RenameFieldResponseProcessor.Factory factory = new RenameFieldResponseProcessor.Factory(); + RenameFieldResponseProcessor processor = factory.create(Collections.emptyMap(), null, null, false, config, null); + assertEquals(processor.getType(), "rename_field"); + assertEquals(processor.getOldField(), oldField); + assertEquals(processor.getNewField(), newField); + assertFalse(processor.isIgnoreMissing()); + + expectThrows( + OpenSearchParseException.class, + () -> factory.create(Collections.emptyMap(), null, null, false, Collections.emptyMap(), null) + ); + } +} diff --git a/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/ScriptRequestProcessorTests.java b/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/ScriptRequestProcessorTests.java new file mode 100644 index 0000000000000..fde9757312e30 --- /dev/null +++ b/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/ScriptRequestProcessorTests.java @@ -0,0 +1,138 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.script.MockScriptEngine; +import org.opensearch.script.Script; +import org.opensearch.script.ScriptModule; +import org.opensearch.script.ScriptService; +import org.opensearch.script.ScriptType; +import org.opensearch.script.SearchScript; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.search.pipeline.common.helpers.SearchRequestMap; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.core.Is.is; + +public class ScriptRequestProcessorTests extends OpenSearchTestCase { + + private ScriptService scriptService; + private Script script; + private SearchScript searchScript; + + @Before + public void setupScripting() { + String scriptName = "search_script"; + scriptService = new ScriptService( + Settings.builder().build(), + Map.of(Script.DEFAULT_SCRIPT_LANG, new MockScriptEngine(Script.DEFAULT_SCRIPT_LANG, Map.of(scriptName, ctx -> { + Object sourceObj = ctx.get("_source"); + if (sourceObj instanceof Map) { + Map source = (SearchRequestMap) sourceObj; + + // Update all modifiable source fields + Integer from = (Integer) source.get("from"); + source.put("from", from + 10); + + Integer size = (Integer) source.get("size"); + source.put("size", size + 10); + + Boolean explain = (Boolean) source.get("explain"); + source.put("explain", !explain); + + Boolean version = (Boolean) source.get("version"); + source.put("version", !version); + + Boolean seqNoAndPrimaryTerm = (Boolean) source.get("seq_no_primary_term"); + source.put("seq_no_primary_term", !seqNoAndPrimaryTerm); + + Boolean trackScores = (Boolean) source.get("track_scores"); + source.put("track_scores", !trackScores); + + Integer trackTotalHitsUpTo = (Integer) source.get("track_total_hits"); + source.put("track_total_hits", trackTotalHitsUpTo + 1); + + Float minScore = (Float) source.get("min_score"); + source.put("min_score", minScore + 1.0f); + + Integer terminateAfter = (Integer) source.get("terminate_after"); + source.put("terminate_after", terminateAfter + 1); + } + return null; + }), Collections.emptyMap())), + new HashMap<>(ScriptModule.CORE_CONTEXTS) + ); + script = new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, scriptName, Collections.emptyMap()); + searchScript = scriptService.compile(script, SearchScript.CONTEXT).newInstance(script.getParams()); + } + + public void testScriptingWithoutPrecompiledScriptFactory() throws Exception { + ScriptRequestProcessor processor = new ScriptRequestProcessor(randomAlphaOfLength(10), null, false, script, null, scriptService); + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(createSearchSourceBuilder()); + + assertNotNull(searchRequest); + processor.processRequest(searchRequest); + assertSearchRequest(searchRequest); + } + + public void testScriptingWithPrecompiledIngestScript() throws Exception { + ScriptRequestProcessor processor = new ScriptRequestProcessor( + randomAlphaOfLength(10), + null, + false, + script, + searchScript, + scriptService + ); + SearchRequest searchRequest = new SearchRequest(); + searchRequest.source(createSearchSourceBuilder()); + + assertNotNull(searchRequest); + processor.processRequest(searchRequest); + assertSearchRequest(searchRequest); + } + + private SearchSourceBuilder createSearchSourceBuilder() { + SearchSourceBuilder source = new SearchSourceBuilder(); + source.from(10); + source.size(20); + source.explain(true); + source.version(true); + source.seqNoAndPrimaryTerm(true); + source.trackScores(true); + source.trackTotalHitsUpTo(3); + source.minScore(1.0f); + source.timeout(new TimeValue(60, TimeUnit.SECONDS)); + source.terminateAfter(5); + return source; + } + + private void assertSearchRequest(SearchRequest searchRequest) { + assertThat(searchRequest.source().from(), is(20)); + assertThat(searchRequest.source().size(), is(30)); + assertThat(searchRequest.source().explain(), is(false)); + assertThat(searchRequest.source().version(), is(false)); + assertThat(searchRequest.source().seqNoAndPrimaryTerm(), is(false)); + assertThat(searchRequest.source().trackScores(), is(false)); + assertThat(searchRequest.source().trackTotalHitsUpTo(), is(4)); + assertThat(searchRequest.source().minScore(), is(2.0f)); + assertThat(searchRequest.source().timeout(), is(new TimeValue(60, TimeUnit.SECONDS))); + assertThat(searchRequest.source().terminateAfter(), is(6)); + } +} diff --git a/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMapTests.java b/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMapTests.java new file mode 100644 index 0000000000000..5572f28335e1c --- /dev/null +++ b/modules/search-pipeline-common/src/test/java/org/opensearch/search/pipeline/common/helpers/SearchRequestMapTests.java @@ -0,0 +1,149 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.search.pipeline.common.helpers; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.search.builder.SearchSourceBuilder; +import org.opensearch.test.AbstractBuilderTestCase; + +public class SearchRequestMapTests extends AbstractBuilderTestCase { + + public void testEmptyMap() { + SearchRequest searchRequest = new SearchRequest(); + SearchRequestMap map = new SearchRequestMap(searchRequest); + + assertTrue(map.isEmpty()); + } + + public void testGet() { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder source = new SearchSourceBuilder(); + source.from(10); + source.size(20); + source.explain(true); + source.version(true); + source.seqNoAndPrimaryTerm(true); + source.trackScores(true); + source.trackTotalHitsUpTo(3); + source.minScore(1.0f); + source.terminateAfter(5); + searchRequest.source(source); + + SearchRequestMap map = new SearchRequestMap(searchRequest); + + assertEquals(10, map.get("from")); + assertEquals(20, map.get("size")); + assertEquals(true, map.get("explain")); + assertEquals(true, map.get("version")); + assertEquals(true, map.get("seq_no_primary_term")); + assertEquals(true, map.get("track_scores")); + assertEquals(3, map.get("track_total_hits")); + assertEquals(1.0f, map.get("min_score")); + assertEquals(5, map.get("terminate_after")); + } + + public void testPut() { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder source = new SearchSourceBuilder(); + searchRequest.source(source); + + SearchRequestMap map = new SearchRequestMap(searchRequest); + + assertEquals(-1, map.put("from", 10)); + assertEquals(10, map.get("from")); + + assertEquals(-1, map.put("size", 20)); + assertEquals(20, map.get("size")); + + assertNull(map.put("explain", true)); + assertEquals(true, map.get("explain")); + + assertNull(map.put("version", true)); + assertEquals(true, map.get("version")); + + assertNull(map.put("seq_no_primary_term", true)); + assertEquals(true, map.get("seq_no_primary_term")); + + assertEquals(false, map.put("track_scores", true)); + assertEquals(true, map.get("track_scores")); + + assertNull(map.put("track_total_hits", 3)); + assertEquals(3, map.get("track_total_hits")); + + assertNull(map.put("min_score", 1.0f)); + assertEquals(1.0f, map.get("min_score")); + + assertEquals(0, map.put("terminate_after", 5)); + assertEquals(5, map.get("terminate_after")); + } + + public void testUnsupportedOperationException() { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder source = new SearchSourceBuilder(); + searchRequest.source(source); + + SearchRequestMap map = new SearchRequestMap(searchRequest); + + assertThrows(UnsupportedOperationException.class, () -> map.size()); + assertThrows(UnsupportedOperationException.class, () -> map.containsValue(null)); + assertThrows(UnsupportedOperationException.class, () -> map.remove(null)); + assertThrows(UnsupportedOperationException.class, () -> map.putAll(null)); + assertThrows(UnsupportedOperationException.class, map::clear); + assertThrows(UnsupportedOperationException.class, map::keySet); + assertThrows(UnsupportedOperationException.class, map::values); + assertThrows(UnsupportedOperationException.class, map::entrySet); + assertThrows(UnsupportedOperationException.class, () -> map.getOrDefault(null, null)); + assertThrows(UnsupportedOperationException.class, () -> map.forEach(null)); + assertThrows(UnsupportedOperationException.class, () -> map.replaceAll(null)); + assertThrows(UnsupportedOperationException.class, () -> map.putIfAbsent(null, null)); + assertThrows(UnsupportedOperationException.class, () -> map.remove(null, null)); + assertThrows(UnsupportedOperationException.class, () -> map.replace(null, null, null)); + assertThrows(UnsupportedOperationException.class, () -> map.replace(null, null)); + assertThrows(UnsupportedOperationException.class, () -> map.computeIfAbsent(null, null)); + assertThrows(UnsupportedOperationException.class, () -> map.computeIfPresent(null, null)); + assertThrows(UnsupportedOperationException.class, () -> map.compute(null, null)); + assertThrows(UnsupportedOperationException.class, () -> map.merge(null, null, null)); + } + + public void testIllegalArgumentException() { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder source = new SearchSourceBuilder(); + searchRequest.source(source); + + SearchRequestMap map = new SearchRequestMap(searchRequest); + + try { + map.get(1); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + public void testSearchRequestMapProcessingException() { + SearchRequest searchRequest = new SearchRequest(); + SearchSourceBuilder source = new SearchSourceBuilder(); + searchRequest.source(source); + + SearchRequestMap map = new SearchRequestMap(searchRequest); + + try { + map.get("unsupported_key"); + fail("Expected SearchRequestMapProcessingException"); + } catch (SearchRequestMapProcessingException e) { + // expected + } + + try { + map.put("unsupported_key", 10); + fail("Expected SearchRequestMapProcessingException"); + } catch (SearchRequestMapProcessingException e) { + // expected + } + } +} diff --git a/modules/search-pipeline-common/src/yamlRestTest/java/org/opensearch/search/pipeline/common/SearchPipelineCommonYamlTestSuiteIT.java b/modules/search-pipeline-common/src/yamlRestTest/java/org/opensearch/search/pipeline/common/SearchPipelineCommonYamlTestSuiteIT.java new file mode 100644 index 0000000000000..d4d53b158420a --- /dev/null +++ b/modules/search-pipeline-common/src/yamlRestTest/java/org/opensearch/search/pipeline/common/SearchPipelineCommonYamlTestSuiteIT.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pipeline.common; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; +import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; + +public class SearchPipelineCommonYamlTestSuiteIT extends OpenSearchClientYamlSuiteTestCase { + public SearchPipelineCommonYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) { + super(testCandidate); + } + + @ParametersFactory + public static Iterable parameters() throws Exception { + return OpenSearchClientYamlSuiteTestCase.createParameters(); + } +} diff --git a/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/10_basic.yml b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/10_basic.yml new file mode 100644 index 0000000000000..ca53f6cd6a7e8 --- /dev/null +++ b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/10_basic.yml @@ -0,0 +1,17 @@ +"Search pipeline common installed": + - skip: + reason: "contains is a newly added assertion" + features: contains + - do: + cluster.state: {} + + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } + + - do: + nodes.info: {} + + - contains: { nodes.$cluster_manager.modules: { name: search-pipeline-common } } + - contains: { nodes.$cluster_manager.search_pipelines.request_processors: { type: filter_query } } + - contains: { nodes.$cluster_manager.search_pipelines.request_processors: { type: script } } + - contains: { nodes.$cluster_manager.search_pipelines.response_processors: { type: rename_field } } diff --git a/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/20_crud.yml b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/20_crud.yml new file mode 100644 index 0000000000000..9b86f6dcc06c2 --- /dev/null +++ b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/20_crud.yml @@ -0,0 +1,47 @@ +--- +teardown: + - do: + search_pipeline.delete: + id: "my_pipeline" + ignore: 404 + +--- +"Test basic pipeline crud": + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "description": "_description", + "request_processors": [ + { + "filter_query" : { + "query" : { + "term" : { + "field" : "value" + } + } + } + } + ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.get: + id: "my_pipeline" + - match: { my_pipeline.description: "_description" } + + - do: + search_pipeline.get: {} + - match: { my_pipeline.description: "_description" } + + - do: + search_pipeline.delete: + id: "my_pipeline" + - match: { acknowledged: true } + + - do: + catch: missing + search_pipeline.get: + id: "my_pipeline" diff --git a/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/25_empty_pipeline.yml b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/25_empty_pipeline.yml new file mode 100644 index 0000000000000..0481d27bb3f4b --- /dev/null +++ b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/25_empty_pipeline.yml @@ -0,0 +1,47 @@ +--- +teardown: + - do: + search_pipeline.delete: + id: "my_pipeline" + ignore: 404 + +--- +"Test explicitly empty pipeline is no-op": + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "description": "_description" + } + - match: { acknowledged: true } + + - do: + index: + index: test + id: 1 + body: { + "field" : "foo" + } + - do: + index: + index: test + id: 2 + body: { + "field": "value" + } + + - do: + indices.refresh: + index: test + + - do: + search: + index: test + - match: { hits.total.value: 2 } + + - do: + search: + index: test + search_pipeline: "my_pipeline" + - match: { hits.total.value: 2 } diff --git a/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/30_filter_query.yml b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/30_filter_query.yml new file mode 100644 index 0000000000000..d5fe91aab5378 --- /dev/null +++ b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/30_filter_query.yml @@ -0,0 +1,148 @@ +--- +teardown: + - do: + search_pipeline.delete: + id: "my_pipeline" + ignore: 404 + +--- +"Test filter_query processor": + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "description": "_description", + "request_processors": [ + { + "filter_query" : { + "query" : { + "term" : { + "field" : "value" + } + } + } + } + ] + } + - match: { acknowledged: true } + + - do: + index: + index: test + id: 1 + body: { + "field": "foo" + } + - do: + index: + index: test + id: 2 + body: { + "field": "value" + } + + - do: + indices.refresh: + index: test + + - do: + search: + body: { } + - match: { hits.total.value: 2 } + + - do: + search: + index: test + search_pipeline: "my_pipeline" + body: { } + - match: { hits.total.value: 1 } + - match: { hits.hits.0._id: "2" } + + # Should also work with no search body specified + - do: + search: + index: test + search_pipeline: "my_pipeline" + - match: { hits.total.value: 1 } + - match: { hits.hits.0._id: "2" } + + # Should also work with existing query + - do: + search: + index: test + search_pipeline: "my_pipeline" + body: { + "query": { + "term": { + "field": "value" + } + } + } + - match: { hits.total.value: 1 } + - match: { hits.hits.0._id: "2" } + + # Should work with inline query + - do: + search: + index: test + body: { + "search_pipeline" : { + "request_processors" : [ + "filter_query": { + "query": { + "term": { + "field": "foo" + } + } + } + ] + } + } + - match: { hits.total.value: 1 } + - match: { hits.hits.0._id: "1" } + + # Make it the default for the index + - do: + indices.put_settings: + index: test + body: + index.search.default_pipeline: my_pipeline + + - do: + search: + index: test + body: { } + - match: { hits.total.value: 1 } + + # Explicitly bypass the pipeline to match both docs + - do: + search: + search_pipeline: _none + index: test + body: { } + - match: { hits.total.value: 2 } +--- +"Test invalid inline query": + - do: + catch: bad_request + search: + index: test + body: { + search_pipeline: { + "request_processors": [ + { + "filter_query": { + "query": { + "woozlewuzzle": { + "field": "foo" + } + } + } + } + ] + } + } + - match: { status: 400 } + - match: { error.type: "parsing_exception"} + - match: { error.reason: "unknown query [woozlewuzzle]"} diff --git a/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/40_rename_response.yml b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/40_rename_response.yml new file mode 100644 index 0000000000000..0528440f7584d --- /dev/null +++ b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/40_rename_response.yml @@ -0,0 +1,180 @@ +--- +teardown: + - do: + search_pipeline.delete: + id: "my_pipeline" + ignore: 404 + +--- +"Test filter_query processor": + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "description": "test pipeline", + "response_processors": [ + { + "rename_field": + { + "field": "a", + "target_field": "b" + } + } + ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.put: + id: "my_pipeline_2" + body: > + { + "description": "test pipeline with ignore missing true", + "response_processors": [ + { + "rename_field": + { + "field": "aa", + "target_field": "b", + "ignore_missing": true + } + } + ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.put: + id: "my_pipeline_3" + body: > + { + "description": "test pipeline with ignore missing false", + "response_processors": [ + { + "rename_field": + { + "field": "aa", + "target_field": "b", + "ignore_missing": false + } + } + ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.put: + id: "my_pipeline_4" + body: > + { + "description": "test pipeline with ignore missing false and ignore failure true", + "response_processors": [ + { + "rename_field": + { + "field": "aa", + "target_field": "b", + "ignore_missing": false, + "ignore_failure": true + } + } + ] + } + - match: { acknowledged: true } + + - do: + indices.create: + index: test + + - do: + indices.put_mapping: + index: test + body: + properties: + a: + type: keyword + store: true + doc_values: true + + - do: + index: + index: test + id: 1 + body: { + "a": "foo" + } + + - do: + indices.refresh: + index: test + + - do: + search: + body: { } + - match: { hits.total.value: 1 } + + - do: + search: + index: test + search_pipeline: "my_pipeline" + body: { } + - match: { hits.total.value: 1 } + - match: { hits.hits.0._source: { "b": "foo" } } + + # Should also work with no search body specified + - do: + search: + index: test + search_pipeline: "my_pipeline" + - match: { hits.total.value: 1 } + - match: { hits.hits.0._source: { "b": "foo" } } + + # Pipeline with ignore_missing set to true + # Should still pass even though index does not contain field + - do: + search: + index: test + search_pipeline: "my_pipeline_2" + - match: { hits.total.value: 1 } + - match: {hits.hits.0._source: { "a": "foo" } } + + # Pipeline with ignore_missing set to false + # Should throw illegal_argument_exception + - do: + catch: bad_request + search: + index: test + search_pipeline: "my_pipeline_3" + - match: { error.type: "illegal_argument_exception" } + + # Pipeline with ignore_missing set to false and ignore_failure set to true + # Should return while catching error + - do: + search: + index: test + search_pipeline: "my_pipeline_4" + - match: { hits.total.value: 1 } + - match: {hits.hits.0._source: { "a": "foo" } } + + # No source, using stored_fields + - do: + search: + index: test + search_pipeline: "my_pipeline" + body: { + "_source": false, + "stored_fields": [ "a" ] + } + - match: { hits.hits.0.fields: { "b": ["foo"] } } + + # No source, using docvalue_fields + - do: + search: + index: test + search_pipeline: "my_pipeline" + body: { + "_source": false, + "docvalue_fields": [ "a" ] + } + - match: { hits.hits.0.fields: { "b": [ "foo" ] } } diff --git a/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/50_script_processor.yml b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/50_script_processor.yml new file mode 100644 index 0000000000000..7a01e68acf75c --- /dev/null +++ b/modules/search-pipeline-common/src/yamlRestTest/resources/rest-api-spec/test/search_pipeline/50_script_processor.yml @@ -0,0 +1,94 @@ +--- +teardown: + - do: + search_pipeline.delete: + id: "my_pipeline" + ignore: 404 + +--- +"Test empty script in script processor": + - do: + catch: bad_request + search_pipeline.put: + id: "my_pipeline" + body: > + { + "description": "_description", + "request_processors": [ + { + "script" : { + "tag": "empty_script", + "lang": "painless", + "source" : "" + } + } + ] + } + + - match: { status: 400 } + - match: { error.root_cause.0.type: "script_exception" } + +--- +"Test supported search source builder fields": + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "description": "_description", + "request_processors": [ + { + "script" : { + "tag": "working", + "lang" : "painless", + "source" : "ctx._source['size'] += 10; ctx._source['from'] = ctx._source['from'] <= 0 ? ctx._source['from'] : ctx._source['from'] - 1 ; ctx._source['explain'] = !ctx._source['explain']; ctx._source['version'] = !ctx._source['version']; ctx._source['seq_no_primary_term'] = !ctx._source['seq_no_primary_term']; ctx._source['track_scores'] = !ctx._source['track_scores']; ctx._source['track_total_hits'] = 1; ctx._source['min_score'] -= 0.9; ctx._source['terminate_after'] += 2; ctx._source['profile'] = !ctx._source['profile'];" + } + } + ] + } + - match: { acknowledged: true } + + - do: + index: + index: test + id: 1 + body: { + "field": 2 + } + - do: + index: + index: test + id: 2 + body: { + "field": 3 + } + + - do: + indices.refresh: + index: test + + - do: + search: + index: test + search_pipeline: "my_pipeline" + body: { + "from": 1, + "size": 1, + "explain": true, + "version": true, + "seq_no_primary_term": true, + "track_scores": true, + "track_total_hits": true, + "min_score": 1.0, + "timeout": "60s", + "terminate_after": 2, + "profile": true + } + - length: { hits.hits: 2 } + - is_false: hits.hits.0._explanation + - is_false: hits.hits.1._explanation + - is_false: hits.hits.0._seq_no + - is_false: hits.hits.1._seq_no + - is_false: hits.hits.0._primary_term + - is_false: hits.hits.1._primary_term + - is_false: profile diff --git a/modules/systemd/build.gradle b/modules/systemd/build.gradle index b157fd9321fc9..26e094a9eeae1 100644 --- a/modules/systemd/build.gradle +++ b/modules/systemd/build.gradle @@ -32,4 +32,3 @@ opensearchplugin { description 'Integrates OpenSearch with systemd' classname 'org.opensearch.systemd.SystemdPlugin' } - diff --git a/modules/systemd/src/main/java/org/opensearch/systemd/SystemdPlugin.java b/modules/systemd/src/main/java/org/opensearch/systemd/SystemdPlugin.java index 3a2d51950f0be..35aae06fe9deb 100644 --- a/modules/systemd/src/main/java/org/opensearch/systemd/SystemdPlugin.java +++ b/modules/systemd/src/main/java/org/opensearch/systemd/SystemdPlugin.java @@ -34,14 +34,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.lucene.util.SetOnce; -import org.opensearch.Build; import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.SetOnce; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.plugins.ClusterPlugin; @@ -68,20 +67,10 @@ final boolean isEnabled() { @SuppressWarnings("unused") public SystemdPlugin() { - this(true, Build.CURRENT.type(), System.getenv("OPENSEARCH_SD_NOTIFY")); + this(System.getenv("OPENSEARCH_SD_NOTIFY")); } - SystemdPlugin(final boolean assertIsPackageDistribution, final Build.Type buildType, final String esSDNotify) { - final boolean isPackageDistribution = buildType == Build.Type.DEB || buildType == Build.Type.RPM; - if (assertIsPackageDistribution) { - // our build is configured to only include this module in the package distributions - assert isPackageDistribution : buildType; - } - if (isPackageDistribution == false) { - logger.debug("disabling sd_notify as the build type [{}] is not a package distribution", buildType); - enabled = false; - return; - } + SystemdPlugin(final String esSDNotify) { logger.trace("OPENSEARCH_SD_NOTIFY is set to [{}]", esSDNotify); if (esSDNotify == null) { enabled = false; diff --git a/modules/systemd/src/test/java/org/opensearch/systemd/SystemdPluginTests.java b/modules/systemd/src/test/java/org/opensearch/systemd/SystemdPluginTests.java index cc8ee649ab782..63d97a7486f58 100644 --- a/modules/systemd/src/test/java/org/opensearch/systemd/SystemdPluginTests.java +++ b/modules/systemd/src/test/java/org/opensearch/systemd/SystemdPluginTests.java @@ -32,7 +32,6 @@ package org.opensearch.systemd; -import org.opensearch.Build; import org.opensearch.common.CheckedConsumer; import org.opensearch.common.unit.TimeValue; import org.opensearch.test.OpenSearchTestCase; @@ -58,13 +57,6 @@ import static org.mockito.Mockito.when; public class SystemdPluginTests extends OpenSearchTestCase { - - private final Build.Type randomPackageBuildType = randomFrom(Build.Type.DEB, Build.Type.RPM); - private final Build.Type randomNonPackageBuildType = randomValueOtherThanMany( - t -> t == Build.Type.DEB || t == Build.Type.RPM, - () -> randomFrom(Build.Type.values()) - ); - final Scheduler.Cancellable extender = mock(Scheduler.Cancellable.class); final ThreadPool threadPool = mock(ThreadPool.class); @@ -74,29 +66,15 @@ public class SystemdPluginTests extends OpenSearchTestCase { .thenReturn(extender); } - public void testIsEnabled() { - final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, Boolean.TRUE.toString()); - plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null); - assertTrue(plugin.isEnabled()); - assertNotNull(plugin.extender()); - } - - public void testIsNotPackageDistribution() { - final SystemdPlugin plugin = new SystemdPlugin(false, randomNonPackageBuildType, Boolean.TRUE.toString()); - plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null); - assertFalse(plugin.isEnabled()); - assertNull(plugin.extender()); - } - public void testIsImplicitlyNotEnabled() { - final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, null); + final SystemdPlugin plugin = new SystemdPlugin(null); plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null); assertFalse(plugin.isEnabled()); assertNull(plugin.extender()); } public void testIsExplicitlyNotEnabled() { - final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, Boolean.FALSE.toString()); + final SystemdPlugin plugin = new SystemdPlugin(Boolean.FALSE.toString()); plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null); assertFalse(plugin.isEnabled()); assertNull(plugin.extender()); @@ -107,7 +85,7 @@ public void testInvalid() { s -> Boolean.TRUE.toString().equals(s) || Boolean.FALSE.toString().equals(s), () -> randomAlphaOfLength(4) ); - final RuntimeException e = expectThrows(RuntimeException.class, () -> new SystemdPlugin(false, randomPackageBuildType, esSDNotify)); + final RuntimeException e = expectThrows(RuntimeException.class, () -> new SystemdPlugin(esSDNotify)); assertThat(e, hasToString(containsString("OPENSEARCH_SD_NOTIFY set to unexpected value [" + esSDNotify + "]"))); } @@ -174,7 +152,7 @@ private void runTest( final AtomicBoolean invoked = new AtomicBoolean(); final AtomicInteger invokedUnsetEnvironment = new AtomicInteger(); final AtomicReference invokedState = new AtomicReference<>(); - final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, esSDNotify) { + final SystemdPlugin plugin = new SystemdPlugin(esSDNotify) { @Override int sd_notify(final int unset_environment, final String state) { diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index f0029837c7d03..a0ae6619199d2 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -31,6 +31,7 @@ import org.opensearch.gradle.info.BuildParams import org.opensearch.gradle.test.RestIntegTestTask +import org.opensearch.gradle.test.TestTask import org.opensearch.gradle.test.rest.JavaRestTestPlugin import org.opensearch.gradle.test.InternalClusterTestPlugin @@ -38,6 +39,9 @@ apply plugin: 'opensearch.yaml-rest-test' apply plugin: 'opensearch.java-rest-test' apply plugin: 'opensearch.internal-cluster-test' +// The transport-netty4 plugin is published to maven +apply plugin: 'opensearch.publish' + /* TODOs: * fix permissions such that only netty4 can open sockets etc? @@ -59,6 +63,7 @@ dependencies { api "io.netty:netty-handler:${versions.netty}" api "io.netty:netty-resolver:${versions.netty}" api "io.netty:netty-transport:${versions.netty}" + api "io.netty:netty-transport-native-unix-common:${versions.netty}" } restResources { @@ -87,13 +92,13 @@ javaRestTest { systemProperty 'opensearch.set.netty.runtime.available.processors', 'false' } -TaskProvider pooledTest = tasks.register("pooledTest", Test) { +TaskProvider pooledTest = tasks.register("pooledTest", TestTask) { include '**/*Tests.class' systemProperty 'opensearch.set.netty.runtime.available.processors', 'false' systemProperty 'opensearch.use_unpooled_allocator', 'false' } -TaskProvider pooledInternalClusterTest = tasks.register("pooledInternalClusterTest", Test) { +TaskProvider pooledInternalClusterTest = tasks.register("pooledInternalClusterTest", TestTask) { include '**/*IT.class' systemProperty 'opensearch.set.netty.runtime.available.processors', 'false' systemProperty 'opensearch.use_unpooled_allocator', 'false' @@ -120,17 +125,11 @@ thirdPartyAudit { 'com.aayushatharva.brotli4j.Brotli4jLoader', 'com.aayushatharva.brotli4j.decoder.DecoderJNI$Status', 'com.aayushatharva.brotli4j.decoder.DecoderJNI$Wrapper', - 'com.aayushatharva.brotli4j.encoder.Encoder', + 'com.aayushatharva.brotli4j.encoder.BrotliEncoderChannel', 'com.aayushatharva.brotli4j.encoder.Encoder$Mode', 'com.aayushatharva.brotli4j.encoder.Encoder$Parameters', // classes are missing - // from io.netty.handler.codec.protobuf.ProtobufDecoder (netty) - 'com.google.protobuf.ExtensionRegistry', - 'com.google.protobuf.MessageLite$Builder', - 'com.google.protobuf.MessageLite', - 'com.google.protobuf.Parser', - // from io.netty.logging.CommonsLoggerFactory (netty) 'org.apache.commons.logging.Log', 'org.apache.commons.logging.LogFactory', @@ -141,6 +140,7 @@ thirdPartyAudit { // from io.netty.handler.ssl.OpenSslEngine (netty) 'io.netty.internal.tcnative.Buffer', + 'io.netty.internal.tcnative.CertificateCompressionAlgo', 'io.netty.internal.tcnative.Library', 'io.netty.internal.tcnative.SSL', 'io.netty.internal.tcnative.SSLContext', @@ -150,6 +150,12 @@ thirdPartyAudit { 'org.bouncycastle.cert.X509v3CertificateBuilder', 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', + 'org.bouncycastle.openssl.PEMEncryptedKeyPair', + 'org.bouncycastle.openssl.PEMParser', + 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', + 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', + 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', + 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', // from io.netty.handler.ssl.JettyNpnSslEngine (netty) 'org.eclipse.jetty.npn.NextProtoNego$ClientProvider', @@ -177,15 +183,8 @@ thirdPartyAudit { 'org.slf4j.LoggerFactory', 'org.slf4j.spi.LocationAwareLogger', - 'com.github.luben.zstd.Zstd', - 'com.google.protobuf.ExtensionRegistryLite', - 'com.google.protobuf.MessageLiteOrBuilder', 'com.google.protobuf.nano.CodedOutputByteBufferNano', 'com.google.protobuf.nano.MessageNano', - 'com.jcraft.jzlib.Deflater', - 'com.jcraft.jzlib.Inflater', - 'com.jcraft.jzlib.JZlib$WrapperType', - 'com.jcraft.jzlib.JZlib', 'com.ning.compress.BufferRecycler', 'com.ning.compress.lzf.ChunkDecoder', 'com.ning.compress.lzf.ChunkEncoder', @@ -227,7 +226,8 @@ thirdPartyAudit { 'io.netty.util.internal.PlatformDependent0$1', 'io.netty.util.internal.PlatformDependent0$2', 'io.netty.util.internal.PlatformDependent0$3', - 'io.netty.util.internal.PlatformDependent0$5', + 'io.netty.util.internal.PlatformDependent0$4', + 'io.netty.util.internal.PlatformDependent0$6', 'io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueConsumerNodeRef', 'io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueProducerNodeRef', 'io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields', @@ -239,6 +239,11 @@ thirdPartyAudit { 'io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField', 'io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess', 'io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess', - 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator' + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$1', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$2', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$3', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$4', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$5' ) } diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.73.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.73.Final.jar.sha1 deleted file mode 100644 index e5833785ebb7e..0000000000000 --- a/modules/transport-netty4/licenses/netty-buffer-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -244a569c9aae973f6f485ac9801d79c1eca36daa \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.99.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..5b393be40e945 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-buffer-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9f02dcb9b15a647a56af210dffdc294a57922fb0 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.73.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.73.Final.jar.sha1 deleted file mode 100644 index dcdc1e4e58afe..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -9496a30a349863a4c6fa10d5c36b4f3b495d3a31 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.99.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..45ea27d29a183 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9984cbd6e5d55c768f198e975d8aaf7fd42a4602 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.73.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.73.Final.jar.sha1 deleted file mode 100644 index 374cfb98614d5..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1ceeac4429b9bd517dc05e376a144bbe6b6bd038 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.99.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..6bb7fcd68b272 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +7142095066eaebd5f29b88c41af7b383b6a953f6 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.73.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.73.Final.jar.sha1 deleted file mode 100644 index e80a6e2569d81..0000000000000 --- a/modules/transport-netty4/licenses/netty-common-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -27731b58d741b6faa6a00fa3285e7a55cc47be01 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.99.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..d53adfa649f5f --- /dev/null +++ b/modules/transport-netty4/licenses/netty-common-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +278f6dfa49d6bd75c40ae1470eb165716f87dce0 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.73.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.73.Final.jar.sha1 deleted file mode 100644 index 0e227997874bf..0000000000000 --- a/modules/transport-netty4/licenses/netty-handler-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1a2231c0074f88254865c3769a4b5842939ea04d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.99.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..258f7c957dda0 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-handler-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +742693761d7ea4c038bccfda96bb38194720b80d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.73.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.73.Final.jar.sha1 deleted file mode 100644 index ba24531724fb5..0000000000000 --- a/modules/transport-netty4/licenses/netty-resolver-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -bfe83710f0c1739019613e81a06101020ca65def \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.99.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..b8bc0a4370f58 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-resolver-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +080e45397d9d5b134477de3ffd0f94283b908621 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.73.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.73.Final.jar.sha1 deleted file mode 100644 index 6a8647497f210..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -abb155ddff196ccedfe85b810d4b9375ef85fcfa \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.99.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..247975e0a64c7 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9ca2e3ae19a6713b749df154622115f480b6716c \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..6b7b66ea768e3 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +cb0fc6c31c387404212949c57950b5d72ce908b9 \ No newline at end of file diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4HttpRequestSizeLimitIT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4HttpRequestSizeLimitIT.java index 08df9259d475f..dcaa9553f5b1f 100644 --- a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4HttpRequestSizeLimitIT.java +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4HttpRequestSizeLimitIT.java @@ -32,15 +32,12 @@ package org.opensearch.http.netty4; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.util.ReferenceCounted; import org.opensearch.OpenSearchNetty4IntegTestCase; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.http.HttpServerTransport; import org.opensearch.indices.breaker.HierarchyCircuitBreakerService; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; @@ -50,6 +47,10 @@ import java.util.Collection; import java.util.List; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.util.ReferenceCounted; + import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4PipeliningIT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4PipeliningIT.java index 2bd1fa07f8afc..c0e1f817c3c50 100644 --- a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4PipeliningIT.java +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/http/netty4/Netty4PipeliningIT.java @@ -32,10 +32,8 @@ package org.opensearch.http.netty4; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.util.ReferenceCounted; import org.opensearch.OpenSearchNetty4IntegTestCase; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.http.HttpServerTransport; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; @@ -43,6 +41,9 @@ import java.util.Collection; import java.util.Locale; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.util.ReferenceCounted; + import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/rest/discovery/Zen2RestApiIT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/rest/discovery/Zen2RestApiIT.java index 198cc11d824e7..2c75123c5d825 100644 --- a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/rest/discovery/Zen2RestApiIT.java +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/rest/discovery/Zen2RestApiIT.java @@ -47,8 +47,8 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.http.HttpServerTransport; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.io.IOException; @@ -69,7 +69,7 @@ protected boolean addMockHttpTransport() { } public void testRollingRestartOfTwoNodeCluster() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(1); + internalCluster().setBootstrapClusterManagerNodeIndex(1); final List nodes = internalCluster().startNodes(2); createIndex( "test", @@ -135,7 +135,7 @@ public Settings onNodeStopped(String nodeName) throws IOException { } public void testClearVotingTombstonesNotWaitingForRemoval() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(2); + internalCluster().setBootstrapClusterManagerNodeIndex(2); List nodes = internalCluster().startNodes(3); ensureStableCluster(3); RestClient restClient = getRestClient(); @@ -150,7 +150,7 @@ public void testClearVotingTombstonesNotWaitingForRemoval() throws Exception { } public void testClearVotingTombstonesWaitingForRemoval() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(2); + internalCluster().setBootstrapClusterManagerNodeIndex(2); List nodes = internalCluster().startNodes(3); ensureStableCluster(3); RestClient restClient = getRestClient(); @@ -165,7 +165,7 @@ public void testClearVotingTombstonesWaitingForRemoval() throws Exception { } public void testFailsOnUnknownNode() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(2); + internalCluster().setBootstrapClusterManagerNodeIndex(2); internalCluster().startNodes(3); ensureStableCluster(3); RestClient restClient = getRestClient(); @@ -176,13 +176,13 @@ public void testFailsOnUnknownNode() throws Exception { assertThat(e.getResponse().getStatusLine().getStatusCode(), is(400)); assertThat( e.getMessage(), - Matchers.containsString("add voting config exclusions request for [invalid] matched no master-eligible nodes") + Matchers.containsString("add voting config exclusions request for [invalid] matched no cluster-manager-eligible nodes") ); } } public void testRemoveTwoNodesAtOnce() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(2); + internalCluster().setBootstrapClusterManagerNodeIndex(2); List nodes = internalCluster().startNodes(3); ensureStableCluster(3); RestClient restClient = getRestClient(); diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java index 3ff3938d23f65..4004d3d1a029d 100644 --- a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java @@ -36,8 +36,8 @@ import org.opensearch.action.admin.cluster.node.info.NodesInfoResponse; import org.opensearch.common.network.NetworkAddress; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.BoundTransportAddress; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.BoundTransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; import org.opensearch.test.junit.annotations.Network; diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/Netty4TransportPublishAddressIT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/Netty4TransportPublishAddressIT.java index a572a181a46fd..f23df4b48a854 100644 --- a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/Netty4TransportPublishAddressIT.java +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/Netty4TransportPublishAddressIT.java @@ -38,8 +38,8 @@ import org.opensearch.common.network.NetworkModule; import org.opensearch.common.network.NetworkUtils; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.BoundTransportAddress; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.BoundTransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.transport.Netty4Plugin; import org.opensearch.transport.TransportInfo; diff --git a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/OpenSearchLoggingHandlerIT.java b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/OpenSearchLoggingHandlerIT.java index ea3f21dd0ed3b..b8369acdf9dc6 100644 --- a/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/OpenSearchLoggingHandlerIT.java +++ b/modules/transport-netty4/src/internalClusterTest/java/org/opensearch/transport/netty4/OpenSearchLoggingHandlerIT.java @@ -36,9 +36,9 @@ import org.apache.logging.log4j.LogManager; import org.opensearch.OpenSearchNetty4IntegTestCase; import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsRequest; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; import org.opensearch.test.MockLogAppender; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.junit.annotations.TestLogging; import org.opensearch.transport.TcpTransport; import org.opensearch.transport.TransportLogger; diff --git a/modules/transport-netty4/src/javaRestTest/java/org/opensearch/rest/Netty4BadRequestIT.java b/modules/transport-netty4/src/javaRestTest/java/org/opensearch/rest/Netty4BadRequestIT.java index e9de417f1efe6..f7e1c6106cf5a 100644 --- a/modules/transport-netty4/src/javaRestTest/java/org/opensearch/rest/Netty4BadRequestIT.java +++ b/modules/transport-netty4/src/javaRestTest/java/org/opensearch/rest/Netty4BadRequestIT.java @@ -38,7 +38,7 @@ import org.opensearch.client.ResponseException; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.http.HttpTransportSettings; import org.opensearch.test.rest.OpenSearchRestTestCase; import org.opensearch.test.rest.yaml.ObjectPath; @@ -47,7 +47,7 @@ import java.nio.charset.Charset; import java.util.Map; -import static org.opensearch.rest.RestStatus.BAD_REQUEST; +import static org.opensearch.core.rest.RestStatus.BAD_REQUEST; import static org.opensearch.test.hamcrest.RegexMatcher.matches; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -86,7 +86,7 @@ public void testBadRequest() throws IOException { () -> client().performRequest(new Request(randomFrom("GET", "POST", "PUT"), path)) ); assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(BAD_REQUEST.getStatus())); - assertThat(e, hasToString(containsString("too_long_frame_exception"))); + assertThat(e, hasToString(containsString("too_long_http_line_exception"))); assertThat(e, hasToString(matches("An HTTP line is larger than \\d+ bytes"))); } diff --git a/modules/transport-netty4/src/javaRestTest/java/org/opensearch/rest/Netty4HeadBodyIsEmptyIT.java b/modules/transport-netty4/src/javaRestTest/java/org/opensearch/rest/Netty4HeadBodyIsEmptyIT.java index 1593488701e26..b4b15c22258de 100644 --- a/modules/transport-netty4/src/javaRestTest/java/org/opensearch/rest/Netty4HeadBodyIsEmptyIT.java +++ b/modules/transport-netty4/src/javaRestTest/java/org/opensearch/rest/Netty4HeadBodyIsEmptyIT.java @@ -34,8 +34,7 @@ import org.opensearch.client.Request; import org.opensearch.client.Response; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.rest.OpenSearchRestTestCase; import org.hamcrest.Matcher; @@ -45,8 +44,8 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.opensearch.rest.RestStatus.NOT_FOUND; -import static org.opensearch.rest.RestStatus.OK; +import static org.opensearch.core.rest.RestStatus.NOT_FOUND; +import static org.opensearch.core.rest.RestStatus.OK; import static org.hamcrest.Matchers.greaterThan; public class Netty4HeadBodyIsEmptyIT extends OpenSearchRestTestCase { @@ -68,7 +67,7 @@ private void createTestDoc(final String indexName) throws IOException { } builder.endObject(); Request request = new Request("PUT", "/" + indexName + "/_doc/" + "1"); - request.setJsonEntity(Strings.toString(builder)); + request.setJsonEntity(builder.toString()); client().performRequest(request); } } @@ -109,7 +108,7 @@ public void testAliasExists() throws IOException { builder.endObject(); Request request = new Request("POST", "/_aliases"); - request.setJsonEntity(Strings.toString(builder)); + request.setJsonEntity(builder.toString()); client().performRequest(request); headTestCase("/_alias/test_alias", emptyMap(), greaterThan(0)); headTestCase("/test/_alias/test_alias", emptyMap(), greaterThan(0)); @@ -136,7 +135,7 @@ public void testTemplateExists() throws IOException { builder.endObject(); Request request = new Request("PUT", "/_template/template"); - request.setJsonEntity(Strings.toString(builder)); + request.setJsonEntity(builder.toString()); client().performRequest(request); headTestCase("/_template/template", emptyMap(), greaterThan(0)); } @@ -163,7 +162,7 @@ public void testGetSourceAction() throws IOException { builder.endObject(); Request request = new Request("PUT", "/test-no-source"); - request.setJsonEntity(Strings.toString(builder)); + request.setJsonEntity(builder.toString()); client().performRequest(request); createTestDoc("test-no-source"); headTestCase("/test-no-source/_source/1", emptyMap(), NOT_FOUND.getStatus(), greaterThan(0)); diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpChannel.java index 66d60032d11a8..5bfb7976c6e18 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpChannel.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpChannel.java @@ -32,15 +32,16 @@ package org.opensearch.http.netty4; -import io.netty.channel.Channel; -import org.opensearch.action.ActionListener; import org.opensearch.common.concurrent.CompletableContext; +import org.opensearch.core.action.ActionListener; import org.opensearch.http.HttpChannel; import org.opensearch.http.HttpResponse; import org.opensearch.transport.netty4.Netty4TcpChannel; import java.net.InetSocketAddress; +import io.netty.channel.Channel; + public class Netty4HttpChannel implements HttpChannel { private final Channel channel; diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpPipeliningHandler.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpPipeliningHandler.java index af975518e087e..1200dcaf9e0f8 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpPipeliningHandler.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpPipeliningHandler.java @@ -32,9 +32,6 @@ package org.opensearch.http.netty4; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; import org.apache.logging.log4j.Logger; import org.opensearch.common.collect.Tuple; import org.opensearch.http.HttpPipelinedRequest; @@ -44,6 +41,10 @@ import java.nio.channels.ClosedChannelException; import java.util.List; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; + /** * Implements HTTP pipelining ordering, ensuring that responses are completely served in the same order as their corresponding requests. */ diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequest.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequest.java index 8ce3af0bb141c..7d937157c1034 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequest.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequest.java @@ -32,21 +32,10 @@ package org.opensearch.http.netty4; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.ServerCookieDecoder; -import io.netty.handler.codec.http.cookie.ServerCookieEncoder; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; import org.opensearch.http.HttpRequest; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; import org.opensearch.transport.netty4.Netty4Utils; import java.util.AbstractMap; @@ -58,6 +47,18 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.ServerCookieDecoder; +import io.netty.handler.codec.http.cookie.ServerCookieEncoder; + public class Netty4HttpRequest implements HttpRequest { private final FullHttpRequest request; diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequestCreator.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequestCreator.java index 52d202f45a728..f97c2059e03d5 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequestCreator.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequestCreator.java @@ -32,13 +32,14 @@ package org.opensearch.http.netty4; +import org.opensearch.ExceptionsHelper; + +import java.util.List; + import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.http.FullHttpRequest; -import org.opensearch.ExceptionsHelper; - -import java.util.List; @ChannelHandler.Sharable class Netty4HttpRequestCreator extends MessageToMessageDecoder { diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequestHandler.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequestHandler.java index caa80587a6e56..1f7aaf17d2191 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequestHandler.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpRequestHandler.java @@ -32,11 +32,12 @@ package org.opensearch.http.netty4; +import org.opensearch.ExceptionsHelper; +import org.opensearch.http.HttpPipelinedRequest; + import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -import org.opensearch.ExceptionsHelper; -import org.opensearch.http.HttpPipelinedRequest; @ChannelHandler.Sharable class Netty4HttpRequestHandler extends SimpleChannelInboundHandler { diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpResponse.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpResponse.java index 78df964ce9cac..83284230be049 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpResponse.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpResponse.java @@ -32,14 +32,15 @@ package org.opensearch.http.netty4; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.http.HttpResponse; +import org.opensearch.transport.netty4.Netty4Utils; + import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.http.HttpResponse; -import org.opensearch.rest.RestStatus; -import org.opensearch.transport.netty4.Netty4Utils; public class Netty4HttpResponse extends DefaultFullHttpResponse implements HttpResponse { diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpResponseCreator.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpResponseCreator.java index 00c3049162270..e2f04f76e6af8 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpResponseCreator.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpResponseCreator.java @@ -32,6 +32,11 @@ package org.opensearch.http.netty4; +import org.opensearch.common.Booleans; +import org.opensearch.transport.NettyAllocator; + +import java.util.List; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; @@ -41,10 +46,6 @@ import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultLastHttpContent; import io.netty.handler.codec.http.HttpResponse; -import org.opensearch.common.Booleans; -import org.opensearch.transport.NettyAllocator; - -import java.util.List; /** * Split up large responses to prevent batch compression {@link JdkZlibEncoder} down the pipeline. diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerChannel.java index 560b7b565fa40..7b8858174e555 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerChannel.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerChannel.java @@ -32,14 +32,15 @@ package org.opensearch.http.netty4; -import io.netty.channel.Channel; -import org.opensearch.action.ActionListener; import org.opensearch.common.concurrent.CompletableContext; +import org.opensearch.core.action.ActionListener; import org.opensearch.http.HttpServerChannel; import org.opensearch.transport.netty4.Netty4TcpChannel; import java.net.InetSocketAddress; +import io.netty.channel.Channel; + public class Netty4HttpServerChannel implements HttpServerChannel { private final Channel channel; diff --git a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java index decab45ffca38..ededfdf27e42b 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/http/netty4/Netty4HttpServerTransport.java @@ -32,26 +32,6 @@ package org.opensearch.http.netty4; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.FixedRecvByteBufAllocator; -import io.netty.channel.RecvByteBufAllocator; -import io.netty.channel.socket.nio.NioChannelOption; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.http.HttpContentCompressor; -import io.netty.handler.codec.http.HttpContentDecompressor; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.timeout.ReadTimeoutException; -import io.netty.handler.timeout.ReadTimeoutHandler; -import io.netty.util.AttributeKey; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.ExceptionsHelper; @@ -60,18 +40,19 @@ import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.util.BigArrays; import org.opensearch.common.util.concurrent.OpenSearchExecutors; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.core.internal.io.IOUtils; -import org.opensearch.core.internal.net.NetUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.common.util.net.NetUtils; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.http.AbstractHttpServerTransport; import org.opensearch.http.HttpChannel; import org.opensearch.http.HttpHandlingSettings; import org.opensearch.http.HttpReadTimeoutException; import org.opensearch.http.HttpServerChannel; +import org.opensearch.telemetry.tracing.Tracer; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.NettyAllocator; import org.opensearch.transport.NettyByteBufSizer; @@ -82,6 +63,27 @@ import java.net.SocketOption; import java.util.concurrent.TimeUnit; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.FixedRecvByteBufAllocator; +import io.netty.channel.RecvByteBufAllocator; +import io.netty.channel.socket.nio.NioChannelOption; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.http.HttpContentCompressor; +import io.netty.handler.codec.http.HttpContentDecompressor; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.handler.timeout.ReadTimeoutException; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.util.AttributeKey; + import static org.opensearch.http.HttpTransportSettings.SETTING_HTTP_MAX_CHUNK_SIZE; import static org.opensearch.http.HttpTransportSettings.SETTING_HTTP_MAX_CONTENT_LENGTH; import static org.opensearch.http.HttpTransportSettings.SETTING_HTTP_MAX_HEADER_SIZE; @@ -173,9 +175,10 @@ public Netty4HttpServerTransport( NamedXContentRegistry xContentRegistry, Dispatcher dispatcher, ClusterSettings clusterSettings, - SharedGroupFactory sharedGroupFactory + SharedGroupFactory sharedGroupFactory, + Tracer tracer ) { - super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings); + super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings, tracer); Netty4Utils.setAvailableProcessors(OpenSearchExecutors.NODE_PROCESSORS_SETTING.get(settings)); NettyAllocator.logAllocatorDescriptionIfNeeded(); this.sharedGroupFactory = sharedGroupFactory; diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/CopyBytesServerSocketChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/CopyBytesServerSocketChannel.java index c98cfbe711821..07dedb744ff11 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/CopyBytesServerSocketChannel.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/CopyBytesServerSocketChannel.java @@ -46,14 +46,15 @@ package org.opensearch.transport; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.util.internal.SocketUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.nio.channels.SocketChannel; import java.util.List; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.util.internal.SocketUtils; + /** * This class is adapted from {@link NioServerSocketChannel} class in the Netty project. It overrides the * channel read messages behavior to ensure that a {@link CopyBytesSocketChannel} socket channel is created. diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/CopyBytesSocketChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/CopyBytesSocketChannel.java index 9a5459a5ab572..b73f74d187a2a 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/CopyBytesSocketChannel.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/CopyBytesSocketChannel.java @@ -46,18 +46,19 @@ package org.opensearch.transport; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelOutboundBuffer; -import io.netty.channel.RecvByteBufAllocator; -import io.netty.channel.socket.nio.NioSocketChannel; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeValue; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelOutboundBuffer; +import io.netty.channel.RecvByteBufAllocator; +import io.netty.channel.socket.nio.NioSocketChannel; + import static io.netty.channel.internal.ChannelUtils.MAX_BYTES_PER_GATHERING_WRITE_ATTEMPTED_LOW_THRESHOLD; /** diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4NioServerSocketChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4NioServerSocketChannel.java new file mode 100644 index 0000000000000..e7626b6ddc57a --- /dev/null +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4NioServerSocketChannel.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.transport; + +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.channels.spi.SelectorProvider; +import java.util.List; + +import io.netty.channel.socket.InternetProtocolFamily; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.util.internal.SocketUtils; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; + +public class Netty4NioServerSocketChannel extends NioServerSocketChannel { + private static final InternalLogger logger = InternalLoggerFactory.getInstance(Netty4NioServerSocketChannel.class); + + public Netty4NioServerSocketChannel() { + super(); + } + + public Netty4NioServerSocketChannel(SelectorProvider provider) { + super(provider); + } + + public Netty4NioServerSocketChannel(SelectorProvider provider, InternetProtocolFamily family) { + super(provider, family); + } + + public Netty4NioServerSocketChannel(ServerSocketChannel channel) { + super(channel); + } + + @Override + protected int doReadMessages(List buf) throws Exception { + SocketChannel ch = SocketUtils.accept(javaChannel()); + + try { + if (ch != null) { + buf.add(new Netty4NioSocketChannel(this, ch)); + return 1; + } + } catch (Throwable t) { + logger.warn("Failed to create a new channel from an accepted socket.", t); + + try { + ch.close(); + } catch (Throwable t2) { + logger.warn("Failed to close a socket.", t2); + } + } + + return 0; + } +} diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4NioSocketChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4NioSocketChannel.java index 9fe5d3731eb7d..cca099b4810b3 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4NioSocketChannel.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4NioSocketChannel.java @@ -32,11 +32,11 @@ package org.opensearch.transport; +import java.nio.channels.SocketChannel; + import io.netty.channel.Channel; import io.netty.channel.socket.nio.NioSocketChannel; -import java.nio.channels.SocketChannel; - /** * Helper class to expose {@link #javaChannel()} method */ diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4Plugin.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4Plugin.java index 73cfe4e46fbda..885315bcc1f3c 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4Plugin.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/Netty4Plugin.java @@ -32,9 +32,8 @@ package org.opensearch.transport; -import org.apache.lucene.util.SetOnce; import org.opensearch.Version; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.SetOnce; import org.opensearch.common.network.NetworkModule; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; @@ -42,12 +41,14 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.util.BigArrays; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.indices.breaker.CircuitBreakerService; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.http.HttpServerTransport; import org.opensearch.http.netty4.Netty4HttpServerTransport; -import org.opensearch.indices.breaker.CircuitBreakerService; import org.opensearch.plugins.NetworkPlugin; import org.opensearch.plugins.Plugin; +import org.opensearch.telemetry.tracing.Tracer; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.netty4.Netty4Transport; @@ -95,7 +96,8 @@ public Map> getTransports( PageCacheRecycler pageCacheRecycler, CircuitBreakerService circuitBreakerService, NamedWriteableRegistry namedWriteableRegistry, - NetworkService networkService + NetworkService networkService, + Tracer tracer ) { return Collections.singletonMap( NETTY_TRANSPORT_NAME, @@ -107,7 +109,8 @@ public Map> getTransports( pageCacheRecycler, namedWriteableRegistry, circuitBreakerService, - getSharedGroupFactory(settings) + getSharedGroupFactory(settings), + tracer ) ); } @@ -122,7 +125,8 @@ public Map> getHttpTransports( NamedXContentRegistry xContentRegistry, NetworkService networkService, HttpServerTransport.Dispatcher dispatcher, - ClusterSettings clusterSettings + ClusterSettings clusterSettings, + Tracer tracer ) { return Collections.singletonMap( NETTY_HTTP_TRANSPORT_NAME, @@ -134,7 +138,8 @@ public Map> getHttpTransports( xContentRegistry, dispatcher, clusterSettings, - getSharedGroupFactory(settings) + getSharedGroupFactory(settings), + tracer ) ); } diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/NettyAllocator.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/NettyAllocator.java index e25853d864813..ff901476c162d 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/NettyAllocator.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/NettyAllocator.java @@ -32,6 +32,14 @@ package org.opensearch.transport; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.common.Booleans; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.monitor.jvm.JvmInfo; + +import java.util.concurrent.atomic.AtomicBoolean; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; @@ -39,14 +47,6 @@ import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ServerChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.opensearch.common.Booleans; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.monitor.jvm.JvmInfo; - -import java.util.concurrent.atomic.AtomicBoolean; public class NettyAllocator { @@ -181,7 +181,7 @@ public static Class getServerChannelType() { if (ALLOCATOR instanceof NoDirectBuffers) { return CopyBytesServerSocketChannel.class; } else { - return NioServerSocketChannel.class; + return Netty4NioServerSocketChannel.class; } } diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/NettyByteBufSizer.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/NettyByteBufSizer.java index 857e07f6feca2..54824bc160747 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/NettyByteBufSizer.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/NettyByteBufSizer.java @@ -32,13 +32,13 @@ package org.opensearch.transport; +import java.util.List; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; -import java.util.List; - @ChannelHandler.Sharable public class NettyByteBufSizer extends MessageToMessageDecoder { diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/SharedGroupFactory.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/SharedGroupFactory.java index d70f6bc83cc73..454293442572c 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/SharedGroupFactory.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/SharedGroupFactory.java @@ -32,9 +32,6 @@ package org.opensearch.transport; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.concurrent.Future; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.common.settings.Settings; @@ -46,6 +43,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.util.concurrent.Future; + import static org.opensearch.common.util.concurrent.OpenSearchExecutors.daemonThreadFactory; /** diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4MessageChannelHandler.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4MessageChannelHandler.java index ad2e287f7a28d..7b9999ce5b20e 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4MessageChannelHandler.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4MessageChannelHandler.java @@ -32,18 +32,12 @@ package org.opensearch.transport.netty4; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.OpenSearchException; import org.opensearch.common.bytes.ReleasableBytesReference; import org.opensearch.common.lease.Releasables; import org.opensearch.common.util.PageCacheRecycler; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.InboundPipeline; import org.opensearch.transport.Transport; @@ -53,6 +47,13 @@ import java.util.ArrayDeque; import java.util.Queue; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; + /** * A handler (must be the last one!) that does size based frame decoding and forwards the actual message * to the relevant action. diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4TcpChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4TcpChannel.java index eeee0ff33e792..5db1f7c333157 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4TcpChannel.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4TcpChannel.java @@ -32,19 +32,20 @@ package org.opensearch.transport.netty4; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelPromise; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionListener; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.concurrent.CompletableContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.transport.TcpChannel; import org.opensearch.transport.TransportException; import java.net.InetSocketAddress; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPromise; + public class Netty4TcpChannel implements TcpChannel { private final Channel channel; diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4TcpServerChannel.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4TcpServerChannel.java index 6131d7fe2acb9..8910e2b51374e 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4TcpServerChannel.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4TcpServerChannel.java @@ -32,13 +32,14 @@ package org.opensearch.transport.netty4; -import io.netty.channel.Channel; -import org.opensearch.action.ActionListener; import org.opensearch.common.concurrent.CompletableContext; +import org.opensearch.core.action.ActionListener; import org.opensearch.transport.TcpServerChannel; import java.net.InetSocketAddress; +import io.netty.channel.Channel; + public class Netty4TcpServerChannel implements TcpServerChannel { private final Channel channel; diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4Transport.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4Transport.java index 45bd5464485d5..e76a227630dc1 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4Transport.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4Transport.java @@ -31,20 +31,6 @@ package org.opensearch.transport.netty4; -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.AdaptiveRecvByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.FixedRecvByteBufAllocator; -import io.netty.channel.RecvByteBufAllocator; -import io.netty.channel.socket.nio.NioChannelOption; -import io.netty.util.AttributeKey; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; @@ -52,18 +38,19 @@ import org.opensearch.Version; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.lease.Releasables; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.util.PageCacheRecycler; import org.opensearch.common.util.concurrent.OpenSearchExecutors; -import org.opensearch.core.internal.net.NetUtils; -import org.opensearch.indices.breaker.CircuitBreakerService; +import org.opensearch.common.util.net.NetUtils; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.indices.breaker.CircuitBreakerService; +import org.opensearch.telemetry.tracing.Tracer; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.Netty4NioSocketChannel; import org.opensearch.transport.NettyAllocator; @@ -77,6 +64,21 @@ import java.net.SocketOption; import java.util.Map; +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.AdaptiveRecvByteBufAllocator; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.FixedRecvByteBufAllocator; +import io.netty.channel.RecvByteBufAllocator; +import io.netty.channel.socket.nio.NioChannelOption; +import io.netty.util.AttributeKey; + import static org.opensearch.common.settings.Setting.byteSizeSetting; import static org.opensearch.common.settings.Setting.intSetting; import static org.opensearch.common.util.concurrent.ConcurrentCollections.newConcurrentMap; @@ -130,9 +132,10 @@ public Netty4Transport( PageCacheRecycler pageCacheRecycler, NamedWriteableRegistry namedWriteableRegistry, CircuitBreakerService circuitBreakerService, - SharedGroupFactory sharedGroupFactory + SharedGroupFactory sharedGroupFactory, + Tracer tracer ) { - super(settings, version, threadPool, pageCacheRecycler, circuitBreakerService, namedWriteableRegistry, networkService); + super(settings, version, threadPool, pageCacheRecycler, circuitBreakerService, namedWriteableRegistry, networkService, tracer); Netty4Utils.setAvailableProcessors(OpenSearchExecutors.NODE_PROCESSORS_SETTING.get(settings)); NettyAllocator.logAllocatorDescriptionIfNeeded(); this.sharedGroupFactory = sharedGroupFactory; diff --git a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4Utils.java b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4Utils.java index e13329b8c2593..11fc74a2afa7f 100644 --- a/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4Utils.java +++ b/modules/transport-netty4/src/main/java/org/opensearch/transport/netty4/Netty4Utils.java @@ -32,15 +32,11 @@ package org.opensearch.transport.netty4; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.util.NettyRuntime; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefIterator; import org.opensearch.common.Booleans; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; import java.nio.ByteBuffer; @@ -49,6 +45,11 @@ import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.util.NettyRuntime; + public class Netty4Utils { private static final AtomicBoolean isAvailableProcessorsSet = new AtomicBoolean(); diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4BadRequestTests.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4BadRequestTests.java index a0100930c7dcb..492da422382ce 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4BadRequestTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4BadRequestTests.java @@ -32,23 +32,22 @@ package org.opensearch.http.netty4; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.util.ReferenceCounted; import org.opensearch.OpenSearchException; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.MockBigArrays; import org.opensearch.common.util.MockPageCacheRecycler; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.rest.RestStatus; import org.opensearch.http.HttpServerTransport; import org.opensearch.http.HttpTransportSettings; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; @@ -61,6 +60,9 @@ import java.util.Collection; import java.util.Collections; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.util.ReferenceCounted; + import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; @@ -111,7 +113,8 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext, xContentRegistry(), dispatcher, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), - new SharedGroupFactory(Settings.EMPTY) + new SharedGroupFactory(Settings.EMPTY), + NoopTracer.INSTANCE ) ) { httpServerTransport.start(); diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpClient.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpClient.java index 57f95a022a33f..1a9f2a558ecd8 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpClient.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpClient.java @@ -32,6 +32,22 @@ package org.opensearch.http.netty4; +import org.opensearch.common.collect.Tuple; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.tasks.Task; +import org.opensearch.transport.NettyAllocator; + +import java.io.Closeable; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -55,21 +71,6 @@ import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseDecoder; import io.netty.handler.codec.http.HttpVersion; -import org.opensearch.common.collect.Tuple; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.tasks.Task; -import org.opensearch.transport.NettyAllocator; - -import java.io.Closeable; -import java.net.SocketAddress; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import static io.netty.handler.codec.http.HttpHeaderNames.HOST; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpPipeliningHandlerTests.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpPipeliningHandlerTests.java index d37e28dabf19d..743b51e979fb7 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpPipeliningHandlerTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpPipeliningHandlerTests.java @@ -32,22 +32,13 @@ package org.opensearch.http.netty4; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.QueryStringDecoder; import org.opensearch.common.Randomness; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; import org.opensearch.http.HttpPipelinedRequest; import org.opensearch.http.HttpPipelinedResponse; import org.opensearch.http.HttpResponse; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchTestCase; import org.junit.After; @@ -64,9 +55,19 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.QueryStringDecoder; + +import static org.hamcrest.core.Is.is; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static org.hamcrest.core.Is.is; public class Netty4HttpPipeliningHandlerTests extends OpenSearchTestCase { diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerPipeliningTests.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerPipeliningTests.java index 029aed1f3cc89..9bca90c3a408a 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerPipeliningTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerPipeliningTests.java @@ -32,28 +32,20 @@ package org.opensearch.http.netty4; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.util.ReferenceCounted; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.MockBigArrays; import org.opensearch.common.util.MockPageCacheRecycler; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.rest.RestStatus; import org.opensearch.http.HttpPipelinedRequest; import org.opensearch.http.HttpResponse; import org.opensearch.http.HttpServerTransport; import org.opensearch.http.NullDispatcher; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; -import org.opensearch.rest.RestStatus; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; @@ -69,6 +61,16 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.util.ReferenceCounted; + import static org.hamcrest.Matchers.contains; /** @@ -134,7 +136,8 @@ class CustomNettyHttpServerTransport extends Netty4HttpServerTransport { xContentRegistry(), new NullDispatcher(), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ); } diff --git a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java index ec879e538fe20..2bdb95389e737 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/http/netty4/Netty4HttpServerTransportTests.java @@ -32,59 +32,36 @@ package org.opensearch.http.netty4; -import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.PoolArenaMetric; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.PooledByteBufAllocatorMetric; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerAdapter; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.OpenSearchException; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.network.NetworkAddress; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.MockBigArrays; import org.opensearch.common.util.MockPageCacheRecycler; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; import org.opensearch.http.BindHttpException; import org.opensearch.http.CorsHandler; import org.opensearch.http.HttpServerTransport; import org.opensearch.http.HttpTransportSettings; import org.opensearch.http.NullDispatcher; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.rest.FakeRestRequest; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.SharedGroupFactory; import org.opensearch.transport.NettyAllocator; +import org.opensearch.transport.SharedGroupFactory; import org.junit.After; import org.junit.Before; @@ -96,10 +73,35 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.PoolArenaMetric; +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.buffer.PooledByteBufAllocatorMetric; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.TooLongFrameException; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.HttpVersion; + +import static org.opensearch.core.rest.RestStatus.BAD_REQUEST; +import static org.opensearch.core.rest.RestStatus.OK; import static org.opensearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_ORIGIN; import static org.opensearch.http.HttpTransportSettings.SETTING_CORS_ENABLED; -import static org.opensearch.rest.RestStatus.BAD_REQUEST; -import static org.opensearch.rest.RestStatus.OK; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -197,7 +199,8 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext, xContentRegistry(), dispatcher, clusterSettings, - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -246,7 +249,8 @@ public void testBindUnavailableAddress() { xContentRegistry(), new NullDispatcher(), clusterSettings, - new SharedGroupFactory(Settings.EMPTY) + new SharedGroupFactory(Settings.EMPTY), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -264,7 +268,8 @@ public void testBindUnavailableAddress() { xContentRegistry(), new NullDispatcher(), clusterSettings, - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ) ) { BindHttpException bindHttpException = expectThrows(BindHttpException.class, otherTransport::start); @@ -316,7 +321,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th xContentRegistry(), dispatcher, clusterSettings, - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -378,7 +384,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th xContentRegistry(), dispatcher, clusterSettings, - new SharedGroupFactory(Settings.EMPTY) + new SharedGroupFactory(Settings.EMPTY), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -447,7 +454,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th xContentRegistry(), dispatcher, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -520,7 +528,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th xContentRegistry(), dispatcher, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ) ) { transport.start(); diff --git a/modules/transport-netty4/src/test/java/org/opensearch/transport/CopyBytesSocketChannelTests.java b/modules/transport-netty4/src/test/java/org/opensearch/transport/CopyBytesSocketChannelTests.java index 7cb4225f02293..2ace9b111a64b 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/transport/CopyBytesSocketChannelTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/transport/CopyBytesSocketChannelTests.java @@ -31,18 +31,6 @@ package org.opensearch.transport; -import io.netty.bootstrap.Bootstrap; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioEventLoopGroup; import org.opensearch.common.SuppressForbidden; import org.opensearch.test.OpenSearchTestCase; @@ -57,6 +45,19 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import io.netty.bootstrap.Bootstrap; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; + public class CopyBytesSocketChannelTests extends OpenSearchTestCase { private final UnpooledByteBufAllocator alloc = new UnpooledByteBufAllocator(false); diff --git a/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java b/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java index cd263a20c9a74..c92ccba82835f 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java @@ -33,13 +33,14 @@ package org.opensearch.transport.netty4; import org.opensearch.Version; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.MockPageCacheRecycler; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.SharedGroupFactory; @@ -86,7 +87,8 @@ public void startThreadPool() { recycler, new NamedWriteableRegistry(Collections.emptyList()), new NoneCircuitBreakerService(), - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ); nettyTransport.start(); diff --git a/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/Netty4UtilsTests.java b/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/Netty4UtilsTests.java index 6aafd78ab2f2c..296f9aa3901c6 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/Netty4UtilsTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/Netty4UtilsTests.java @@ -32,22 +32,23 @@ package org.opensearch.transport.netty4; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.breaker.CircuitBreaker; import org.opensearch.common.bytes.AbstractBytesReferenceTestCase; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.io.stream.ReleasableBytesStreamOutput; import org.opensearch.common.util.BigArrays; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; + public class Netty4UtilsTests extends OpenSearchTestCase { private static final int PAGE_SIZE = PageCacheRecycler.BYTE_PAGE_SIZE; diff --git a/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/NettyTransportMultiPortTests.java b/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/NettyTransportMultiPortTests.java index 78a3a353fbf2f..7cca00db68559 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/NettyTransportMultiPortTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/NettyTransportMultiPortTests.java @@ -32,14 +32,15 @@ package org.opensearch.transport.netty4; import org.opensearch.Version; -import org.opensearch.common.component.Lifecycle; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.lifecycle.Lifecycle; import org.opensearch.common.network.NetworkService; import org.opensearch.common.network.NetworkUtils; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.MockPageCacheRecycler; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; @@ -141,7 +142,8 @@ private TcpTransport startTransport(Settings settings, ThreadPool threadPool) { recycler, new NamedWriteableRegistry(Collections.emptyList()), new NoneCircuitBreakerService(), - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ); transport.start(); diff --git a/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/SimpleNetty4TransportTests.java b/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/SimpleNetty4TransportTests.java index b690ba9e35e35..710b3ff6bd0ca 100644 --- a/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/SimpleNetty4TransportTests.java +++ b/modules/transport-netty4/src/test/java/org/opensearch/transport/netty4/SimpleNetty4TransportTests.java @@ -33,18 +33,18 @@ package org.opensearch.transport.netty4; import org.opensearch.Version; -import org.opensearch.action.ActionListener; -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.core.internal.io.IOUtils; -import org.opensearch.core.internal.net.NetUtils; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.common.util.net.NetUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.transport.MockTransportService; import org.opensearch.test.transport.StubbableTransport; import org.opensearch.transport.AbstractSimpleTransportTestCase; @@ -83,7 +83,8 @@ protected Transport build(Settings settings, final Version version, ClusterSetti PageCacheRecycler.NON_RECYCLING_INSTANCE, namedWriteableRegistry, new NoneCircuitBreakerService(), - new SharedGroupFactory(settings) + new SharedGroupFactory(settings), + NoopTracer.INSTANCE ) { @Override @@ -121,10 +122,7 @@ public void testConnectException() throws UnknownHostException { } public void testDefaultKeepAliveSettings() throws IOException { - assumeTrue( - "setting default keepalive options not supported on this platform", - (IOUtils.LINUX || IOUtils.MAC_OS_X) && JavaVersion.current().compareTo(JavaVersion.parse("11")) >= 0 - ); + assumeTrue("setting default keepalive options not supported on this platform", (IOUtils.LINUX || IOUtils.MAC_OS_X)); try ( MockTransportService serviceC = buildService("TS_C", Version.CURRENT, Settings.EMPTY); MockTransportService serviceD = buildService("TS_D", Version.CURRENT, Settings.EMPTY) diff --git a/modules/transport-netty4/src/yamlRestTest/resources/rest-api-spec/test/10_basic.yml b/modules/transport-netty4/src/yamlRestTest/resources/rest-api-spec/test/10_basic.yml index 19728c7d34cff..f4ac484757a41 100644 --- a/modules/transport-netty4/src/yamlRestTest/resources/rest-api-spec/test/10_basic.yml +++ b/modules/transport-netty4/src/yamlRestTest/resources/rest-api-spec/test/10_basic.yml @@ -7,13 +7,13 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.modules: { name: transport-netty4 } } + - contains: { nodes.$cluster_manager.modules: { name: transport-netty4 } } - do: cluster.stats: {} diff --git a/plugins/analysis-icu/licenses/lucene-analysis-icu-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/plugins/analysis-icu/licenses/lucene-analysis-icu-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 2f0a6ad50e337..0000000000000 --- a/plugins/analysis-icu/licenses/lucene-analysis-icu-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -77930f802430648980eded22ca6ed47fedaeaba4 \ No newline at end of file diff --git a/plugins/analysis-icu/licenses/lucene-analysis-icu-9.7.0.jar.sha1 b/plugins/analysis-icu/licenses/lucene-analysis-icu-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..0ed030926ab93 --- /dev/null +++ b/plugins/analysis-icu/licenses/lucene-analysis-icu-9.7.0.jar.sha1 @@ -0,0 +1 @@ +94293b169fb8572f440a5a4a523320ecf9778ffe \ No newline at end of file diff --git a/plugins/analysis-icu/src/internalClusterTest/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapperIT.java b/plugins/analysis-icu/src/internalClusterTest/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapperIT.java index 4735f39033081..9ef539caf24f9 100644 --- a/plugins/analysis-icu/src/internalClusterTest/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapperIT.java +++ b/plugins/analysis-icu/src/internalClusterTest/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapperIT.java @@ -31,19 +31,13 @@ package org.opensearch.index.mapper; -import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertOrderedSearchHits; - import com.ibm.icu.text.Collator; import com.ibm.icu.text.RuleBasedCollator; import com.ibm.icu.util.ULocale; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugin.analysis.icu.AnalysisICUPlugin; import org.opensearch.plugins.Plugin; @@ -56,6 +50,12 @@ import java.util.Collection; import java.util.Collections; +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertOrderedSearchHits; + public class ICUCollationKeywordFieldMapperIT extends OpenSearchIntegTestCase { @Override @@ -91,8 +91,12 @@ public void testBasicUsage() throws Exception { // both values should collate to same value indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", XContentType.JSON) + client().prepareIndex(index) + .setId("1") + .setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index) + .setId("2") + .setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", MediaTypeRegistry.JSON) ); // searching for either of the terms should return both results since they collate to the same value @@ -134,8 +138,10 @@ public void testMultipleValues() throws Exception { true, client().prepareIndex(index) .setId("1") - .setSource("{\"id\":\"1\", \"collate\":[\"" + equivalent[0] + "\", \"" + equivalent[1] + "\"]}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[2] + "\"}", XContentType.JSON) + .setSource("{\"id\":\"1\", \"collate\":[\"" + equivalent[0] + "\", \"" + equivalent[1] + "\"]}", MediaTypeRegistry.JSON), + client().prepareIndex(index) + .setId("2") + .setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[2] + "\"}", MediaTypeRegistry.JSON) ); // using sort mode = max, values B and C will be used for the sort @@ -195,8 +201,12 @@ public void testNormalization() throws Exception { indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", XContentType.JSON) + client().prepareIndex(index) + .setId("1") + .setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index) + .setId("2") + .setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", MediaTypeRegistry.JSON) ); // searching for either of the terms should return both results since they collate to the same value @@ -240,8 +250,12 @@ public void testSecondaryStrength() throws Exception { indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", XContentType.JSON) + client().prepareIndex(index) + .setId("1") + .setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index) + .setId("2") + .setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", MediaTypeRegistry.JSON) ); SearchRequest request = new SearchRequest().indices(index) @@ -285,8 +299,12 @@ public void testIgnorePunctuation() throws Exception { indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", XContentType.JSON) + client().prepareIndex(index) + .setId("1") + .setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index) + .setId("2") + .setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", MediaTypeRegistry.JSON) ); SearchRequest request = new SearchRequest().indices(index) @@ -330,9 +348,9 @@ public void testIgnoreWhitespace() throws Exception { indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"foo bar\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"foobar\"}", XContentType.JSON), - client().prepareIndex(index).setId("3").setSource("{\"id\":\"3\",\"collate\":\"foo-bar\"}", XContentType.JSON) + client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"foo bar\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"foobar\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index).setId("3").setSource("{\"id\":\"3\",\"collate\":\"foo-bar\"}", MediaTypeRegistry.JSON) ); SearchRequest request = new SearchRequest().indices(index) @@ -372,8 +390,8 @@ public void testNumerics() throws Exception { indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"collate\":\"foobar-10\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"collate\":\"foobar-9\"}", XContentType.JSON) + client().prepareIndex(index).setId("1").setSource("{\"collate\":\"foobar-10\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index).setId("2").setSource("{\"collate\":\"foobar-9\"}", MediaTypeRegistry.JSON) ); SearchRequest request = new SearchRequest().indices(index) @@ -411,10 +429,10 @@ public void testIgnoreAccentsButNotCase() throws Exception { indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"résumé\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"Resume\"}", XContentType.JSON), - client().prepareIndex(index).setId("3").setSource("{\"id\":\"3\",\"collate\":\"resume\"}", XContentType.JSON), - client().prepareIndex(index).setId("4").setSource("{\"id\":\"4\",\"collate\":\"Résumé\"}", XContentType.JSON) + client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"résumé\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"Resume\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index).setId("3").setSource("{\"id\":\"3\",\"collate\":\"resume\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index).setId("4").setSource("{\"id\":\"4\",\"collate\":\"Résumé\"}", MediaTypeRegistry.JSON) ); SearchRequest request = new SearchRequest().indices(index) @@ -449,8 +467,8 @@ public void testUpperCaseFirst() throws Exception { indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"collate\":\"resume\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"collate\":\"Resume\"}", XContentType.JSON) + client().prepareIndex(index).setId("1").setSource("{\"collate\":\"resume\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index).setId("2").setSource("{\"collate\":\"Resume\"}", MediaTypeRegistry.JSON) ); SearchRequest request = new SearchRequest().indices(index) @@ -497,8 +515,12 @@ public void testCustomRules() throws Exception { indexRandom( true, - client().prepareIndex(index).setId("1").setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", XContentType.JSON), - client().prepareIndex(index).setId("2").setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", XContentType.JSON) + client().prepareIndex(index) + .setId("1") + .setSource("{\"id\":\"1\",\"collate\":\"" + equivalent[0] + "\"}", MediaTypeRegistry.JSON), + client().prepareIndex(index) + .setId("2") + .setSource("{\"id\":\"2\",\"collate\":\"" + equivalent[1] + "\"}", MediaTypeRegistry.JSON) ); SearchRequest request = new SearchRequest().indices(index) diff --git a/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/ICUCollationKeyFilter.java b/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/ICUCollationKeyFilter.java index d7e097ce79798..35e0ed7651547 100644 --- a/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/ICUCollationKeyFilter.java +++ b/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/ICUCollationKeyFilter.java @@ -34,8 +34,8 @@ import com.ibm.icu.text.RawCollationKey; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.icu.ICUCollationDocValuesField; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import java.io.IOException; diff --git a/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuCollationTokenFilterFactory.java b/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuCollationTokenFilterFactory.java index 757a55487a162..09f4c2286faee 100644 --- a/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuCollationTokenFilterFactory.java +++ b/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuCollationTokenFilterFactory.java @@ -32,20 +32,20 @@ package org.opensearch.index.analysis; -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; - +import com.ibm.icu.text.Collator; +import com.ibm.icu.text.RuleBasedCollator; +import com.ibm.icu.util.ULocale; +import org.apache.logging.log4j.LogManager; import org.apache.lucene.analysis.TokenStream; import org.opensearch.common.io.Streams; import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; -import com.ibm.icu.text.Collator; -import com.ibm.icu.text.RuleBasedCollator; -import com.ibm.icu.util.ULocale; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; /** * An ICU based collation token filter. There are two ways to configure collation: @@ -80,9 +80,12 @@ public IcuCollationTokenFilterFactory(IndexSettings indexSettings, Environment e collator = new RuleBasedCollator(rules); } catch (Exception e) { if (failureToResolve != null) { - throw new IllegalArgumentException("Failed to resolve collation rules location", failureToResolve); + LogManager.getLogger(IcuCollationTokenFilterFactory.class) + .error("Failed to resolve collation rules location", failureToResolve); + throw new IllegalArgumentException("Failed to resolve collation rules location"); } else { - throw new IllegalArgumentException("Failed to parse collation rules", e); + LogManager.getLogger(IcuCollationTokenFilterFactory.class).error("Failed to parse collation rules", e); + throw new IllegalArgumentException("Failed to parse collation rules"); } } } else { diff --git a/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuFoldingTokenFilterFactory.java b/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuFoldingTokenFilterFactory.java index 605d5065edec9..d0c809fba1f86 100644 --- a/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuFoldingTokenFilterFactory.java +++ b/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuFoldingTokenFilterFactory.java @@ -33,7 +33,6 @@ package org.opensearch.index.analysis; import com.ibm.icu.text.Normalizer2; - import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.icu.ICUFoldingFilter; import org.opensearch.common.settings.Settings; diff --git a/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuNormalizerCharFilterFactory.java b/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuNormalizerCharFilterFactory.java index 754c52ad5dfe6..e6e21232d17f8 100644 --- a/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuNormalizerCharFilterFactory.java +++ b/plugins/analysis-icu/src/main/java/org/opensearch/index/analysis/IcuNormalizerCharFilterFactory.java @@ -33,7 +33,6 @@ package org.opensearch.index.analysis; import com.ibm.icu.text.Normalizer2; - import org.apache.lucene.analysis.icu.ICUNormalizer2CharFilter; import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; diff --git a/plugins/analysis-icu/src/main/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapper.java b/plugins/analysis-icu/src/main/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapper.java index 59b01b0ddb466..bd9c164811093 100644 --- a/plugins/analysis-icu/src/main/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapper.java +++ b/plugins/analysis-icu/src/main/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapper.java @@ -44,12 +44,12 @@ import org.apache.lucene.search.Query; import org.apache.lucene.util.BytesRef; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.Lucene; import org.opensearch.common.unit.Fuzziness; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.analysis.IndexableBinaryStringTools; import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData; diff --git a/plugins/analysis-icu/src/main/java/org/opensearch/plugin/analysis/icu/AnalysisICUPlugin.java b/plugins/analysis-icu/src/main/java/org/opensearch/plugin/analysis/icu/AnalysisICUPlugin.java index 1f9d7d7463fbd..7a61d1400c217 100644 --- a/plugins/analysis-icu/src/main/java/org/opensearch/plugin/analysis/icu/AnalysisICUPlugin.java +++ b/plugins/analysis-icu/src/main/java/org/opensearch/plugin/analysis/icu/AnalysisICUPlugin.java @@ -32,10 +32,8 @@ package org.opensearch.plugin.analysis.icu; -import static java.util.Collections.singletonMap; - import org.apache.lucene.analysis.Analyzer; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.index.analysis.AnalyzerProvider; import org.opensearch.index.analysis.CharFilterFactory; import org.opensearch.index.analysis.IcuAnalyzerProvider; @@ -60,6 +58,8 @@ import java.util.List; import java.util.Map; +import static java.util.Collections.singletonMap; + public class AnalysisICUPlugin extends Plugin implements AnalysisPlugin, MapperPlugin { @Override public Map> getCharFilters() { diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuAnalyzerTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuAnalyzerTests.java index e490248fc8122..c363bc6eb43f8 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuAnalyzerTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuAnalyzerTests.java @@ -90,10 +90,9 @@ public void testBadSettings() { Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT).put("mode", "wrong").build(); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings("index", settings); - IllegalArgumentException e = expectThrows( - IllegalArgumentException.class, - () -> { new IcuAnalyzerProvider(idxSettings, null, "icu", settings); } - ); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> { + new IcuAnalyzerProvider(idxSettings, null, "icu", settings); + }); assertThat(e.getMessage(), containsString("Unknown mode [wrong] in analyzer [icu], expected one of [compose, decompose]")); diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuTokenizerFactoryTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuTokenizerFactoryTests.java index 115171aec184f..6062aaced9dea 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuTokenizerFactoryTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IcuTokenizerFactoryTests.java @@ -36,8 +36,8 @@ import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.Index; import org.opensearch.env.Environment; -import org.opensearch.index.Index; import org.opensearch.plugin.analysis.icu.AnalysisICUPlugin; import org.opensearch.test.OpenSearchTestCase; diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IndexableBinaryStringToolsTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IndexableBinaryStringToolsTests.java index 63e21b2f7903b..d11a09069b71c 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IndexableBinaryStringToolsTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/IndexableBinaryStringToolsTests.java @@ -34,9 +34,10 @@ import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope.Scope; import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite; -import org.apache.lucene.util.ArrayUtil; + import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.lucene.tests.util.TimeUnits; +import org.apache.lucene.util.ArrayUtil; import org.opensearch.test.junit.listeners.ReproduceInfoPrinter; import org.junit.BeforeClass; diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuAnalysisTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuAnalysisTests.java index b7f42d524dbe8..2a5268d0c0d9d 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuAnalysisTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuAnalysisTests.java @@ -33,7 +33,7 @@ package org.opensearch.index.analysis; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugin.analysis.icu.AnalysisICUPlugin; import org.opensearch.test.OpenSearchTestCase; diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuCollationTokenFilterTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuCollationTokenFilterTests.java index a7bd2ca5a0440..980fcc6deb837 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuCollationTokenFilterTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuCollationTokenFilterTests.java @@ -40,7 +40,7 @@ import org.apache.lucene.analysis.core.KeywordTokenizer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugin.analysis.icu.AnalysisICUPlugin; import org.opensearch.test.OpenSearchTestCase; diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuNormalizerCharFilterTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuNormalizerCharFilterTests.java index 4ba8a6ca0b1f1..9bfa997badaad 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuNormalizerCharFilterTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/analysis/SimpleIcuNormalizerCharFilterTests.java @@ -35,7 +35,7 @@ import com.ibm.icu.text.Normalizer2; import org.apache.lucene.analysis.CharFilter; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugin.analysis.icu.AnalysisICUPlugin; import org.opensearch.test.OpenSearchTestCase; diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/mapper/CollationFieldTypeTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/mapper/CollationFieldTypeTests.java index 8dcec874d3771..1e08000117f61 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/mapper/CollationFieldTypeTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/mapper/CollationFieldTypeTests.java @@ -32,6 +32,7 @@ package org.opensearch.index.mapper; import com.carrotsearch.randomizedtesting.generators.RandomStrings; + import com.ibm.icu.text.Collator; import com.ibm.icu.text.RawCollationKey; import com.ibm.icu.util.ULocale; diff --git a/plugins/analysis-icu/src/test/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapperTests.java b/plugins/analysis-icu/src/test/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapperTests.java index 0cf82c408ae6f..0a2f48f4215cb 100644 --- a/plugins/analysis-icu/src/test/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapperTests.java +++ b/plugins/analysis-icu/src/test/java/org/opensearch/index/mapper/ICUCollationKeywordFieldMapperTests.java @@ -39,15 +39,14 @@ import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.IndexableFieldType; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.List; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugin.analysis.icu.AnalysisICUPlugin; import org.opensearch.plugins.Plugin; import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Set; import static org.hamcrest.Matchers.containsString; @@ -69,7 +68,7 @@ protected ICUCollationKeywordFieldMapper.Builder newBuilder() { @Override protected Set unsupportedProperties() { - return org.opensearch.common.collect.Set.of("analyzer", "similarity"); + return Set.of("analyzer", "similarity"); } @Override @@ -96,7 +95,7 @@ protected void writeFieldValue(XContentBuilder builder) throws IOException { public void testDefaults() throws Exception { DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping)); - assertEquals(Strings.toString(fieldMapping(this::minimalMapping)), mapper.mappingSource().toString()); + assertEquals(fieldMapping(this::minimalMapping).toString(), mapper.mappingSource().toString()); ParsedDocument doc = mapper.parse(source(b -> b.field("field", "1234"))); IndexableField[] fields = doc.rootDoc().getFields("field"); diff --git a/plugins/analysis-icu/src/test/resources/org/opensearch/index/analysis/KeywordTokenizer.rbbi b/plugins/analysis-icu/src/test/resources/org/opensearch/index/analysis/KeywordTokenizer.rbbi index 8e6de8aa94abb..86eb398ee9157 100644 --- a/plugins/analysis-icu/src/test/resources/org/opensearch/index/analysis/KeywordTokenizer.rbbi +++ b/plugins/analysis-icu/src/test/resources/org/opensearch/index/analysis/KeywordTokenizer.rbbi @@ -18,4 +18,4 @@ # Apply rule status {200}=RBBI.WORD_LETTER, which is mapped # to token type by DefaultICUTokenizerConfig. -.+ {200}; \ No newline at end of file +.+ {200}; diff --git a/plugins/analysis-kuromoji/build.gradle b/plugins/analysis-kuromoji/build.gradle index 60738fb28b6d5..426b85f44bf55 100644 --- a/plugins/analysis-kuromoji/build.gradle +++ b/plugins/analysis-kuromoji/build.gradle @@ -46,4 +46,3 @@ restResources { tasks.named("dependencyLicenses").configure { mapping from: /lucene-.*/, to: 'lucene' } - diff --git a/plugins/analysis-kuromoji/licenses/lucene-analysis-kuromoji-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/plugins/analysis-kuromoji/licenses/lucene-analysis-kuromoji-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index a0d112dd733ab..0000000000000 --- a/plugins/analysis-kuromoji/licenses/lucene-analysis-kuromoji-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c66f568fa9138c6ab6f3abf1efbfab3c7b5991d4 \ No newline at end of file diff --git a/plugins/analysis-kuromoji/licenses/lucene-analysis-kuromoji-9.7.0.jar.sha1 b/plugins/analysis-kuromoji/licenses/lucene-analysis-kuromoji-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..ddd67276606a5 --- /dev/null +++ b/plugins/analysis-kuromoji/licenses/lucene-analysis-kuromoji-9.7.0.jar.sha1 @@ -0,0 +1 @@ +2df800a38b64867b8dcd61fc2cd986114e4a80cb \ No newline at end of file diff --git a/plugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiPartOfSpeechFilterFactory.java b/plugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiPartOfSpeechFilterFactory.java index fef8d06c466b9..8e9c209ae421d 100644 --- a/plugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiPartOfSpeechFilterFactory.java +++ b/plugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiPartOfSpeechFilterFactory.java @@ -49,7 +49,7 @@ public class KuromojiPartOfSpeechFilterFactory extends AbstractTokenFilterFactor public KuromojiPartOfSpeechFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) { super(indexSettings, name, settings); - List wordList = Analysis.getWordList(env, settings, "stoptags"); + List wordList = Analysis.parseWordList(env, settings, "stoptags", s -> s); if (wordList != null) { stopTags.addAll(wordList); } else { diff --git a/plugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiTokenizerFactory.java b/plugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiTokenizerFactory.java index b5e718eaa6fa0..2939711f6f7e1 100644 --- a/plugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiTokenizerFactory.java +++ b/plugins/analysis-kuromoji/src/main/java/org/opensearch/index/analysis/KuromojiTokenizerFactory.java @@ -32,6 +32,8 @@ package org.opensearch.index.analysis; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.ja.JapaneseTokenizer; import org.apache.lucene.analysis.ja.JapaneseTokenizer.Mode; @@ -50,6 +52,7 @@ public class KuromojiTokenizerFactory extends AbstractTokenizerFactory { + private static final Logger LOGGER = LogManager.getLogger(KuromojiTokenizerFactory.class); private static final String USER_DICT_PATH_OPTION = "user_dictionary"; private static final String USER_DICT_RULES_OPTION = "user_dictionary_rules"; private static final String NBEST_COST = "nbest_cost"; @@ -74,6 +77,17 @@ public KuromojiTokenizerFactory(IndexSettings indexSettings, Environment env, St discardCompoundToken = settings.getAsBoolean(DISCARD_COMPOUND_TOKEN, false); } + private static String parse(String rule, Set dup) { + String[] values = CSVUtil.parse(rule); + if (values.length == 0) { + throw new IllegalArgumentException("Malformed csv in user dictionary."); + } + if (dup.add(values[0]) == false) { + throw new IllegalArgumentException("Found duplicate term [" + values[0] + "] in user dictionary."); + } + return rule; + } + public static UserDictionary getUserDictionary(Environment env, Settings settings) { if (settings.get(USER_DICT_PATH_OPTION) != null && settings.get(USER_DICT_RULES_OPTION) != null) { throw new IllegalArgumentException( @@ -81,31 +95,26 @@ public static UserDictionary getUserDictionary(Environment env, Settings setting ); } try { - List ruleList = Analysis.getWordList(env, settings, USER_DICT_PATH_OPTION, USER_DICT_RULES_OPTION, false); + Set dup = new HashSet<>(); + List ruleList = Analysis.parseWordList( + env, + settings, + USER_DICT_PATH_OPTION, + USER_DICT_RULES_OPTION, + s -> parse(s, dup) + ); if (ruleList == null || ruleList.isEmpty()) { return null; } - Set dup = new HashSet<>(); - int lineNum = 0; - for (String line : ruleList) { - // ignore comments - if (line.startsWith("#") == false) { - String[] values = CSVUtil.parse(line); - if (dup.add(values[0]) == false) { - throw new IllegalArgumentException( - "Found duplicate term [" + values[0] + "] in user dictionary " + "at line [" + lineNum + "]" - ); - } - } - ++lineNum; - } + StringBuilder sb = new StringBuilder(); for (String line : ruleList) { sb.append(line).append(System.lineSeparator()); } return UserDictionary.open(new StringReader(sb.toString())); } catch (IOException e) { - throw new OpenSearchException("failed to load kuromoji user dictionary", e); + LOGGER.error("Failed to load kuromoji user dictionary", e); + throw new OpenSearchException("Failed to load kuromoji user dictionary"); } } diff --git a/plugins/analysis-kuromoji/src/test/java/org/opensearch/index/analysis/KuromojiAnalysisTests.java b/plugins/analysis-kuromoji/src/test/java/org/opensearch/index/analysis/KuromojiAnalysisTests.java index e17658d83a085..ffc2db6672899 100644 --- a/plugins/analysis-kuromoji/src/test/java/org/opensearch/index/analysis/KuromojiAnalysisTests.java +++ b/plugins/analysis-kuromoji/src/test/java/org/opensearch/index/analysis/KuromojiAnalysisTests.java @@ -41,8 +41,8 @@ import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.Index; import org.opensearch.env.Environment; -import org.opensearch.index.Index; import org.opensearch.plugin.analysis.kuromoji.AnalysisKuromojiPlugin; import org.opensearch.test.OpenSearchTestCase; @@ -53,12 +53,12 @@ import java.nio.file.Files; import java.nio.file.Path; -import static org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertTokenStreamContents; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; +import static org.apache.lucene.tests.analysis.BaseTokenStreamTestCase.assertTokenStreamContents; public class KuromojiAnalysisTests extends OpenSearchTestCase { public void testDefaultsKuromojiAnalysis() throws IOException { @@ -379,6 +379,15 @@ public void testKuromojiAnalyzerInvalidUserDictOption() throws Exception { ); } + public void testKuromojiAnalyzerEmptyDictRule() throws Exception { + Settings settings = Settings.builder() + .put("index.analysis.analyzer.my_analyzer.type", "kuromoji") + .putList("index.analysis.analyzer.my_analyzer.user_dictionary_rules", "\"") + .build(); + RuntimeException exc = expectThrows(RuntimeException.class, () -> createTestAnalysis(settings)); + assertThat(exc.getMessage(), equalTo("Line [1]: Malformed csv in user dictionary.")); + } + public void testKuromojiAnalyzerDuplicateUserDictRule() throws Exception { Settings settings = Settings.builder() .put("index.analysis.analyzer.my_analyzer.type", "kuromoji") @@ -390,8 +399,8 @@ public void testKuromojiAnalyzerDuplicateUserDictRule() throws Exception { "制限スピード,制限スピード,セイゲンスピード,テスト名詞" ) .build(); - IllegalArgumentException exc = expectThrows(IllegalArgumentException.class, () -> createTestAnalysis(settings)); - assertThat(exc.getMessage(), containsString("[制限スピード] in user dictionary at line [3]")); + RuntimeException exc = expectThrows(RuntimeException.class, () -> createTestAnalysis(settings)); + assertThat(exc.getMessage(), equalTo("Line [4]: Found duplicate term [制限スピード] in user dictionary.")); } public void testDiscardCompoundToken() throws Exception { diff --git a/plugins/analysis-nori/licenses/lucene-analysis-nori-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/plugins/analysis-nori/licenses/lucene-analysis-nori-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index a3f939bfe9e05..0000000000000 --- a/plugins/analysis-nori/licenses/lucene-analysis-nori-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e8c47600ea859b999a5f5647341b0350b03dafcd \ No newline at end of file diff --git a/plugins/analysis-nori/licenses/lucene-analysis-nori-9.7.0.jar.sha1 b/plugins/analysis-nori/licenses/lucene-analysis-nori-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..0cd68af98e724 --- /dev/null +++ b/plugins/analysis-nori/licenses/lucene-analysis-nori-9.7.0.jar.sha1 @@ -0,0 +1 @@ +a01e8153f34d72e8c8c0180c1dea5b10f677dd3a \ No newline at end of file diff --git a/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriAnalyzerProvider.java b/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriAnalyzerProvider.java index 3dee606185429..d3c452c1f2e69 100644 --- a/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriAnalyzerProvider.java +++ b/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriAnalyzerProvider.java @@ -32,17 +32,18 @@ package org.opensearch.index.analysis; -import java.util.List; -import java.util.Set; import org.apache.lucene.analysis.ko.KoreanAnalyzer; import org.apache.lucene.analysis.ko.KoreanPartOfSpeechStopFilter; import org.apache.lucene.analysis.ko.KoreanTokenizer; -import org.apache.lucene.analysis.ko.dict.UserDictionary; import org.apache.lucene.analysis.ko.POS; +import org.apache.lucene.analysis.ko.dict.UserDictionary; import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; +import java.util.List; +import java.util.Set; + import static org.opensearch.index.analysis.NoriPartOfSpeechStopFilterFactory.resolvePOSList; public class NoriAnalyzerProvider extends AbstractIndexAnalyzerProvider { @@ -52,7 +53,7 @@ public NoriAnalyzerProvider(IndexSettings indexSettings, Environment env, String super(indexSettings, name, settings); final KoreanTokenizer.DecompoundMode mode = NoriTokenizerFactory.getMode(settings); final UserDictionary userDictionary = NoriTokenizerFactory.getUserDictionary(env, settings); - final List tagList = Analysis.getWordList(env, settings, "stoptags"); + final List tagList = Analysis.parseWordList(env, settings, "stoptags", s -> s); final Set stopTags = tagList != null ? resolvePOSList(tagList) : KoreanPartOfSpeechStopFilter.DEFAULT_STOP_TAGS; analyzer = new KoreanAnalyzer(userDictionary, mode, stopTags, false); } diff --git a/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriPartOfSpeechStopFilterFactory.java b/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriPartOfSpeechStopFilterFactory.java index 18cbc3c7c153d..5023db50422fc 100644 --- a/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriPartOfSpeechStopFilterFactory.java +++ b/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriPartOfSpeechStopFilterFactory.java @@ -48,7 +48,7 @@ public class NoriPartOfSpeechStopFilterFactory extends AbstractTokenFilterFactor public NoriPartOfSpeechStopFilterFactory(IndexSettings indexSettings, Environment env, String name, Settings settings) { super(indexSettings, name, settings); - List tagList = Analysis.getWordList(env, settings, "stoptags"); + List tagList = Analysis.parseWordList(env, settings, "stoptags", s -> s); this.stopTags = tagList != null ? resolvePOSList(tagList) : KoreanPartOfSpeechStopFilter.DEFAULT_STOP_TAGS; } diff --git a/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriTokenizerFactory.java b/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriTokenizerFactory.java index 5136277611e3a..9f3183194cdae 100644 --- a/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriTokenizerFactory.java +++ b/plugins/analysis-nori/src/main/java/org/opensearch/index/analysis/NoriTokenizerFactory.java @@ -32,6 +32,8 @@ package org.opensearch.index.analysis; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.ko.KoreanTokenizer; import org.apache.lucene.analysis.ko.dict.UserDictionary; @@ -47,6 +49,7 @@ import java.util.Locale; public class NoriTokenizerFactory extends AbstractTokenizerFactory { + private static final Logger LOGGER = LogManager.getLogger(NoriTokenizerFactory.class); private static final String USER_DICT_PATH_OPTION = "user_dictionary"; private static final String USER_DICT_RULES_OPTION = "user_dictionary_rules"; @@ -67,7 +70,7 @@ public static UserDictionary getUserDictionary(Environment env, Settings setting "It is not allowed to use [" + USER_DICT_PATH_OPTION + "] in conjunction" + " with [" + USER_DICT_RULES_OPTION + "]" ); } - List ruleList = Analysis.getWordList(env, settings, USER_DICT_PATH_OPTION, USER_DICT_RULES_OPTION, true); + List ruleList = Analysis.parseWordList(env, settings, USER_DICT_PATH_OPTION, USER_DICT_RULES_OPTION, s -> s); StringBuilder sb = new StringBuilder(); if (ruleList == null || ruleList.isEmpty()) { return null; @@ -78,7 +81,8 @@ public static UserDictionary getUserDictionary(Environment env, Settings setting try (Reader rulesReader = new StringReader(sb.toString())) { return UserDictionary.open(rulesReader); } catch (IOException e) { - throw new OpenSearchException("failed to load nori user dictionary", e); + LOGGER.error("Failed to load nori user dictionary", e); + throw new OpenSearchException("Failed to load nori user dictionary"); } } diff --git a/plugins/analysis-phonetic/licenses/commons-codec-1.13.jar.sha1 b/plugins/analysis-phonetic/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/analysis-phonetic/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/analysis-phonetic/licenses/commons-codec-1.15.jar.sha1 b/plugins/analysis-phonetic/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/analysis-phonetic/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/analysis-phonetic/licenses/lucene-analysis-phonetic-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/plugins/analysis-phonetic/licenses/lucene-analysis-phonetic-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index e2006546433fd..0000000000000 --- a/plugins/analysis-phonetic/licenses/lucene-analysis-phonetic-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -6f0f5c71052beee26e4ce99e1147ce406234f417 \ No newline at end of file diff --git a/plugins/analysis-phonetic/licenses/lucene-analysis-phonetic-9.7.0.jar.sha1 b/plugins/analysis-phonetic/licenses/lucene-analysis-phonetic-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..c7b4d2dc6da75 --- /dev/null +++ b/plugins/analysis-phonetic/licenses/lucene-analysis-phonetic-9.7.0.jar.sha1 @@ -0,0 +1 @@ +b7d47d54683b0b1e09b271c32d1b7d3eb1990f49 \ No newline at end of file diff --git a/plugins/analysis-phonetic/src/main/java/org/opensearch/index/analysis/phonetic/KoelnerPhonetik.java b/plugins/analysis-phonetic/src/main/java/org/opensearch/index/analysis/phonetic/KoelnerPhonetik.java index 01056b0983936..33e386af9f364 100644 --- a/plugins/analysis-phonetic/src/main/java/org/opensearch/index/analysis/phonetic/KoelnerPhonetik.java +++ b/plugins/analysis-phonetic/src/main/java/org/opensearch/index/analysis/phonetic/KoelnerPhonetik.java @@ -32,6 +32,9 @@ package org.opensearch.index.analysis.phonetic; +import org.apache.commons.codec.EncoderException; +import org.apache.commons.codec.StringEncoder; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -41,9 +44,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.codec.EncoderException; -import org.apache.commons.codec.StringEncoder; - /** * Kölner Phonetik * diff --git a/plugins/analysis-phonetic/src/test/java/org/opensearch/index/analysis/SimplePhoneticAnalysisTests.java b/plugins/analysis-phonetic/src/test/java/org/opensearch/index/analysis/SimplePhoneticAnalysisTests.java index 69d9fe9762c32..d14aeae41bd63 100644 --- a/plugins/analysis-phonetic/src/test/java/org/opensearch/index/analysis/SimplePhoneticAnalysisTests.java +++ b/plugins/analysis-phonetic/src/test/java/org/opensearch/index/analysis/SimplePhoneticAnalysisTests.java @@ -32,14 +32,14 @@ package org.opensearch.index.analysis; -import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.core.WhitespaceTokenizer; import org.apache.lucene.analysis.phonetic.DaitchMokotoffSoundexFilter; +import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase; import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugin.analysis.AnalysisPhoneticPlugin; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.MatcherAssert; diff --git a/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/10_metaphone.yml b/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/10_metaphone.yml index 1be0d8525a1c6..2268d30c986df 100644 --- a/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/10_metaphone.yml +++ b/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/10_metaphone.yml @@ -31,4 +31,3 @@ - match: { tokens.1.token: joe } - match: { tokens.2.token: BLKS } - match: { tokens.3.token: bloggs } - diff --git a/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/20_double_metaphone.yml b/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/20_double_metaphone.yml index 84b0129414c8e..40215cc469fc9 100644 --- a/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/20_double_metaphone.yml +++ b/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/20_double_metaphone.yml @@ -28,4 +28,3 @@ - length: { tokens: 1 } - match: { tokens.0.token: SPRKLF } - diff --git a/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/30_beider_morse.yml b/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/30_beider_morse.yml index bdd1ddef388df..dcc46484780dc 100644 --- a/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/30_beider_morse.yml +++ b/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/30_beider_morse.yml @@ -30,4 +30,3 @@ - length: { tokens: 1 } - match: { tokens.0.token: Svarts } - diff --git a/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/50_daitch_mokotoff.yml b/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/50_daitch_mokotoff.yml index bee4c8bf5f432..9b173a710ea43 100644 --- a/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/50_daitch_mokotoff.yml +++ b/plugins/analysis-phonetic/src/yamlRestTest/resources/rest-api-spec/test/analysis_phonetic/50_daitch_mokotoff.yml @@ -27,4 +27,3 @@ - length: { tokens: 1 } - match: { tokens.0.token: "645740" } - diff --git a/plugins/analysis-smartcn/build.gradle b/plugins/analysis-smartcn/build.gradle index 92f2774854715..d74d314ab0673 100644 --- a/plugins/analysis-smartcn/build.gradle +++ b/plugins/analysis-smartcn/build.gradle @@ -47,4 +47,3 @@ restResources { tasks.named("dependencyLicenses").configure { mapping from: /lucene-.*/, to: 'lucene' } - diff --git a/plugins/analysis-smartcn/licenses/lucene-analysis-smartcn-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/plugins/analysis-smartcn/licenses/lucene-analysis-smartcn-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index e675c5774f5a4..0000000000000 --- a/plugins/analysis-smartcn/licenses/lucene-analysis-smartcn-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -32aad8b8491df3c9862e7fe75e98bccdb6a25bda \ No newline at end of file diff --git a/plugins/analysis-smartcn/licenses/lucene-analysis-smartcn-9.7.0.jar.sha1 b/plugins/analysis-smartcn/licenses/lucene-analysis-smartcn-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..8df7245044171 --- /dev/null +++ b/plugins/analysis-smartcn/licenses/lucene-analysis-smartcn-9.7.0.jar.sha1 @@ -0,0 +1 @@ +5e68b9816e6cff8ee15f5b350cf2ffa54f9828b7 \ No newline at end of file diff --git a/plugins/analysis-smartcn/src/main/java/org/opensearch/index/analysis/SmartChineseStopTokenFilterFactory.java b/plugins/analysis-smartcn/src/main/java/org/opensearch/index/analysis/SmartChineseStopTokenFilterFactory.java index c7449ce87111d..6aa251ceb65e8 100644 --- a/plugins/analysis-smartcn/src/main/java/org/opensearch/index/analysis/SmartChineseStopTokenFilterFactory.java +++ b/plugins/analysis-smartcn/src/main/java/org/opensearch/index/analysis/SmartChineseStopTokenFilterFactory.java @@ -40,6 +40,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; import org.opensearch.index.IndexSettings; + import java.util.Map; import java.util.Set; diff --git a/plugins/analysis-smartcn/src/test/java/org/opensearch/index/analysis/SimpleSmartChineseAnalysisTests.java b/plugins/analysis-smartcn/src/test/java/org/opensearch/index/analysis/SimpleSmartChineseAnalysisTests.java index 1c71490dd9294..1e99d5b624da6 100644 --- a/plugins/analysis-smartcn/src/test/java/org/opensearch/index/analysis/SimpleSmartChineseAnalysisTests.java +++ b/plugins/analysis-smartcn/src/test/java/org/opensearch/index/analysis/SimpleSmartChineseAnalysisTests.java @@ -33,7 +33,7 @@ package org.opensearch.index.analysis; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugin.analysis.smartcn.AnalysisSmartChinesePlugin; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.MatcherAssert; diff --git a/plugins/analysis-stempel/licenses/lucene-analysis-stempel-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/plugins/analysis-stempel/licenses/lucene-analysis-stempel-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 053f5c97d65dc..0000000000000 --- a/plugins/analysis-stempel/licenses/lucene-analysis-stempel-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -ef546cfaaf727d93c4e86ddc7f77b525af135623 \ No newline at end of file diff --git a/plugins/analysis-stempel/licenses/lucene-analysis-stempel-9.7.0.jar.sha1 b/plugins/analysis-stempel/licenses/lucene-analysis-stempel-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..974e4202f5ffb --- /dev/null +++ b/plugins/analysis-stempel/licenses/lucene-analysis-stempel-9.7.0.jar.sha1 @@ -0,0 +1 @@ +d23b1f05b471e05d0d6068b3ece7c8c65672eae7 \ No newline at end of file diff --git a/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/AnalysisPolishFactoryTests.java b/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/AnalysisPolishFactoryTests.java index da6699fc1e95e..567f5644acbf8 100644 --- a/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/AnalysisPolishFactoryTests.java +++ b/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/AnalysisPolishFactoryTests.java @@ -33,9 +33,9 @@ package org.opensearch.index.analysis; import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase; import org.apache.lucene.tests.analysis.MockTokenizer; -import org.apache.lucene.analysis.Tokenizer; import org.opensearch.Version; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.UUIDs; diff --git a/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/PolishAnalysisTests.java b/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/PolishAnalysisTests.java index 56e48c4fb30b0..ee9b2a6520de2 100644 --- a/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/PolishAnalysisTests.java +++ b/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/PolishAnalysisTests.java @@ -35,7 +35,7 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.pl.PolishAnalyzer; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.analysis.pl.PolishStemTokenFilterFactory; import org.opensearch.plugin.analysis.stempel.AnalysisStempelPlugin; import org.opensearch.test.OpenSearchTestCase; diff --git a/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/SimplePolishTokenFilterTests.java b/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/SimplePolishTokenFilterTests.java index fb47a6a24c198..34906b6508856 100644 --- a/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/SimplePolishTokenFilterTests.java +++ b/plugins/analysis-stempel/src/test/java/org/opensearch/index/analysis/SimplePolishTokenFilterTests.java @@ -38,7 +38,7 @@ import org.apache.lucene.analysis.core.KeywordTokenizer; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugin.analysis.stempel.AnalysisStempelPlugin; import org.opensearch.test.OpenSearchTestCase; diff --git a/plugins/analysis-ukrainian/licenses/lucene-analysis-morfologik-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/plugins/analysis-ukrainian/licenses/lucene-analysis-morfologik-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index e5a2a0b0b4ab3..0000000000000 --- a/plugins/analysis-ukrainian/licenses/lucene-analysis-morfologik-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21c3511469f67019804e41a8d83ffc5c36de6479 \ No newline at end of file diff --git a/plugins/analysis-ukrainian/licenses/lucene-analysis-morfologik-9.7.0.jar.sha1 b/plugins/analysis-ukrainian/licenses/lucene-analysis-morfologik-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..dce408a7d40ef --- /dev/null +++ b/plugins/analysis-ukrainian/licenses/lucene-analysis-morfologik-9.7.0.jar.sha1 @@ -0,0 +1 @@ +dfb4313f3c68d337310522840d7144c1605d084a \ No newline at end of file diff --git a/plugins/analysis-ukrainian/src/test/java/org/opensearch/index/analysis/SimpleUkrainianAnalyzerTests.java b/plugins/analysis-ukrainian/src/test/java/org/opensearch/index/analysis/SimpleUkrainianAnalyzerTests.java index deb5cf275d7f6..1e9cde8569063 100644 --- a/plugins/analysis-ukrainian/src/test/java/org/opensearch/index/analysis/SimpleUkrainianAnalyzerTests.java +++ b/plugins/analysis-ukrainian/src/test/java/org/opensearch/index/analysis/SimpleUkrainianAnalyzerTests.java @@ -36,7 +36,7 @@ import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugin.analysis.ukrainian.AnalysisUkrainianPlugin; import org.opensearch.test.OpenSearchTestCase; diff --git a/plugins/analysis-ukrainian/src/test/java/org/opensearch/index/analysis/UkrainianAnalysisTests.java b/plugins/analysis-ukrainian/src/test/java/org/opensearch/index/analysis/UkrainianAnalysisTests.java index d0aefdaa1d9ec..5c2528b4814bd 100644 --- a/plugins/analysis-ukrainian/src/test/java/org/opensearch/index/analysis/UkrainianAnalysisTests.java +++ b/plugins/analysis-ukrainian/src/test/java/org/opensearch/index/analysis/UkrainianAnalysisTests.java @@ -35,7 +35,7 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.uk.UkrainianMorfologikAnalyzer; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugin.analysis.ukrainian.AnalysisUkrainianPlugin; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.MatcherAssert; diff --git a/plugins/crypto-kms/build.gradle b/plugins/crypto-kms/build.gradle new file mode 100644 index 0000000000000..c4a8609b6df48 --- /dev/null +++ b/plugins/crypto-kms/build.gradle @@ -0,0 +1,74 @@ +import org.opensearch.gradle.info.BuildParams + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +apply plugin: 'opensearch.build' +apply plugin: 'opensearch.publish' +apply plugin: 'opensearch.yaml-rest-test' + +opensearchplugin { + description 'AWS KMS plugin to provide crypto keys' + classname 'org.opensearch.crypto.kms.CryptoKmsPlugin' +} + +ext { + // Do not fail on `javadoc` warning (ANTLR generated code) + failOnJavadocWarning = false +} + +dependencies { + api "software.amazon.awssdk:sdk-core:${versions.aws}" + api "software.amazon.awssdk:aws-core:${versions.aws}" + api "software.amazon.awssdk:utils:${versions.aws}" + api "software.amazon.awssdk:auth:${versions.aws}" + api "software.amazon.awssdk:kms:${versions.aws}" + api "software.amazon.awssdk:http-client-spi:${versions.aws}" + api "software.amazon.awssdk:apache-client:${versions.aws}" + api "software.amazon.awssdk:regions:${versions.aws}" + api "software.amazon.awssdk:profiles:${versions.aws}" + api "software.amazon.awssdk:endpoints-spi:${versions.aws}" + api "software.amazon.awssdk:annotations:${versions.aws}" + api "software.amazon.awssdk:metrics-spi:${versions.aws}" + api "software.amazon.awssdk:json-utils:${versions.aws}" + api "software.amazon.awssdk:protocol-core:${versions.aws}" + api "software.amazon.awssdk:aws-query-protocol:${versions.aws}" + api "software.amazon.awssdk:aws-json-protocol:${versions.aws}" + api "software.amazon.awssdk:third-party-jackson-core:${versions.aws}" + api "org.apache.httpcomponents:httpclient:${versions.httpclient}" + api "org.apache.httpcomponents:httpcore:${versions.httpcore}" + api "commons-logging:commons-logging:${versions.commonslogging}" + api "org.apache.logging.log4j:log4j-1.2-api:${versions.log4j}" + api "org.slf4j:slf4j-api:${versions.slf4j}" + api "commons-codec:commons-codec:${versions.commonscodec}" + api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}" + api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" + api "org.reactivestreams:reactive-streams:${versions.reactivestreams}" +} + +//testClusters.all { +// module ':modules:crypto' +//} + +tasks.named("dependencyLicenses").configure { + mapping from: /jackson-.*/, to: 'jackson' + mapping from: /jaxb-.*/, to: 'jaxb' + mapping from: /netty-.*/, to: 'netty' +} + +bundlePlugin { + from('config/crypto-kms') { + into 'config' + } +} + +thirdPartyAudit.enabled = false +testingConventions.enabled = false diff --git a/plugins/crypto-kms/config/crypto-kms/log4j2.properties b/plugins/crypto-kms/config/crypto-kms/log4j2.properties new file mode 100644 index 0000000000000..285ac4a1d1376 --- /dev/null +++ b/plugins/crypto-kms/config/crypto-kms/log4j2.properties @@ -0,0 +1,22 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. +# + +logger.com_amazonaws.name = software.amazon.awssdk +logger.com_amazonaws.level = warn + +logger.com_amazonaws_jmx_SdkMBeanRegistrySupport.name = software.amazon.awssdk.jmx.SdkMBeanRegistrySupport +logger.com_amazonaws_jmx_SdkMBeanRegistrySupport.level = error + +logger.com_amazonaws_metrics_AwsSdkMetrics.name = software.amazon.awssdk.metrics.AwsSdkMetrics +logger.com_amazonaws_metrics_AwsSdkMetrics.level = error + +logger.com_amazonaws_auth_profile_internal_BasicProfileConfigFileLoader.name = software.amazon.awssdk.auth.profile.internal.BasicProfileConfigFileLoader +logger.com_amazonaws_auth_profile_internal_BasicProfileConfigFileLoader.level = error diff --git a/plugins/crypto-kms/licenses/annotations-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/annotations-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5a626eeb5725b --- /dev/null +++ b/plugins/crypto-kms/licenses/annotations-2.20.55.jar.sha1 @@ -0,0 +1 @@ +330e9d0e5f2401fffba5afe30f3740f400e8308d \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/grpc-context-LICENSE.txt b/plugins/crypto-kms/licenses/annotations-LICENSE.txt similarity index 100% rename from plugins/repository-gcs/licenses/grpc-context-LICENSE.txt rename to plugins/crypto-kms/licenses/annotations-LICENSE.txt diff --git a/plugins/crypto-kms/licenses/annotations-NOTICE.txt b/plugins/crypto-kms/licenses/annotations-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/annotations-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/apache-client-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/apache-client-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..3ee96bb6e4076 --- /dev/null +++ b/plugins/crypto-kms/licenses/apache-client-2.20.55.jar.sha1 @@ -0,0 +1 @@ +5c149885667d41a306769505cfa481cfddf6f113 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/apache-client-LICENSE.txt b/plugins/crypto-kms/licenses/apache-client-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/apache-client-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/apache-client-NOTICE.txt b/plugins/crypto-kms/licenses/apache-client-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/apache-client-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/auth-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/auth-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..010464bdf9fd1 --- /dev/null +++ b/plugins/crypto-kms/licenses/auth-2.20.55.jar.sha1 @@ -0,0 +1 @@ +e21f00a8a2096d5044f3eff176944256e01a175e \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/auth-LICENSE.txt b/plugins/crypto-kms/licenses/auth-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/auth-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/auth-NOTICE.txt b/plugins/crypto-kms/licenses/auth-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/auth-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/aws-core-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/aws-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..4b4ee1db864a8 --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +734427c2cece98a8cb90871b78d2311e4a7ef746 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/aws-core-LICENSE.txt b/plugins/crypto-kms/licenses/aws-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/aws-core-NOTICE.txt b/plugins/crypto-kms/licenses/aws-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/aws-json-protocol-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/aws-json-protocol-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..45a88305c1928 --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-json-protocol-2.20.55.jar.sha1 @@ -0,0 +1 @@ +a52731c86b974aefa5bbb1c545f407811a0163b1 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/aws-json-protocol-LICENSE.txt b/plugins/crypto-kms/licenses/aws-json-protocol-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-json-protocol-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/aws-json-protocol-NOTICE.txt b/plugins/crypto-kms/licenses/aws-json-protocol-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-json-protocol-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/aws-query-protocol-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/aws-query-protocol-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..ba5f43378730c --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-query-protocol-2.20.55.jar.sha1 @@ -0,0 +1 @@ +ac116215cc85366f0bdffee53b4c21e7a7fe03ef \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/aws-query-protocol-LICENSE.txt b/plugins/crypto-kms/licenses/aws-query-protocol-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-query-protocol-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/aws-query-protocol-NOTICE.txt b/plugins/crypto-kms/licenses/aws-query-protocol-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/aws-query-protocol-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/commons-codec-1.15.jar.sha1 b/plugins/crypto-kms/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/crypto-kms/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/commons-codec-LICENSE.txt b/plugins/crypto-kms/licenses/commons-codec-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/commons-codec-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/commons-codec-NOTICE.txt b/plugins/crypto-kms/licenses/commons-codec-NOTICE.txt new file mode 100644 index 0000000000000..56916449bbe10 --- /dev/null +++ b/plugins/crypto-kms/licenses/commons-codec-NOTICE.txt @@ -0,0 +1,17 @@ +Apache Commons Codec +Copyright 2002-2015 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java +contains test data from http://aspell.net/test/orig/batch0.tab. +Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org) + +=============================================================================== + +The content of package org.apache.commons.codec.language.bm has been translated +from the original php source code available at http://stevemorse.org/phoneticinfo.htm +with permission from the original authors. +Original source copyright: +Copyright (c) 2008 Alexander Beider & Stephen P. Morse. diff --git a/plugins/crypto-kms/licenses/commons-logging-1.2.jar.sha1 b/plugins/crypto-kms/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/crypto-kms/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/commons-logging-LICENSE.txt b/plugins/crypto-kms/licenses/commons-logging-LICENSE.txt new file mode 100644 index 0000000000000..57bc88a15a0ee --- /dev/null +++ b/plugins/crypto-kms/licenses/commons-logging-LICENSE.txt @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/plugins/crypto-kms/licenses/commons-logging-NOTICE.txt b/plugins/crypto-kms/licenses/commons-logging-NOTICE.txt new file mode 100644 index 0000000000000..72eb32a902458 --- /dev/null +++ b/plugins/crypto-kms/licenses/commons-logging-NOTICE.txt @@ -0,0 +1,5 @@ +Apache Commons CLI +Copyright 2001-2009 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/crypto-kms/licenses/endpoints-spi-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/endpoints-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5bc0e31166c77 --- /dev/null +++ b/plugins/crypto-kms/licenses/endpoints-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +085f82038ee86a7d6cd568fe7edd842978d92de3 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/endpoints-spi-LICENSE.txt b/plugins/crypto-kms/licenses/endpoints-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/endpoints-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/endpoints-spi-NOTICE.txt b/plugins/crypto-kms/licenses/endpoints-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/endpoints-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/http-client-spi-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/http-client-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..523cf43dcb2e9 --- /dev/null +++ b/plugins/crypto-kms/licenses/http-client-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +34f9b10c1a46038a0ceebdd750ba3a413a862ceb \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/http-client-spi-LICENSE.txt b/plugins/crypto-kms/licenses/http-client-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/http-client-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/http-client-spi-NOTICE.txt b/plugins/crypto-kms/licenses/http-client-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/http-client-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/httpclient-4.5.14.jar.sha1 b/plugins/crypto-kms/licenses/httpclient-4.5.14.jar.sha1 new file mode 100644 index 0000000000000..66e05851c2e3c --- /dev/null +++ b/plugins/crypto-kms/licenses/httpclient-4.5.14.jar.sha1 @@ -0,0 +1 @@ +1194890e6f56ec29177673f2f12d0b8e627dec98 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/httpclient-LICENSE.txt b/plugins/crypto-kms/licenses/httpclient-LICENSE.txt new file mode 100644 index 0000000000000..32f01eda18fe9 --- /dev/null +++ b/plugins/crypto-kms/licenses/httpclient-LICENSE.txt @@ -0,0 +1,558 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +========================================================================= + +This project includes Public Suffix List copied from + +licensed under the terms of the Mozilla Public License, v. 2.0 + +Full license text: + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/plugins/crypto-kms/licenses/httpclient-NOTICE.txt b/plugins/crypto-kms/licenses/httpclient-NOTICE.txt new file mode 100644 index 0000000000000..4f6058178b201 --- /dev/null +++ b/plugins/crypto-kms/licenses/httpclient-NOTICE.txt @@ -0,0 +1,5 @@ +Apache HttpComponents Client +Copyright 1999-2015 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/crypto-kms/licenses/httpcore-4.4.16.jar.sha1 b/plugins/crypto-kms/licenses/httpcore-4.4.16.jar.sha1 new file mode 100644 index 0000000000000..172110694b5bd --- /dev/null +++ b/plugins/crypto-kms/licenses/httpcore-4.4.16.jar.sha1 @@ -0,0 +1 @@ +51cf043c87253c9f58b539c9f7e44c8894223850 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/httpcore-LICENSE.txt b/plugins/crypto-kms/licenses/httpcore-LICENSE.txt new file mode 100644 index 0000000000000..72819a9f06f2a --- /dev/null +++ b/plugins/crypto-kms/licenses/httpcore-LICENSE.txt @@ -0,0 +1,241 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +========================================================================= + +This project contains annotations in the package org.apache.http.annotation +which are derived from JCIP-ANNOTATIONS +Copyright (c) 2005 Brian Goetz and Tim Peierls. +See http://www.jcip.net and the Creative Commons Attribution License +(http://creativecommons.org/licenses/by/2.5) +Full text: http://creativecommons.org/licenses/by/2.5/legalcode + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License. + "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License. + "Licensor" means the individual or entity that offers the Work under the terms of this License. + "Original Author" means the individual or entity who created the Work. + "Work" means the copyrightable work of authorship offered under the terms of this License. + "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. + +2. Fair Use Rights. Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: + + to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works; + to create and reproduce Derivative Works; + to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works; + to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works. + + For the avoidance of doubt, where the work is a musical composition: + Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work. + Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights agency or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions). + Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions). + +The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved. + +4. Restrictions.The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: + + You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(b), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(b), as requested. + If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. + +8. Miscellaneous + + Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. + Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. + If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. + This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. diff --git a/plugins/crypto-kms/licenses/httpcore-NOTICE.txt b/plugins/crypto-kms/licenses/httpcore-NOTICE.txt new file mode 100644 index 0000000000000..c0be50a505ec1 --- /dev/null +++ b/plugins/crypto-kms/licenses/httpcore-NOTICE.txt @@ -0,0 +1,8 @@ +Apache HttpComponents Core +Copyright 2005-2014 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +This project contains annotations derived from JCIP-ANNOTATIONS +Copyright (c) 2005 Brian Goetz and Tim Peierls. See http://www.jcip.net diff --git a/plugins/repository-hdfs/licenses/jackson-databind-LICENSE.txt b/plugins/crypto-kms/licenses/jackson-LICENSE similarity index 100% rename from plugins/repository-hdfs/licenses/jackson-databind-LICENSE.txt rename to plugins/crypto-kms/licenses/jackson-LICENSE diff --git a/plugins/repository-hdfs/licenses/jackson-databind-NOTICE.txt b/plugins/crypto-kms/licenses/jackson-NOTICE similarity index 100% rename from plugins/repository-hdfs/licenses/jackson-databind-NOTICE.txt rename to plugins/crypto-kms/licenses/jackson-NOTICE diff --git a/plugins/crypto-kms/licenses/jackson-annotations-2.15.2.jar.sha1 b/plugins/crypto-kms/licenses/jackson-annotations-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f63416ddb8ceb --- /dev/null +++ b/plugins/crypto-kms/licenses/jackson-annotations-2.15.2.jar.sha1 @@ -0,0 +1 @@ +4724a65ac8e8d156a24898d50fd5dbd3642870b8 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/jackson-databind-2.15.2.jar.sha1 b/plugins/crypto-kms/licenses/jackson-databind-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f16d80af8dce6 --- /dev/null +++ b/plugins/crypto-kms/licenses/jackson-databind-2.15.2.jar.sha1 @@ -0,0 +1 @@ +9353b021f10c307c00328f52090de2bdb4b6ff9c \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/json-utils-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/json-utils-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..a19b00e62f8b5 --- /dev/null +++ b/plugins/crypto-kms/licenses/json-utils-2.20.55.jar.sha1 @@ -0,0 +1 @@ +cd6710900e3190eac4c4496ae529ce08680dd320 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/json-utils-LICENSE.txt b/plugins/crypto-kms/licenses/json-utils-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/json-utils-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/json-utils-NOTICE.txt b/plugins/crypto-kms/licenses/json-utils-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/json-utils-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/kms-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/kms-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..0b4e98f59a066 --- /dev/null +++ b/plugins/crypto-kms/licenses/kms-2.20.55.jar.sha1 @@ -0,0 +1 @@ +389780132dd417ab58e0bb9b269d738ff839605f \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/kms-LICENSE.txt b/plugins/crypto-kms/licenses/kms-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/kms-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/kms-NOTICE.txt b/plugins/crypto-kms/licenses/kms-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/kms-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/log4j-1.2-api-2.20.0.jar.sha1 b/plugins/crypto-kms/licenses/log4j-1.2-api-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..9829576d38ce0 --- /dev/null +++ b/plugins/crypto-kms/licenses/log4j-1.2-api-2.20.0.jar.sha1 @@ -0,0 +1 @@ +689151374756cb809cb029f2501015bdc7733179 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/log4j-LICENSE.txt b/plugins/crypto-kms/licenses/log4j-LICENSE.txt new file mode 100644 index 0000000000000..6279e5206de13 --- /dev/null +++ b/plugins/crypto-kms/licenses/log4j-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 1999-2005 The Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/log4j-NOTICE.txt b/plugins/crypto-kms/licenses/log4j-NOTICE.txt new file mode 100644 index 0000000000000..0375732360047 --- /dev/null +++ b/plugins/crypto-kms/licenses/log4j-NOTICE.txt @@ -0,0 +1,5 @@ +Apache log4j +Copyright 2007 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/metrics-spi-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/metrics-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..db6701d87892a --- /dev/null +++ b/plugins/crypto-kms/licenses/metrics-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +8a0eae705b27465516f3b09cc9918e40963d534d \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/metrics-spi-LICENSE.txt b/plugins/crypto-kms/licenses/metrics-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/metrics-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/metrics-spi-NOTICE.txt b/plugins/crypto-kms/licenses/metrics-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/metrics-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/profiles-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/profiles-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..b7104cf0939e6 --- /dev/null +++ b/plugins/crypto-kms/licenses/profiles-2.20.55.jar.sha1 @@ -0,0 +1 @@ +959aad08b2f24057bf286c761b49e3af31a0a623 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/profiles-LICENSE.txt b/plugins/crypto-kms/licenses/profiles-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/profiles-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/profiles-NOTICE.txt b/plugins/crypto-kms/licenses/profiles-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/profiles-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/protocol-core-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/protocol-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..4dee45f4d9dd3 --- /dev/null +++ b/plugins/crypto-kms/licenses/protocol-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +0935e3ab32962a890f1d13bf39ba2167d9d692f9 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/protocol-core-LICENSE.txt b/plugins/crypto-kms/licenses/protocol-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/protocol-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/protocol-core-NOTICE.txt b/plugins/crypto-kms/licenses/protocol-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/protocol-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/reactive-streams-1.0.4.jar.sha1 b/plugins/crypto-kms/licenses/reactive-streams-1.0.4.jar.sha1 new file mode 100644 index 0000000000000..45a80e3f7e361 --- /dev/null +++ b/plugins/crypto-kms/licenses/reactive-streams-1.0.4.jar.sha1 @@ -0,0 +1 @@ +3864a1320d97d7b045f729a326e1e077661f31b7 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/reactive-streams-LICENSE.txt b/plugins/crypto-kms/licenses/reactive-streams-LICENSE.txt new file mode 100644 index 0000000000000..1e3c7e7c77495 --- /dev/null +++ b/plugins/crypto-kms/licenses/reactive-streams-LICENSE.txt @@ -0,0 +1,21 @@ +MIT No Attribution + +Copyright 2014 Reactive Streams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcpkix-jdk15on-NOTICE.txt b/plugins/crypto-kms/licenses/reactive-streams-NOTICE.txt similarity index 100% rename from plugins/ingest-attachment/licenses/bcpkix-jdk15on-NOTICE.txt rename to plugins/crypto-kms/licenses/reactive-streams-NOTICE.txt diff --git a/plugins/crypto-kms/licenses/regions-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/regions-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..993fc2f97de62 --- /dev/null +++ b/plugins/crypto-kms/licenses/regions-2.20.55.jar.sha1 @@ -0,0 +1 @@ +a117c19b4a30e902f4f1cc4bef6b5c10cc9aef31 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/regions-LICENSE.txt b/plugins/crypto-kms/licenses/regions-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/regions-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/regions-NOTICE.txt b/plugins/crypto-kms/licenses/regions-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/regions-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/sdk-core-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/sdk-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5f12be9c08c5b --- /dev/null +++ b/plugins/crypto-kms/licenses/sdk-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +8f2347feaf2575560ca89a2caa8d0243dbeb17a9 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/sdk-core-LICENSE.txt b/plugins/crypto-kms/licenses/sdk-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/sdk-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/sdk-core-NOTICE.txt b/plugins/crypto-kms/licenses/sdk-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/sdk-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/crypto-kms/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/crypto-kms/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/slf4j-api-LICENSE.txt b/plugins/crypto-kms/licenses/slf4j-api-LICENSE.txt new file mode 100644 index 0000000000000..2be7689435062 --- /dev/null +++ b/plugins/crypto-kms/licenses/slf4j-api-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2004-2022 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk15on-NOTICE.txt b/plugins/crypto-kms/licenses/slf4j-api-NOTICE.txt similarity index 100% rename from plugins/ingest-attachment/licenses/bcprov-jdk15on-NOTICE.txt rename to plugins/crypto-kms/licenses/slf4j-api-NOTICE.txt diff --git a/plugins/crypto-kms/licenses/third-party-jackson-core-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/third-party-jackson-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..e7eebbb98f1fe --- /dev/null +++ b/plugins/crypto-kms/licenses/third-party-jackson-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +956912f26056fc7d46b2db566362fe5f7a8c0e14 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/third-party-jackson-core-LICENSE.txt b/plugins/crypto-kms/licenses/third-party-jackson-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/third-party-jackson-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/third-party-jackson-core-NOTICE.txt b/plugins/crypto-kms/licenses/third-party-jackson-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/third-party-jackson-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/licenses/utils-2.20.55.jar.sha1 b/plugins/crypto-kms/licenses/utils-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..fc4cde604e33c --- /dev/null +++ b/plugins/crypto-kms/licenses/utils-2.20.55.jar.sha1 @@ -0,0 +1 @@ +d3e1bbbc19795eadbeb4dd963a94647576644097 \ No newline at end of file diff --git a/plugins/crypto-kms/licenses/utils-LICENSE.txt b/plugins/crypto-kms/licenses/utils-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/crypto-kms/licenses/utils-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/crypto-kms/licenses/utils-NOTICE.txt b/plugins/crypto-kms/licenses/utils-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/crypto-kms/licenses/utils-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/AmazonKmsClientReference.java b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/AmazonKmsClientReference.java new file mode 100644 index 0000000000000..8cd07f681f9cd --- /dev/null +++ b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/AmazonKmsClientReference.java @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import software.amazon.awssdk.services.kms.KmsClient; + +import org.opensearch.common.concurrent.RefCountedReleasable; + +/** + * Handles the shutdown of the wrapped {@link KmsClient} using reference + * counting. + */ +public class AmazonKmsClientReference extends RefCountedReleasable { + + AmazonKmsClientReference(KmsClient client) { + super("AWS_KMS_CLIENT", client, client::close); + } +} diff --git a/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/CredentialProviderFactory.java b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/CredentialProviderFactory.java new file mode 100644 index 0000000000000..87a10f1408b8d --- /dev/null +++ b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/CredentialProviderFactory.java @@ -0,0 +1,65 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider; +import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.SdkSystemSetting; + +import java.util.function.Supplier; + +/** + * Creates credential providers based on the provided configuration. + */ +public class CredentialProviderFactory { + + /** + * Credential provider for EC2/ECS container + */ + static class PrivilegedInstanceProfileCredentialsProvider implements AwsCredentialsProvider { + private final AwsCredentialsProvider credentials; + + private PrivilegedInstanceProfileCredentialsProvider() { + this.credentials = initializeProvider(); + } + + private AwsCredentialsProvider initializeProvider() { + if (SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.getStringValue().isPresent() + || SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_FULL_URI.getStringValue().isPresent()) { + + return ContainerCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build(); + } + // InstanceProfileCredentialsProvider as last item of chain + return InstanceProfileCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build(); + } + + @Override + public AwsCredentials resolveCredentials() { + return SocketAccess.doPrivileged(credentials::resolveCredentials); + } + } + + /** + * Creates a credential provider based on the provided configuration. + * @param staticCredsSupplier Static credentials are used in case supplier returns a non-null instance. + * @return Credential provider instance. + */ + public AwsCredentialsProvider createAwsCredentialsProvider(Supplier staticCredsSupplier) { + AwsCredentials awsCredentials = staticCredsSupplier.get(); + if (awsCredentials != null) { + return StaticCredentialsProvider.create(awsCredentials); + } + + // Add other credential providers here + return new PrivilegedInstanceProfileCredentialsProvider(); + } +} diff --git a/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/CryptoKmsPlugin.java b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/CryptoKmsPlugin.java new file mode 100644 index 0000000000000..f2ea9e37a0c09 --- /dev/null +++ b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/CryptoKmsPlugin.java @@ -0,0 +1,87 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import org.opensearch.SpecialPermission; +import org.opensearch.cluster.metadata.CryptoMetadata; +import org.opensearch.common.crypto.MasterKeyProvider; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; +import org.opensearch.plugins.CryptoKeyProviderPlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.plugins.ReloadablePlugin; + +import java.util.Arrays; +import java.util.List; + +/** + * AWS KMS based crypto key provider plugin. + */ +public class CryptoKmsPlugin extends Plugin implements CryptoKeyProviderPlugin, ReloadablePlugin { + private static final String PROVIDER_NAME = "aws-kms"; + + static { + SpecialPermission.check(); + } + + private final Settings settings; + // protected for testing + protected final KmsService kmsService; + + public CryptoKmsPlugin(Settings settings) { + this(settings, new KmsService()); + } + + protected CryptoKmsPlugin(Settings settings, KmsService kmsService) { + this.settings = settings; + this.kmsService = kmsService; + // eagerly load client settings when secure settings are accessible + reload(settings); + } + + @Override + public MasterKeyProvider createKeyProvider(CryptoMetadata cryptoMetadata) { + return kmsService.createMasterKeyProvider(cryptoMetadata); + } + + @Override + public String type() { + return PROVIDER_NAME; + } + + @Override + public List> getSettings() { + return Arrays.asList( + KmsClientSettings.ACCESS_KEY_SETTING, + KmsClientSettings.SECRET_KEY_SETTING, + KmsClientSettings.SESSION_TOKEN_SETTING, + KmsClientSettings.ENDPOINT_SETTING, + KmsClientSettings.REGION_SETTING, + KmsClientSettings.PROXY_HOST_SETTING, + KmsClientSettings.PROXY_PORT_SETTING, + KmsClientSettings.PROXY_USERNAME_SETTING, + KmsClientSettings.PROXY_PASSWORD_SETTING, + KmsClientSettings.READ_TIMEOUT_SETTING, + KmsService.ENC_CTX_SETTING, + KmsService.KEY_ARN_SETTING + ); + } + + @Override + public void reload(Settings settings) { + // secure settings should be readable + final KmsClientSettings clientSettings = KmsClientSettings.getClientSettings(settings); + kmsService.refreshAndClearCache(clientSettings); + } + + @Override + public void close() { + kmsService.close(); + } +} diff --git a/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsClientSettings.java b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsClientSettings.java new file mode 100644 index 0000000000000..187a80a6355f7 --- /dev/null +++ b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsClientSettings.java @@ -0,0 +1,258 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.common.settings.SecureSetting; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Setting.Property; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.settings.SettingsException; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.settings.SecureString; + +import java.util.Locale; +import java.util.Objects; + +/** + * A container for settings used to create an kms client. + */ +public class KmsClientSettings { + + /** The access key (ie login id) for connecting to kms. */ + static final Setting ACCESS_KEY_SETTING = SecureSetting.secureString("kms.access_key", null); + + /** The secret key (ie password) for connecting to kms. */ + static final Setting SECRET_KEY_SETTING = SecureSetting.secureString("kms.secret_key", null); + + /** The session token for connecting to kms. */ + static final Setting SESSION_TOKEN_SETTING = SecureSetting.secureString("kms.session_token", null); + + /** The host name of a proxy to connect to kms through. */ + static final Setting PROXY_HOST_SETTING = Setting.simpleString("kms.proxy.host", Property.NodeScope); + + /** The port of a proxy to connect to kms through. */ + static final Setting PROXY_PORT_SETTING = Setting.intSetting("kms.proxy.port", 80, 0, 1 << 16, Property.NodeScope); + + /** An override for the kms endpoint to connect to. */ + static final Setting ENDPOINT_SETTING = new Setting<>("kms.endpoint", "", s -> s.toLowerCase(Locale.ROOT), Property.NodeScope); + + /** An override for the scoping region for authentication. */ + static final Setting REGION_SETTING = new Setting<>("kms.region", "", s -> s.toLowerCase(Locale.ROOT), Property.NodeScope); + + /** The username of a proxy to connect to kms through. */ + static final Setting PROXY_USERNAME_SETTING = SecureSetting.secureString("kms.proxy.username", null); + + /** The password of a proxy to connect to kms through. */ + static final Setting PROXY_PASSWORD_SETTING = SecureSetting.secureString("kms.proxy.password", null); + + /** The socket timeout for connecting to kms. */ + static final Setting READ_TIMEOUT_SETTING = Setting.timeSetting( + "kms.read_timeout", + TimeValue.timeValueMillis(50_000), + Property.NodeScope + ); + + private static final Logger logger = LogManager.getLogger(KmsClientSettings.class); + + /** Credentials to authenticate with kms. */ + final AwsCredentials credentials; + + /** + * The kms endpoint the client should talk to, or empty string to use the + * default. + */ + final String endpoint; + + /** + * The kms signing region. + */ + final String region; + + /** An optional proxy host that requests to kms should be made through. */ + final String proxyHost; + + /** The port number the proxy host should be connected on. */ + final int proxyPort; + + // these should be "secure" yet the api for the kms client only takes String, so + // storing them + // as SecureString here won't really help with anything + /** An optional username for the proxy host, for basic authentication. */ + final String proxyUsername; + + /** An optional password for the proxy host, for basic authentication. */ + final String proxyPassword; + + /** The read timeout for the kms client. */ + final int readTimeoutMillis; + + protected KmsClientSettings( + AwsCredentials credentials, + String endpoint, + String region, + String proxyHost, + int proxyPort, + String proxyUsername, + String proxyPassword, + int readTimeoutMillis + ) { + this.credentials = credentials; + this.endpoint = endpoint; + this.region = region; + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + this.proxyUsername = proxyUsername; + this.proxyPassword = proxyPassword; + this.readTimeoutMillis = readTimeoutMillis; + } + + static AwsCredentials loadCredentials(Settings settings) { + try ( + SecureString key = ACCESS_KEY_SETTING.get(settings); + SecureString secret = SECRET_KEY_SETTING.get(settings); + SecureString sessionToken = SESSION_TOKEN_SETTING.get(settings) + ) { + if (key.length() == 0 && secret.length() == 0) { + if (sessionToken.length() > 0) { + throw new SettingsException( + "Setting [{}] is set but [{}] and [{}] are not", + SESSION_TOKEN_SETTING.getKey(), + ACCESS_KEY_SETTING.getKey(), + SECRET_KEY_SETTING.getKey() + ); + } + + logger.debug("Using either environment variables, system properties or instance profile credentials"); + return null; + } else { + if (key.length() == 0) { + throw new SettingsException( + "Setting [{}] is set but [{}] is not", + SECRET_KEY_SETTING.getKey(), + ACCESS_KEY_SETTING.getKey(), + SECRET_KEY_SETTING.getKey() + ); + } + if (secret.length() == 0) { + throw new SettingsException( + "Setting [{}] is set but [{}] is not", + ACCESS_KEY_SETTING.getKey(), + SECRET_KEY_SETTING.getKey() + ); + } + + final AwsCredentials credentials; + if (sessionToken.length() == 0) { + logger.debug("Using basic key/secret credentials"); + credentials = AwsBasicCredentials.create(key.toString(), secret.toString()); + } else { + logger.debug("Using basic session credentials"); + credentials = AwsSessionCredentials.create(key.toString(), secret.toString(), sessionToken.toString()); + } + return credentials; + } + } + } + + /** Parse settings for a single client. */ + static KmsClientSettings getClientSettings(Settings settings) { + final AwsCredentials credentials = loadCredentials(settings); + try ( + SecureString proxyUsername = PROXY_USERNAME_SETTING.get(settings); + SecureString proxyPassword = PROXY_PASSWORD_SETTING.get(settings) + ) { + return new KmsClientSettings( + credentials, + ENDPOINT_SETTING.get(settings), + REGION_SETTING.get(settings), + PROXY_HOST_SETTING.get(settings), + PROXY_PORT_SETTING.get(settings), + proxyUsername.toString(), + proxyPassword.toString(), + (int) READ_TIMEOUT_SETTING.get(settings).millis() + ); + } + } + + KmsClientSettings getMetadataSettings(Settings settings) { + AwsCredentials newCredentials = loadCredentials(settings); + newCredentials = newCredentials == null ? this.credentials : newCredentials; + final Settings normalizedSettings = Settings.builder().put(settings).normalizePrefix("kms.").build(); + + String newProxyUsername = this.proxyUsername, newProxyPassword = this.proxyPassword; + if (PROXY_USERNAME_SETTING.exists(normalizedSettings)) { + try (SecureString proxyUsername = PROXY_USERNAME_SETTING.get(settings)) { + newProxyUsername = proxyUsername.toString(); + } + } + if (PROXY_PASSWORD_SETTING.exists(normalizedSettings)) { + try (SecureString proxyPassword = PROXY_PASSWORD_SETTING.get(settings)) { + newProxyPassword = proxyPassword.toString(); + } + } + + String newEndpoint = getCryptoMetadataSettingOrExisting(ENDPOINT_SETTING, normalizedSettings, this.endpoint); + String newRegion = getCryptoMetadataSettingOrExisting(REGION_SETTING, normalizedSettings, this.region); + String newProxyHost = getCryptoMetadataSettingOrExisting(PROXY_HOST_SETTING, normalizedSettings, this.proxyHost); + int newProxyPort = getCryptoMetadataSettingOrExisting(PROXY_PORT_SETTING, normalizedSettings, this.proxyPort); + TimeValue newReadTimeout = getCryptoMetadataSettingOrExisting( + READ_TIMEOUT_SETTING, + normalizedSettings, + TimeValue.timeValueMillis(this.readTimeoutMillis) + ); + + return new KmsClientSettings( + newCredentials, + newEndpoint, + newRegion, + newProxyHost, + newProxyPort, + newProxyUsername, + newProxyPassword, + (int) newReadTimeout.millis() + ); + } + + private static T getCryptoMetadataSettingOrExisting(Setting setting, Settings normalizedSettings, T defaultValue) { + if (setting.exists(normalizedSettings)) { + return setting.get(normalizedSettings); + } + return defaultValue; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final KmsClientSettings that = (KmsClientSettings) o; + return readTimeoutMillis == that.readTimeoutMillis + && Objects.equals(credentials, that.credentials) + && Objects.equals(endpoint, that.endpoint) + && Objects.equals(region, that.region) + && Objects.equals(proxyHost, that.proxyHost) + && Objects.equals(proxyPort, that.proxyPort) + && Objects.equals(proxyUsername, that.proxyUsername) + && Objects.equals(proxyPassword, that.proxyPassword); + } + + @Override + public int hashCode() { + return Objects.hash(readTimeoutMillis, credentials, endpoint, region, proxyHost, proxyPort, proxyUsername, proxyPassword); + } +} diff --git a/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsMasterKeyProvider.java b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsMasterKeyProvider.java new file mode 100644 index 0000000000000..9003e8bebd5ff --- /dev/null +++ b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsMasterKeyProvider.java @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.services.kms.model.DataKeySpec; +import software.amazon.awssdk.services.kms.model.DecryptRequest; +import software.amazon.awssdk.services.kms.model.DecryptResponse; +import software.amazon.awssdk.services.kms.model.GenerateDataKeyRequest; +import software.amazon.awssdk.services.kms.model.GenerateDataKeyResponse; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.common.crypto.DataKeyPair; +import org.opensearch.common.crypto.MasterKeyProvider; + +import java.util.Map; +import java.util.function.Supplier; + +public class KmsMasterKeyProvider implements MasterKeyProvider { + private final Map encryptionContext; + private final String keyArn; + private final Supplier clientReferenceSupplier; + + private static final Logger logger = LogManager.getLogger(KmsMasterKeyProvider.class); + + public KmsMasterKeyProvider( + Map encryptionContext, + String keyArn, + Supplier clientReferenceSupplier + ) { + this.encryptionContext = encryptionContext; + this.keyArn = keyArn; + this.clientReferenceSupplier = clientReferenceSupplier; + } + + @Override + public DataKeyPair generateDataPair() { + logger.info("Generating new data key pair"); + try (AmazonKmsClientReference clientReference = clientReferenceSupplier.get()) { + GenerateDataKeyRequest request = GenerateDataKeyRequest.builder() + .encryptionContext(encryptionContext) + // Currently only 32 byte data key is supported. To add support for other key sizes add key providers + // in org.opensearch.encryption.CryptoManagerFactory.createCryptoProvider. + .keySpec(DataKeySpec.AES_256) + .keyId(keyArn) + .build(); + GenerateDataKeyResponse dataKeyPair = SocketAccess.doPrivileged(() -> clientReference.get().generateDataKey(request)); + return new DataKeyPair(dataKeyPair.plaintext().asByteArray(), dataKeyPair.ciphertextBlob().asByteArray()); + } + } + + @Override + public byte[] decryptKey(byte[] encryptedKey) { + try (AmazonKmsClientReference clientReference = clientReferenceSupplier.get()) { + DecryptRequest decryptRequest = DecryptRequest.builder().ciphertextBlob(SdkBytes.fromByteArray(encryptedKey)).build(); + DecryptResponse decryptResponse = SocketAccess.doPrivileged(() -> clientReference.get().decrypt(decryptRequest)); + return decryptResponse.plaintext().asByteArray(); + } + } + + @Override + public String getKeyId() { + return keyArn; + } + + @Override + public Map getEncryptionContext() { + return encryptionContext; + } + + @Override + public void close() {} +} diff --git a/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsService.java b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsService.java new file mode 100644 index 0000000000000..108c88bd3bf80 --- /dev/null +++ b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/KmsService.java @@ -0,0 +1,272 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.retry.RetryPolicy; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.http.apache.ProxyConfiguration; +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.kms.KmsClient; +import software.amazon.awssdk.services.kms.KmsClientBuilder; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.cluster.metadata.CryptoMetadata; +import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.collect.MapBuilder; +import org.opensearch.common.crypto.MasterKeyProvider; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; + +import java.io.Closeable; +import java.net.URI; +import java.net.URISyntaxException; +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyMap; + +/** + * Service class which exposes APIs for communication with AWS KMS. + */ +public class KmsService implements Closeable { + + private static final Logger logger = LogManager.getLogger(KmsService.class); + private final CredentialProviderFactory credentialProviderFactory; + + static final Setting ENC_CTX_SETTING = Setting.simpleString("kms.encryption_context", Setting.Property.NodeScope); + + static final Setting KEY_ARN_SETTING = Setting.simpleString("kms.key_arn", Setting.Property.NodeScope); + + private volatile Map clientsCache = emptyMap(); + + /** + * Client settings calculated from static configuration and settings in the keystore. + */ + private volatile KmsClientSettings staticClientSettings; + + /** + * Client settings derived from those in {@link #staticClientSettings} by combining them with crypto settings + */ + private volatile Map derivedClientSettings; + + public KmsService() { + credentialProviderFactory = new CredentialProviderFactory(); + } + + private KmsClient buildClient(KmsClientSettings clientSettings) { + SocketAccess.doPrivilegedVoid(KmsService::setDefaultAwsProfilePath); + final AwsCredentialsProvider awsCredentialsProvider = buildCredentials(clientSettings); + final ClientOverrideConfiguration overrideConfiguration = buildOverrideConfiguration(); + final ProxyConfiguration proxyConfiguration = SocketAccess.doPrivileged(() -> buildProxyConfiguration(clientSettings)); + return buildClient( + awsCredentialsProvider, + proxyConfiguration, + overrideConfiguration, + clientSettings.endpoint, + clientSettings.region, + clientSettings.readTimeoutMillis + ); + } + + // proxy for testing + protected KmsClient buildClient( + AwsCredentialsProvider awsCredentialsProvider, + ProxyConfiguration proxyConfiguration, + ClientOverrideConfiguration overrideConfiguration, + String endpoint, + String region, + long readTimeoutMillis + ) { + ApacheHttpClient.Builder clientBuilder = ApacheHttpClient.builder() + .proxyConfiguration(proxyConfiguration) + .socketTimeout(Duration.ofMillis(readTimeoutMillis)); + + KmsClientBuilder builder = KmsClient.builder() + .region(Region.of(region)) + .overrideConfiguration(overrideConfiguration) + .httpClientBuilder(clientBuilder) + .credentialsProvider(awsCredentialsProvider); + + if (Strings.hasText(endpoint)) { + logger.debug("using explicit kms endpoint [{}]", endpoint); + builder.endpointOverride(URI.create(endpoint)); + } + + if (Strings.hasText(region)) { + logger.debug("using explicit kms region [{}]", region); + builder.region(Region.of(region)); + } + + return SocketAccess.doPrivileged(builder::build); + } + + ProxyConfiguration buildProxyConfiguration(KmsClientSettings clientSettings) { + if (Strings.hasText(clientSettings.proxyHost)) { + try { + return ProxyConfiguration.builder() + .endpoint(new URI("https", null, clientSettings.proxyHost, clientSettings.proxyPort, null, null, null)) + .username(clientSettings.proxyUsername) + .password(clientSettings.proxyPassword) + .build(); + } catch (URISyntaxException e) { + throw SdkException.create("Invalid proxy URL", e); + } + } else { + return ProxyConfiguration.builder().build(); + } + } + + ClientOverrideConfiguration buildOverrideConfiguration() { + return ClientOverrideConfiguration.builder().retryPolicy(buildRetryPolicy()).build(); + } + + // pkg private for tests + RetryPolicy buildRetryPolicy() { + // Increase the number of retries in case of 5xx API responses. + // Note that AWS SDK v2 introduced a concept of TokenBucketRetryCondition, which effectively limits retries for + // APIs that have been failing continuously. It allocates tokens (default is 500), which means that once 500 + // retries fail for any API on a bucket, new retries will only be allowed once some retries are rejected. + // https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html + RetryPolicy.Builder retryPolicy = RetryPolicy.builder().numRetries(10); + return retryPolicy.build(); + } + + AwsCredentialsProvider buildCredentials(KmsClientSettings clientSettings) { + final AwsCredentials credentials = clientSettings.credentials; + return credentialProviderFactory.createAwsCredentialsProvider(() -> credentials); + } + + public AmazonKmsClientReference client(CryptoMetadata cryptoMetadata) { + final KmsClientSettings clientSettings = settings(cryptoMetadata); + { + final AmazonKmsClientReference clientReference = clientsCache.get(clientSettings); + if (clientReference != null && clientReference.tryIncRef()) { + return clientReference; + } + } + synchronized (this) { + final AmazonKmsClientReference existing = clientsCache.get(clientSettings); + if (existing != null && existing.tryIncRef()) { + return existing; + } + final AmazonKmsClientReference clientReference = new AmazonKmsClientReference( + SocketAccess.doPrivileged(() -> buildClient(clientSettings)) + ); + clientReference.incRef(); + clientsCache = MapBuilder.newMapBuilder(clientsCache).put(clientSettings, clientReference).immutableMap(); + return clientReference; + } + } + + /** + * Either fetches {@link KmsClientSettings} for a given {@link CryptoMetadata} from cached settings or creates them + * by overriding static client settings from {@link #staticClientSettings} with settings found in the crypto metadata. + * @param cryptoMetadata Crypto Metadata + * @return KmsClientSettings + */ + KmsClientSettings settings(CryptoMetadata cryptoMetadata) { + final Settings settings = cryptoMetadata.settings(); + { + final KmsClientSettings existing = derivedClientSettings.get(settings); + if (existing != null) { + return existing; + } + } + synchronized (this) { + final KmsClientSettings existing = derivedClientSettings.get(settings); + if (existing != null) { + return existing; + } + final KmsClientSettings newSettings = staticClientSettings.getMetadataSettings(settings); + derivedClientSettings = MapBuilder.newMapBuilder(derivedClientSettings).put(settings, newSettings).immutableMap(); + return newSettings; + } + } + + /** + * Refreshes the settings for the AmazonKMS client. The new client will be build + * using these new settings. The old client is usable until released. On release it + * will be destroyed instead of being returned to the cache. + */ + public void refreshAndClearCache(KmsClientSettings clientSettings) { + // shutdown all unused clients + // others will shutdown on their respective release + releaseCachedClients(); + this.staticClientSettings = clientSettings; + derivedClientSettings = emptyMap(); + } + + private synchronized void releaseCachedClients() { + // the clients will shutdown when they will not be used anymore + for (final AmazonKmsClientReference clientReference : clientsCache.values()) { + clientReference.decRef(); + } + + // clear previously cached clients, they will be build lazily + clientsCache = emptyMap(); + derivedClientSettings = emptyMap(); + } + + @Override + public void close() { + releaseCachedClients(); + } + + // By default, AWS v2 SDK loads a default profile from $USER_HOME, which is restricted. Use the OpenSearch configuration path instead. + @SuppressForbidden(reason = "Prevent AWS SDK v2 from using ~/.aws/config and ~/.aws/credentials.") + static void setDefaultAwsProfilePath() { + if (ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.getStringValue().isEmpty()) { + logger.info("setting aws.sharedCredentialsFile={}", System.getProperty("opensearch.path.conf")); + System.setProperty(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property(), System.getProperty("opensearch.path.conf")); + } + if (ProfileFileSystemSetting.AWS_CONFIG_FILE.getStringValue().isEmpty()) { + logger.info("setting aws.sharedCredentialsFile={}", System.getProperty("opensearch.path.conf")); + System.setProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), System.getProperty("opensearch.path.conf")); + } + } + + public MasterKeyProvider createMasterKeyProvider(CryptoMetadata cryptoMetadata) { + Settings cryptoSettings = Settings.builder().put(cryptoMetadata.settings()).normalizePrefix("kms.").build(); + String keyArn = KEY_ARN_SETTING.get(cryptoSettings); + if (!Strings.hasText(keyArn)) { + throw new IllegalArgumentException("Missing key_arn setting"); + } + + String kmsEncCtx = ENC_CTX_SETTING.get(cryptoSettings); + Map encCtx; + if (Strings.hasText(kmsEncCtx)) { + try { + encCtx = Arrays.stream(kmsEncCtx.split(",")) + .map(s -> s.split("=")) + .collect(Collectors.toMap(e -> e[0].trim(), e -> e[1].trim())); + } catch (Exception ex) { + throw new IllegalArgumentException(ENC_CTX_SETTING.getKey() + " Format should be: Name1=Value1, Name2=Value2"); + } + } else { + encCtx = new HashMap<>(); + } + + // Verify client creation is successful to early detect any failure. + try (AmazonKmsClientReference clientReference = client(cryptoMetadata)) { + clientReference.get(); + } + + return new KmsMasterKeyProvider(encCtx, keyArn, () -> client(cryptoMetadata)); + } +} diff --git a/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/SocketAccess.java b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/SocketAccess.java new file mode 100644 index 0000000000000..5b026c30017ca --- /dev/null +++ b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/SocketAccess.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import org.opensearch.SpecialPermission; + +import java.net.SocketPermission; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * This plugin uses aws libraries to connect to Aws services. For these remote calls the plugin needs + * {@link SocketPermission} 'connect' to establish connections. This class wraps the operations requiring access in + * {@link AccessController#doPrivileged(PrivilegedAction)} blocks. + */ +public final class SocketAccess { + + private SocketAccess() {} + + public static T doPrivileged(PrivilegedAction operation) { + SpecialPermission.check(); + return AccessController.doPrivileged(operation); + } + + public static void doPrivilegedVoid(Runnable action) { + SpecialPermission.check(); + AccessController.doPrivileged((PrivilegedAction) () -> { + action.run(); + return null; + }); + } +} diff --git a/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/package-info.java b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/package-info.java new file mode 100644 index 0000000000000..787adc32d8941 --- /dev/null +++ b/plugins/crypto-kms/src/main/java/org/opensearch/crypto/kms/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Crypto plugin to for encryption and decryption use cases. + */ +package org.opensearch.crypto.kms; diff --git a/plugins/crypto-kms/src/main/plugin-metadata/plugin-security.policy b/plugins/crypto-kms/src/main/plugin-metadata/plugin-security.policy new file mode 100644 index 0000000000000..46fb79010f9ef --- /dev/null +++ b/plugins/crypto-kms/src/main/plugin-metadata/plugin-security.policy @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +grant { + // needed because of problems in ClientConfiguration + // TODO: get these fixed in aws sdk + permission java.lang.RuntimePermission "accessDeclaredMembers"; + permission java.lang.RuntimePermission "getClassLoader"; + permission java.lang.RuntimePermission "setContextClassLoader"; + // Needed because of problems in kms client + // When no region is set on a kms client instance, the + // AWS SDK loads all known partitions from a JSON file and + // uses a Jackson's ObjectMapper for that: this one, in + // version 2.5.3 with the default binding options, tries + // to suppress access checks of ctor/field/method and thus + // requires this special permission. AWS must be fixed to + // uses Jackson correctly and have the correct modifiers + // on binded classes. + // TODO: get these fixed in aws sdk + // See https://github.com/aws/aws-sdk-java/issues/766 + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; + + permission java.lang.RuntimePermission "getClassLoader"; + + // kms client opens socket connections for to kms + permission java.net.SocketPermission "*", "connect,resolve"; + + // kms client set Authenticator for proxy username/password + permission java.net.NetPermission "setDefaultAuthenticator"; + + permission java.util.PropertyPermission "aws.sharedCredentialsFile", "read,write"; + permission java.util.PropertyPermission "aws.configFile", "read,write"; + permission java.util.PropertyPermission "aws.region", "read,write"; + permission java.util.PropertyPermission "aws.accessKeyId", "read,write"; + permission java.util.PropertyPermission "aws.secretAccessKey", "read,write"; + permission java.util.PropertyPermission "opensearch.path.conf", "read,write"; + + permission java.io.FilePermission "config", "read"; +}; diff --git a/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/AbstractAwsTestCase.java b/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/AbstractAwsTestCase.java new file mode 100644 index 0000000000000..3fe49f9d3b523 --- /dev/null +++ b/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/AbstractAwsTestCase.java @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; + +import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.io.PathUtils; +import org.opensearch.test.OpenSearchTestCase; + +import java.nio.file.Path; + +public abstract class AbstractAwsTestCase extends OpenSearchTestCase { + @Override + public void setUp() throws Exception { + super.setUp(); + setUpAwsProfile(); + } + + @Override + public void tearDown() throws Exception { + resetAwsProfile(); + super.tearDown(); + } + + private Path configPath() { + return PathUtils.get("config"); + } + + private String previousOpenSearchPathConf; + private String awsRegion; + private String awsAccessKeyId; + private String awsSecretAccessKey; + private String awsSharedCredentialsFile; + private String awsConfigFile; + + @SuppressForbidden(reason = "set predictable aws defaults") + private void setUpAwsProfile() throws Exception { + previousOpenSearchPathConf = SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); + awsRegion = SocketAccess.doPrivileged(() -> System.setProperty("aws.region", "us-west-2")); + awsAccessKeyId = SocketAccess.doPrivileged(() -> System.setProperty("aws.accessKeyId", "aws-access-key-id")); + awsSecretAccessKey = SocketAccess.doPrivileged(() -> System.setProperty("aws.secretAccessKey", "aws-secret-access-key")); + awsSharedCredentialsFile = System.getProperty(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property()); + awsConfigFile = System.getProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property()); + SocketAccess.doPrivilegedVoid(KmsService::setDefaultAwsProfilePath); + } + + @SuppressForbidden(reason = "reset aws settings") + private void resetAwsProfile() throws Exception { + resetPropertyValue("opensearch.path.conf", previousOpenSearchPathConf); + resetPropertyValue("aws.region", awsRegion); + resetPropertyValue("aws.accessKeyId", awsAccessKeyId); + resetPropertyValue("aws.secretAccessKey", awsSecretAccessKey); + resetPropertyValue(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property(), awsSharedCredentialsFile); + resetPropertyValue(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), awsConfigFile); + } + + @SuppressForbidden(reason = "reset aws settings") + private void resetPropertyValue(String key, String value) { + if (value != null) { + SocketAccess.doPrivileged(() -> System.setProperty(key, value)); + } else { + SocketAccess.doPrivileged(() -> System.clearProperty(key)); + } + } +} diff --git a/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/CryptoKmsClientSettingsTests.java b/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/CryptoKmsClientSettingsTests.java new file mode 100644 index 0000000000000..842d85faaa677 --- /dev/null +++ b/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/CryptoKmsClientSettingsTests.java @@ -0,0 +1,164 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; +import software.amazon.awssdk.regions.Region; + +import org.opensearch.common.settings.MockSecureSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.settings.SettingsException; +import org.opensearch.common.unit.TimeValue; + +import java.io.IOException; + +public class CryptoKmsClientSettingsTests extends AbstractAwsTestCase { + + public void testNondefaultClientCreatedBySettingItsSettings() { + final KmsClientSettings settings = KmsClientSettings.getClientSettings( + Settings.builder().put("kms.endpoint", "custom_endpoint").build() + ); + + assertEquals(settings.endpoint, "custom_endpoint"); + // Check if defaults are still present + assertNotNull(settings.proxyHost); + } + + public void testRejectionOfLoneAccessKey() throws IOException { + try (final MockSecureSettings secureSettings = new MockSecureSettings()) { + secureSettings.setString("kms.access_key", "aws_secret"); + final SettingsException e = expectThrows( + SettingsException.class, + () -> KmsClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) + ); + assertTrue(e.getMessage().contains("Setting [kms.access_key] is set but [kms.secret_key] is not")); + } + } + + public void testRejectionOfLoneSecretKey() throws IOException { + try (final MockSecureSettings secureSettings = new MockSecureSettings()) { + secureSettings.setString("kms.secret_key", "aws_key"); + final SettingsException e = expectThrows( + SettingsException.class, + () -> KmsClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) + ); + assertTrue(e.getMessage().contains("Setting [kms.secret_key] is set but [kms.access_key] is not")); + } + } + + public void testRejectionOfLoneSessionToken() throws IOException { + try (final MockSecureSettings secureSettings = new MockSecureSettings()) { + secureSettings.setString("kms.session_token", "aws_session_token"); + final SettingsException e = expectThrows( + SettingsException.class, + () -> KmsClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) + ); + assertTrue(e.getMessage().contains("Setting [kms.session_token] is set but [kms.access_key] and [kms.secret_key] are not")); + } + } + + public void testDefaultEndpoint() { + KmsClientSettings baseSettings = KmsClientSettings.getClientSettings(Settings.EMPTY); + assertEquals(baseSettings.endpoint, ""); + } + + public void testDefaultRegion() { + final Settings settings = Settings.builder().build(); + KmsClientSettings baseSettings = KmsClientSettings.getClientSettings(settings); + assertEquals(baseSettings.region, ""); + } + + public void testSpecificRegion() { + final Settings settings = Settings.builder().put(KmsClientSettings.REGION_SETTING.getKey(), "us-west-2").build(); + KmsClientSettings baseSettings = KmsClientSettings.getClientSettings(settings); + assertEquals(baseSettings.region, Region.US_WEST_2.toString()); + } + + public void testSpecificEndpoint() { + final Settings settings = Settings.builder().put(KmsClientSettings.ENDPOINT_SETTING.getKey(), "kms.endpoint").build(); + KmsClientSettings baseSettings = KmsClientSettings.getClientSettings(settings); + assertEquals(baseSettings.endpoint, "kms.endpoint"); + } + + public void testOverrideWithPrefixedMetadataSettings() { + overrideWithMetadataSettings("kms."); + } + + public void testOverrideWithNoPrefixMetadataSettings() { + overrideWithMetadataSettings(""); + } + + public void overrideWithMetadataSettings(String prefix) { + final MockSecureSettings secureSettings = new MockSecureSettings(); + String accessKey = "access_key", secretKey = "secret_key", sessionToken = "session_token"; + secureSettings.setString("kms.access_key", accessKey); + secureSettings.setString("kms.secret_key", secretKey); + secureSettings.setString("kms.session_token", sessionToken); + final KmsClientSettings baseSettings = KmsClientSettings.getClientSettings( + Settings.builder().setSecureSettings(secureSettings).build() + ); + + { + final KmsClientSettings refinedSettings = baseSettings.getMetadataSettings(Settings.EMPTY); + assertEquals(refinedSettings, baseSettings); + } + + { + final String endpoint = "some.host"; + final KmsClientSettings refinedSettings = baseSettings.getMetadataSettings( + Settings.builder().put(prefix + "endpoint", endpoint).build() + ); + assertEquals(refinedSettings.endpoint, endpoint); + validateCredsAreStillSame(refinedSettings, accessKey, secretKey, sessionToken); + } + + { + String region = "eu-west-1"; + final KmsClientSettings refinedSettings = baseSettings.getMetadataSettings( + Settings.builder().put(prefix + "region", region).build() + ); + assertEquals(refinedSettings.region, region); + validateCredsAreStillSame(refinedSettings, accessKey, secretKey, sessionToken); + } + + { + String proxyHost = "proxy-host"; + final KmsClientSettings refinedSettings = baseSettings.getMetadataSettings( + Settings.builder().put(prefix + "proxy.host", proxyHost).build() + ); + assertEquals(refinedSettings.proxyHost, proxyHost); + validateCredsAreStillSame(refinedSettings, accessKey, secretKey, sessionToken); + } + + { + int proxyPort = 70; + final KmsClientSettings refinedSettings = baseSettings.getMetadataSettings( + Settings.builder().put(prefix + "proxy.port", proxyPort).build() + ); + assertEquals(refinedSettings.proxyPort, proxyPort); + validateCredsAreStillSame(refinedSettings, accessKey, secretKey, sessionToken); + } + + { + TimeValue readTimeout = TimeValue.timeValueMillis(5000); + final KmsClientSettings refinedSettings = baseSettings.getMetadataSettings( + Settings.builder().put(prefix + "read_timeout", readTimeout).build() + ); + assertEquals(refinedSettings.readTimeoutMillis, readTimeout.getMillis()); + validateCredsAreStillSame(refinedSettings, accessKey, secretKey, sessionToken); + } + } + + private void validateCredsAreStillSame(KmsClientSettings refinedSettings, String accessKey, String secretKey, String sessionToken) { + AwsSessionCredentials credentials = (AwsSessionCredentials) refinedSettings.credentials; + assertEquals(credentials.accessKeyId(), accessKey); + assertEquals(credentials.secretAccessKey(), secretKey); + assertEquals(credentials.sessionToken(), sessionToken); + } +} diff --git a/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/KmsServiceTests.java b/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/KmsServiceTests.java new file mode 100644 index 0000000000000..1424cce473592 --- /dev/null +++ b/plugins/crypto-kms/src/test/java/org/opensearch/crypto/kms/KmsServiceTests.java @@ -0,0 +1,255 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.crypto.kms; + +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.retry.RetryPolicy; +import software.amazon.awssdk.http.apache.ProxyConfiguration; +import software.amazon.awssdk.services.kms.KmsClient; + +import org.opensearch.cluster.metadata.CryptoMetadata; +import org.opensearch.common.settings.MockSecureSettings; +import org.opensearch.common.settings.Settings; + +public class KmsServiceTests extends AbstractAwsTestCase { + private final CryptoMetadata cryptoMetadata = new CryptoMetadata("kp1", "kp2", Settings.EMPTY); + + public void testAWSDefaultConfiguration() { + try (KmsService kmsService = new KmsService()) { + // proxy configuration + final ProxyConfiguration proxyConfiguration = kmsService.buildProxyConfiguration( + KmsClientSettings.getClientSettings(Settings.EMPTY) + ); + + assertNull(proxyConfiguration.scheme()); + assertNull(proxyConfiguration.host()); + assertEquals(proxyConfiguration.port(), 0); + assertNull(proxyConfiguration.username()); + assertNull(proxyConfiguration.password()); + + // retry policy + RetryPolicy retryPolicyConfiguration = SocketAccess.doPrivileged(kmsService::buildRetryPolicy); + + assertEquals(retryPolicyConfiguration.numRetries().intValue(), 10); + + ClientOverrideConfiguration clientOverrideConfiguration = SocketAccess.doPrivileged(kmsService::buildOverrideConfiguration); + assertTrue(clientOverrideConfiguration.retryPolicy().isPresent()); + assertEquals(clientOverrideConfiguration.retryPolicy().get().numRetries().intValue(), 10); + } + } + + public void testAWSConfigurationWithAwsSettings() { + final MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("kms.proxy.username", "aws_proxy_username"); + secureSettings.setString("kms.proxy.password", "aws_proxy_password"); + + final Settings settings = Settings.builder() + // NOTE: a host cannot contain the _ character when parsed by URI, hence aws-proxy-host and not aws_proxy_host + .put("kms.proxy.host", "aws-proxy-host") + .put("kms.proxy.port", 8080) + .put("kms.read_timeout", "10s") + .setSecureSettings(secureSettings) + .build(); + + try (KmsService kmsService = new KmsService()) { + // proxy configuration + final ProxyConfiguration proxyConfiguration = SocketAccess.doPrivileged( + () -> kmsService.buildProxyConfiguration(KmsClientSettings.getClientSettings(settings)) + ); + + assertEquals(proxyConfiguration.host(), "aws-proxy-host"); + assertEquals(proxyConfiguration.port(), 8080); + assertEquals(proxyConfiguration.username(), "aws_proxy_username"); + assertEquals(proxyConfiguration.password(), "aws_proxy_password"); + + // retry policy + RetryPolicy retryPolicyConfiguration = SocketAccess.doPrivileged(kmsService::buildRetryPolicy); + assertEquals(retryPolicyConfiguration.numRetries().intValue(), 10); + + ClientOverrideConfiguration clientOverrideConfiguration = SocketAccess.doPrivileged(kmsService::buildOverrideConfiguration); + assertTrue(clientOverrideConfiguration.retryPolicy().isPresent()); + assertEquals(clientOverrideConfiguration.retryPolicy().get().numRetries().intValue(), 10); + } + } + + public void testClientSettingsReInit() { + final MockSecureSettings mockSecure1 = new MockSecureSettings(); + mockSecure1.setString(KmsClientSettings.ACCESS_KEY_SETTING.getKey(), "kms_access_1"); + mockSecure1.setString(KmsClientSettings.SECRET_KEY_SETTING.getKey(), "kms_secret_1"); + final boolean mockSecure1HasSessionToken = randomBoolean(); + if (mockSecure1HasSessionToken) { + mockSecure1.setString(KmsClientSettings.SESSION_TOKEN_SETTING.getKey(), "kms_session_token_1"); + } + mockSecure1.setString(KmsClientSettings.PROXY_USERNAME_SETTING.getKey(), "proxy_username_1"); + mockSecure1.setString(KmsClientSettings.PROXY_PASSWORD_SETTING.getKey(), "proxy_password_1"); + final Settings settings1 = Settings.builder() + .put(KmsClientSettings.PROXY_HOST_SETTING.getKey(), "proxy-host-1") + .put(KmsClientSettings.PROXY_PORT_SETTING.getKey(), 881) + .put(KmsClientSettings.REGION_SETTING.getKey(), "kms_region") + .put(KmsClientSettings.ENDPOINT_SETTING.getKey(), "kms_endpoint_1") + .setSecureSettings(mockSecure1) + .build(); + final MockSecureSettings mockSecure2 = new MockSecureSettings(); + mockSecure2.setString(KmsClientSettings.ACCESS_KEY_SETTING.getKey(), "kms_access_2"); + mockSecure2.setString(KmsClientSettings.SECRET_KEY_SETTING.getKey(), "kms_secret_2"); + final boolean mockSecure2HasSessionToken = randomBoolean(); + if (mockSecure2HasSessionToken) { + mockSecure2.setString(KmsClientSettings.SESSION_TOKEN_SETTING.getKey(), "kms_session_token_2"); + } + mockSecure2.setString(KmsClientSettings.PROXY_USERNAME_SETTING.getKey(), "proxy_username_2"); + mockSecure2.setString(KmsClientSettings.PROXY_PASSWORD_SETTING.getKey(), "proxy_password_2"); + final Settings settings2 = Settings.builder() + .put(KmsClientSettings.PROXY_HOST_SETTING.getKey(), "proxy-host-2") + .put(KmsClientSettings.PROXY_PORT_SETTING.getKey(), 882) + .put(KmsClientSettings.REGION_SETTING.getKey(), "kms_region") + .put(KmsClientSettings.ENDPOINT_SETTING.getKey(), "kms_endpoint_2") + .setSecureSettings(mockSecure2) + .build(); + try (CryptoKmsPluginMockTest plugin = new CryptoKmsPluginMockTest(settings1)) { + try (AmazonKmsClientReference clientReference = plugin.kmsService.client(cryptoMetadata)) { + { + final MockKmsClientTest mockKmsClientTest = (MockKmsClientTest) clientReference.get(); + assertEquals(mockKmsClientTest.endpoint, "kms_endpoint_1"); + + final AwsCredentials credentials = mockKmsClientTest.credentials.resolveCredentials(); + assertEquals(credentials.accessKeyId(), "kms_access_1"); + assertEquals(credentials.secretAccessKey(), "kms_secret_1"); + if (mockSecure1HasSessionToken) { + assertTrue(credentials instanceof AwsSessionCredentials); + assertEquals(((AwsSessionCredentials) credentials).sessionToken(), "kms_session_token_1"); + } else { + assertTrue(credentials instanceof AwsBasicCredentials); + } + + assertEquals( + mockKmsClientTest.proxyConfiguration.toString(), + "ProxyConfiguration(endpoint=https://proxy-host-1:881, username=proxy_username_1, preemptiveBasicAuthenticationEnabled=false)" + ); + assertEquals(mockKmsClientTest.proxyConfiguration.host(), "proxy-host-1"); + assertEquals(mockKmsClientTest.proxyConfiguration.port(), 881); + assertEquals(mockKmsClientTest.proxyConfiguration.username(), "proxy_username_1"); + assertEquals(mockKmsClientTest.proxyConfiguration.password(), "proxy_password_1"); + } + // reload secure settings2 + plugin.reload(settings2); + // client is not released, it is still using the old settings + { + final MockKmsClientTest mockKmsClientTest = (MockKmsClientTest) clientReference.get(); + assertEquals(mockKmsClientTest.endpoint, "kms_endpoint_1"); + + final AwsCredentials credentials = ((MockKmsClientTest) clientReference.get()).credentials.resolveCredentials(); + if (mockSecure1HasSessionToken) { + assertTrue(credentials instanceof AwsSessionCredentials); + assertEquals(((AwsSessionCredentials) credentials).sessionToken(), "kms_session_token_1"); + } else { + assertTrue(credentials instanceof AwsBasicCredentials); + } + + assertEquals( + mockKmsClientTest.proxyConfiguration.toString(), + "ProxyConfiguration(endpoint=https://proxy-host-1:881, username=proxy_username_1, preemptiveBasicAuthenticationEnabled=false)" + ); + assertEquals(mockKmsClientTest.proxyConfiguration.host(), "proxy-host-1"); + assertEquals(mockKmsClientTest.proxyConfiguration.port(), 881); + assertEquals(mockKmsClientTest.proxyConfiguration.username(), "proxy_username_1"); + assertEquals(mockKmsClientTest.proxyConfiguration.password(), "proxy_password_1"); + } + } + try (AmazonKmsClientReference clientReference = plugin.kmsService.client(cryptoMetadata)) { + final MockKmsClientTest mockKmsClientTest = (MockKmsClientTest) clientReference.get(); + assertEquals(mockKmsClientTest.endpoint, "kms_endpoint_2"); + + final AwsCredentials credentials = ((MockKmsClientTest) clientReference.get()).credentials.resolveCredentials(); + assertEquals(credentials.accessKeyId(), "kms_access_2"); + assertEquals(credentials.secretAccessKey(), "kms_secret_2"); + if (mockSecure2HasSessionToken) { + assertTrue(credentials instanceof AwsSessionCredentials); + assertEquals(((AwsSessionCredentials) credentials).sessionToken(), "kms_session_token_2"); + } else { + assertTrue(credentials instanceof AwsBasicCredentials); + } + + assertEquals( + mockKmsClientTest.proxyConfiguration.toString(), + "ProxyConfiguration(endpoint=https://proxy-host-2:882, username=proxy_username_2, preemptiveBasicAuthenticationEnabled=false)" + ); + assertEquals(mockKmsClientTest.proxyConfiguration.host(), "proxy-host-2"); + assertEquals(mockKmsClientTest.proxyConfiguration.port(), 882); + assertEquals(mockKmsClientTest.proxyConfiguration.username(), "proxy_username_2"); + assertEquals(mockKmsClientTest.proxyConfiguration.password(), "proxy_password_2"); + } + } + } + + static class CryptoKmsPluginMockTest extends CryptoKmsPlugin { + + CryptoKmsPluginMockTest(Settings settings) { + super(settings, new KmsService() { + @Override + protected KmsClient buildClient( + AwsCredentialsProvider credentials, + ProxyConfiguration proxyConfiguration, + ClientOverrideConfiguration overrideConfiguration, + String endpoint, + String region, + long readTimeoutMillis + ) { + return new MockKmsClientTest( + credentials, + proxyConfiguration, + overrideConfiguration, + endpoint, + region, + readTimeoutMillis + ); + } + }); + } + } + + static class MockKmsClientTest implements KmsClient { + + String endpoint; + final String region; + final AwsCredentialsProvider credentials; + final ClientOverrideConfiguration clientOverrideConfiguration; + final ProxyConfiguration proxyConfiguration; + final long readTimeoutMillis; + + MockKmsClientTest( + AwsCredentialsProvider credentials, + ProxyConfiguration proxyConfiguration, + ClientOverrideConfiguration clientOverrideConfiguration, + String endpoint, + String region, + long readTimeoutMillis + ) { + this.credentials = credentials; + this.proxyConfiguration = proxyConfiguration; + this.clientOverrideConfiguration = clientOverrideConfiguration; + this.endpoint = endpoint; + this.region = region; + this.readTimeoutMillis = readTimeoutMillis; + } + + @Override + public String serviceName() { + return "kms"; + } + + @Override + public void close() { + // ignore + } + } +} diff --git a/plugins/crypto-kms/src/yamlRestTest/java/org/opensearch/kms/CloudAwsClientYamlTestSuiteIT.java b/plugins/crypto-kms/src/yamlRestTest/java/org/opensearch/kms/CloudAwsClientYamlTestSuiteIT.java new file mode 100644 index 0000000000000..87e6691e40855 --- /dev/null +++ b/plugins/crypto-kms/src/yamlRestTest/java/org/opensearch/kms/CloudAwsClientYamlTestSuiteIT.java @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.kms; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; +import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; + +public class CloudAwsClientYamlTestSuiteIT extends OpenSearchClientYamlSuiteTestCase { + + public CloudAwsClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) { + super(testCandidate); + } + + @ParametersFactory + public static Iterable parameters() throws Exception { + return OpenSearchClientYamlSuiteTestCase.createParameters(); + } +} diff --git a/plugins/crypto-kms/src/yamlRestTest/resources/rest-api-spec/test/kms/10_basic.yml b/plugins/crypto-kms/src/yamlRestTest/resources/rest-api-spec/test/kms/10_basic.yml new file mode 100644 index 0000000000000..3d6c3056a6975 --- /dev/null +++ b/plugins/crypto-kms/src/yamlRestTest/resources/rest-api-spec/test/kms/10_basic.yml @@ -0,0 +1,16 @@ +# Integration tests for KMS component +# +"KMS loaded": + - skip: + reason: "contains is a newly added assertion" + features: contains + - do: + cluster.state: {} + + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } + + - do: + nodes.info: {} + + - contains: { nodes.$cluster_manager.plugins: { name: crypto-kms } } diff --git a/plugins/discovery-azure-classic/build.gradle b/plugins/discovery-azure-classic/build.gradle index 28cbc647ac31a..1fed446016647 100644 --- a/plugins/discovery-azure-classic/build.gradle +++ b/plugins/discovery-azure-classic/build.gradle @@ -53,13 +53,13 @@ dependencies { api "org.apache.logging.log4j:log4j-1.2-api:${versions.log4j}" api "commons-codec:commons-codec:${versions.commonscodec}" api "commons-lang:commons-lang:2.6" - api "commons-io:commons-io:2.11.0" + api "commons-io:commons-io:2.13.0" api 'javax.mail:mail:1.4.7' api 'javax.inject:javax.inject:1' api "com.sun.jersey:jersey-client:${versions.jersey}" api "com.sun.jersey:jersey-core:${versions.jersey}" api "com.sun.jersey:jersey-json:${versions.jersey}" - api 'org.codehaus.jettison:jettison:1.1' + api "org.codehaus.jettison:jettison:${versions.jettison}" api 'com.sun.xml.bind:jaxb-impl:2.2.3-1' // HACK: javax.xml.bind was removed from default modules in java 9, so we pull the api in here, diff --git a/plugins/discovery-azure-classic/licenses/commons-codec-1.13.jar.sha1 b/plugins/discovery-azure-classic/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/discovery-azure-classic/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/commons-codec-1.15.jar.sha1 b/plugins/discovery-azure-classic/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/commons-io-2.11.0.jar.sha1 b/plugins/discovery-azure-classic/licenses/commons-io-2.11.0.jar.sha1 deleted file mode 100644 index 8adec30bade49..0000000000000 --- a/plugins/discovery-azure-classic/licenses/commons-io-2.11.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a2503f302b11ebde7ebc3df41daebe0e4eea3689 \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/commons-io-2.13.0.jar.sha1 b/plugins/discovery-azure-classic/licenses/commons-io-2.13.0.jar.sha1 new file mode 100644 index 0000000000000..c165136eb5822 --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/commons-io-2.13.0.jar.sha1 @@ -0,0 +1 @@ +8bb2bc9b4df17e2411533a0708a69f983bf5e83b \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/commons-logging-1.1.3.jar.sha1 b/plugins/discovery-azure-classic/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index c8756c438320f..0000000000000 --- a/plugins/discovery-azure-classic/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f diff --git a/plugins/discovery-azure-classic/licenses/commons-logging-1.2.jar.sha1 b/plugins/discovery-azure-classic/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/commons-logging-LICENSE.txt b/plugins/discovery-azure-classic/licenses/commons-logging-LICENSE.txt index d645695673349..57bc88a15a0ee 100644 --- a/plugins/discovery-azure-classic/licenses/commons-logging-LICENSE.txt +++ b/plugins/discovery-azure-classic/licenses/commons-logging-LICENSE.txt @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -200,3 +199,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + diff --git a/plugins/discovery-azure-classic/licenses/commons-logging-NOTICE.txt b/plugins/discovery-azure-classic/licenses/commons-logging-NOTICE.txt index d3d6e140ce4f3..72eb32a902458 100644 --- a/plugins/discovery-azure-classic/licenses/commons-logging-NOTICE.txt +++ b/plugins/discovery-azure-classic/licenses/commons-logging-NOTICE.txt @@ -1,5 +1,5 @@ -Apache Commons Logging -Copyright 2003-2014 The Apache Software Foundation +Apache Commons CLI +Copyright 2001-2009 The Apache Software Foundation -This product includes software developed at +This product includes software developed by The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/discovery-azure-classic/licenses/httpclient-4.5.13.jar.sha1 b/plugins/discovery-azure-classic/licenses/httpclient-4.5.13.jar.sha1 deleted file mode 100644 index 3281e21595b39..0000000000000 --- a/plugins/discovery-azure-classic/licenses/httpclient-4.5.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/httpclient-4.5.14.jar.sha1 b/plugins/discovery-azure-classic/licenses/httpclient-4.5.14.jar.sha1 new file mode 100644 index 0000000000000..66e05851c2e3c --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/httpclient-4.5.14.jar.sha1 @@ -0,0 +1 @@ +1194890e6f56ec29177673f2f12d0b8e627dec98 \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/httpcore-4.4.12.jar.sha1 b/plugins/discovery-azure-classic/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/plugins/discovery-azure-classic/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/httpcore-4.4.16.jar.sha1 b/plugins/discovery-azure-classic/licenses/httpcore-4.4.16.jar.sha1 new file mode 100644 index 0000000000000..172110694b5bd --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/httpcore-4.4.16.jar.sha1 @@ -0,0 +1 @@ +51cf043c87253c9f58b539c9f7e44c8894223850 \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/jettison-1.1.jar.sha1 b/plugins/discovery-azure-classic/licenses/jettison-1.1.jar.sha1 deleted file mode 100644 index 53133f3b018e6..0000000000000 --- a/plugins/discovery-azure-classic/licenses/jettison-1.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1a01a2a1218fcf9faa2cc2a6ced025bdea687262 diff --git a/plugins/discovery-azure-classic/licenses/jettison-1.5.4.jar.sha1 b/plugins/discovery-azure-classic/licenses/jettison-1.5.4.jar.sha1 new file mode 100644 index 0000000000000..a87b7691bfce8 --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/jettison-1.5.4.jar.sha1 @@ -0,0 +1 @@ +174ca56c411b05aec323d8f53e66709c0d28b337 \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/log4j-1.2-api-2.17.1.jar.sha1 b/plugins/discovery-azure-classic/licenses/log4j-1.2-api-2.17.1.jar.sha1 deleted file mode 100644 index 23aa5c60bd596..0000000000000 --- a/plugins/discovery-azure-classic/licenses/log4j-1.2-api-2.17.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -db3a7e7f07e878b92ac4a8f1100bee8325d5713a \ No newline at end of file diff --git a/plugins/discovery-azure-classic/licenses/log4j-1.2-api-2.20.0.jar.sha1 b/plugins/discovery-azure-classic/licenses/log4j-1.2-api-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..9829576d38ce0 --- /dev/null +++ b/plugins/discovery-azure-classic/licenses/log4j-1.2-api-2.20.0.jar.sha1 @@ -0,0 +1 @@ +689151374756cb809cb029f2501015bdc7733179 \ No newline at end of file diff --git a/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/cloud/azure/classic/AbstractAzureComputeServiceTestCase.java b/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/cloud/azure/classic/AbstractAzureComputeServiceTestCase.java index f95f358532bac..9c4b577df3e5f 100644 --- a/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/cloud/azure/classic/AbstractAzureComputeServiceTestCase.java +++ b/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/cloud/azure/classic/AbstractAzureComputeServiceTestCase.java @@ -39,7 +39,6 @@ import com.microsoft.windowsazure.management.compute.models.RoleInstance; import com.microsoft.windowsazure.management.compute.models.RoleInstancePowerState; import org.opensearch.action.admin.cluster.node.info.NodesInfoResponse; - import org.opensearch.cloud.azure.classic.management.AzureComputeService; import org.opensearch.cloud.azure.classic.management.AzureComputeService.Discovery; import org.opensearch.cloud.azure.classic.management.AzureComputeService.Management; @@ -47,13 +46,12 @@ import org.opensearch.common.network.NetworkAddress; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.discovery.azure.classic.AzureSeedHostsProvider; import org.opensearch.plugin.discovery.azure.classic.AzureDiscoveryPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.transport.TransportService; - import org.junit.After; import java.util.ArrayList; @@ -63,7 +61,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import static org.opensearch.common.util.CollectionUtils.newSingletonArrayList; +import static org.opensearch.core.common.util.CollectionUtils.newSingletonArrayList; import static org.opensearch.discovery.DiscoveryModule.DISCOVERY_SEED_PROVIDERS_SETTING; public abstract class AbstractAzureComputeServiceTestCase extends OpenSearchIntegTestCase { diff --git a/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureDiscoveryClusterFormationTests.java b/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureDiscoveryClusterFormationTests.java index c4f533fd2ee36..ad622e68f5ccb 100644 --- a/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureDiscoveryClusterFormationTests.java +++ b/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureDiscoveryClusterFormationTests.java @@ -32,18 +32,18 @@ package org.opensearch.discovery.azure.classic; -import com.microsoft.windowsazure.management.compute.models.DeploymentSlot; -import com.microsoft.windowsazure.management.compute.models.DeploymentStatus; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsServer; + +import com.microsoft.windowsazure.management.compute.models.DeploymentSlot; +import com.microsoft.windowsazure.management.compute.models.DeploymentStatus; import org.apache.logging.log4j.LogManager; -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.cloud.azure.classic.management.AzureComputeService; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.io.FileSystemUtils; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; +import org.opensearch.core.util.FileSystemUtils; import org.opensearch.discovery.DiscoveryModule; import org.opensearch.env.Environment; import org.opensearch.node.Node; @@ -63,10 +63,12 @@ import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; +import java.lang.Runtime.Version; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; @@ -295,15 +297,13 @@ private static SSLContext getSSLContext() throws Exception { * 12.0.1 so we pin to TLSv1.2 when running on an earlier JDK */ private static String getProtocol() { - if (JavaVersion.current().compareTo(JavaVersion.parse("11")) < 0) { - return "TLS"; - } else if (JavaVersion.current().compareTo(JavaVersion.parse("12")) < 0) { + if (Runtime.version().compareTo(Version.parse("12")) < 0) { return "TLSv1.2"; } else { - JavaVersion full = AccessController.doPrivileged( - (PrivilegedAction) () -> JavaVersion.parse(System.getProperty("java.version")) + Version full = AccessController.doPrivileged( + (PrivilegedAction) () -> Version.parse(System.getProperty("java.version")) ); - if (full.compareTo(JavaVersion.parse("12.0.1")) < 0) { + if (full.compareTo(Version.parse("12.0.1")) < 0) { return "TLSv1.2"; } } diff --git a/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureSimpleTests.java b/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureSimpleTests.java index f4268443a707a..4c2dda180eda6 100644 --- a/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureSimpleTests.java +++ b/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureSimpleTests.java @@ -50,7 +50,9 @@ public void testOneNodeShouldRunUsingPrivateIp() { final String node1 = internalCluster().startNode(settings); registerAzureNode(node1); - assertNotNull(client().admin().cluster().prepareState().setMasterNodeTimeout("1s").get().getState().nodes().getMasterNodeId()); + assertNotNull( + client().admin().cluster().prepareState().setClusterManagerNodeTimeout("1s").get().getState().nodes().getClusterManagerNodeId() + ); // We expect having 1 node as part of the cluster, let's test that assertNumberOfNodes(1); @@ -63,7 +65,9 @@ public void testOneNodeShouldRunUsingPublicIp() { final String node1 = internalCluster().startNode(settings); registerAzureNode(node1); - assertNotNull(client().admin().cluster().prepareState().setMasterNodeTimeout("1s").get().getState().nodes().getMasterNodeId()); + assertNotNull( + client().admin().cluster().prepareState().setClusterManagerNodeTimeout("1s").get().getState().nodes().getClusterManagerNodeId() + ); // We expect having 1 node as part of the cluster, let's test that assertNumberOfNodes(1); diff --git a/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureTwoStartedNodesTests.java b/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureTwoStartedNodesTests.java index d8ea8a91fd21d..10e8a65e906cc 100644 --- a/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureTwoStartedNodesTests.java +++ b/plugins/discovery-azure-classic/src/internalClusterTest/java/org/opensearch/discovery/azure/classic/AzureTwoStartedNodesTests.java @@ -53,12 +53,16 @@ public void testTwoNodesShouldRunUsingPrivateOrPublicIp() { logger.info("--> start first node"); final String node1 = internalCluster().startNode(settings); registerAzureNode(node1); - assertNotNull(client().admin().cluster().prepareState().setMasterNodeTimeout("1s").get().getState().nodes().getMasterNodeId()); + assertNotNull( + client().admin().cluster().prepareState().setClusterManagerNodeTimeout("1s").get().getState().nodes().getClusterManagerNodeId() + ); logger.info("--> start another node"); final String node2 = internalCluster().startNode(settings); registerAzureNode(node2); - assertNotNull(client().admin().cluster().prepareState().setMasterNodeTimeout("1s").get().getState().nodes().getMasterNodeId()); + assertNotNull( + client().admin().cluster().prepareState().setClusterManagerNodeTimeout("1s").get().getState().nodes().getClusterManagerNodeId() + ); // We expect having 2 nodes as part of the cluster, let's test that assertNumberOfNodes(2); diff --git a/plugins/discovery-azure-classic/src/main/java/org/opensearch/cloud/azure/classic/management/AzureComputeServiceImpl.java b/plugins/discovery-azure-classic/src/main/java/org/opensearch/cloud/azure/classic/management/AzureComputeServiceImpl.java index 6a8b8d83f539b..1bac80e576199 100644 --- a/plugins/discovery-azure-classic/src/main/java/org/opensearch/cloud/azure/classic/management/AzureComputeServiceImpl.java +++ b/plugins/discovery-azure-classic/src/main/java/org/opensearch/cloud/azure/classic/management/AzureComputeServiceImpl.java @@ -32,12 +32,6 @@ package org.opensearch.cloud.azure.classic.management; -import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.ServiceLoader; - import com.microsoft.windowsazure.Configuration; import com.microsoft.windowsazure.core.Builder; import com.microsoft.windowsazure.core.DefaultBuilder; @@ -51,10 +45,16 @@ import org.opensearch.OpenSearchException; import org.opensearch.SpecialPermission; import org.opensearch.cloud.azure.classic.AzureServiceRemoteException; -import org.opensearch.common.Strings; -import org.opensearch.common.component.AbstractLifecycleComponent; +import org.opensearch.common.lifecycle.AbstractLifecycleComponent; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; + +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ServiceLoader; public class AzureComputeServiceImpl extends AbstractLifecycleComponent implements AzureComputeService { private static final Logger logger = LogManager.getLogger(AzureComputeServiceImpl.class); diff --git a/plugins/discovery-azure-classic/src/main/java/org/opensearch/discovery/azure/classic/AzureSeedHostsProvider.java b/plugins/discovery-azure-classic/src/main/java/org/opensearch/discovery/azure/classic/AzureSeedHostsProvider.java index 0fe6904e83242..9d5958fa2d40d 100644 --- a/plugins/discovery-azure-classic/src/main/java/org/opensearch/discovery/azure/classic/AzureSeedHostsProvider.java +++ b/plugins/discovery-azure-classic/src/main/java/org/opensearch/discovery/azure/classic/AzureSeedHostsProvider.java @@ -37,20 +37,19 @@ import com.microsoft.windowsazure.management.compute.models.HostedServiceGetDetailedResponse; import com.microsoft.windowsazure.management.compute.models.InstanceEndpoint; import com.microsoft.windowsazure.management.compute.models.RoleInstance; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.cloud.azure.classic.AzureServiceDisableException; import org.opensearch.cloud.azure.classic.AzureServiceRemoteException; import org.opensearch.cloud.azure.classic.management.AzureComputeService; import org.opensearch.cloud.azure.classic.management.AzureComputeService.Discovery; -import org.opensearch.common.Strings; import org.opensearch.common.network.InetAddresses; import org.opensearch.common.network.NetworkAddress; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.discovery.SeedHostsProvider; import org.opensearch.transport.TransportService; diff --git a/plugins/discovery-azure-classic/src/yamlRestTest/resources/rest-api-spec/test/discovery_azure_classic/10_basic.yml b/plugins/discovery-azure-classic/src/yamlRestTest/resources/rest-api-spec/test/discovery_azure_classic/10_basic.yml index 39aa9929f8a92..73523d17bbfb9 100644 --- a/plugins/discovery-azure-classic/src/yamlRestTest/resources/rest-api-spec/test/discovery_azure_classic/10_basic.yml +++ b/plugins/discovery-azure-classic/src/yamlRestTest/resources/rest-api-spec/test/discovery_azure_classic/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: discovery-azure-classic } } + - contains: { nodes.$cluster_manager.plugins: { name: discovery-azure-classic } } diff --git a/plugins/discovery-ec2/README.md b/plugins/discovery-ec2/README.md new file mode 100644 index 0000000000000..6daeb6305b988 --- /dev/null +++ b/plugins/discovery-ec2/README.md @@ -0,0 +1,94 @@ +# discovery-ec2 + +The discovery-ec2 plugin allows OpenSearch to find the master-eligible nodes in a cluster running on AWS EC2 by querying the AWS Instance Metadata Service ([IMDS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)) API for the addresses of the EC2 instances running these nodes. + +This is typically configured as follows in `opensearch.yml`. + +```yml +cluster.initial_cluster_manager_nodes: ["seed"] +discovery.seed_providers: ec2 +discovery.ec2.tag.role: my-cluster-manager +``` + +The implementation in the discovery-ec2 plugin queries for instances of a given type (e.g. private or public IP), with a tag, or running in a specific availability zone. + +## Testing + +### Unit and Integration Tests + +``` +./gradlew :plugins:discovery-ec2:check +``` + +### Manual Testing + +The following instructions manually exercise the plugin. + +Setup a 5-node (Ubuntu) cluster on EC2, with 3 of them tagged with `role: my-cluster-manager`, and a custom TCP rule to expose ports 9200 to 9300 to allow TCP traffic. Default EC2 configuration only allows SSH TCP traffic and hence only exposes port 22. + +Set the following properties. + +- `network.host`: Set this to the IP of the eth0 network adapter, can be fetched by `ipconfig` or by running `hostname -I` on Linux hosts. +- `discovery.seed_hosts`: List of all nodes in the cluster. +- `cluster.initial_cluster_manager_nodes`: List of all initial cluster manager nodes. +- `discovery.seed_providers`: Set this to `ec2` for EC2 IMDS based discovery. +- `discovery.ec2.tag.role: my-cluster-manager`: Filter out only those nodes with value `my-cluster-manager` for the `role` tag. +- `discovery.ec2.region`: Optionally set to your region, e.g. `us-east-1`, by default the plugin uses the region of the current EC2 instance. + +While sending the request to IMDS, specified discovery settings for finding master-eligible nodes are being set. You will see the following in the logs. + +``` +[2023-05-31T15:22:39,274][DEBUG][o.o.d.e.AwsEc2SeedHostsProvider] [ip-172-31-73-184] using host_type [private_ip], tags [{role=[my-cluster-manager]}], groups [[]] with any_group [true], availability_zones [[]] +``` + +The nodes getting added as eligible masters are ones with the role tag set to `my-cluster-manager`. + +``` +[2023-05-31T15:23:03,676][TRACE][o.o.d.e.AwsEc2SeedHostsProvider] [ip-172-31-73-184] finding seed nodes... +[2023-05-31T15:23:03,677][TRACE][o.o.d.e.AwsEc2SeedHostsProvider] [ip-172-31-73-184] adding i-01fa1736e8566c693, address 172.31.73.184, transport_address 172.31.73.184:9300 +[2023-05-31T15:23:03,677][TRACE][o.o.d.e.AwsEc2SeedHostsProvider] [ip-172-31-73-184] adding i-03d70a4521045cc3b, address 172.31.74.169, transport_address 172.31.74.169:9300 +[2023-05-31T15:23:03,677][TRACE][o.o.d.e.AwsEc2SeedHostsProvider] [ip-172-31-73-184] adding i-0c6ffdd10ebd3c2f1, address 172.31.74.156, transport_address 172.31.74.156:9300 +``` + +## Troubleshooting + +### Trace Level Logs + +Enable `TRACE`-level logging in `opensearch.yml` to see more output. + +```yml +logger.org.opensearch.discovery.ec2: trace +``` + +### Sample IMDS Query + +You may need to query IMDS without running OpenSearch. Use [this sample](https://github.com/dblock/aws-imds-sample) or copy the following code that makes a query to IMDS that looks for `running`, `pending` or `stopped` instances. + +```java +DefaultCredentialsProvider credentialsProviderChain = DefaultCredentialsProvider.create(); + +RetryPolicy retryPolicy = RetryPolicy.builder() + .numRetries(10) + .build(); + +Ec2Client client = Ec2Client.builder() + .httpClientBuilder(ApacheHttpClient.builder()) + .credentialsProvider(credentialsProviderChain) + .build(); + +DescribeInstancesRequest describeInstancesRequest = DescribeInstancesRequest.builder() + .filters( + Filter.builder() + .name("instance-state-name") + .values("running", "pending", "stopped") + .build() + ).build(); + +DescribeInstancesResponse describeInstancesResponse = client.describeInstances(describeInstancesRequest); +for (final Reservation reservation : describeInstancesResponse.reservations()) { + System.out.println(reservation.reservationId()); + for (final Instance instance : reservation.instances()) { + System.out.println("\t" + instance.publicDnsName()); + } +} +``` diff --git a/plugins/discovery-ec2/build.gradle b/plugins/discovery-ec2/build.gradle index 7998e0861c7b1..9c9f64f09b915 100644 --- a/plugins/discovery-ec2/build.gradle +++ b/plugins/discovery-ec2/build.gradle @@ -38,20 +38,33 @@ opensearchplugin { classname 'org.opensearch.discovery.ec2.Ec2DiscoveryPlugin' } -versions << [ - 'aws': '1.11.749' -] - dependencies { - api "com.amazonaws:aws-java-sdk-ec2:${versions.aws}" - api "com.amazonaws:aws-java-sdk-core:${versions.aws}" + api "software.amazon.awssdk:sdk-core:${versions.aws}" + api "software.amazon.awssdk:aws-core:${versions.aws}" + api "software.amazon.awssdk:utils:${versions.aws}" + api "software.amazon.awssdk:auth:${versions.aws}" + api "software.amazon.awssdk:ec2:${versions.aws}" + api "software.amazon.awssdk:http-client-spi:${versions.aws}" + api "software.amazon.awssdk:apache-client:${versions.aws}" + api "software.amazon.awssdk:regions:${versions.aws}" + api "software.amazon.awssdk:profiles:${versions.aws}" + api "software.amazon.awssdk:endpoints-spi:${versions.aws}" + api "software.amazon.awssdk:annotations:${versions.aws}" + api "software.amazon.awssdk:metrics-spi:${versions.aws}" + api "software.amazon.awssdk:json-utils:${versions.aws}" + api "software.amazon.awssdk:protocol-core:${versions.aws}" + api "software.amazon.awssdk:aws-query-protocol:${versions.aws}" + api "software.amazon.awssdk:aws-json-protocol:${versions.aws}" + api "software.amazon.awssdk:third-party-jackson-core:${versions.aws}" api "org.apache.httpcomponents:httpclient:${versions.httpclient}" api "org.apache.httpcomponents:httpcore:${versions.httpcore}" api "commons-logging:commons-logging:${versions.commonslogging}" api "org.apache.logging.log4j:log4j-1.2-api:${versions.log4j}" + api "org.slf4j:slf4j-api:${versions.slf4j}" api "commons-codec:commons-codec:${versions.commonscodec}" - api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}" api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" + api "org.reactivestreams:reactive-streams:${versions.reactivestreams}" } restResources { @@ -61,7 +74,7 @@ restResources { } tasks.named("dependencyLicenses").configure { - mapping from: /aws-java-sdk-.*/, to: 'aws-java-sdk' + mapping from: /software.amazon.awssdk-.*/, to: 'software.amazon.awssdk' mapping from: /jackson-.*/, to: 'jackson' } @@ -94,7 +107,8 @@ tasks.register("writeTestJavaPolicy") { " permission org.bouncycastle.crypto.CryptoServicesPermission \"exportSecretKey\";", " permission org.bouncycastle.crypto.CryptoServicesPermission \"exportPrivateKey\";", " permission java.io.FilePermission \"\${javax.net.ssl.trustStore}\", \"read\";", - " permission java.util.PropertyPermission \"com.amazonaws.sdk.ec2MetadataServiceEndpointOverride\", \"write\";", + " permission java.util.PropertyPermission \"aws.ec2MetadataServiceEndpoint\", \"write\";", + " permission java.io.FilePermission \"config\", \"read\";", "};" ].join("\n") ) @@ -102,7 +116,8 @@ tasks.register("writeTestJavaPolicy") { javaPolicy.write( [ "grant {", - " permission java.util.PropertyPermission \"com.amazonaws.sdk.ec2MetadataServiceEndpointOverride\", \"write\";", + " permission java.util.PropertyPermission \"aws.ec2MetadataServiceEndpoint\", \"write\";", + " permission java.io.FilePermission \"config\", \"read\";", "};" ].join("\n")) } @@ -114,7 +129,7 @@ tasks.named("test").configure { // this is needed for insecure plugins, remove if possible! systemProperty 'tests.artifact', project.name - // Setting a custom policy to manipulate com.amazonaws.sdk.ec2MetadataServiceEndpointOverride system property + // Setting a custom policy to manipulate aws.ec2MetadataServiceEndpoint system property // it is better rather disable security manager at all with `systemProperty 'tests.security.manager', 'false'` if (BuildParams.inFipsJvm){ // Using the key==value format to override default JVM security settings and policy @@ -132,30 +147,17 @@ tasks.named("check").configure { tasks.named("thirdPartyAudit").configure { ignoreMissingClasses( - // classes are missing - 'javax.jms.Message', - 'com.amazonaws.jmespath.JmesPathExpression', - 'com.amazonaws.jmespath.ObjectMapperSingleton', - 'software.amazon.ion.IonReader', - 'software.amazon.ion.IonSystem', - 'software.amazon.ion.IonType', - 'software.amazon.ion.IonWriter', - 'software.amazon.ion.Timestamp', - 'software.amazon.ion.system.IonBinaryWriterBuilder', - 'software.amazon.ion.system.IonSystemBuilder', - 'software.amazon.ion.system.IonTextWriterBuilder', - 'software.amazon.ion.system.IonWriterBuilder', - 'javax.servlet.ServletContextEvent', - 'javax.servlet.ServletContextListener', - 'org.apache.avalon.framework.logger.Logger', - 'org.apache.log.Hierarchy', - 'org.apache.log.Logger' + 'javax.jms.Message', + 'javax.servlet.ServletContextEvent', + 'javax.servlet.ServletContextListener', + 'org.apache.avalon.framework.logger.Logger', + 'org.apache.log.Hierarchy', + 'org.apache.log.Logger', + 'org.slf4j.impl.StaticLoggerBinder', + 'org.slf4j.impl.StaticMDCBinder', + 'org.slf4j.impl.StaticMarkerBinder', + 'software.amazon.eventstream.HeaderValue', + 'software.amazon.eventstream.Message', + 'software.amazon.eventstream.MessageDecoder' ) - - if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_1_8) { - ignoreMissingClasses( - 'javax.xml.bind.DatatypeConverter', - 'javax.xml.bind.JAXBContext' - ) - } } diff --git a/plugins/discovery-ec2/config/discovery-ec2/log4j2.properties b/plugins/discovery-ec2/config/discovery-ec2/log4j2.properties index 7263bbcba521c..ceb9a546b9b08 100644 --- a/plugins/discovery-ec2/config/discovery-ec2/log4j2.properties +++ b/plugins/discovery-ec2/config/discovery-ec2/log4j2.properties @@ -9,17 +9,17 @@ # GitHub history for details. # -logger.com_amazonaws.name = com.amazonaws +logger.com_amazonaws.name = software.amazon.awssdk logger.com_amazonaws.level = warn -logger.com_amazonaws_jmx_SdkMBeanRegistrySupport.name = com.amazonaws.jmx.SdkMBeanRegistrySupport +logger.com_amazonaws_jmx_SdkMBeanRegistrySupport.name = software.amazon.awssdk.jmx.SdkMBeanRegistrySupport logger.com_amazonaws_jmx_SdkMBeanRegistrySupport.level = error -logger.com_amazonaws_metrics_AwsSdkMetrics.name = com.amazonaws.metrics.AwsSdkMetrics +logger.com_amazonaws_metrics_AwsSdkMetrics.name = software.amazon.awssdk.metrics.AwsSdkMetrics logger.com_amazonaws_metrics_AwsSdkMetrics.level = error -logger.com_amazonaws_auth_profile_internal_BasicProfileConfigFileLoader.name = com.amazonaws.auth.profile.internal.BasicProfileConfigFileLoader +logger.com_amazonaws_auth_profile_internal_BasicProfileConfigFileLoader.name = software.amazon.awssdk.auth.profile.internal.BasicProfileConfigFileLoader logger.com_amazonaws_auth_profile_internal_BasicProfileConfigFileLoader.level = error -logger.com_amazonaws_services_s3_internal_UseArnRegionResolver.name = com.amazonaws.services.s3.internal.UseArnRegionResolver +logger.com_amazonaws_services_s3_internal_UseArnRegionResolver.name = software.amazon.awssdk.services.s3.internal.UseArnRegionResolver logger.com_amazonaws_services_s3_internal_UseArnRegionResolver.level = error diff --git a/plugins/discovery-ec2/licenses/annotations-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/annotations-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5a626eeb5725b --- /dev/null +++ b/plugins/discovery-ec2/licenses/annotations-2.20.55.jar.sha1 @@ -0,0 +1 @@ +330e9d0e5f2401fffba5afe30f3740f400e8308d \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/annotations-LICENSE.txt b/plugins/discovery-ec2/licenses/annotations-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/annotations-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/annotations-NOTICE.txt b/plugins/discovery-ec2/licenses/annotations-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/annotations-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/apache-client-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/apache-client-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..3ee96bb6e4076 --- /dev/null +++ b/plugins/discovery-ec2/licenses/apache-client-2.20.55.jar.sha1 @@ -0,0 +1 @@ +5c149885667d41a306769505cfa481cfddf6f113 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/apache-client-LICENSE.txt b/plugins/discovery-ec2/licenses/apache-client-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/apache-client-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/apache-client-NOTICE.txt b/plugins/discovery-ec2/licenses/apache-client-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/apache-client-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/auth-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/auth-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..010464bdf9fd1 --- /dev/null +++ b/plugins/discovery-ec2/licenses/auth-2.20.55.jar.sha1 @@ -0,0 +1 @@ +e21f00a8a2096d5044f3eff176944256e01a175e \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/auth-LICENSE.txt b/plugins/discovery-ec2/licenses/auth-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/auth-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/auth-NOTICE.txt b/plugins/discovery-ec2/licenses/auth-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/auth-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/aws-core-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/aws-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..4b4ee1db864a8 --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +734427c2cece98a8cb90871b78d2311e4a7ef746 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/aws-core-LICENSE.txt b/plugins/discovery-ec2/licenses/aws-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/aws-core-NOTICE.txt b/plugins/discovery-ec2/licenses/aws-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/aws-java-sdk-LICENSE.txt b/plugins/discovery-ec2/licenses/aws-java-sdk-LICENSE.txt deleted file mode 100644 index 98d1f9319f374..0000000000000 --- a/plugins/discovery-ec2/licenses/aws-java-sdk-LICENSE.txt +++ /dev/null @@ -1,63 +0,0 @@ -Apache License -Version 2.0, January 2004 - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and - 2. You must cause any modified files to carry prominent notices stating that You changed the files; and - 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -Note: Other license terms may apply to certain, identified software files contained within or distributed with the accompanying software if such terms are included in the directory containing the accompanying software. Such other license terms will then apply in lieu of the terms of the software license above. - -JSON processing code subject to the JSON License from JSON.org: - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/discovery-ec2/licenses/aws-java-sdk-NOTICE.txt b/plugins/discovery-ec2/licenses/aws-java-sdk-NOTICE.txt deleted file mode 100644 index 565bd6085c71a..0000000000000 --- a/plugins/discovery-ec2/licenses/aws-java-sdk-NOTICE.txt +++ /dev/null @@ -1,15 +0,0 @@ -AWS SDK for Java -Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -This product includes software developed by -Amazon Technologies, Inc (http://www.amazon.com/). - -********************** -THIRD PARTY COMPONENTS -********************** -This software includes third party software subject to the following copyrights: -- XML parsing and utility functions from JetS3t - Copyright 2006-2009 James Murty. -- JSON parsing and utility functions from JSON.org - Copyright 2002 JSON.org. -- PKCS#1 PEM encoded private key parsing and utility functions from oauth.googlecode.com - Copyright 1998-2010 AOL Inc. - -The licenses for these third party components are included in LICENSE.txt diff --git a/plugins/discovery-ec2/licenses/aws-java-sdk-core-1.11.749.jar.sha1 b/plugins/discovery-ec2/licenses/aws-java-sdk-core-1.11.749.jar.sha1 deleted file mode 100644 index 7bc18d6d4f681..0000000000000 --- a/plugins/discovery-ec2/licenses/aws-java-sdk-core-1.11.749.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1da5c1549295cfeebc67fc1c7539785a9441755b \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/aws-java-sdk-ec2-1.11.749.jar.sha1 b/plugins/discovery-ec2/licenses/aws-java-sdk-ec2-1.11.749.jar.sha1 deleted file mode 100644 index c7c7220005fc3..0000000000000 --- a/plugins/discovery-ec2/licenses/aws-java-sdk-ec2-1.11.749.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0865e0937c6500acf62ce9c8964eac76a8718f5f \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/aws-json-protocol-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/aws-json-protocol-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..45a88305c1928 --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-json-protocol-2.20.55.jar.sha1 @@ -0,0 +1 @@ +a52731c86b974aefa5bbb1c545f407811a0163b1 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/aws-json-protocol-LICENSE.txt b/plugins/discovery-ec2/licenses/aws-json-protocol-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-json-protocol-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/aws-json-protocol-NOTICE.txt b/plugins/discovery-ec2/licenses/aws-json-protocol-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-json-protocol-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/aws-query-protocol-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/aws-query-protocol-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..ba5f43378730c --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-query-protocol-2.20.55.jar.sha1 @@ -0,0 +1 @@ +ac116215cc85366f0bdffee53b4c21e7a7fe03ef \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/aws-query-protocol-LICENSE.txt b/plugins/discovery-ec2/licenses/aws-query-protocol-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-query-protocol-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/aws-query-protocol-NOTICE.txt b/plugins/discovery-ec2/licenses/aws-query-protocol-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/aws-query-protocol-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/commons-codec-1.13.jar.sha1 b/plugins/discovery-ec2/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/discovery-ec2/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/commons-codec-1.15.jar.sha1 b/plugins/discovery-ec2/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/discovery-ec2/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/commons-logging-1.1.3.jar.sha1 b/plugins/discovery-ec2/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index c8756c438320f..0000000000000 --- a/plugins/discovery-ec2/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f diff --git a/plugins/discovery-ec2/licenses/commons-logging-1.2.jar.sha1 b/plugins/discovery-ec2/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/discovery-ec2/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/ec2-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/ec2-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..f123343bfe27e --- /dev/null +++ b/plugins/discovery-ec2/licenses/ec2-2.20.55.jar.sha1 @@ -0,0 +1 @@ +c095e527442835130b18387da6b1d01f365a6dbf \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/ec2-LICENSE.txt b/plugins/discovery-ec2/licenses/ec2-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/ec2-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/ec2-NOTICE.txt b/plugins/discovery-ec2/licenses/ec2-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/ec2-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/endpoints-spi-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/endpoints-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5bc0e31166c77 --- /dev/null +++ b/plugins/discovery-ec2/licenses/endpoints-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +085f82038ee86a7d6cd568fe7edd842978d92de3 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/endpoints-spi-LICENSE.txt b/plugins/discovery-ec2/licenses/endpoints-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/endpoints-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/endpoints-spi-NOTICE.txt b/plugins/discovery-ec2/licenses/endpoints-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/endpoints-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/http-client-spi-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/http-client-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..523cf43dcb2e9 --- /dev/null +++ b/plugins/discovery-ec2/licenses/http-client-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +34f9b10c1a46038a0ceebdd750ba3a413a862ceb \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/http-client-spi-LICENSE.txt b/plugins/discovery-ec2/licenses/http-client-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/http-client-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/http-client-spi-NOTICE.txt b/plugins/discovery-ec2/licenses/http-client-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/http-client-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/httpclient-4.5.13.jar.sha1 b/plugins/discovery-ec2/licenses/httpclient-4.5.13.jar.sha1 deleted file mode 100644 index 3281e21595b39..0000000000000 --- a/plugins/discovery-ec2/licenses/httpclient-4.5.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/httpclient-4.5.14.jar.sha1 b/plugins/discovery-ec2/licenses/httpclient-4.5.14.jar.sha1 new file mode 100644 index 0000000000000..66e05851c2e3c --- /dev/null +++ b/plugins/discovery-ec2/licenses/httpclient-4.5.14.jar.sha1 @@ -0,0 +1 @@ +1194890e6f56ec29177673f2f12d0b8e627dec98 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/httpcore-4.4.12.jar.sha1 b/plugins/discovery-ec2/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/plugins/discovery-ec2/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/httpcore-4.4.16.jar.sha1 b/plugins/discovery-ec2/licenses/httpcore-4.4.16.jar.sha1 new file mode 100644 index 0000000000000..172110694b5bd --- /dev/null +++ b/plugins/discovery-ec2/licenses/httpcore-4.4.16.jar.sha1 @@ -0,0 +1 @@ +51cf043c87253c9f58b539c9f7e44c8894223850 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/jackson-annotations-2.13.2.jar.sha1 b/plugins/discovery-ec2/licenses/jackson-annotations-2.13.2.jar.sha1 deleted file mode 100644 index ecd3fb49d5b12..0000000000000 --- a/plugins/discovery-ec2/licenses/jackson-annotations-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -ec18851f1976d5b810ae1a5fcc32520d2d38f77a \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/jackson-annotations-2.15.2.jar.sha1 b/plugins/discovery-ec2/licenses/jackson-annotations-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f63416ddb8ceb --- /dev/null +++ b/plugins/discovery-ec2/licenses/jackson-annotations-2.15.2.jar.sha1 @@ -0,0 +1 @@ +4724a65ac8e8d156a24898d50fd5dbd3642870b8 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/jackson-databind-2.13.2.jar.sha1 b/plugins/discovery-ec2/licenses/jackson-databind-2.13.2.jar.sha1 deleted file mode 100644 index 5d356f3fd045f..0000000000000 --- a/plugins/discovery-ec2/licenses/jackson-databind-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -926e48c451166a291f1ce6c6276d9abbefa7c00f \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/jackson-databind-2.15.2.jar.sha1 b/plugins/discovery-ec2/licenses/jackson-databind-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f16d80af8dce6 --- /dev/null +++ b/plugins/discovery-ec2/licenses/jackson-databind-2.15.2.jar.sha1 @@ -0,0 +1 @@ +9353b021f10c307c00328f52090de2bdb4b6ff9c \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/json-utils-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/json-utils-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..a19b00e62f8b5 --- /dev/null +++ b/plugins/discovery-ec2/licenses/json-utils-2.20.55.jar.sha1 @@ -0,0 +1 @@ +cd6710900e3190eac4c4496ae529ce08680dd320 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/json-utils-LICENSE.txt b/plugins/discovery-ec2/licenses/json-utils-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/json-utils-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/json-utils-NOTICE.txt b/plugins/discovery-ec2/licenses/json-utils-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/json-utils-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/log4j-1.2-api-2.17.1.jar.sha1 b/plugins/discovery-ec2/licenses/log4j-1.2-api-2.17.1.jar.sha1 deleted file mode 100644 index 23aa5c60bd596..0000000000000 --- a/plugins/discovery-ec2/licenses/log4j-1.2-api-2.17.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -db3a7e7f07e878b92ac4a8f1100bee8325d5713a \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/log4j-1.2-api-2.20.0.jar.sha1 b/plugins/discovery-ec2/licenses/log4j-1.2-api-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..9829576d38ce0 --- /dev/null +++ b/plugins/discovery-ec2/licenses/log4j-1.2-api-2.20.0.jar.sha1 @@ -0,0 +1 @@ +689151374756cb809cb029f2501015bdc7733179 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/metrics-spi-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/metrics-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..db6701d87892a --- /dev/null +++ b/plugins/discovery-ec2/licenses/metrics-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +8a0eae705b27465516f3b09cc9918e40963d534d \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/metrics-spi-LICENSE.txt b/plugins/discovery-ec2/licenses/metrics-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/metrics-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/metrics-spi-NOTICE.txt b/plugins/discovery-ec2/licenses/metrics-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/metrics-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/profiles-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/profiles-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..b7104cf0939e6 --- /dev/null +++ b/plugins/discovery-ec2/licenses/profiles-2.20.55.jar.sha1 @@ -0,0 +1 @@ +959aad08b2f24057bf286c761b49e3af31a0a623 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/profiles-LICENSE.txt b/plugins/discovery-ec2/licenses/profiles-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/profiles-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/profiles-NOTICE.txt b/plugins/discovery-ec2/licenses/profiles-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/profiles-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/protocol-core-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/protocol-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..4dee45f4d9dd3 --- /dev/null +++ b/plugins/discovery-ec2/licenses/protocol-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +0935e3ab32962a890f1d13bf39ba2167d9d692f9 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/protocol-core-LICENSE.txt b/plugins/discovery-ec2/licenses/protocol-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/protocol-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/protocol-core-NOTICE.txt b/plugins/discovery-ec2/licenses/protocol-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/protocol-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/reactive-streams-1.0.4.jar.sha1 b/plugins/discovery-ec2/licenses/reactive-streams-1.0.4.jar.sha1 new file mode 100644 index 0000000000000..45a80e3f7e361 --- /dev/null +++ b/plugins/discovery-ec2/licenses/reactive-streams-1.0.4.jar.sha1 @@ -0,0 +1 @@ +3864a1320d97d7b045f729a326e1e077661f31b7 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/reactive-streams-LICENSE.txt b/plugins/discovery-ec2/licenses/reactive-streams-LICENSE.txt new file mode 100644 index 0000000000000..1e3c7e7c77495 --- /dev/null +++ b/plugins/discovery-ec2/licenses/reactive-streams-LICENSE.txt @@ -0,0 +1,21 @@ +MIT No Attribution + +Copyright 2014 Reactive Streams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/grpc-context-NOTICE.txt b/plugins/discovery-ec2/licenses/reactive-streams-NOTICE.txt similarity index 100% rename from plugins/repository-gcs/licenses/grpc-context-NOTICE.txt rename to plugins/discovery-ec2/licenses/reactive-streams-NOTICE.txt diff --git a/plugins/discovery-ec2/licenses/regions-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/regions-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..993fc2f97de62 --- /dev/null +++ b/plugins/discovery-ec2/licenses/regions-2.20.55.jar.sha1 @@ -0,0 +1 @@ +a117c19b4a30e902f4f1cc4bef6b5c10cc9aef31 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/regions-LICENSE.txt b/plugins/discovery-ec2/licenses/regions-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/regions-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/regions-NOTICE.txt b/plugins/discovery-ec2/licenses/regions-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/regions-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/sdk-core-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/sdk-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5f12be9c08c5b --- /dev/null +++ b/plugins/discovery-ec2/licenses/sdk-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +8f2347feaf2575560ca89a2caa8d0243dbeb17a9 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/sdk-core-LICENSE.txt b/plugins/discovery-ec2/licenses/sdk-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/sdk-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/sdk-core-NOTICE.txt b/plugins/discovery-ec2/licenses/sdk-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/sdk-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/discovery-ec2/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/discovery-ec2/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/slf4j-api-LICENSE.txt b/plugins/discovery-ec2/licenses/slf4j-api-LICENSE.txt new file mode 100644 index 0000000000000..2be7689435062 --- /dev/null +++ b/plugins/discovery-ec2/licenses/slf4j-api-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2004-2022 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/slf4j-api-NOTICE.txt b/plugins/discovery-ec2/licenses/slf4j-api-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/discovery-ec2/licenses/third-party-jackson-core-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/third-party-jackson-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..e7eebbb98f1fe --- /dev/null +++ b/plugins/discovery-ec2/licenses/third-party-jackson-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +956912f26056fc7d46b2db566362fe5f7a8c0e14 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/third-party-jackson-core-LICENSE.txt b/plugins/discovery-ec2/licenses/third-party-jackson-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/third-party-jackson-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/third-party-jackson-core-NOTICE.txt b/plugins/discovery-ec2/licenses/third-party-jackson-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/third-party-jackson-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/licenses/utils-2.20.55.jar.sha1 b/plugins/discovery-ec2/licenses/utils-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..fc4cde604e33c --- /dev/null +++ b/plugins/discovery-ec2/licenses/utils-2.20.55.jar.sha1 @@ -0,0 +1 @@ +d3e1bbbc19795eadbeb4dd963a94647576644097 \ No newline at end of file diff --git a/plugins/discovery-ec2/licenses/utils-LICENSE.txt b/plugins/discovery-ec2/licenses/utils-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/discovery-ec2/licenses/utils-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/discovery-ec2/licenses/utils-NOTICE.txt b/plugins/discovery-ec2/licenses/utils-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/discovery-ec2/licenses/utils-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/discovery-ec2/qa/amazon-ec2/build.gradle b/plugins/discovery-ec2/qa/amazon-ec2/build.gradle index d63015f25b408..a844576d67ece 100644 --- a/plugins/discovery-ec2/qa/amazon-ec2/build.gradle +++ b/plugins/discovery-ec2/qa/amazon-ec2/build.gradle @@ -67,7 +67,7 @@ yamlRestTest.enabled = false * Test using various credential providers (see also https://docs.aws.amazon.com/sdk-for-java/v2/developer-guide/credentials.html): * - Elasticsearch Keystore (secure settings discovery.ec2.access_key and discovery.ec2.secret_key) * - Java system properties (aws.accessKeyId and aws.secretAccessKey) - * - Environment variables (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY) + * - Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_REGION) * - ECS container credentials (loaded from ECS if the environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is set) * - Instance profile credentials (delivered through the EC2 metadata service) * @@ -100,8 +100,9 @@ yamlRestTest.enabled = false setting 'discovery.seed_providers', 'ec2' setting 'network.host', '_ec2_' setting 'discovery.ec2.endpoint', { "http://${-> fixture.addressAndPort}" }, IGNORE_VALUE + setting 'discovery.ec2.region', 'us-east-1' - systemProperty "com.amazonaws.sdk.ec2MetadataServiceEndpointOverride", { "http://${-> fixture.addressAndPort}" }, IGNORE_VALUE + systemProperty "aws.ec2MetadataServiceEndpoint", { "http://${-> fixture.addressAndPort}" }, IGNORE_VALUE } } @@ -115,12 +116,14 @@ testClusters.yamlRestTestKeyStore { testClusters.yamlRestTestEnvVariables { environment 'AWS_ACCESS_KEY_ID', 'ec2_integration_test_access_key' environment 'AWS_SECRET_ACCESS_KEY', 'ec2_integration_test_secret_key' + environment 'AWS_REGION', 'ec2_integration_test_region' } // Extra config for SystemProperties testClusters.yamlRestTestSystemProperties { systemProperty 'aws.accessKeyId', 'ec2_integration_test_access_key' - systemProperty 'aws.secretKey', 'ec2_integration_test_secret_key' + systemProperty 'aws.secretAccessKey', 'ec2_integration_test_secret_key' + systemProperty 'aws.region', 'ec2_integration_test_region' } // Extra config for ContainerCredentials diff --git a/plugins/discovery-ec2/qa/amazon-ec2/src/yamlRestTest/java/org/opensearch/discovery/ec2/AmazonEC2DiscoveryClientYamlTestSuiteIT.java b/plugins/discovery-ec2/qa/amazon-ec2/src/yamlRestTest/java/org/opensearch/discovery/ec2/AmazonEC2DiscoveryClientYamlTestSuiteIT.java index a0f4984751a7b..6e86148f208bd 100644 --- a/plugins/discovery-ec2/qa/amazon-ec2/src/yamlRestTest/java/org/opensearch/discovery/ec2/AmazonEC2DiscoveryClientYamlTestSuiteIT.java +++ b/plugins/discovery-ec2/qa/amazon-ec2/src/yamlRestTest/java/org/opensearch/discovery/ec2/AmazonEC2DiscoveryClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/discovery-ec2/qa/amazon-ec2/src/yamlRestTest/java/org/opensearch/discovery/ec2/AmazonEC2Fixture.java b/plugins/discovery-ec2/qa/amazon-ec2/src/yamlRestTest/java/org/opensearch/discovery/ec2/AmazonEC2Fixture.java index 7ff6a0f0ec16a..8dc9db69b674f 100644 --- a/plugins/discovery-ec2/qa/amazon-ec2/src/yamlRestTest/java/org/opensearch/discovery/ec2/AmazonEC2Fixture.java +++ b/plugins/discovery-ec2/qa/amazon-ec2/src/yamlRestTest/java/org/opensearch/discovery/ec2/AmazonEC2Fixture.java @@ -31,21 +31,22 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.util.DateUtils; +import software.amazon.awssdk.utils.DateUtils; + import org.apache.http.NameValuePair; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.utils.URLEncodedUtils; - import org.opensearch.common.Booleans; import org.opensearch.common.SuppressForbidden; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.test.fixture.AbstractHttpFixture; import javax.xml.XMLConstants; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamWriter; + import java.io.IOException; import java.io.StringWriter; import java.nio.file.Files; @@ -138,7 +139,7 @@ protected Response handle(final Request request) throws IOException { + "ec2_integration_test_access_key" + "\"," + "\"Expiration\": \"" - + DateUtils.formatISO8601Date(expiration) + + DateUtils.formatIso8601Date(expiration.toInstant()) + "\"," + "\"RoleArn\": \"" + "test" diff --git a/plugins/discovery-ec2/src/internalClusterTest/java/org/opensearch/discovery/ec2/AbstractAwsTestCase.java b/plugins/discovery-ec2/src/internalClusterTest/java/org/opensearch/discovery/ec2/AbstractAwsTestCase.java index 1e7422ea0ac02..9529d1e67ea09 100644 --- a/plugins/discovery-ec2/src/internalClusterTest/java/org/opensearch/discovery/ec2/AbstractAwsTestCase.java +++ b/plugins/discovery-ec2/src/internalClusterTest/java/org/opensearch/discovery/ec2/AbstractAwsTestCase.java @@ -32,10 +32,10 @@ package org.opensearch.discovery.ec2; -import org.opensearch.common.Strings; import org.opensearch.common.io.PathUtils; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; +import org.opensearch.core.common.Strings; import org.opensearch.env.Environment; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; diff --git a/plugins/discovery-ec2/src/internalClusterTest/java/org/opensearch/discovery/ec2/Ec2DiscoveryUpdateSettingsTests.java b/plugins/discovery-ec2/src/internalClusterTest/java/org/opensearch/discovery/ec2/Ec2DiscoveryUpdateSettingsTests.java index 453ea2b3268a8..659bec0c6e89e 100644 --- a/plugins/discovery-ec2/src/internalClusterTest/java/org/opensearch/discovery/ec2/Ec2DiscoveryUpdateSettingsTests.java +++ b/plugins/discovery-ec2/src/internalClusterTest/java/org/opensearch/discovery/ec2/Ec2DiscoveryUpdateSettingsTests.java @@ -48,7 +48,7 @@ */ @ClusterScope(scope = Scope.TEST, numDataNodes = 0, numClientNodes = 0) public class Ec2DiscoveryUpdateSettingsTests extends AbstractAwsTestCase { - public void testMinimumMasterNodesStart() { + public void testMinimumClusterManagerNodesStart() { Settings nodeSettings = Settings.builder().put(DiscoveryModule.DISCOVERY_SEED_PROVIDERS_SETTING.getKey(), "ec2").build(); internalCluster().startNode(nodeSettings); diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AmazonEc2ClientReference.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AmazonEc2ClientReference.java new file mode 100644 index 0000000000000..1c5ffbfb38ec3 --- /dev/null +++ b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AmazonEc2ClientReference.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.discovery.ec2; + +import software.amazon.awssdk.services.ec2.Ec2Client; + +import org.opensearch.common.concurrent.RefCountedReleasable; + +/** + * Handles the shutdown of the wrapped {@link Ec2Client} using reference + * counting. + */ +public class AmazonEc2ClientReference extends RefCountedReleasable { + + AmazonEc2ClientReference(Ec2Client client) { + super("AWS_EC2_CLIENT", client, client::close); + } +} diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AmazonEc2Reference.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AmazonEc2Reference.java deleted file mode 100644 index 2686c376213f3..0000000000000 --- a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AmazonEc2Reference.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.discovery.ec2; - -import com.amazonaws.services.ec2.AmazonEC2; -import org.opensearch.common.concurrent.RefCountedReleasable; - -/** - * Handles the shutdown of the wrapped {@link AmazonEC2} using reference - * counting. - */ -public class AmazonEc2Reference extends RefCountedReleasable { - - AmazonEc2Reference(AmazonEC2 client) { - super("AWS_EC2_CLIENT", client, client::shutdown); - } -} diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2SeedHostsProvider.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2SeedHostsProvider.java index f26ecfab501f8..fb46b82065fd1 100644 --- a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2SeedHostsProvider.java +++ b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2SeedHostsProvider.java @@ -32,23 +32,23 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.AmazonClientException; -import com.amazonaws.services.ec2.model.DescribeInstancesRequest; -import com.amazonaws.services.ec2.model.DescribeInstancesResult; -import com.amazonaws.services.ec2.model.Filter; -import com.amazonaws.services.ec2.model.GroupIdentifier; -import com.amazonaws.services.ec2.model.Instance; -import com.amazonaws.services.ec2.model.Reservation; -import com.amazonaws.services.ec2.model.Tag; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest; +import software.amazon.awssdk.services.ec2.model.DescribeInstancesResponse; +import software.amazon.awssdk.services.ec2.model.Filter; +import software.amazon.awssdk.services.ec2.model.GroupIdentifier; +import software.amazon.awssdk.services.ec2.model.Instance; +import software.amazon.awssdk.services.ec2.model.Reservation; +import software.amazon.awssdk.services.ec2.model.Tag; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.SingleObjectCache; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.discovery.SeedHostsProvider; import org.opensearch.transport.TransportService; @@ -85,6 +85,8 @@ class AwsEc2SeedHostsProvider implements SeedHostsProvider { private final TransportAddressesCache dynamicHosts; + private final Set instanceStates; + AwsEc2SeedHostsProvider(Settings settings, TransportService transportService, AwsEc2Service awsEc2Service) { this.transportService = transportService; this.awsEc2Service = awsEc2Service; @@ -101,6 +103,10 @@ class AwsEc2SeedHostsProvider implements SeedHostsProvider { this.availabilityZones = new HashSet<>(); availabilityZones.addAll(AwsEc2Service.AVAILABILITY_ZONES_SETTING.get(settings)); + this.instanceStates = new HashSet<>(); + instanceStates.add("running"); + instanceStates.add("pending"); + if (logger.isDebugEnabled()) { logger.debug( "using host_type [{}], tags [{}], groups [{}] with any_group [{}], availability_zones [{}]", @@ -119,41 +125,47 @@ public List getSeedAddresses(HostsResolver hostsResolver) { } protected List fetchDynamicNodes() { + logger.info( + "fetching nodes from IMDS (instance-states={}, availability-zones={}, tags={}) ...", + instanceStates, + availabilityZones, + tags + ); final List dynamicHosts = new ArrayList<>(); - final DescribeInstancesResult descInstances; - try (AmazonEc2Reference clientReference = awsEc2Service.client()) { + final DescribeInstancesResponse descInstances; + try (AmazonEc2ClientReference clientReference = awsEc2Service.client()) { // Query EC2 API based on AZ, instance state, and tag. // NOTE: we don't filter by security group during the describe instances request for two reasons: // 1. differences in VPCs require different parameters during query (ID vs Name) // 2. We want to use two different strategies: (all security groups vs. any security groups) - descInstances = SocketAccess.doPrivileged(() -> clientReference.get().describeInstances(buildDescribeInstancesRequest())); - } catch (final AmazonClientException e) { - logger.info("Exception while retrieving instance list from AWS API: {}", e.getMessage()); - logger.debug("Full exception:", e); + DescribeInstancesRequest instancesRequest = buildDescribeInstancesRequest(); + descInstances = SocketAccess.doPrivileged(() -> clientReference.get().describeInstances(instancesRequest)); + } catch (final SdkException e) { + logger.warn("error retrieving instance list from IMDS", e); return dynamicHosts; } - logger.trace("finding seed nodes..."); - for (final Reservation reservation : descInstances.getReservations()) { - for (final Instance instance : reservation.getInstances()) { + logger.trace("finding seed nodes ..."); + for (final Reservation reservation : descInstances.reservations()) { + for (final Instance instance : reservation.instances()) { // lets see if we can filter based on groups if (!groups.isEmpty()) { - final List instanceSecurityGroups = instance.getSecurityGroups(); + final List instanceSecurityGroups = instance.securityGroups(); final List securityGroupNames = new ArrayList<>(instanceSecurityGroups.size()); final List securityGroupIds = new ArrayList<>(instanceSecurityGroups.size()); for (final GroupIdentifier sg : instanceSecurityGroups) { - securityGroupNames.add(sg.getGroupName()); - securityGroupIds.add(sg.getGroupId()); + securityGroupNames.add(sg.groupName()); + securityGroupIds.add(sg.groupId()); } if (bindAnyGroup) { // We check if we can find at least one group name or one group id in groups. if (disjoint(securityGroupNames, groups) && disjoint(securityGroupIds, groups)) { logger.trace( "filtering out instance {} based on groups {}, not part of {}", - instance.getInstanceId(), + instance.instanceId(), instanceSecurityGroups, groups ); @@ -165,7 +177,7 @@ protected List fetchDynamicNodes() { if (!(securityGroupNames.containsAll(groups) || securityGroupIds.containsAll(groups))) { logger.trace( "filtering out instance {} based on groups {}, does not include all of {}", - instance.getInstanceId(), + instance.instanceId(), instanceSecurityGroups, groups ); @@ -177,21 +189,21 @@ protected List fetchDynamicNodes() { String address = null; if (hostType.equals(PRIVATE_DNS)) { - address = instance.getPrivateDnsName(); + address = instance.privateDnsName(); } else if (hostType.equals(PRIVATE_IP)) { - address = instance.getPrivateIpAddress(); + address = instance.privateIpAddress(); } else if (hostType.equals(PUBLIC_DNS)) { - address = instance.getPublicDnsName(); + address = instance.publicDnsName(); } else if (hostType.equals(PUBLIC_IP)) { - address = instance.getPublicIpAddress(); + address = instance.publicIpAddress(); } else if (hostType.startsWith(TAG_PREFIX)) { // Reading the node host from its metadata final String tagName = hostType.substring(TAG_PREFIX.length()); logger.debug("reading hostname from [{}] instance tag", tagName); - final List tags = instance.getTags(); + final List tags = instance.tags(); for (final Tag tag : tags) { - if (tag.getKey().equals(tagName)) { - address = tag.getValue(); + if (tag.key().equals(tagName)) { + address = tag.value(); logger.debug("using [{}] as the instance address", address); } } @@ -202,7 +214,7 @@ protected List fetchDynamicNodes() { try { final TransportAddress[] addresses = transportService.addressesFromString(address); for (int i = 0; i < addresses.length; i++) { - logger.trace("adding {}, address {}, transport_address {}", instance.getInstanceId(), address, addresses[i]); + logger.debug("adding {}, address {}, transport_address {}", instance.instanceId(), address, addresses[i]); dynamicHosts.add(addresses[i]); } } catch (final Exception e) { @@ -210,39 +222,38 @@ protected List fetchDynamicNodes() { logger.warn( (Supplier) () -> new ParameterizedMessage( "failed to add {}, address {}", - instance.getInstanceId(), + instance.instanceId(), finalAddress ), e ); } } else { - logger.trace("not adding {}, address is null, host_type {}", instance.getInstanceId(), hostType); + logger.warn("not adding {}, address is null, host_type {}", instance.instanceId(), hostType); } } } - logger.debug("using dynamic transport addresses {}", dynamicHosts); + logger.info("using dynamic transport addresses {}", dynamicHosts); return dynamicHosts; } private DescribeInstancesRequest buildDescribeInstancesRequest() { - final DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest().withFilters( - new Filter("instance-state-name").withValues("running", "pending") - ); + ArrayList filters = new ArrayList(); + filters.add(Filter.builder().name("instance-state-name").values(instanceStates).build()); for (final Map.Entry> tagFilter : tags.entrySet()) { // for a given tag key, OR relationship for multiple different values - describeInstancesRequest.withFilters(new Filter("tag:" + tagFilter.getKey()).withValues(tagFilter.getValue())); + filters.add(Filter.builder().name("tag:" + tagFilter.getKey()).values(tagFilter.getValue()).build()); } if (!availabilityZones.isEmpty()) { // OR relationship amongst multiple values of the availability-zone filter - describeInstancesRequest.withFilters(new Filter("availability-zone").withValues(availabilityZones)); + filters.add(Filter.builder().name("availability-zone").values(availabilityZones).build()); } - return describeInstancesRequest; + return DescribeInstancesRequest.builder().filters(filters).build(); } private final class TransportAddressesCache extends SingleObjectCache> { diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2Service.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2Service.java index c3ed4340467bd..58d0cdb5e87aa 100644 --- a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2Service.java +++ b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2Service.java @@ -43,6 +43,7 @@ import java.util.function.Function; interface AwsEc2Service extends Closeable { + Setting AUTO_ATTRIBUTE_SETTING = Setting.boolSetting("cloud.node.auto_attributes", false, Property.NodeScope); class HostType { @@ -115,7 +116,7 @@ class HostType { * settings. Returns an {@code AmazonEc2Reference} wrapper which should be * released as soon as it is not required anymore. */ - AmazonEc2Reference client(); + AmazonEc2ClientReference client(); /** * Updates the settings for building the client and releases the cached one. @@ -125,5 +126,4 @@ class HostType { * @param clientSettings the new refreshed settings */ void refreshAndClearCache(Ec2ClientSettings clientSettings); - } diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2ServiceImpl.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2ServiceImpl.java index 7566a7094e635..51f0ad9526e55 100644 --- a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2ServiceImpl.java +++ b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/AwsEc2ServiceImpl.java @@ -32,82 +32,140 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.http.IdleConnectionReaper; -import com.amazonaws.services.ec2.AmazonEC2; -import com.amazonaws.services.ec2.AmazonEC2ClientBuilder; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.retry.RetryPolicy; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.http.apache.ProxyConfiguration; +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.ec2.Ec2Client; +import software.amazon.awssdk.services.ec2.Ec2ClientBuilder; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.OpenSearchException; -import org.opensearch.common.Strings; +import org.opensearch.common.SuppressForbidden; import org.opensearch.common.util.LazyInitializable; +import org.opensearch.core.common.Strings; +import java.net.URI; +import java.net.URISyntaxException; +import java.time.Duration; import java.util.concurrent.atomic.AtomicReference; class AwsEc2ServiceImpl implements AwsEc2Service { - private static final Logger logger = LogManager.getLogger(AwsEc2ServiceImpl.class); - private final AtomicReference> lazyClientReference = new AtomicReference<>(); - - private AmazonEC2 buildClient(Ec2ClientSettings clientSettings) { - final AWSCredentialsProvider credentials = buildCredentials(logger, clientSettings); - final ClientConfiguration configuration = buildConfiguration(logger, clientSettings); - return buildClient(credentials, configuration, clientSettings.endpoint); + private final AtomicReference> lazyClientReference = + new AtomicReference<>(); + + private Ec2Client buildClient(Ec2ClientSettings clientSettings) { + SocketAccess.doPrivilegedVoid(AwsEc2ServiceImpl::setDefaultAwsProfilePath); + final AwsCredentialsProvider awsCredentialsProvider = buildCredentials(logger, clientSettings); + final ClientOverrideConfiguration overrideConfiguration = buildOverrideConfiguration(logger, clientSettings); + final ProxyConfiguration proxyConfiguration = SocketAccess.doPrivileged(() -> buildProxyConfiguration(logger, clientSettings)); + return buildClient( + awsCredentialsProvider, + proxyConfiguration, + overrideConfiguration, + clientSettings.endpoint, + clientSettings.region, + clientSettings.readTimeoutMillis + ); } // proxy for testing - AmazonEC2 buildClient(AWSCredentialsProvider credentials, ClientConfiguration configuration, String endpoint) { - final AmazonEC2ClientBuilder builder = AmazonEC2ClientBuilder.standard() - .withCredentials(credentials) - .withClientConfiguration(configuration); + protected Ec2Client buildClient( + AwsCredentialsProvider awsCredentialsProvider, + ProxyConfiguration proxyConfiguration, + ClientOverrideConfiguration overrideConfiguration, + String endpoint, + String region, + long readTimeoutMillis + ) { + ApacheHttpClient.Builder clientBuilder = ApacheHttpClient.builder() + .proxyConfiguration(proxyConfiguration) + .socketTimeout(Duration.ofMillis(readTimeoutMillis)); + + Ec2ClientBuilder builder = Ec2Client.builder() + .overrideConfiguration(overrideConfiguration) + .httpClientBuilder(clientBuilder) + .credentialsProvider(awsCredentialsProvider); + if (Strings.hasText(endpoint)) { logger.debug("using explicit ec2 endpoint [{}]", endpoint); - builder.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, null)); + builder.endpointOverride(URI.create(endpoint)); } + + if (Strings.hasText(region)) { + logger.debug("using explicit ec2 region [{}]", region); + builder.region(Region.of(region)); + } + return SocketAccess.doPrivileged(builder::build); } - // pkg private for tests - static ClientConfiguration buildConfiguration(Logger logger, Ec2ClientSettings clientSettings) { - final ClientConfiguration clientConfiguration = new ClientConfiguration(); - // the response metadata cache is only there for diagnostics purposes, - // but can force objects from every response to the old generation. - clientConfiguration.setResponseMetadataCacheSize(0); - clientConfiguration.setProtocol(clientSettings.protocol); + static ProxyConfiguration buildProxyConfiguration(Logger logger, Ec2ClientSettings clientSettings) { if (Strings.hasText(clientSettings.proxyHost)) { - // TODO: remove this leniency, these settings should exist together and be validated - clientConfiguration.setProxyHost(clientSettings.proxyHost); - clientConfiguration.setProxyPort(clientSettings.proxyPort); - clientConfiguration.setProxyUsername(clientSettings.proxyUsername); - clientConfiguration.setProxyPassword(clientSettings.proxyPassword); + try { + // TODO: remove this leniency, these settings should exist together and be validated + return ProxyConfiguration.builder() + .endpoint( + new URI( + clientSettings.protocol.toString(), + null, + clientSettings.proxyHost, + clientSettings.proxyPort, + null, + null, + null + ) + ) + .username(clientSettings.proxyUsername) + .password(clientSettings.proxyPassword) + .build(); + } catch (URISyntaxException e) { + throw SdkException.create("Invalid proxy URL", e); + } + } else { + return ProxyConfiguration.builder().build(); } - // Increase the number of retries in case of 5xx API responses - clientConfiguration.setMaxErrorRetry(10); - clientConfiguration.setSocketTimeout(clientSettings.readTimeoutMillis); - return clientConfiguration; + } + + static ClientOverrideConfiguration buildOverrideConfiguration(Logger logger, Ec2ClientSettings clientSettings) { + return ClientOverrideConfiguration.builder().retryPolicy(buildRetryPolicy(logger, clientSettings)).build(); } // pkg private for tests - static AWSCredentialsProvider buildCredentials(Logger logger, Ec2ClientSettings clientSettings) { - final AWSCredentials credentials = clientSettings.credentials; + static RetryPolicy buildRetryPolicy(Logger logger, Ec2ClientSettings clientSettings) { + // Increase the number of retries in case of 5xx API responses. + // Note that AWS SDK v2 introduced a concept of TokenBucketRetryCondition, which effectively limits retries for + // APIs that have been failing continuously. It allocates tokens (default is 500), which means that once 500 + // retries fail for any API on a bucket, new retries will only be allowed once some retries are rejected. + // https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html + RetryPolicy.Builder retryPolicy = RetryPolicy.builder().numRetries(10); + return retryPolicy.build(); + } + + static AwsCredentialsProvider buildCredentials(Logger logger, Ec2ClientSettings clientSettings) { + final AwsCredentials credentials = clientSettings.credentials; if (credentials == null) { - logger.debug("Using default provider chain"); - return DefaultAWSCredentialsProviderChain.getInstance(); + logger.debug("Using default credentials provider"); + return DefaultCredentialsProvider.create(); } else { logger.debug("Using basic key/secret credentials"); - return new AWSStaticCredentialsProvider(credentials); + return StaticCredentialsProvider.create(credentials); } } @Override - public AmazonEc2Reference client() { - final LazyInitializable clientReference = this.lazyClientReference.get(); + public AmazonEc2ClientReference client() { + final LazyInitializable clientReference = this.lazyClientReference.get(); if (clientReference == null) { throw new IllegalStateException("Missing ec2 client configs"); } @@ -121,12 +179,12 @@ public AmazonEc2Reference client() { */ @Override public void refreshAndClearCache(Ec2ClientSettings clientSettings) { - final LazyInitializable newClient = new LazyInitializable<>( - () -> new AmazonEc2Reference(buildClient(clientSettings)), + final LazyInitializable newClient = new LazyInitializable<>( + () -> new AmazonEc2ClientReference(buildClient(clientSettings)), clientReference -> clientReference.incRef(), clientReference -> clientReference.decRef() ); - final LazyInitializable oldClient = this.lazyClientReference.getAndSet(newClient); + final LazyInitializable oldClient = this.lazyClientReference.getAndSet(newClient); if (oldClient != null) { oldClient.reset(); } @@ -134,13 +192,22 @@ public void refreshAndClearCache(Ec2ClientSettings clientSettings) { @Override public void close() { - final LazyInitializable clientReference = this.lazyClientReference.getAndSet(null); + final LazyInitializable clientReference = this.lazyClientReference.getAndSet(null); if (clientReference != null) { clientReference.reset(); } - // shutdown IdleConnectionReaper background thread - // it will be restarted on new client usage - IdleConnectionReaper.shutdown(); } + // By default, AWS v2 SDK loads a default profile from $USER_HOME, which is restricted. Use the OpenSearch configuration path instead. + @SuppressForbidden(reason = "Prevent AWS SDK v2 from using ~/.aws/config and ~/.aws/credentials.") + static void setDefaultAwsProfilePath() { + if (ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.getStringValue().isEmpty()) { + logger.info("setting aws.sharedCredentialsFile={}", System.getProperty("opensearch.path.conf")); + System.setProperty(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property(), System.getProperty("opensearch.path.conf")); + } + if (ProfileFileSystemSetting.AWS_CONFIG_FILE.getStringValue().isEmpty()) { + logger.info("setting aws.sharedCredentialsFile={}", System.getProperty("opensearch.path.conf")); + System.setProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), System.getProperty("opensearch.path.conf")); + } + } } diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2ClientSettings.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2ClientSettings.java index 406be47503be2..8c010bbcdec3a 100644 --- a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2ClientSettings.java +++ b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2ClientSettings.java @@ -32,21 +32,21 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.Protocol; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.auth.BasicSessionCredentials; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; +import software.amazon.awssdk.core.Protocol; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.settings.SecureSetting; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.settings.SecureString; import java.util.Locale; @@ -78,7 +78,16 @@ final class Ec2ClientSettings { Property.NodeScope ); - /** The protocol to use to connect to to ec2. */ + /** An override for the scoping region for authentication. */ + static final Setting REGION_SETTING = new Setting<>( + "discovery.ec2.region", + "", + s -> s.toLowerCase(Locale.ROOT), + Property.NodeScope + ); + + /** The protocol to use to connect to ec2. AWS SDKv2 only supports HTTPs, deprecated in 3.0. */ + @Deprecated static final Setting PROTOCOL_SETTING = new Setting<>( "discovery.ec2.protocol", "https", @@ -95,7 +104,7 @@ final class Ec2ClientSettings { /** The socket timeout for connecting to s3. */ static final Setting READ_TIMEOUT_SETTING = Setting.timeSetting( "discovery.ec2.read_timeout", - TimeValue.timeValueMillis(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT), + TimeValue.timeValueMillis(50_000), Property.NodeScope ); @@ -104,7 +113,7 @@ final class Ec2ClientSettings { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(Ec2ClientSettings.class); /** Credentials to authenticate with ec2. */ - final AWSCredentials credentials; + final AwsCredentials credentials; /** * The ec2 endpoint the client should talk to, or empty string to use the @@ -112,6 +121,11 @@ final class Ec2ClientSettings { */ final String endpoint; + /** + * The ec2 signing region. + */ + final String region; + /** The protocol to use to talk to ec2. Defaults to https. */ final Protocol protocol; @@ -134,8 +148,9 @@ final class Ec2ClientSettings { final int readTimeoutMillis; protected Ec2ClientSettings( - AWSCredentials credentials, + AwsCredentials credentials, String endpoint, + String region, Protocol protocol, String proxyHost, int proxyPort, @@ -145,6 +160,7 @@ protected Ec2ClientSettings( ) { this.credentials = credentials; this.endpoint = endpoint; + this.region = region; this.protocol = protocol; this.proxyHost = proxyHost; this.proxyPort = proxyPort; @@ -153,7 +169,7 @@ protected Ec2ClientSettings( this.readTimeoutMillis = readTimeoutMillis; } - static AWSCredentials loadCredentials(Settings settings) { + static AwsCredentials loadCredentials(Settings settings) { try ( SecureString key = ACCESS_KEY_SETTING.get(settings); SecureString secret = SECRET_KEY_SETTING.get(settings); @@ -173,39 +189,37 @@ static AWSCredentials loadCredentials(Settings settings) { return null; } else { if (key.length() == 0) { - deprecationLogger.deprecate( - "ec2_invalid_key_settings", - "Setting [{}] is set but [{}] is not, which will be unsupported in future", + throw new SettingsException( + "Setting [{}] is set but [{}] is not", SECRET_KEY_SETTING.getKey(), - ACCESS_KEY_SETTING.getKey() + ACCESS_KEY_SETTING.getKey(), + SECRET_KEY_SETTING.getKey() ); } if (secret.length() == 0) { - deprecationLogger.deprecate( - "ec2_invalid_settings", - "Setting [{}] is set but [{}] is not, which will be unsupported in future", + throw new SettingsException( + "Setting [{}] is set but [{}] is not", ACCESS_KEY_SETTING.getKey(), SECRET_KEY_SETTING.getKey() ); } - final AWSCredentials credentials; + final AwsCredentials credentials; if (sessionToken.length() == 0) { logger.debug("Using basic key/secret credentials"); - credentials = new BasicAWSCredentials(key.toString(), secret.toString()); + credentials = AwsBasicCredentials.create(key.toString(), secret.toString()); } else { logger.debug("Using basic session credentials"); - credentials = new BasicSessionCredentials(key.toString(), secret.toString(), sessionToken.toString()); + credentials = AwsSessionCredentials.create(key.toString(), secret.toString(), sessionToken.toString()); } return credentials; } } } - // pkg private for tests /** Parse settings for a single client. */ static Ec2ClientSettings getClientSettings(Settings settings) { - final AWSCredentials credentials = loadCredentials(settings); + final AwsCredentials credentials = loadCredentials(settings); try ( SecureString proxyUsername = PROXY_USERNAME_SETTING.get(settings); SecureString proxyPassword = PROXY_PASSWORD_SETTING.get(settings) @@ -213,6 +227,7 @@ static Ec2ClientSettings getClientSettings(Settings settings) { return new Ec2ClientSettings( credentials, ENDPOINT_SETTING.get(settings), + REGION_SETTING.get(settings), PROTOCOL_SETTING.get(settings), PROXY_HOST_SETTING.get(settings), PROXY_PORT_SETTING.get(settings), @@ -222,5 +237,4 @@ static Ec2ClientSettings getClientSettings(Settings settings) { ); } } - } diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2DiscoveryPlugin.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2DiscoveryPlugin.java index ef89a351e5767..eb02e99582f93 100644 --- a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2DiscoveryPlugin.java +++ b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2DiscoveryPlugin.java @@ -32,8 +32,8 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.util.EC2MetadataUtils; -import com.amazonaws.util.json.Jackson; +import software.amazon.awssdk.core.SdkSystemSetting; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.SpecialPermission; @@ -56,12 +56,11 @@ import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Supplier; public class Ec2DiscoveryPlugin extends Plugin implements DiscoveryPlugin, ReloadablePlugin { @@ -71,20 +70,6 @@ public class Ec2DiscoveryPlugin extends Plugin implements DiscoveryPlugin, Reloa static { SpecialPermission.check(); - // Initializing Jackson requires RuntimePermission accessDeclaredMembers - // The ClientConfiguration class requires RuntimePermission getClassLoader - AccessController.doPrivileged((PrivilegedAction) () -> { - try { - // kick jackson to do some static caching of declared members info - Jackson.jsonNodeOf("{}"); - // ClientConfiguration clinit has some classloader problems - // TODO: fix that - Class.forName("com.amazonaws.ClientConfiguration"); - } catch (final ClassNotFoundException e) { - throw new RuntimeException(e); - } - return null; - }); } private final Settings settings; @@ -121,6 +106,7 @@ public List> getSettings() { Ec2ClientSettings.SECRET_KEY_SETTING, Ec2ClientSettings.SESSION_TOKEN_SETTING, Ec2ClientSettings.ENDPOINT_SETTING, + Ec2ClientSettings.REGION_SETTING, Ec2ClientSettings.PROTOCOL_SETTING, Ec2ClientSettings.PROXY_HOST_SETTING, Ec2ClientSettings.PROXY_PORT_SETTING, @@ -143,9 +129,15 @@ public Settings additionalSettings() { final Settings.Builder builder = Settings.builder(); // Adds a node attribute for the ec2 availability zone - final String azMetadataUrl = EC2MetadataUtils.getHostAddressForEC2MetadataService() - + "/latest/meta-data/placement/availability-zone"; - builder.put(getAvailabilityZoneNodeAttributes(settings, azMetadataUrl)); + Optional ec2MetadataServiceEndpoint = SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.getStringValue(); + if (ec2MetadataServiceEndpoint.isPresent()) { + builder.put( + getAvailabilityZoneNodeAttributes( + settings, + ec2MetadataServiceEndpoint.get() + "/latest/meta-data/placement/availability-zone" + ) + ); + } return builder.build(); } @@ -161,6 +153,9 @@ static Settings getAvailabilityZoneNodeAttributes(Settings settings, String azMe final URLConnection urlConnection; try { url = new URL(azMetadataUrl); + // Obtain the current EC2 instance availability zone from IMDS. + // Same as curl http://169.254.169.254/latest/meta-data/placement/availability-zone/. + // TODO: use EC2MetadataUtils::getAvailabilityZone that was added in AWS SDK v2 instead of rolling our own logger.debug("obtaining ec2 [placement/availability-zone] from ec2 meta-data url {}", url); urlConnection = SocketAccess.doPrivilegedIOException(url::openConnection); urlConnection.setConnectTimeout(2000); diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2NameResolver.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2NameResolver.java index 9fa479e90c956..7efaf41bc3133 100644 --- a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2NameResolver.java +++ b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/Ec2NameResolver.java @@ -32,13 +32,13 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.util.EC2MetadataUtils; +import software.amazon.awssdk.core.SdkSystemSetting; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.network.NetworkService.CustomNameResolver; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import java.io.BufferedReader; import java.io.IOException; @@ -48,6 +48,7 @@ import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; +import java.util.Optional; /** * Resolves certain ec2 related 'meta' hostnames into an actual hostname @@ -104,25 +105,31 @@ private enum Ec2HostnameType { @SuppressForbidden(reason = "We call getInputStream in doPrivileged and provide SocketPermission") public InetAddress[] resolve(Ec2HostnameType type) throws IOException { InputStream in = null; - String metadataUrl = EC2MetadataUtils.getHostAddressForEC2MetadataService() + "/latest/meta-data/" + type.ec2Name; - try { - URL url = new URL(metadataUrl); - logger.debug("obtaining ec2 hostname from ec2 meta-data url {}", url); - URLConnection urlConnection = SocketAccess.doPrivilegedIOException(url::openConnection); - urlConnection.setConnectTimeout(2000); - in = SocketAccess.doPrivilegedIOException(urlConnection::getInputStream); - BufferedReader urlReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); - - String metadataResult = urlReader.readLine(); - if (metadataResult == null || metadataResult.length() == 0) { - throw new IOException("no gce metadata returned from [" + url + "] for [" + type.configName + "]"); + Optional ec2MetadataServiceEndpoint = SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.getStringValue(); + if (ec2MetadataServiceEndpoint.isPresent()) { + String metadataUrl = ec2MetadataServiceEndpoint.get() + "/latest/meta-data/" + type.ec2Name; + try { + URL url = new URL(metadataUrl); + logger.debug("obtaining ec2 hostname from ec2 meta-data url {}", url); + URLConnection urlConnection = SocketAccess.doPrivilegedIOException(url::openConnection); + urlConnection.setConnectTimeout(2000); + in = SocketAccess.doPrivilegedIOException(urlConnection::getInputStream); + BufferedReader urlReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + + String metadataResult = urlReader.readLine(); + if (metadataResult == null || metadataResult.length() == 0) { + throw new IOException("no ec2 metadata returned from [" + url + "] for [" + type.configName + "]"); + } + logger.debug("obtained ec2 hostname from ec2 meta-data url {}: {}", url, metadataResult); + // only one address: because we explicitly ask for only one via the Ec2HostnameType + return new InetAddress[] { InetAddress.getByName(metadataResult) }; + } catch (IOException e) { + throw new IOException("IOException caught when fetching InetAddress from [" + metadataUrl + "]", e); + } finally { + IOUtils.closeWhileHandlingException(in); } - // only one address: because we explicitly ask for only one via the Ec2HostnameType - return new InetAddress[] { InetAddress.getByName(metadataResult) }; - } catch (IOException e) { - throw new IOException("IOException caught when fetching InetAddress from [" + metadataUrl + "]", e); - } finally { - IOUtils.closeWhileHandlingException(in); + } else { + throw new IOException("Missing ec2 meta-data url (AWS_EC2_METADATA_SERVICE_ENDPOINT)"); } } diff --git a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/SocketAccess.java b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/SocketAccess.java index 292a3b167f5ad..c6605002c4462 100644 --- a/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/SocketAccess.java +++ b/plugins/discovery-ec2/src/main/java/org/opensearch/discovery/ec2/SocketAccess.java @@ -50,6 +50,14 @@ final class SocketAccess { private SocketAccess() {} + public static void doPrivilegedVoid(Runnable action) { + SpecialPermission.check(); + AccessController.doPrivileged((PrivilegedAction) () -> { + action.run(); + return null; + }); + } + public static T doPrivileged(PrivilegedAction operation) { SpecialPermission.check(); return AccessController.doPrivileged(operation); diff --git a/plugins/discovery-ec2/src/main/plugin-metadata/plugin-security.policy b/plugins/discovery-ec2/src/main/plugin-metadata/plugin-security.policy index 65dd1d13b5a6d..8712fab93620e 100644 --- a/plugins/discovery-ec2/src/main/plugin-metadata/plugin-security.policy +++ b/plugins/discovery-ec2/src/main/plugin-metadata/plugin-security.policy @@ -42,4 +42,13 @@ grant { permission java.net.SocketPermission "*", "connect"; permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; + + permission java.util.PropertyPermission "aws.sharedCredentialsFile", "read,write"; + permission java.util.PropertyPermission "aws.configFile", "read,write"; + permission java.util.PropertyPermission "aws.region", "read,write"; + permission java.util.PropertyPermission "aws.accessKeyId", "read,write"; + permission java.util.PropertyPermission "aws.secretAccessKey", "read,write"; + permission java.util.PropertyPermission "opensearch.path.conf", "read,write"; + + permission java.io.FilePermission "config", "read"; }; diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEC2MockAPITestCase.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEC2MockAPITestCase.java deleted file mode 100644 index 51b48a8a45f21..0000000000000 --- a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEC2MockAPITestCase.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.discovery.ec2; - -import com.amazonaws.services.ec2.model.Instance; -import com.amazonaws.services.ec2.model.Tag; -import com.sun.net.httpserver.HttpServer; -import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.network.InetAddresses; -import org.opensearch.common.network.NetworkService; -import org.opensearch.common.settings.MockSecureSettings; -import org.opensearch.common.settings.Settings; -import org.opensearch.core.internal.io.IOUtils; -import org.opensearch.test.OpenSearchTestCase; -import org.opensearch.test.transport.MockTransportService; -import org.opensearch.threadpool.TestThreadPool; -import org.opensearch.threadpool.ThreadPool; -import org.junit.After; -import org.junit.Before; - -import javax.xml.XMLConstants; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamWriter; - -import java.io.StringWriter; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -import static java.nio.charset.StandardCharsets.UTF_8; - -@SuppressForbidden(reason = "use a http server") -public abstract class AbstractEC2MockAPITestCase extends OpenSearchTestCase { - - protected HttpServer httpServer; - - protected ThreadPool threadPool; - - protected MockTransportService transportService; - - protected NetworkService networkService = new NetworkService(Collections.emptyList()); - - @Before - public void setUp() throws Exception { - httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); - httpServer.start(); - threadPool = new TestThreadPool(EC2RetriesTests.class.getName()); - transportService = createTransportService(); - super.setUp(); - } - - protected abstract MockTransportService createTransportService(); - - protected Settings buildSettings(String accessKey) { - final InetSocketAddress address = httpServer.getAddress(); - final String endpoint = "http://" + InetAddresses.toUriString(address.getAddress()) + ":" + address.getPort(); - final MockSecureSettings mockSecure = new MockSecureSettings(); - mockSecure.setString(Ec2ClientSettings.ACCESS_KEY_SETTING.getKey(), accessKey); - mockSecure.setString(Ec2ClientSettings.SECRET_KEY_SETTING.getKey(), "ec2_secret"); - return Settings.builder().put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), endpoint).setSecureSettings(mockSecure).build(); - } - - @After - public void tearDown() throws Exception { - try { - IOUtils.close(transportService, () -> terminate(threadPool), () -> httpServer.stop(0)); - } finally { - super.tearDown(); - } - } - - /** - * Generates a XML response that describe the EC2 instances - * TODO: org.opensearch.discovery.ec2.AmazonEC2Fixture uses pretty much the same code. We should dry up that test fixture. - */ - static byte[] generateDescribeInstancesResponse(List instances) { - final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); - xmlOutputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); - - final StringWriter out = new StringWriter(); - XMLStreamWriter sw; - try { - sw = xmlOutputFactory.createXMLStreamWriter(out); - sw.writeStartDocument(); - - String namespace = "http://ec2.amazonaws.com/doc/2013-02-01/"; - sw.setDefaultNamespace(namespace); - sw.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "DescribeInstancesResponse", namespace); - { - sw.writeStartElement("requestId"); - sw.writeCharacters(UUID.randomUUID().toString()); - sw.writeEndElement(); - - sw.writeStartElement("reservationSet"); - { - for (Instance instance : instances) { - sw.writeStartElement("item"); - { - sw.writeStartElement("reservationId"); - sw.writeCharacters(UUID.randomUUID().toString()); - sw.writeEndElement(); - - sw.writeStartElement("instancesSet"); - { - sw.writeStartElement("item"); - { - sw.writeStartElement("instanceId"); - sw.writeCharacters(instance.getInstanceId()); - sw.writeEndElement(); - - sw.writeStartElement("imageId"); - sw.writeCharacters(instance.getImageId()); - sw.writeEndElement(); - - sw.writeStartElement("instanceState"); - { - sw.writeStartElement("code"); - sw.writeCharacters("16"); - sw.writeEndElement(); - - sw.writeStartElement("name"); - sw.writeCharacters("running"); - sw.writeEndElement(); - } - sw.writeEndElement(); - - sw.writeStartElement("privateDnsName"); - sw.writeCharacters(instance.getPrivateDnsName()); - sw.writeEndElement(); - - sw.writeStartElement("dnsName"); - sw.writeCharacters(instance.getPublicDnsName()); - sw.writeEndElement(); - - sw.writeStartElement("instanceType"); - sw.writeCharacters("m1.medium"); - sw.writeEndElement(); - - sw.writeStartElement("placement"); - { - sw.writeStartElement("availabilityZone"); - sw.writeCharacters("use-east-1e"); - sw.writeEndElement(); - - sw.writeEmptyElement("groupName"); - - sw.writeStartElement("tenancy"); - sw.writeCharacters("default"); - sw.writeEndElement(); - } - sw.writeEndElement(); - - sw.writeStartElement("privateIpAddress"); - sw.writeCharacters(instance.getPrivateIpAddress()); - sw.writeEndElement(); - - sw.writeStartElement("ipAddress"); - sw.writeCharacters(instance.getPublicIpAddress()); - sw.writeEndElement(); - - sw.writeStartElement("tagSet"); - for (Tag tag : instance.getTags()) { - sw.writeStartElement("item"); - { - sw.writeStartElement("key"); - sw.writeCharacters(tag.getKey()); - sw.writeEndElement(); - - sw.writeStartElement("value"); - sw.writeCharacters(tag.getValue()); - sw.writeEndElement(); - } - sw.writeEndElement(); - } - sw.writeEndElement(); - } - sw.writeEndElement(); - } - sw.writeEndElement(); - } - sw.writeEndElement(); - } - sw.writeEndElement(); - } - sw.writeEndElement(); - - sw.writeEndDocument(); - sw.flush(); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - return out.toString().getBytes(UTF_8); - } -} diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEc2DiscoveryTestCase.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEc2DiscoveryTestCase.java new file mode 100644 index 0000000000000..5250f8d88855e --- /dev/null +++ b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEc2DiscoveryTestCase.java @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.discovery.ec2; + +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; + +import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.io.PathUtils; +import org.opensearch.test.OpenSearchTestCase; + +import java.nio.file.Path; + +public abstract class AbstractEc2DiscoveryTestCase extends OpenSearchTestCase { + @Override + public void setUp() throws Exception { + super.setUp(); + setUpAwsProfile(); + } + + @Override + public void tearDown() throws Exception { + resetAwsProfile(); + super.tearDown(); + } + + private Path configPath() { + return PathUtils.get("config"); + } + + private String previousOpenSearchPathConf; + private String awsRegion; + private String awsAccessKeyId; + private String awsSecretAccessKey; + private String awsSharedCredentialsFile; + private String awsConfigFile; + + @SuppressForbidden(reason = "set predictable aws defaults") + private void setUpAwsProfile() throws Exception { + previousOpenSearchPathConf = SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); + awsRegion = SocketAccess.doPrivileged(() -> System.setProperty("aws.region", "us-west-2")); + awsAccessKeyId = SocketAccess.doPrivileged(() -> System.setProperty("aws.accessKeyId", "aws-access-key-id")); + awsSecretAccessKey = SocketAccess.doPrivileged(() -> System.setProperty("aws.secretAccessKey", "aws-secret-access-key")); + awsSharedCredentialsFile = System.getProperty(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property()); + awsConfigFile = System.getProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property()); + SocketAccess.doPrivilegedVoid(AwsEc2ServiceImpl::setDefaultAwsProfilePath); + } + + @SuppressForbidden(reason = "reset aws settings") + private void resetAwsProfile() throws Exception { + resetPropertyValue("opensearch.path.conf", previousOpenSearchPathConf); + resetPropertyValue("aws.region", awsRegion); + resetPropertyValue("aws.accessKeyId", awsAccessKeyId); + resetPropertyValue("aws.secretAccessKey", awsSecretAccessKey); + resetPropertyValue(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property(), awsSharedCredentialsFile); + resetPropertyValue(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), awsConfigFile); + } + + @SuppressForbidden(reason = "reset aws settings") + private void resetPropertyValue(String key, String value) { + if (value != null) { + SocketAccess.doPrivileged(() -> System.setProperty(key, value)); + } else { + SocketAccess.doPrivileged(() -> System.clearProperty(key)); + } + } +} diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEc2MockAPITestCase.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEc2MockAPITestCase.java new file mode 100644 index 0000000000000..595c51bdb1f47 --- /dev/null +++ b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AbstractEc2MockAPITestCase.java @@ -0,0 +1,231 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.discovery.ec2; + +import com.sun.net.httpserver.HttpServer; + +import software.amazon.awssdk.services.ec2.model.Instance; +import software.amazon.awssdk.services.ec2.model.Tag; + +import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.network.InetAddresses; +import org.opensearch.common.network.NetworkService; +import org.opensearch.common.settings.MockSecureSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; +import org.junit.After; +import org.junit.Before; + +import javax.xml.XMLConstants; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamWriter; + +import java.io.StringWriter; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import static java.nio.charset.StandardCharsets.UTF_8; + +@SuppressForbidden(reason = "use a http server") +public abstract class AbstractEc2MockAPITestCase extends AbstractEc2DiscoveryTestCase { + + protected HttpServer httpServer; + + protected ThreadPool threadPool; + + protected MockTransportService transportService; + + protected NetworkService networkService = new NetworkService(Collections.emptyList()); + + @Before + public void setUp() throws Exception { + httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); + httpServer.start(); + threadPool = new TestThreadPool(Ec2RetriesTests.class.getName()); + transportService = createTransportService(); + super.setUp(); + } + + protected abstract MockTransportService createTransportService(); + + protected Settings buildSettings(String accessKey) { + final InetSocketAddress address = httpServer.getAddress(); + final String endpoint = "http://" + InetAddresses.toUriString(address.getAddress()) + ":" + address.getPort(); + final MockSecureSettings mockSecure = new MockSecureSettings(); + mockSecure.setString(Ec2ClientSettings.ACCESS_KEY_SETTING.getKey(), accessKey); + mockSecure.setString(Ec2ClientSettings.SECRET_KEY_SETTING.getKey(), "ec2_secret"); + return Settings.builder() + .put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), endpoint) + .put(Ec2ClientSettings.REGION_SETTING.getKey(), "ec2_region") + .setSecureSettings(mockSecure) + .build(); + } + + @After + public void tearDown() throws Exception { + try { + IOUtils.close(transportService, () -> terminate(threadPool), () -> httpServer.stop(0)); + } finally { + super.tearDown(); + } + } + + /** + * Generates a XML response that describe the EC2 instances + * TODO: org.opensearch.discovery.ec2.AmazonEC2Fixture uses pretty much the same code. We should dry up that test fixture. + */ + static byte[] generateDescribeInstancesResponse(List instances) { + final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); + xmlOutputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); + + final StringWriter out = new StringWriter(); + XMLStreamWriter sw; + try { + sw = xmlOutputFactory.createXMLStreamWriter(out); + sw.writeStartDocument(); + + String namespace = "http://ec2.amazonaws.com/doc/2013-02-01/"; + sw.setDefaultNamespace(namespace); + sw.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "DescribeInstancesResponse", namespace); + { + sw.writeStartElement("requestId"); + sw.writeCharacters(UUID.randomUUID().toString()); + sw.writeEndElement(); + + sw.writeStartElement("reservationSet"); + { + for (Instance instance : instances) { + sw.writeStartElement("item"); + { + sw.writeStartElement("reservationId"); + sw.writeCharacters(UUID.randomUUID().toString()); + sw.writeEndElement(); + + sw.writeStartElement("instancesSet"); + { + sw.writeStartElement("item"); + { + sw.writeStartElement("instanceId"); + sw.writeCharacters(instance.instanceId()); + sw.writeEndElement(); + + sw.writeStartElement("imageId"); + sw.writeCharacters(instance.imageId()); + sw.writeEndElement(); + + sw.writeStartElement("instanceState"); + { + sw.writeStartElement("code"); + sw.writeCharacters("16"); + sw.writeEndElement(); + + sw.writeStartElement("name"); + sw.writeCharacters("running"); + sw.writeEndElement(); + } + sw.writeEndElement(); + + sw.writeStartElement("privateDnsName"); + sw.writeCharacters(instance.privateDnsName()); + sw.writeEndElement(); + + sw.writeStartElement("dnsName"); + sw.writeCharacters(instance.publicDnsName()); + sw.writeEndElement(); + + sw.writeStartElement("instanceType"); + sw.writeCharacters("m1.medium"); + sw.writeEndElement(); + + sw.writeStartElement("placement"); + { + sw.writeStartElement("availabilityZone"); + sw.writeCharacters("use-east-1e"); + sw.writeEndElement(); + + sw.writeEmptyElement("groupName"); + + sw.writeStartElement("tenancy"); + sw.writeCharacters("default"); + sw.writeEndElement(); + } + sw.writeEndElement(); + + sw.writeStartElement("privateIpAddress"); + sw.writeCharacters(instance.privateIpAddress()); + sw.writeEndElement(); + + sw.writeStartElement("ipAddress"); + sw.writeCharacters(instance.publicIpAddress()); + sw.writeEndElement(); + + sw.writeStartElement("tagSet"); + for (Tag tag : instance.tags()) { + sw.writeStartElement("item"); + { + sw.writeStartElement("key"); + sw.writeCharacters(tag.key()); + sw.writeEndElement(); + + sw.writeStartElement("value"); + sw.writeCharacters(tag.value()); + sw.writeEndElement(); + } + sw.writeEndElement(); + } + sw.writeEndElement(); + } + sw.writeEndElement(); + } + sw.writeEndElement(); + } + sw.writeEndElement(); + } + sw.writeEndElement(); + } + sw.writeEndElement(); + + sw.writeEndDocument(); + sw.flush(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return out.toString().getBytes(UTF_8); + } +} diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AwsEc2ServiceImplTests.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AwsEc2ServiceImplTests.java index 3150f96443695..81310f7e2e3c3 100644 --- a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AwsEc2ServiceImplTests.java +++ b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/AwsEc2ServiceImplTests.java @@ -32,40 +32,40 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.Protocol; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.BasicSessionCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; +import software.amazon.awssdk.core.Protocol; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.retry.RetryPolicy; +import software.amazon.awssdk.http.apache.ProxyConfiguration; + import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; -import org.opensearch.test.OpenSearchTestCase; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -public class AwsEc2ServiceImplTests extends OpenSearchTestCase { - - public void testAWSCredentialsWithSystemProviders() { - final AWSCredentialsProvider credentialsProvider = AwsEc2ServiceImpl.buildCredentials( +public class AwsEc2ServiceImplTests extends AbstractEc2DiscoveryTestCase { + public void testAwsCredentialsWithSystemProviders() { + final AwsCredentialsProvider credentialsProvider = AwsEc2ServiceImpl.buildCredentials( logger, Ec2ClientSettings.getClientSettings(Settings.EMPTY) ); - assertThat(credentialsProvider, instanceOf(DefaultAWSCredentialsProviderChain.class)); + assertThat(credentialsProvider, instanceOf(AwsCredentialsProvider.class)); } - public void testAWSCredentialsWithOpenSearchAwsSettings() { + public void testAwsCredentialsWithOpenSearchAwsSettings() { final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString("discovery.ec2.access_key", "aws_key"); secureSettings.setString("discovery.ec2.secret_key", "aws_secret"); - final AWSCredentials credentials = AwsEc2ServiceImpl.buildCredentials( + final AwsCredentials credentials = AwsEc2ServiceImpl.buildCredentials( logger, Ec2ClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) - ).getCredentials(); - assertThat(credentials.getAWSAccessKeyId(), is("aws_key")); - assertThat(credentials.getAWSSecretKey(), is("aws_secret")); + ).resolveCredentials(); + assertThat(credentials.accessKeyId(), is("aws_key")); + assertThat(credentials.secretAccessKey(), is("aws_secret")); } public void testAWSSessionCredentialsWithOpenSearchAwsSettings() { @@ -73,43 +73,39 @@ public void testAWSSessionCredentialsWithOpenSearchAwsSettings() { secureSettings.setString("discovery.ec2.access_key", "aws_key"); secureSettings.setString("discovery.ec2.secret_key", "aws_secret"); secureSettings.setString("discovery.ec2.session_token", "aws_session_token"); - final BasicSessionCredentials credentials = (BasicSessionCredentials) AwsEc2ServiceImpl.buildCredentials( + final AwsSessionCredentials credentials = (AwsSessionCredentials) AwsEc2ServiceImpl.buildCredentials( logger, Ec2ClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) - ).getCredentials(); - assertThat(credentials.getAWSAccessKeyId(), is("aws_key")); - assertThat(credentials.getAWSSecretKey(), is("aws_secret")); - assertThat(credentials.getSessionToken(), is("aws_session_token")); + ).resolveCredentials(); + assertThat(credentials.accessKeyId(), is("aws_key")); + assertThat(credentials.secretAccessKey(), is("aws_secret")); + assertThat(credentials.sessionToken(), is("aws_session_token")); } - public void testDeprecationOfLoneAccessKey() { + public void testRejectionOfLoneAccessKey() { final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString("discovery.ec2.access_key", "aws_key"); - final AWSCredentials credentials = AwsEc2ServiceImpl.buildCredentials( - logger, - Ec2ClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) - ).getCredentials(); - assertThat(credentials.getAWSAccessKeyId(), is("aws_key")); - assertThat(credentials.getAWSSecretKey(), is("")); - assertSettingDeprecationsAndWarnings( - new String[] {}, - "Setting [discovery.ec2.access_key] is set but [discovery.ec2.secret_key] is not, which will be unsupported in future" + SettingsException e = expectThrows( + SettingsException.class, + () -> AwsEc2ServiceImpl.buildCredentials( + logger, + Ec2ClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) + ) ); + assertThat(e.getMessage(), is("Setting [discovery.ec2.access_key] is set but [discovery.ec2.secret_key] is not")); } public void testDeprecationOfLoneSecretKey() { final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString("discovery.ec2.secret_key", "aws_secret"); - final AWSCredentials credentials = AwsEc2ServiceImpl.buildCredentials( - logger, - Ec2ClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) - ).getCredentials(); - assertThat(credentials.getAWSAccessKeyId(), is("")); - assertThat(credentials.getAWSSecretKey(), is("aws_secret")); - assertSettingDeprecationsAndWarnings( - new String[] {}, - "Setting [discovery.ec2.secret_key] is set but [discovery.ec2.access_key] is not, which will be unsupported in future" + SettingsException e = expectThrows( + SettingsException.class, + () -> AwsEc2ServiceImpl.buildCredentials( + logger, + Ec2ClientSettings.getClientSettings(Settings.builder().setSecureSettings(secureSettings).build()) + ) ); + assertThat(e.getMessage(), is("Setting [discovery.ec2.secret_key] is set but [discovery.ec2.access_key] is not")); } public void testRejectionOfLoneSessionToken() { @@ -129,44 +125,81 @@ public void testRejectionOfLoneSessionToken() { } public void testAWSDefaultConfiguration() { - launchAWSConfigurationTest(Settings.EMPTY, Protocol.HTTPS, null, -1, null, null, ClientConfiguration.DEFAULT_SOCKET_TIMEOUT); + // proxy configuration + final ProxyConfiguration proxyConfiguration = AwsEc2ServiceImpl.buildProxyConfiguration( + logger, + Ec2ClientSettings.getClientSettings(Settings.EMPTY) + ); + + assertNull(proxyConfiguration.scheme()); + assertNull(proxyConfiguration.host()); + assertThat(proxyConfiguration.port(), is(0)); + assertNull(proxyConfiguration.username()); + assertNull(proxyConfiguration.password()); + + // retry policy + RetryPolicy retryPolicyConfiguration = AwsEc2ServiceImpl.buildRetryPolicy( + logger, + Ec2ClientSettings.getClientSettings(Settings.EMPTY) + ); + + assertThat(retryPolicyConfiguration.numRetries(), is(10)); + + final AwsCredentials credentials = AwsEc2ServiceImpl.buildCredentials(logger, Ec2ClientSettings.getClientSettings(Settings.EMPTY)) + .resolveCredentials(); + + assertThat(credentials.accessKeyId(), is("aws-access-key-id")); + assertThat(credentials.secretAccessKey(), is("aws-secret-access-key")); + + ClientOverrideConfiguration clientOverrideConfiguration = AwsEc2ServiceImpl.buildOverrideConfiguration( + logger, + Ec2ClientSettings.getClientSettings(Settings.EMPTY) + ); + assertTrue(clientOverrideConfiguration.retryPolicy().isPresent()); + assertThat(clientOverrideConfiguration.retryPolicy().get().numRetries(), is(10)); } public void testAWSConfigurationWithAwsSettings() { final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString("discovery.ec2.proxy.username", "aws_proxy_username"); secureSettings.setString("discovery.ec2.proxy.password", "aws_proxy_password"); + final Settings settings = Settings.builder() .put("discovery.ec2.protocol", "http") - .put("discovery.ec2.proxy.host", "aws_proxy_host") + // NOTE: a host cannot contain the _ character when parsed by URI, hence aws-proxy-host and not aws_proxy_host + .put("discovery.ec2.proxy.host", "aws-proxy-host") .put("discovery.ec2.proxy.port", 8080) .put("discovery.ec2.read_timeout", "10s") .setSecureSettings(secureSettings) .build(); - launchAWSConfigurationTest(settings, Protocol.HTTP, "aws_proxy_host", 8080, "aws_proxy_username", "aws_proxy_password", 10000); - } - protected void launchAWSConfigurationTest( - Settings settings, - Protocol expectedProtocol, - String expectedProxyHost, - int expectedProxyPort, - String expectedProxyUsername, - String expectedProxyPassword, - int expectedReadTimeout - ) { - final ClientConfiguration configuration = AwsEc2ServiceImpl.buildConfiguration( + // proxy configuration + final ProxyConfiguration proxyConfiguration = AwsEc2ServiceImpl.buildProxyConfiguration( logger, Ec2ClientSettings.getClientSettings(settings) ); - assertThat(configuration.getResponseMetadataCacheSize(), is(0)); - assertThat(configuration.getProtocol(), is(expectedProtocol)); - assertThat(configuration.getProxyHost(), is(expectedProxyHost)); - assertThat(configuration.getProxyPort(), is(expectedProxyPort)); - assertThat(configuration.getProxyUsername(), is(expectedProxyUsername)); - assertThat(configuration.getProxyPassword(), is(expectedProxyPassword)); - assertThat(configuration.getSocketTimeout(), is(expectedReadTimeout)); - } + assertThat(proxyConfiguration.scheme(), is(Protocol.HTTP.toString())); + assertThat(proxyConfiguration.host(), is("aws-proxy-host")); + assertThat(proxyConfiguration.port(), is(8080)); + assertThat(proxyConfiguration.username(), is("aws_proxy_username")); + assertThat(proxyConfiguration.password(), is("aws_proxy_password")); + + // retry policy + RetryPolicy retryPolicyConfiguration = AwsEc2ServiceImpl.buildRetryPolicy(logger, Ec2ClientSettings.getClientSettings(settings)); + assertThat(retryPolicyConfiguration.numRetries(), is(10)); + + final AwsCredentials credentials = AwsEc2ServiceImpl.buildCredentials(logger, Ec2ClientSettings.getClientSettings(Settings.EMPTY)) + .resolveCredentials(); + assertThat(credentials.accessKeyId(), is("aws-access-key-id")); + assertThat(credentials.secretAccessKey(), is("aws-secret-access-key")); + + ClientOverrideConfiguration clientOverrideConfiguration = AwsEc2ServiceImpl.buildOverrideConfiguration( + logger, + Ec2ClientSettings.getClientSettings(Settings.EMPTY) + ); + assertTrue(clientOverrideConfiguration.retryPolicy().isPresent()); + assertThat(clientOverrideConfiguration.retryPolicy().get().numRetries(), is(10)); + } } diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/EC2RetriesTests.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/EC2RetriesTests.java deleted file mode 100644 index 9443eed5efae9..0000000000000 --- a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/EC2RetriesTests.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.discovery.ec2; - -import com.amazonaws.http.HttpMethodName; -import com.amazonaws.services.ec2.model.Instance; -import org.apache.http.HttpStatus; -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; -import org.opensearch.Version; -import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.io.Streams; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.discovery.SeedHostsProvider; -import org.opensearch.discovery.SeedHostsResolver; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; -import org.opensearch.test.transport.MockTransportService; -import org.opensearch.transport.TransportService; -import org.opensearch.transport.nio.MockNioTransport; -import org.hamcrest.Matchers; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.hamcrest.Matchers.aMapWithSize; -import static org.hamcrest.Matchers.is; - -@SuppressForbidden(reason = "use a http server") -public class EC2RetriesTests extends AbstractEC2MockAPITestCase { - - @Override - protected MockTransportService createTransportService() { - return new MockTransportService( - Settings.EMPTY, - new MockNioTransport( - Settings.EMPTY, - Version.CURRENT, - threadPool, - networkService, - PageCacheRecycler.NON_RECYCLING_INSTANCE, - new NamedWriteableRegistry(Collections.emptyList()), - new NoneCircuitBreakerService() - ), - threadPool, - TransportService.NOOP_TRANSPORT_INTERCEPTOR, - null - ); - } - - public void testEC2DiscoveryRetriesOnRateLimiting() throws IOException { - final String accessKey = "ec2_access"; - final List hosts = Collections.singletonList("127.0.0.1:9300"); - final Map failedRequests = new ConcurrentHashMap<>(); - // retry the same request 5 times at most - final int maxRetries = randomIntBetween(1, 5); - httpServer.createContext("/", exchange -> { - if (exchange.getRequestMethod().equals(HttpMethodName.POST.name())) { - final String request = Streams.readFully(exchange.getRequestBody()).utf8ToString(); - final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); - if (userAgent != null && userAgent.startsWith("aws-sdk-java")) { - final String auth = exchange.getRequestHeaders().getFirst("Authorization"); - if (auth == null || auth.contains(accessKey) == false) { - throw new IllegalArgumentException("wrong access key: " + auth); - } - if (failedRequests.compute( - exchange.getRequestHeaders().getFirst("Amz-sdk-invocation-id"), - (requestId, count) -> (count == null ? 0 : count) + 1 - ) < maxRetries) { - exchange.sendResponseHeaders(HttpStatus.SC_SERVICE_UNAVAILABLE, -1); - return; - } - // Simulate an EC2 DescribeInstancesResponse - byte[] responseBody = null; - for (NameValuePair parse : URLEncodedUtils.parse(request, UTF_8)) { - if ("Action".equals(parse.getName())) { - responseBody = generateDescribeInstancesResponse( - hosts.stream().map(address -> new Instance().withPublicIpAddress(address)).collect(Collectors.toList()) - ); - break; - } - } - responseBody = responseBody == null ? new byte[0] : responseBody; - exchange.getResponseHeaders().set("Content-Type", "text/xml; charset=UTF-8"); - exchange.sendResponseHeaders(HttpStatus.SC_OK, responseBody.length); - exchange.getResponseBody().write(responseBody); - return; - } - } - fail("did not send response"); - }); - try (Ec2DiscoveryPlugin plugin = new Ec2DiscoveryPlugin(buildSettings(accessKey))) { - final SeedHostsProvider seedHostsProvider = plugin.getSeedHostProviders(transportService, networkService).get("ec2").get(); - final SeedHostsResolver resolver = new SeedHostsResolver("test", Settings.EMPTY, transportService, seedHostsProvider); - resolver.start(); - final List addressList = seedHostsProvider.getSeedAddresses(null); - assertThat(addressList, Matchers.hasSize(1)); - assertThat(addressList.get(0).toString(), is(hosts.get(0))); - assertThat(failedRequests, aMapWithSize(1)); - assertThat(failedRequests.values().iterator().next(), is(maxRetries)); - } - } -} diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2DiscoveryPluginTests.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2DiscoveryPluginTests.java index cb19c0d4255ac..bde508a0afe96 100644 --- a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2DiscoveryPluginTests.java +++ b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2DiscoveryPluginTests.java @@ -32,17 +32,18 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.auth.BasicSessionCredentials; -import com.amazonaws.services.ec2.AbstractAmazonEC2; -import com.amazonaws.services.ec2.AmazonEC2; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.http.apache.ProxyConfiguration; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.ec2.Ec2Client; + import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; import org.opensearch.node.Node; -import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; import java.io.UncheckedIOException; @@ -51,10 +52,8 @@ import java.util.Arrays; import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; - -public class Ec2DiscoveryPluginTests extends OpenSearchTestCase { +public class Ec2DiscoveryPluginTests extends AbstractEc2DiscoveryTestCase { private Settings getNodeAttributes(Settings settings, String url) { final Settings realSettings = Settings.builder().put(AwsEc2Service.AUTO_ATTRIBUTE_SETTING.getKey(), true).put(settings).build(); return Ec2DiscoveryPlugin.getAvailabilityZoneNodeAttributes(realSettings, url); @@ -103,16 +102,32 @@ public void testNodeAttributesErrorLenient() throws Exception { public void testDefaultEndpoint() throws IOException { try (Ec2DiscoveryPluginMock plugin = new Ec2DiscoveryPluginMock(Settings.EMPTY)) { - final String endpoint = ((AmazonEC2Mock) plugin.ec2Service.client().get()).endpoint; - assertThat(endpoint, is("")); + final String endpoint = ((MockEc2Client) plugin.ec2Service.client().get()).endpoint; + assertEquals(endpoint, ""); + } + } + + public void testDefaultRegion() throws IOException { + final Settings settings = Settings.builder().build(); + try (Ec2DiscoveryPluginMock plugin = new Ec2DiscoveryPluginMock(settings)) { + final String region = ((MockEc2Client) plugin.ec2Service.client().get()).region; + assertEquals(region, ""); + } + } + + public void testSpecificRegion() throws IOException { + final Settings settings = Settings.builder().put(Ec2ClientSettings.REGION_SETTING.getKey(), "us-west-2").build(); + try (Ec2DiscoveryPluginMock plugin = new Ec2DiscoveryPluginMock(settings)) { + final String region = ((MockEc2Client) plugin.ec2Service.client().get()).region; + assertEquals(region, Region.US_WEST_2.toString()); } } public void testSpecificEndpoint() throws IOException { final Settings settings = Settings.builder().put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), "ec2.endpoint").build(); try (Ec2DiscoveryPluginMock plugin = new Ec2DiscoveryPluginMock(settings)) { - final String endpoint = ((AmazonEC2Mock) plugin.ec2Service.client().get()).endpoint; - assertThat(endpoint, is("ec2.endpoint")); + final String endpoint = ((MockEc2Client) plugin.ec2Service.client().get()).endpoint; + assertEquals(endpoint, "ec2.endpoint"); } } @@ -127,8 +142,9 @@ public void testClientSettingsReInit() throws IOException { mockSecure1.setString(Ec2ClientSettings.PROXY_USERNAME_SETTING.getKey(), "proxy_username_1"); mockSecure1.setString(Ec2ClientSettings.PROXY_PASSWORD_SETTING.getKey(), "proxy_password_1"); final Settings settings1 = Settings.builder() - .put(Ec2ClientSettings.PROXY_HOST_SETTING.getKey(), "proxy_host_1") + .put(Ec2ClientSettings.PROXY_HOST_SETTING.getKey(), "proxy-host-1") .put(Ec2ClientSettings.PROXY_PORT_SETTING.getKey(), 881) + .put(Ec2ClientSettings.REGION_SETTING.getKey(), "ec2_region") .put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), "ec2_endpoint_1") .setSecureSettings(mockSecure1) .build(); @@ -142,62 +158,84 @@ public void testClientSettingsReInit() throws IOException { mockSecure2.setString(Ec2ClientSettings.PROXY_USERNAME_SETTING.getKey(), "proxy_username_2"); mockSecure2.setString(Ec2ClientSettings.PROXY_PASSWORD_SETTING.getKey(), "proxy_password_2"); final Settings settings2 = Settings.builder() - .put(Ec2ClientSettings.PROXY_HOST_SETTING.getKey(), "proxy_host_2") + .put(Ec2ClientSettings.PROXY_HOST_SETTING.getKey(), "proxy-host-2") .put(Ec2ClientSettings.PROXY_PORT_SETTING.getKey(), 882) + .put(Ec2ClientSettings.REGION_SETTING.getKey(), "ec2_region") .put(Ec2ClientSettings.ENDPOINT_SETTING.getKey(), "ec2_endpoint_2") .setSecureSettings(mockSecure2) .build(); try (Ec2DiscoveryPluginMock plugin = new Ec2DiscoveryPluginMock(settings1)) { - try (AmazonEc2Reference clientReference = plugin.ec2Service.client()) { + try (AmazonEc2ClientReference clientReference = plugin.ec2Service.client()) { { - final AWSCredentials credentials = ((AmazonEC2Mock) clientReference.get()).credentials.getCredentials(); - assertThat(credentials.getAWSAccessKeyId(), is("ec2_access_1")); - assertThat(credentials.getAWSSecretKey(), is("ec2_secret_1")); + final MockEc2Client mockEc2Client = (MockEc2Client) clientReference.get(); + assertEquals(mockEc2Client.endpoint, "ec2_endpoint_1"); + + final AwsCredentials credentials = mockEc2Client.credentials.resolveCredentials(); + assertEquals(credentials.accessKeyId(), "ec2_access_1"); + assertEquals(credentials.secretAccessKey(), "ec2_secret_1"); if (mockSecure1HasSessionToken) { - assertThat(credentials, instanceOf(BasicSessionCredentials.class)); - assertThat(((BasicSessionCredentials) credentials).getSessionToken(), is("ec2_session_token_1")); + assertThat(credentials, instanceOf(AwsSessionCredentials.class)); + assertEquals(((AwsSessionCredentials) credentials).sessionToken(), "ec2_session_token_1"); } else { - assertThat(credentials, instanceOf(BasicAWSCredentials.class)); + assertThat(credentials, instanceOf(AwsBasicCredentials.class)); } - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyUsername(), is("proxy_username_1")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyPassword(), is("proxy_password_1")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyHost(), is("proxy_host_1")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyPort(), is(881)); - assertThat(((AmazonEC2Mock) clientReference.get()).endpoint, is("ec2_endpoint_1")); + + assertEquals( + mockEc2Client.proxyConfiguration.toString(), + "ProxyConfiguration(endpoint=https://proxy-host-1:881, username=proxy_username_1, preemptiveBasicAuthenticationEnabled=false)" + ); + assertEquals(mockEc2Client.proxyConfiguration.host(), "proxy-host-1"); + assertEquals(mockEc2Client.proxyConfiguration.port(), 881); + assertEquals(mockEc2Client.proxyConfiguration.username(), "proxy_username_1"); + assertEquals(mockEc2Client.proxyConfiguration.password(), "proxy_password_1"); } // reload secure settings2 plugin.reload(settings2); // client is not released, it is still using the old settings { - final AWSCredentials credentials = ((AmazonEC2Mock) clientReference.get()).credentials.getCredentials(); + final MockEc2Client mockEc2Client = (MockEc2Client) clientReference.get(); + assertEquals(mockEc2Client.endpoint, "ec2_endpoint_1"); + + final AwsCredentials credentials = ((MockEc2Client) clientReference.get()).credentials.resolveCredentials(); if (mockSecure1HasSessionToken) { - assertThat(credentials, instanceOf(BasicSessionCredentials.class)); - assertThat(((BasicSessionCredentials) credentials).getSessionToken(), is("ec2_session_token_1")); + assertThat(credentials, instanceOf(AwsSessionCredentials.class)); + assertEquals(((AwsSessionCredentials) credentials).sessionToken(), "ec2_session_token_1"); } else { - assertThat(credentials, instanceOf(BasicAWSCredentials.class)); + assertThat(credentials, instanceOf(AwsBasicCredentials.class)); } - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyUsername(), is("proxy_username_1")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyPassword(), is("proxy_password_1")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyHost(), is("proxy_host_1")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyPort(), is(881)); - assertThat(((AmazonEC2Mock) clientReference.get()).endpoint, is("ec2_endpoint_1")); + + assertEquals( + mockEc2Client.proxyConfiguration.toString(), + "ProxyConfiguration(endpoint=https://proxy-host-1:881, username=proxy_username_1, preemptiveBasicAuthenticationEnabled=false)" + ); + assertEquals(mockEc2Client.proxyConfiguration.host(), "proxy-host-1"); + assertEquals(mockEc2Client.proxyConfiguration.port(), 881); + assertEquals(mockEc2Client.proxyConfiguration.username(), "proxy_username_1"); + assertEquals(mockEc2Client.proxyConfiguration.password(), "proxy_password_1"); } } - try (AmazonEc2Reference clientReference = plugin.ec2Service.client()) { - final AWSCredentials credentials = ((AmazonEC2Mock) clientReference.get()).credentials.getCredentials(); - assertThat(credentials.getAWSAccessKeyId(), is("ec2_access_2")); - assertThat(credentials.getAWSSecretKey(), is("ec2_secret_2")); + try (AmazonEc2ClientReference clientReference = plugin.ec2Service.client()) { + final MockEc2Client mockEc2Client = (MockEc2Client) clientReference.get(); + assertEquals(mockEc2Client.endpoint, "ec2_endpoint_2"); + + final AwsCredentials credentials = ((MockEc2Client) clientReference.get()).credentials.resolveCredentials(); + assertEquals(credentials.accessKeyId(), "ec2_access_2"); + assertEquals(credentials.secretAccessKey(), "ec2_secret_2"); if (mockSecure2HasSessionToken) { - assertThat(credentials, instanceOf(BasicSessionCredentials.class)); - assertThat(((BasicSessionCredentials) credentials).getSessionToken(), is("ec2_session_token_2")); + assertThat(credentials, instanceOf(AwsSessionCredentials.class)); + assertEquals(((AwsSessionCredentials) credentials).sessionToken(), "ec2_session_token_2"); } else { - assertThat(credentials, instanceOf(BasicAWSCredentials.class)); + assertThat(credentials, instanceOf(AwsBasicCredentials.class)); } - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyUsername(), is("proxy_username_2")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyPassword(), is("proxy_password_2")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyHost(), is("proxy_host_2")); - assertThat(((AmazonEC2Mock) clientReference.get()).configuration.getProxyPort(), is(882)); - assertThat(((AmazonEC2Mock) clientReference.get()).endpoint, is("ec2_endpoint_2")); + + assertEquals( + mockEc2Client.proxyConfiguration.toString(), + "ProxyConfiguration(endpoint=https://proxy-host-2:882, username=proxy_username_2, preemptiveBasicAuthenticationEnabled=false)" + ); + assertEquals(mockEc2Client.proxyConfiguration.host(), "proxy-host-2"); + assertEquals(mockEc2Client.proxyConfiguration.port(), 882); + assertEquals(mockEc2Client.proxyConfiguration.username(), "proxy_username_2"); + assertEquals(mockEc2Client.proxyConfiguration.password(), "proxy_password_2"); } } } @@ -207,26 +245,53 @@ private static class Ec2DiscoveryPluginMock extends Ec2DiscoveryPlugin { Ec2DiscoveryPluginMock(Settings settings) { super(settings, new AwsEc2ServiceImpl() { @Override - AmazonEC2 buildClient(AWSCredentialsProvider credentials, ClientConfiguration configuration, String endpoint) { - return new AmazonEC2Mock(credentials, configuration, endpoint); + protected Ec2Client buildClient( + AwsCredentialsProvider credentials, + ProxyConfiguration proxyConfiguration, + ClientOverrideConfiguration overrideConfiguration, + String endpoint, + String region, + long readTimeoutMillis + ) { + return new MockEc2Client(credentials, proxyConfiguration, overrideConfiguration, endpoint, region, readTimeoutMillis); } }); } } - private static class AmazonEC2Mock extends AbstractAmazonEC2 { + private static class MockEc2Client implements Ec2Client { String endpoint; - final AWSCredentialsProvider credentials; - final ClientConfiguration configuration; + final String region; + final AwsCredentialsProvider credentials; + final ClientOverrideConfiguration clientOverrideConfiguration; + final ProxyConfiguration proxyConfiguration; + final long readTimeoutMillis; - AmazonEC2Mock(AWSCredentialsProvider credentials, ClientConfiguration configuration, String endpoint) { + MockEc2Client( + AwsCredentialsProvider credentials, + ProxyConfiguration proxyConfiguration, + ClientOverrideConfiguration clientOverrideConfiguration, + String endpoint, + String region, + long readTimeoutMillis + ) { this.credentials = credentials; - this.configuration = configuration; + this.proxyConfiguration = proxyConfiguration; + this.clientOverrideConfiguration = clientOverrideConfiguration; this.endpoint = endpoint; + this.region = region; + this.readTimeoutMillis = readTimeoutMillis; } @Override - public void shutdown() {} + public String serviceName() { + return "ec2"; + } + + @Override + public void close() { + // ignore + } } } diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2DiscoveryTests.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2DiscoveryTests.java index f1870a1c487e0..02e1ff40f7ed6 100644 --- a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2DiscoveryTests.java +++ b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2DiscoveryTests.java @@ -32,11 +32,11 @@ package org.opensearch.discovery.ec2; -import com.amazonaws.http.HttpMethodName; -import com.amazonaws.services.ec2.model.Instance; -import com.amazonaws.services.ec2.model.InstanceState; -import com.amazonaws.services.ec2.model.InstanceStateName; -import com.amazonaws.services.ec2.model.Tag; +import software.amazon.awssdk.services.ec2.model.Instance; +import software.amazon.awssdk.services.ec2.model.InstanceState; +import software.amazon.awssdk.services.ec2.model.InstanceStateName; +import software.amazon.awssdk.services.ec2.model.Tag; + import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; @@ -45,9 +45,10 @@ import org.opensearch.common.io.Streams; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.transport.MockTransportService; import org.opensearch.transport.Transport; import org.opensearch.transport.TransportService; @@ -72,7 +73,7 @@ import static org.hamcrest.Matchers.is; @SuppressForbidden(reason = "use a http server") -public class Ec2DiscoveryTests extends AbstractEC2MockAPITestCase { +public class Ec2DiscoveryTests extends AbstractEc2MockAPITestCase { private static final String SUFFIX_PRIVATE_DNS = ".ec2.internal"; private static final String PREFIX_PRIVATE_DNS = "mock-ip-"; @@ -91,7 +92,8 @@ protected MockTransportService createTransportService() { new NetworkService(Collections.emptyList()), PageCacheRecycler.NON_RECYCLING_INSTANCE, writableRegistry(), - new NoneCircuitBreakerService() + new NoneCircuitBreakerService(), + NoopTracer.INSTANCE ) { @Override public TransportAddress[] addressesFromString(String address) { @@ -99,7 +101,14 @@ public TransportAddress[] addressesFromString(String address) { return new TransportAddress[] { poorMansDNS.getOrDefault(address, buildNewFakeTransportAddress()) }; } }; - return new MockTransportService(Settings.EMPTY, transport, threadPool, TransportService.NOOP_TRANSPORT_INTERCEPTOR, null); + return new MockTransportService( + Settings.EMPTY, + transport, + threadPool, + TransportService.NOOP_TRANSPORT_INTERCEPTOR, + null, + NoopTracer.INSTANCE + ); } protected List buildDynamicHosts(Settings nodeSettings, int nodes) { @@ -111,7 +120,7 @@ protected List buildDynamicHosts(Settings nodeSettings, int no try (Ec2DiscoveryPlugin plugin = new Ec2DiscoveryPlugin(buildSettings(accessKey))) { AwsEc2SeedHostsProvider provider = new AwsEc2SeedHostsProvider(nodeSettings, transportService, plugin.ec2Service); httpServer.createContext("/", exchange -> { - if (exchange.getRequestMethod().equals(HttpMethodName.POST.name())) { + if (exchange.getRequestMethod().equals("POST")) { final String request = Streams.readFully(exchange.getRequestBody()).toBytesRef().utf8ToString(); final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); if (userAgent != null && userAgent.startsWith("aws-sdk-java")) { @@ -124,7 +133,7 @@ protected List buildDynamicHosts(Settings nodeSettings, int no final String[] params = request.split("&"); Arrays.stream(params).filter(entry -> entry.startsWith("Filter.") && entry.contains("=tag%3A")).forEach(entry -> { final int startIndex = "Filter.".length(); - final int filterId = Integer.parseInt(entry.substring(startIndex, entry.indexOf(".", startIndex))); + final String filterId = entry.substring(startIndex, entry.indexOf(".", startIndex)); tagsIncluded.put( entry.substring(entry.indexOf("=tag%3A") + "=tag%3A".length()), Arrays.stream(params) @@ -135,25 +144,26 @@ protected List buildDynamicHosts(Settings nodeSettings, int no }); final List instances = IntStream.range(1, nodes + 1).mapToObj(node -> { final String instanceId = "node" + node; - final Instance instance = new Instance().withInstanceId(instanceId) - .withState(new InstanceState().withName(InstanceStateName.Running)) - .withPrivateDnsName(PREFIX_PRIVATE_DNS + instanceId + SUFFIX_PRIVATE_DNS) - .withPublicDnsName(PREFIX_PUBLIC_DNS + instanceId + SUFFIX_PUBLIC_DNS) - .withPrivateIpAddress(PREFIX_PRIVATE_IP + node) - .withPublicIpAddress(PREFIX_PUBLIC_IP + node); + final Instance.Builder instanceBuilder = Instance.builder() + .instanceId(instanceId) + .state(InstanceState.builder().name(InstanceStateName.RUNNING).build()) + .privateDnsName(PREFIX_PRIVATE_DNS + instanceId + SUFFIX_PRIVATE_DNS) + .publicDnsName(PREFIX_PUBLIC_DNS + instanceId + SUFFIX_PUBLIC_DNS) + .privateIpAddress(PREFIX_PRIVATE_IP + node) + .publicIpAddress(PREFIX_PUBLIC_IP + node); if (tagsList != null) { - instance.setTags(tagsList.get(node - 1)); + instanceBuilder.tags(tagsList.get(node - 1)); } - return instance; + return instanceBuilder.build(); }) .filter( instance -> tagsIncluded.entrySet() .stream() .allMatch( - entry -> instance.getTags() + entry -> instance.tags() .stream() - .filter(t -> t.getKey().equals(entry.getKey())) - .map(Tag::getValue) + .filter(t -> t.key().equals(entry.getKey())) + .map(Tag::value) .collect(Collectors.toList()) .containsAll(entry.getValue()) ) @@ -273,10 +283,10 @@ public void testFilterByTags() throws InterruptedException { for (int node = 0; node < nodes; node++) { List tags = new ArrayList<>(); if (randomBoolean()) { - tags.add(new Tag("stage", "prod")); + tags.add(Tag.builder().key("stage").value("prod").build()); prodInstances++; } else { - tags.add(new Tag("stage", "dev")); + tags.add(Tag.builder().key("stage").value("dev").build()); } tagsList.add(tags); } @@ -296,15 +306,15 @@ public void testFilterByMultipleTags() throws InterruptedException { for (int node = 0; node < nodes; node++) { List tags = new ArrayList<>(); if (randomBoolean()) { - tags.add(new Tag("stage", "prod")); + tags.add(Tag.builder().key("stage").value("prod").build()); if (randomBoolean()) { - tags.add(new Tag("stage", "preprod")); + tags.add(Tag.builder().key("stage").value("preprod").build()); prodInstances++; } } else { - tags.add(new Tag("stage", "dev")); + tags.add(Tag.builder().key("stage").value("dev").build()); if (randomBoolean()) { - tags.add(new Tag("stage", "preprod")); + tags.add(Tag.builder().key("stage").value("preprod").build()); } } tagsList.add(tags); @@ -331,7 +341,7 @@ public void testReadHostFromTag() throws UnknownHostException { for (int node = 0; node < nodes; node++) { List tags = new ArrayList<>(); - tags.add(new Tag("foo", "node" + (node + 1))); + tags.add(Tag.builder().key("foo").value("node" + (node + 1)).build()); tagsList.add(tags); } diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2NetworkTests.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2NetworkTests.java index f7faf33bf6b30..b4ed613c0d8dd 100644 --- a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2NetworkTests.java +++ b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2NetworkTests.java @@ -34,13 +34,11 @@ import com.sun.net.httpserver.HttpServer; -import org.opensearch.common.Strings; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; -import org.opensearch.rest.RestStatus; -import org.opensearch.test.OpenSearchTestCase; - +import org.opensearch.core.common.Strings; +import org.opensearch.core.rest.RestStatus; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -55,7 +53,6 @@ import java.util.Collections; import java.util.function.BiConsumer; -import static com.amazonaws.SDKGlobalConfiguration.EC2_METADATA_SERVICE_OVERRIDE_SYSTEM_PROPERTY; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.equalTo; @@ -67,7 +64,7 @@ * They aren't. */ @SuppressForbidden(reason = "use http server") -public class Ec2NetworkTests extends OpenSearchTestCase { +public class Ec2NetworkTests extends AbstractEc2DiscoveryTestCase { private static HttpServer httpServer; @@ -97,7 +94,7 @@ public void setup() { // redirect EC2 metadata service to httpServer AccessController.doPrivileged( (PrivilegedAction) () -> System.setProperty( - EC2_METADATA_SERVICE_OVERRIDE_SYSTEM_PROPERTY, + "aws.ec2MetadataServiceEndpoint", "http://" + httpServer.getAddress().getHostName() + ":" + httpServer.getAddress().getPort() ) ); @@ -122,7 +119,7 @@ public void testNetworkHostEc2() throws IOException { public void testNetworkHostUnableToResolveEc2() { // redirect EC2 metadata service to unknown location AccessController.doPrivileged( - (PrivilegedAction) () -> System.setProperty(EC2_METADATA_SERVICE_OVERRIDE_SYSTEM_PROPERTY, "http://127.0.0.1/") + (PrivilegedAction) () -> System.setProperty("aws.ec2MetadataServiceEndpoint", "http://127.0.0.1/") ); try { diff --git a/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2RetriesTests.java b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2RetriesTests.java new file mode 100644 index 0000000000000..ce097667f9c4b --- /dev/null +++ b/plugins/discovery-ec2/src/test/java/org/opensearch/discovery/ec2/Ec2RetriesTests.java @@ -0,0 +1,144 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.discovery.ec2; + +import software.amazon.awssdk.services.ec2.model.Instance; + +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.opensearch.Version; +import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.io.Streams; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.PageCacheRecycler; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.discovery.SeedHostsProvider; +import org.opensearch.discovery.SeedHostsResolver; +import org.opensearch.telemetry.tracing.noop.NoopTracer; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.transport.TransportService; +import org.opensearch.transport.nio.MockNioTransport; +import org.hamcrest.Matchers; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.is; + +@SuppressForbidden(reason = "use a http server") +public class Ec2RetriesTests extends AbstractEc2MockAPITestCase { + @Override + protected MockTransportService createTransportService() { + return new MockTransportService( + Settings.EMPTY, + new MockNioTransport( + Settings.EMPTY, + Version.CURRENT, + threadPool, + networkService, + PageCacheRecycler.NON_RECYCLING_INSTANCE, + new NamedWriteableRegistry(Collections.emptyList()), + new NoneCircuitBreakerService(), + NoopTracer.INSTANCE + ), + threadPool, + TransportService.NOOP_TRANSPORT_INTERCEPTOR, + null, + NoopTracer.INSTANCE + ); + } + + public void testEC2DiscoveryRetriesOnRateLimiting() throws IOException { + final String accessKey = "ec2_access"; + final List hosts = Collections.singletonList("127.0.0.1:9300"); + final Map failedRequests = new ConcurrentHashMap<>(); + // retry the same request 5 times at most + final int maxRetries = randomIntBetween(1, 5); + httpServer.createContext("/", exchange -> { + if (exchange.getRequestMethod().equals("POST")) { + final String request = Streams.readFully(exchange.getRequestBody()).utf8ToString(); + final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); + if (userAgent != null && userAgent.startsWith("aws-sdk-java")) { + final String auth = exchange.getRequestHeaders().getFirst("Authorization"); + if (auth == null || auth.contains(accessKey) == false) { + throw new IllegalArgumentException("wrong access key: " + auth); + } + if (failedRequests.compute( + exchange.getRequestHeaders().getFirst("Amz-sdk-invocation-id"), + (requestId, count) -> (count == null ? 0 : count) + 1 + ) < maxRetries) { + exchange.sendResponseHeaders(HttpStatus.SC_SERVICE_UNAVAILABLE, -1); + return; + } + // Simulate an EC2 DescribeInstancesResponse + byte[] responseBody = null; + for (NameValuePair parse : URLEncodedUtils.parse(request, UTF_8)) { + if ("Action".equals(parse.getName())) { + responseBody = generateDescribeInstancesResponse( + hosts.stream() + .map(address -> Instance.builder().publicIpAddress(address).build()) + .collect(Collectors.toList()) + ); + break; + } + } + responseBody = responseBody == null ? new byte[0] : responseBody; + exchange.getResponseHeaders().set("Content-Type", "text/xml; charset=UTF-8"); + exchange.sendResponseHeaders(HttpStatus.SC_OK, responseBody.length); + exchange.getResponseBody().write(responseBody); + return; + } + } + fail("did not send response"); + }); + try (Ec2DiscoveryPlugin plugin = new Ec2DiscoveryPlugin(buildSettings(accessKey))) { + final SeedHostsProvider seedHostsProvider = plugin.getSeedHostProviders(transportService, networkService).get("ec2").get(); + final SeedHostsResolver resolver = new SeedHostsResolver("test", Settings.EMPTY, transportService, seedHostsProvider); + resolver.start(); + final List addressList = seedHostsProvider.getSeedAddresses(null); + assertThat(addressList, Matchers.hasSize(1)); + assertThat(addressList.get(0).toString(), is(hosts.get(0))); + assertThat(failedRequests, aMapWithSize(1)); + assertThat(failedRequests.values().iterator().next(), is(maxRetries)); + } + } +} diff --git a/plugins/discovery-ec2/src/yamlRestTest/resources/rest-api-spec/test/discovery_ec2/10_basic.yml b/plugins/discovery-ec2/src/yamlRestTest/resources/rest-api-spec/test/discovery_ec2/10_basic.yml index ba51c623fe888..f91565c5d204b 100644 --- a/plugins/discovery-ec2/src/yamlRestTest/resources/rest-api-spec/test/discovery_ec2/10_basic.yml +++ b/plugins/discovery-ec2/src/yamlRestTest/resources/rest-api-spec/test/discovery_ec2/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: discovery-ec2 } } + - contains: { nodes.$cluster_manager.plugins: { name: discovery-ec2 } } diff --git a/plugins/discovery-gce/build.gradle b/plugins/discovery-gce/build.gradle index 2396b228d77a0..beae0d84685a4 100644 --- a/plugins/discovery-gce/build.gradle +++ b/plugins/discovery-gce/build.gradle @@ -24,7 +24,7 @@ versions << [ dependencies { api "com.google.apis:google-api-services-compute:v1-rev160-${versions.google}" api "com.google.api-client:google-api-client:${versions.google}" - api "com.google.oauth-client:google-oauth-client:1.33.1" + api "com.google.oauth-client:google-oauth-client:1.33.3" api "com.google.http-client:google-http-client:${versions.google}" api "com.google.http-client:google-http-client-jackson2:${versions.google}" api 'com.google.code.findbugs:jsr305:3.0.2' @@ -58,13 +58,21 @@ test { thirdPartyAudit.ignoreMissingClasses( // classes are missing 'javax.jms.Message', - 'com.google.common.base.Splitter', - 'com.google.common.collect.Lists', 'javax.servlet.ServletContextEvent', 'javax.servlet.ServletContextListener', 'org.apache.avalon.framework.logger.Logger', 'org.apache.log.Hierarchy', 'org.apache.log.Logger', + 'com.google.api.client.json.gson.GsonFactory', + 'com.google.common.base.Preconditions', + 'com.google.common.base.Splitter', + 'com.google.common.cache.CacheBuilder', + 'com.google.common.cache.CacheLoader', + 'com.google.common.cache.LoadingCache', + 'com.google.common.collect.ImmutableMap', + 'com.google.common.collect.ImmutableMap$Builder', + 'com.google.common.collect.ImmutableSet', + 'com.google.common.collect.Lists', 'com.google.common.collect.Multiset', 'com.google.common.collect.SortedMultiset', 'com.google.common.collect.TreeMultiset', diff --git a/plugins/discovery-gce/licenses/commons-codec-1.13.jar.sha1 b/plugins/discovery-gce/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/discovery-gce/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/commons-codec-1.15.jar.sha1 b/plugins/discovery-gce/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/discovery-gce/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/commons-logging-1.1.3.jar.sha1 b/plugins/discovery-gce/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index c8756c438320f..0000000000000 --- a/plugins/discovery-gce/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f diff --git a/plugins/discovery-gce/licenses/commons-logging-1.2.jar.sha1 b/plugins/discovery-gce/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/discovery-gce/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/commons-logging-LICENSE.txt b/plugins/discovery-gce/licenses/commons-logging-LICENSE.txt index d645695673349..57bc88a15a0ee 100644 --- a/plugins/discovery-gce/licenses/commons-logging-LICENSE.txt +++ b/plugins/discovery-gce/licenses/commons-logging-LICENSE.txt @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -200,3 +199,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + diff --git a/plugins/discovery-gce/licenses/commons-logging-NOTICE.txt b/plugins/discovery-gce/licenses/commons-logging-NOTICE.txt index d3d6e140ce4f3..72eb32a902458 100644 --- a/plugins/discovery-gce/licenses/commons-logging-NOTICE.txt +++ b/plugins/discovery-gce/licenses/commons-logging-NOTICE.txt @@ -1,5 +1,5 @@ -Apache Commons Logging -Copyright 2003-2014 The Apache Software Foundation +Apache Commons CLI +Copyright 2001-2009 The Apache Software Foundation -This product includes software developed at +This product includes software developed by The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/discovery-gce/licenses/google-oauth-client-1.33.1.jar.sha1 b/plugins/discovery-gce/licenses/google-oauth-client-1.33.1.jar.sha1 deleted file mode 100644 index 3897a85310ec6..0000000000000 --- a/plugins/discovery-gce/licenses/google-oauth-client-1.33.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0a431f1a677c5f89507591ab47a7ccdb0b18b6f7 \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/google-oauth-client-1.33.3.jar.sha1 b/plugins/discovery-gce/licenses/google-oauth-client-1.33.3.jar.sha1 new file mode 100644 index 0000000000000..f2afaa1bc2dba --- /dev/null +++ b/plugins/discovery-gce/licenses/google-oauth-client-1.33.3.jar.sha1 @@ -0,0 +1 @@ +9d445a8649b0de731922b9a3ebf1552b5403611d \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/httpclient-4.5.13.jar.sha1 b/plugins/discovery-gce/licenses/httpclient-4.5.13.jar.sha1 deleted file mode 100644 index 3281e21595b39..0000000000000 --- a/plugins/discovery-gce/licenses/httpclient-4.5.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/httpclient-4.5.14.jar.sha1 b/plugins/discovery-gce/licenses/httpclient-4.5.14.jar.sha1 new file mode 100644 index 0000000000000..66e05851c2e3c --- /dev/null +++ b/plugins/discovery-gce/licenses/httpclient-4.5.14.jar.sha1 @@ -0,0 +1 @@ +1194890e6f56ec29177673f2f12d0b8e627dec98 \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/httpcore-4.4.12.jar.sha1 b/plugins/discovery-gce/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/plugins/discovery-gce/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/httpcore-4.4.16.jar.sha1 b/plugins/discovery-gce/licenses/httpcore-4.4.16.jar.sha1 new file mode 100644 index 0000000000000..172110694b5bd --- /dev/null +++ b/plugins/discovery-gce/licenses/httpcore-4.4.16.jar.sha1 @@ -0,0 +1 @@ +51cf043c87253c9f58b539c9f7e44c8894223850 \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/log4j-1.2-api-2.17.1.jar.sha1 b/plugins/discovery-gce/licenses/log4j-1.2-api-2.17.1.jar.sha1 deleted file mode 100644 index 23aa5c60bd596..0000000000000 --- a/plugins/discovery-gce/licenses/log4j-1.2-api-2.17.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -db3a7e7f07e878b92ac4a8f1100bee8325d5713a \ No newline at end of file diff --git a/plugins/discovery-gce/licenses/log4j-1.2-api-2.20.0.jar.sha1 b/plugins/discovery-gce/licenses/log4j-1.2-api-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..9829576d38ce0 --- /dev/null +++ b/plugins/discovery-gce/licenses/log4j-1.2-api-2.20.0.jar.sha1 @@ -0,0 +1 @@ +689151374756cb809cb029f2501015bdc7733179 \ No newline at end of file diff --git a/plugins/discovery-gce/qa/gce/src/yamlRestTest/java/org/opensearch/cloud/gce/GCEDiscoveryClientYamlTestSuiteIT.java b/plugins/discovery-gce/qa/gce/src/yamlRestTest/java/org/opensearch/cloud/gce/GCEDiscoveryClientYamlTestSuiteIT.java index 8b05c700a9650..adb7c46665560 100644 --- a/plugins/discovery-gce/qa/gce/src/yamlRestTest/java/org/opensearch/cloud/gce/GCEDiscoveryClientYamlTestSuiteIT.java +++ b/plugins/discovery-gce/qa/gce/src/yamlRestTest/java/org/opensearch/cloud/gce/GCEDiscoveryClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/discovery-gce/qa/gce/src/yamlRestTest/java/org/opensearch/cloud/gce/GCEFixture.java b/plugins/discovery-gce/qa/gce/src/yamlRestTest/java/org/opensearch/cloud/gce/GCEFixture.java index f1eba6a635504..f5a231ef869f5 100644 --- a/plugins/discovery-gce/qa/gce/src/yamlRestTest/java/org/opensearch/cloud/gce/GCEFixture.java +++ b/plugins/discovery-gce/qa/gce/src/yamlRestTest/java/org/opensearch/cloud/gce/GCEFixture.java @@ -32,12 +32,10 @@ package org.opensearch.cloud.gce; import org.apache.http.client.methods.HttpGet; - -import org.opensearch.common.Strings; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.path.PathTrie; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.rest.RestUtils; import org.opensearch.test.fixture.AbstractHttpFixture; @@ -139,13 +137,12 @@ private PathTrie defaultHandlers() { handlers.insert( nonAuthPath(HttpGet.METHOD_NAME, "/computeMetadata/v1/instance/service-accounts/default/token"), request -> jsonValue.apply( - Strings.toString( - jsonBuilder().startObject() - .field("access_token", TOKEN) - .field("expires_in", TimeUnit.HOURS.toSeconds(1)) - .field("token_type", TOKEN_TYPE) - .endObject() - ) + jsonBuilder().startObject() + .field("access_token", TOKEN) + .field("expires_in", TimeUnit.HOURS.toSeconds(1)) + .field("token_type", TOKEN_TYPE) + .endObject() + .toString() ) ); @@ -179,9 +176,7 @@ private PathTrie defaultHandlers() { ); } - final String json = Strings.toString( - jsonBuilder().startObject().field("id", "test-instances").field("items", items).endObject() - ); + final String json = jsonBuilder().startObject().field("id", "test-instances").field("items", items).endObject().toString(); final byte[] responseAsBytes = json.getBytes(StandardCharsets.UTF_8); final Map headers = new HashMap<>(JSON_CONTENT_TYPE); @@ -213,29 +208,28 @@ protected Response handle(final Request request) throws IOException { } private static Response newError(final RestStatus status, final String code, final String message) throws IOException { - final String response = Strings.toString( - jsonBuilder().startObject() - .field( - "error", - MapBuilder.newMapBuilder() - .put( - "errors", - Collections.singletonList( - MapBuilder.newMapBuilder() - .put("domain", "global") - .put("reason", "required") - .put("message", message) - .put("locationType", "header") - .put("location", code) - .immutableMap() - ) + final String response = jsonBuilder().startObject() + .field( + "error", + MapBuilder.newMapBuilder() + .put( + "errors", + Collections.singletonList( + MapBuilder.newMapBuilder() + .put("domain", "global") + .put("reason", "required") + .put("message", message) + .put("locationType", "header") + .put("location", code) + .immutableMap() ) - .put("code", status.getStatus()) - .put("message", message) - .immutableMap() - ) - .endObject() - ); + ) + .put("code", status.getStatus()) + .put("message", message) + .immutableMap() + ) + .endObject() + .toString(); return new Response(status.getStatus(), JSON_CONTENT_TYPE, response.getBytes(UTF_8)); } diff --git a/plugins/discovery-gce/src/internalClusterTest/java/org/opensearch/discovery/gce/GceDiscoverTests.java b/plugins/discovery-gce/src/internalClusterTest/java/org/opensearch/discovery/gce/GceDiscoverTests.java index 815537b534586..e97a4650ca8ae 100644 --- a/plugins/discovery-gce/src/internalClusterTest/java/org/opensearch/discovery/gce/GceDiscoverTests.java +++ b/plugins/discovery-gce/src/internalClusterTest/java/org/opensearch/discovery/gce/GceDiscoverTests.java @@ -35,7 +35,6 @@ import com.google.api.services.compute.model.Instance; import com.google.api.services.compute.model.NetworkInterface; import org.opensearch.action.admin.cluster.state.ClusterStateResponse; - import org.opensearch.cloud.gce.GceInstancesService; import org.opensearch.cloud.gce.util.Access; import org.opensearch.cluster.node.DiscoveryNode; @@ -44,7 +43,6 @@ import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.transport.TransportService; - import org.junit.After; import java.io.IOException; @@ -85,18 +83,18 @@ protected Settings nodeSettings(int nodeOrdinal) { } public void testJoin() { - // start master node - final String masterNode = internalCluster().startMasterOnlyNode(); - registerGceNode(masterNode); + // start cluster-manager node + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + registerGceNode(clusterManagerNode); - ClusterStateResponse clusterStateResponse = client(masterNode).admin() + ClusterStateResponse clusterStateResponse = client(clusterManagerNode).admin() .cluster() .prepareState() - .setMasterNodeTimeout("1s") + .setClusterManagerNodeTimeout("1s") .clear() .setNodes(true) .get(); - assertNotNull(clusterStateResponse.getState().nodes().getMasterNodeId()); + assertNotNull(clusterStateResponse.getState().nodes().getClusterManagerNodeId()); // start another node final String secondNode = internalCluster().startNode(); @@ -104,12 +102,12 @@ public void testJoin() { clusterStateResponse = client(secondNode).admin() .cluster() .prepareState() - .setMasterNodeTimeout("1s") + .setClusterManagerNodeTimeout("1s") .clear() .setNodes(true) .setLocal(true) .get(); - assertNotNull(clusterStateResponse.getState().nodes().getMasterNodeId()); + assertNotNull(clusterStateResponse.getState().nodes().getClusterManagerNodeId()); // wait for the cluster to form assertNoTimeout(client().admin().cluster().prepareHealth().setWaitForNodes(Integer.toString(2)).get()); diff --git a/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/GceInstancesServiceImpl.java b/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/GceInstancesServiceImpl.java index f25faaf415140..46cc1c8eab537 100644 --- a/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/GceInstancesServiceImpl.java +++ b/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/GceInstancesServiceImpl.java @@ -46,7 +46,6 @@ import com.google.api.services.compute.Compute; import com.google.api.services.compute.model.Instance; import com.google.api.services.compute.model.InstanceList; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; diff --git a/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/GceMetadataService.java b/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/GceMetadataService.java index 4873cb6dcbf7a..ef73f741ad20c 100644 --- a/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/GceMetadataService.java +++ b/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/GceMetadataService.java @@ -32,12 +32,6 @@ package org.opensearch.cloud.gce; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.GeneralSecurityException; -import java.util.function.Function; - import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpHeaders; @@ -46,10 +40,16 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.cloud.gce.util.Access; -import org.opensearch.common.component.AbstractLifecycleComponent; +import org.opensearch.common.lifecycle.AbstractLifecycleComponent; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.GeneralSecurityException; +import java.util.function.Function; + public class GceMetadataService extends AbstractLifecycleComponent { private static final Logger logger = LogManager.getLogger(GceMetadataService.class); diff --git a/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/network/GceNameResolver.java b/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/network/GceNameResolver.java index 7482dfac401ef..28e41963de489 100644 --- a/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/network/GceNameResolver.java +++ b/plugins/discovery-gce/src/main/java/org/opensearch/cloud/gce/network/GceNameResolver.java @@ -34,8 +34,8 @@ import org.opensearch.cloud.gce.GceMetadataService; import org.opensearch.cloud.gce.util.Access; -import org.opensearch.common.Strings; import org.opensearch.common.network.NetworkService.CustomNameResolver; +import org.opensearch.core.common.Strings; import java.io.IOException; import java.net.InetAddress; diff --git a/plugins/discovery-gce/src/main/java/org/opensearch/discovery/gce/GceSeedHostsProvider.java b/plugins/discovery-gce/src/main/java/org/opensearch/discovery/gce/GceSeedHostsProvider.java index 19247a7bb536d..5958c07e244ad 100644 --- a/plugins/discovery-gce/src/main/java/org/opensearch/discovery/gce/GceSeedHostsProvider.java +++ b/plugins/discovery-gce/src/main/java/org/opensearch/discovery/gce/GceSeedHostsProvider.java @@ -35,20 +35,19 @@ import com.google.api.services.compute.model.AccessConfig; import com.google.api.services.compute.model.Instance; import com.google.api.services.compute.model.NetworkInterface; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.opensearch.cloud.gce.GceInstancesService; -import org.opensearch.common.Strings; import org.opensearch.common.network.NetworkAddress; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.discovery.SeedHostsProvider; import org.opensearch.transport.TransportService; diff --git a/plugins/discovery-gce/src/main/java/org/opensearch/discovery/gce/RetryHttpInitializerWrapper.java b/plugins/discovery-gce/src/main/java/org/opensearch/discovery/gce/RetryHttpInitializerWrapper.java index 26b8215bed7ff..6e5372cad0a4b 100644 --- a/plugins/discovery-gce/src/main/java/org/opensearch/discovery/gce/RetryHttpInitializerWrapper.java +++ b/plugins/discovery-gce/src/main/java/org/opensearch/discovery/gce/RetryHttpInitializerWrapper.java @@ -32,8 +32,6 @@ package org.opensearch.discovery.gce; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.googleapis.testing.auth.oauth2.MockGoogleCredential; import com.google.api.client.http.HttpBackOffIOExceptionHandler; @@ -44,6 +42,8 @@ import com.google.api.client.http.HttpUnsuccessfulResponseHandler; import com.google.api.client.util.ExponentialBackOff; import com.google.api.client.util.Sleeper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.opensearch.cloud.gce.util.Access; import org.opensearch.common.unit.TimeValue; diff --git a/plugins/discovery-gce/src/main/java/org/opensearch/plugin/discovery/gce/GceDiscoveryPlugin.java b/plugins/discovery-gce/src/main/java/org/opensearch/plugin/discovery/gce/GceDiscoveryPlugin.java index 6d015f54ffb29..d4df6d94c061b 100644 --- a/plugins/discovery-gce/src/main/java/org/opensearch/plugin/discovery/gce/GceDiscoveryPlugin.java +++ b/plugins/discovery-gce/src/main/java/org/opensearch/plugin/discovery/gce/GceDiscoveryPlugin.java @@ -36,17 +36,17 @@ import com.google.api.client.util.ClassInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.lucene.util.SetOnce; import org.opensearch.cloud.gce.GceInstancesService; import org.opensearch.cloud.gce.GceInstancesServiceImpl; import org.opensearch.cloud.gce.GceMetadataService; import org.opensearch.cloud.gce.network.GceNameResolver; import org.opensearch.cloud.gce.util.Access; import org.opensearch.common.Booleans; +import org.opensearch.common.SetOnce; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.discovery.SeedHostsProvider; import org.opensearch.discovery.gce.GceSeedHostsProvider; import org.opensearch.plugins.DiscoveryPlugin; diff --git a/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceDiscoveryTests.java b/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceDiscoveryTests.java index 2ca1234bb8a04..b4af9773f33de 100644 --- a/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceDiscoveryTests.java +++ b/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceDiscoveryTests.java @@ -37,7 +37,8 @@ import org.opensearch.cloud.gce.GceMetadataService; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.threadpool.TestThreadPool; @@ -109,7 +110,7 @@ public void setProjectName() { @Before public void createTransportService() { - transportService = MockTransportService.createNewService(Settings.EMPTY, Version.CURRENT, threadPool, null); + transportService = MockTransportService.createNewService(Settings.EMPTY, Version.CURRENT, threadPool, NoopTracer.INSTANCE); } @After diff --git a/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceMockUtils.java b/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceMockUtils.java index 5109600be7488..f39ab200da8ed 100644 --- a/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceMockUtils.java +++ b/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceMockUtils.java @@ -41,9 +41,9 @@ import com.google.api.client.testing.http.MockLowLevelHttpResponse; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.common.Strings; -import org.opensearch.common.io.FileSystemUtils; import org.opensearch.common.io.Streams; +import org.opensearch.core.common.Strings; +import org.opensearch.core.util.FileSystemUtils; import java.io.IOException; import java.io.InputStream; diff --git a/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceNetworkTests.java b/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceNetworkTests.java index a1d7613bf2ba4..091a4f720d258 100644 --- a/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceNetworkTests.java +++ b/plugins/discovery-gce/src/test/java/org/opensearch/discovery/gce/GceNetworkTests.java @@ -33,9 +33,9 @@ package org.opensearch.discovery.gce; import org.opensearch.cloud.gce.network.GceNameResolver; -import org.opensearch.common.Strings; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.test.OpenSearchTestCase; import java.io.IOException; diff --git a/plugins/discovery-gce/src/test/resources/org/opensearch/discovery/gce/computeMetadata/v1/project/attributes/google-compute-default-zone b/plugins/discovery-gce/src/test/resources/org/opensearch/discovery/gce/computeMetadata/v1/project/attributes/google-compute-default-zone index 6cf886270bef1..218127ccfb695 100644 --- a/plugins/discovery-gce/src/test/resources/org/opensearch/discovery/gce/computeMetadata/v1/project/attributes/google-compute-default-zone +++ b/plugins/discovery-gce/src/test/resources/org/opensearch/discovery/gce/computeMetadata/v1/project/attributes/google-compute-default-zone @@ -1 +1 @@ -europe-west1-b \ No newline at end of file +europe-west1-b diff --git a/plugins/discovery-gce/src/test/resources/org/opensearch/discovery/gce/computeMetadata/v1/project/project-id b/plugins/discovery-gce/src/test/resources/org/opensearch/discovery/gce/computeMetadata/v1/project/project-id index 25b8069381897..44be476f3ae83 100644 --- a/plugins/discovery-gce/src/test/resources/org/opensearch/discovery/gce/computeMetadata/v1/project/project-id +++ b/plugins/discovery-gce/src/test/resources/org/opensearch/discovery/gce/computeMetadata/v1/project/project-id @@ -1 +1 @@ -metadataserver \ No newline at end of file +metadataserver diff --git a/plugins/discovery-gce/src/yamlRestTest/resources/rest-api-spec/test/discovery_gce/10_basic.yml b/plugins/discovery-gce/src/yamlRestTest/resources/rest-api-spec/test/discovery_gce/10_basic.yml index a5379c2c68bed..828e006ae822d 100644 --- a/plugins/discovery-gce/src/yamlRestTest/resources/rest-api-spec/test/discovery_gce/10_basic.yml +++ b/plugins/discovery-gce/src/yamlRestTest/resources/rest-api-spec/test/discovery_gce/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: discovery-gce } } + - contains: { nodes.$cluster_manager.plugins: { name: discovery-gce } } diff --git a/plugins/examples/build.gradle b/plugins/examples/build.gradle index e4e0ca6f7be99..4a6228b227f95 100644 --- a/plugins/examples/build.gradle +++ b/plugins/examples/build.gradle @@ -31,9 +31,8 @@ gradle.projectsEvaluated { configure(project('painless-whitelist')) { configurations.all { resolutionStrategy.dependencySubstitution { - substitute module('org.opensearch.plugin:opensearch-scripting-painless-spi') with project(':modules:lang-painless:spi') - substitute module('org.opensearch.test:logger-usage') with project(':test:logger-usage') + substitute module('org.opensearch.plugin:opensearch-scripting-painless-spi') using project(':modules:lang-painless:spi') + substitute module('org.opensearch.test:logger-usage') using project(':test:logger-usage') } } } - diff --git a/plugins/examples/custom-settings/build.gradle b/plugins/examples/custom-settings/build.gradle index 104660c458991..5b35d887b3db1 100644 --- a/plugins/examples/custom-settings/build.gradle +++ b/plugins/examples/custom-settings/build.gradle @@ -42,4 +42,3 @@ testClusters.all { // Adds a setting in the OpenSearch keystore before running the integration tests keystore 'custom.secured', 'password' } - diff --git a/plugins/examples/custom-settings/src/main/config/custom.yml b/plugins/examples/custom-settings/src/main/config/custom.yml index 1759e0ff96d40..258e050a0664b 100644 --- a/plugins/examples/custom-settings/src/main/config/custom.yml +++ b/plugins/examples/custom-settings/src/main/config/custom.yml @@ -2,4 +2,4 @@ custom: simple: foo list: [0, 1, 1, 2, 3, 5, 8, 13, 21] - filtered: secret \ No newline at end of file + filtered: secret diff --git a/plugins/examples/custom-settings/src/main/java/org/opensearch/example/customsettings/ExampleCustomSettingsConfig.java b/plugins/examples/custom-settings/src/main/java/org/opensearch/example/customsettings/ExampleCustomSettingsConfig.java index 5f494147c870f..b373d602d6e04 100644 --- a/plugins/examples/custom-settings/src/main/java/org/opensearch/example/customsettings/ExampleCustomSettingsConfig.java +++ b/plugins/examples/custom-settings/src/main/java/org/opensearch/example/customsettings/ExampleCustomSettingsConfig.java @@ -33,10 +33,10 @@ import org.opensearch.OpenSearchException; import org.opensearch.common.settings.SecureSetting; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; import java.io.IOException; diff --git a/plugins/examples/custom-settings/src/yamlRestTest/java/org/opensearch/example/customsettings/ExampleCustomSettingsClientYamlTestSuiteIT.java b/plugins/examples/custom-settings/src/yamlRestTest/java/org/opensearch/example/customsettings/ExampleCustomSettingsClientYamlTestSuiteIT.java index 2e6da40553401..2f163aa5aabf5 100644 --- a/plugins/examples/custom-settings/src/yamlRestTest/java/org/opensearch/example/customsettings/ExampleCustomSettingsClientYamlTestSuiteIT.java +++ b/plugins/examples/custom-settings/src/yamlRestTest/java/org/opensearch/example/customsettings/ExampleCustomSettingsClientYamlTestSuiteIT.java @@ -33,6 +33,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/examples/custom-significance-heuristic/src/main/java/org/opensearch/example/customsigheuristic/SimpleHeuristic.java b/plugins/examples/custom-significance-heuristic/src/main/java/org/opensearch/example/customsigheuristic/SimpleHeuristic.java index 8365a56bcfe4e..8320c274f3023 100644 --- a/plugins/examples/custom-significance-heuristic/src/main/java/org/opensearch/example/customsigheuristic/SimpleHeuristic.java +++ b/plugins/examples/custom-significance-heuristic/src/main/java/org/opensearch/example/customsigheuristic/SimpleHeuristic.java @@ -32,10 +32,10 @@ package org.opensearch.example.customsigheuristic; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.bucket.terms.heuristic.SignificanceHeuristic; import java.io.IOException; diff --git a/plugins/examples/custom-significance-heuristic/src/test/java/org/opensearch/example/customsigheuristic/SimpleHeuristicWireTests.java b/plugins/examples/custom-significance-heuristic/src/test/java/org/opensearch/example/customsigheuristic/SimpleHeuristicWireTests.java index 197b8be9c5536..fb93fc3bc49c8 100644 --- a/plugins/examples/custom-significance-heuristic/src/test/java/org/opensearch/example/customsigheuristic/SimpleHeuristicWireTests.java +++ b/plugins/examples/custom-significance-heuristic/src/test/java/org/opensearch/example/customsigheuristic/SimpleHeuristicWireTests.java @@ -32,9 +32,9 @@ package org.opensearch.example.customsigheuristic; -import org.opensearch.common.io.stream.Writeable.Reader; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.common.io.stream.Writeable.Reader; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.test.AbstractSerializingTestCase; import java.io.IOException; diff --git a/plugins/examples/custom-significance-heuristic/src/yamlRestTest/java/org/opensearch/example/customsigheuristic/CustomSignificanceHeuristicClientYamlTestSuiteIT.java b/plugins/examples/custom-significance-heuristic/src/yamlRestTest/java/org/opensearch/example/customsigheuristic/CustomSignificanceHeuristicClientYamlTestSuiteIT.java index 881905fbceade..69ba949f60271 100644 --- a/plugins/examples/custom-significance-heuristic/src/yamlRestTest/java/org/opensearch/example/customsigheuristic/CustomSignificanceHeuristicClientYamlTestSuiteIT.java +++ b/plugins/examples/custom-significance-heuristic/src/yamlRestTest/java/org/opensearch/example/customsigheuristic/CustomSignificanceHeuristicClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/examples/custom-significance-heuristic/src/yamlRestTest/resources/rest-api-spec/test/custom-significance-heuristic/10_basic.yml b/plugins/examples/custom-significance-heuristic/src/yamlRestTest/resources/rest-api-spec/test/custom-significance-heuristic/10_basic.yml index 620aa393a6b5b..5cd56683f427d 100644 --- a/plugins/examples/custom-significance-heuristic/src/yamlRestTest/resources/rest-api-spec/test/custom-significance-heuristic/10_basic.yml +++ b/plugins/examples/custom-significance-heuristic/src/yamlRestTest/resources/rest-api-spec/test/custom-significance-heuristic/10_basic.yml @@ -5,12 +5,12 @@ reason: "contains is a newly added assertion" features: contains - # Get master node id + # Get cluster-manager node id - do: cluster.state: {} - - set: { master_node: master } + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: custom-significance-heuristic } } + - contains: { nodes.$cluster_manager.plugins: { name: custom-significance-heuristic } } diff --git a/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggester.java b/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggester.java index da154609e5f2f..23166135b829b 100644 --- a/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggester.java +++ b/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggester.java @@ -34,7 +34,7 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.util.CharsRefBuilder; -import org.opensearch.common.text.Text; +import org.opensearch.core.common.text.Text; import org.opensearch.search.suggest.Suggest; import org.opensearch.search.suggest.Suggester; diff --git a/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggestion.java b/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggestion.java index 50ee700c3a253..34d3aec2c3d9f 100644 --- a/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggestion.java +++ b/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggestion.java @@ -32,19 +32,19 @@ package org.opensearch.example.customsuggester; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.text.Text; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.text.Text; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.suggest.Suggest; import java.io.IOException; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; public class CustomSuggestion extends Suggest.Suggestion { diff --git a/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggestionBuilder.java b/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggestionBuilder.java index a7551fca3cf24..2a75bbcf7dbf4 100644 --- a/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggestionBuilder.java +++ b/plugins/examples/custom-suggester/src/main/java/org/opensearch/example/customsuggester/CustomSuggestionBuilder.java @@ -32,13 +32,13 @@ package org.opensearch.example.customsuggester; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.BytesRefs; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.QueryShardContext; import org.opensearch.search.suggest.SuggestionBuilder; import org.opensearch.search.suggest.SuggestionSearchContext; diff --git a/plugins/examples/custom-suggester/src/yamlRestTest/java/org/opensearch/example/customsuggester/CustomSuggesterClientYamlTestSuiteIT.java b/plugins/examples/custom-suggester/src/yamlRestTest/java/org/opensearch/example/customsuggester/CustomSuggesterClientYamlTestSuiteIT.java index b6237a7a9f855..b924a003c4e1d 100644 --- a/plugins/examples/custom-suggester/src/yamlRestTest/java/org/opensearch/example/customsuggester/CustomSuggesterClientYamlTestSuiteIT.java +++ b/plugins/examples/custom-suggester/src/yamlRestTest/java/org/opensearch/example/customsuggester/CustomSuggesterClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/examples/custom-suggester/src/yamlRestTest/resources/rest-api-spec/test/custom-suggester/10_basic.yml b/plugins/examples/custom-suggester/src/yamlRestTest/resources/rest-api-spec/test/custom-suggester/10_basic.yml index ed8d0f78a092b..0ef1d4a0679d7 100644 --- a/plugins/examples/custom-suggester/src/yamlRestTest/resources/rest-api-spec/test/custom-suggester/10_basic.yml +++ b/plugins/examples/custom-suggester/src/yamlRestTest/resources/rest-api-spec/test/custom-suggester/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: custom-suggester } } + - contains: { nodes.$cluster_manager.plugins: { name: custom-suggester } } diff --git a/plugins/examples/painless-whitelist/src/yamlRestTest/java/org/opensearch/example/painlesswhitelist/PainlessWhitelistClientYamlTestSuiteIT.java b/plugins/examples/painless-whitelist/src/yamlRestTest/java/org/opensearch/example/painlesswhitelist/PainlessWhitelistClientYamlTestSuiteIT.java index 63214ad3de67c..a1f38e091ec75 100644 --- a/plugins/examples/painless-whitelist/src/yamlRestTest/java/org/opensearch/example/painlesswhitelist/PainlessWhitelistClientYamlTestSuiteIT.java +++ b/plugins/examples/painless-whitelist/src/yamlRestTest/java/org/opensearch/example/painlesswhitelist/PainlessWhitelistClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/examples/painless-whitelist/src/yamlRestTest/resources/rest-api-spec/test/painless_whitelist/10_basic.yml b/plugins/examples/painless-whitelist/src/yamlRestTest/resources/rest-api-spec/test/painless_whitelist/10_basic.yml index cc3762eb42d68..fab9789eeec10 100644 --- a/plugins/examples/painless-whitelist/src/yamlRestTest/resources/rest-api-spec/test/painless_whitelist/10_basic.yml +++ b/plugins/examples/painless-whitelist/src/yamlRestTest/resources/rest-api-spec/test/painless_whitelist/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: painless-whitelist } } + - contains: { nodes.$cluster_manager.plugins: { name: painless-whitelist } } diff --git a/plugins/examples/rescore/src/main/java/org/opensearch/example/rescore/ExampleRescoreBuilder.java b/plugins/examples/rescore/src/main/java/org/opensearch/example/rescore/ExampleRescoreBuilder.java index 724f9f6743624..f0a1fc9511de6 100644 --- a/plugins/examples/rescore/src/main/java/org/opensearch/example/rescore/ExampleRescoreBuilder.java +++ b/plugins/examples/rescore/src/main/java/org/opensearch/example/rescore/ExampleRescoreBuilder.java @@ -38,15 +38,15 @@ import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.index.fielddata.LeafFieldData; import org.opensearch.index.fielddata.LeafNumericFieldData; -import org.opensearch.index.fielddata.IndexFieldData; import org.opensearch.index.fielddata.SortedNumericDoubleValues; import org.opensearch.index.query.QueryRewriteContext; import org.opensearch.index.query.QueryShardContext; @@ -60,23 +60,38 @@ import java.util.Objects; import static java.util.Collections.singletonList; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Example rescorer that multiplies the score of the hit by some factor and doesn't resort them. */ public class ExampleRescoreBuilder extends RescorerBuilder { + /** + * The name of this builder. + */ public static final String NAME = "example"; private final float factor; private final String factorField; + /** + * Instantiate this builder with a weighting factor and optional field. + * + * @param factor The weighting factor. + * @param factorField An optional field. + */ public ExampleRescoreBuilder(float factor, @Nullable String factorField) { this.factor = factor; this.factorField = factorField; } + /** + * Instantiate this object from a stream. + * + * @param in Input to read the value from + * @throws IOException on failure to read the value. + */ ExampleRescoreBuilder(StreamInput in) throws IOException { super(in); factor = in.readFloat(); @@ -119,6 +134,11 @@ protected void doXContent(XContentBuilder builder, Params params) throws IOExcep PARSER.declareString(optionalConstructorArg(), FACTOR_FIELD); } + /** + * Instantiate an ExampleRescoreBuilder from XContent. + * + * @param parser The XContent parser to use + */ public static ExampleRescoreBuilder fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } diff --git a/plugins/examples/rescore/src/test/java/org/opensearch/example/rescore/ExampleRescoreBuilderTests.java b/plugins/examples/rescore/src/test/java/org/opensearch/example/rescore/ExampleRescoreBuilderTests.java index 53bd89999f990..88f845d86f4aa 100644 --- a/plugins/examples/rescore/src/test/java/org/opensearch/example/rescore/ExampleRescoreBuilderTests.java +++ b/plugins/examples/rescore/src/test/java/org/opensearch/example/rescore/ExampleRescoreBuilderTests.java @@ -35,7 +35,7 @@ import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TotalHits; -import org.opensearch.common.io.stream.Writeable.Reader; +import org.opensearch.core.common.io.stream.Writeable.Reader; import org.opensearch.search.rescore.RescoreContext; import org.opensearch.test.AbstractWireSerializingTestCase; import org.opensearch.test.OpenSearchTestCase; diff --git a/plugins/examples/rescore/src/yamlRestTest/java/org/opensearch/example/rescore/ExampleRescoreClientYamlTestSuiteIT.java b/plugins/examples/rescore/src/yamlRestTest/java/org/opensearch/example/rescore/ExampleRescoreClientYamlTestSuiteIT.java index aec77cffdc9e9..7e870c7d5cb97 100644 --- a/plugins/examples/rescore/src/yamlRestTest/java/org/opensearch/example/rescore/ExampleRescoreClientYamlTestSuiteIT.java +++ b/plugins/examples/rescore/src/yamlRestTest/java/org/opensearch/example/rescore/ExampleRescoreClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/examples/rescore/src/yamlRestTest/resources/rest-api-spec/test/example-rescore/10_basic.yml b/plugins/examples/rescore/src/yamlRestTest/resources/rest-api-spec/test/example-rescore/10_basic.yml index f0d0bcb35fad9..00c5977529c6c 100644 --- a/plugins/examples/rescore/src/yamlRestTest/resources/rest-api-spec/test/example-rescore/10_basic.yml +++ b/plugins/examples/rescore/src/yamlRestTest/resources/rest-api-spec/test/example-rescore/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: example-rescore } } + - contains: { nodes.$cluster_manager.plugins: { name: example-rescore } } diff --git a/plugins/examples/rest-handler/build.gradle b/plugins/examples/rest-handler/build.gradle index cc939b15854d5..b97d091af9d08 100644 --- a/plugins/examples/rest-handler/build.gradle +++ b/plugins/examples/rest-handler/build.gradle @@ -56,4 +56,3 @@ javaRestTest { dependsOn exampleFixture nonInputProperties.systemProperty 'external.address', "${-> exampleFixture.addressAndPort}" } - diff --git a/plugins/examples/rest-handler/src/yamlRestTest/java/org/opensearch/example/resthandler/ExampleRestHandlerClientYamlTestSuiteIT.java b/plugins/examples/rest-handler/src/yamlRestTest/java/org/opensearch/example/resthandler/ExampleRestHandlerClientYamlTestSuiteIT.java index 4fe87c089d09f..6e374ec7e70db 100644 --- a/plugins/examples/rest-handler/src/yamlRestTest/java/org/opensearch/example/resthandler/ExampleRestHandlerClientYamlTestSuiteIT.java +++ b/plugins/examples/rest-handler/src/yamlRestTest/java/org/opensearch/example/resthandler/ExampleRestHandlerClientYamlTestSuiteIT.java @@ -33,6 +33,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/examples/script-expert-scoring/build.gradle b/plugins/examples/script-expert-scoring/build.gradle index 9f2bab20a7db0..e4ddd97abbe4c 100644 --- a/plugins/examples/script-expert-scoring/build.gradle +++ b/plugins/examples/script-expert-scoring/build.gradle @@ -39,4 +39,3 @@ opensearchplugin { } test.enabled = false - diff --git a/plugins/examples/script-expert-scoring/src/main/java/org/opensearch/example/expertscript/ExpertScriptPlugin.java b/plugins/examples/script-expert-scoring/src/main/java/org/opensearch/example/expertscript/ExpertScriptPlugin.java index 819176e3e1cb0..074ecc3942d38 100644 --- a/plugins/examples/script-expert-scoring/src/main/java/org/opensearch/example/expertscript/ExpertScriptPlugin.java +++ b/plugins/examples/script-expert-scoring/src/main/java/org/opensearch/example/expertscript/ExpertScriptPlugin.java @@ -35,6 +35,7 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; import org.opensearch.common.settings.Settings; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.ScriptPlugin; @@ -115,20 +116,22 @@ public boolean isResultDeterministic() { @Override public LeafFactory newFactory( Map params, - SearchLookup lookup + SearchLookup lookup, + IndexSearcher indexSearcher ) { - return new PureDfLeafFactory(params, lookup); + return new PureDfLeafFactory(params, lookup, indexSearcher); } } private static class PureDfLeafFactory implements LeafFactory { private final Map params; private final SearchLookup lookup; + private final IndexSearcher indexSearcher; private final String field; private final String term; private PureDfLeafFactory( - Map params, SearchLookup lookup) { + Map params, SearchLookup lookup, IndexSearcher indexSearcher) { if (params.containsKey("field") == false) { throw new IllegalArgumentException( "Missing parameter [field]"); @@ -139,6 +142,7 @@ private PureDfLeafFactory( } this.params = params; this.lookup = lookup; + this.indexSearcher = indexSearcher; field = params.get("field").toString(); term = params.get("term").toString(); } @@ -158,7 +162,7 @@ public ScoreScript newInstance(LeafReaderContext context) * the field and/or term don't exist in this segment, * so always return 0 */ - return new ScoreScript(params, lookup, context) { + return new ScoreScript(params, lookup, indexSearcher, context) { @Override public double execute( ExplanationHolder explanation @@ -167,7 +171,7 @@ public double execute( } }; } - return new ScoreScript(params, lookup, context) { + return new ScoreScript(params, lookup, indexSearcher, context) { int currentDocid = -1; @Override public void setDocument(int docid) { diff --git a/plugins/examples/script-expert-scoring/src/yamlRestTest/java/org/opensearch/example/expertscript/ExpertScriptClientYamlTestSuiteIT.java b/plugins/examples/script-expert-scoring/src/yamlRestTest/java/org/opensearch/example/expertscript/ExpertScriptClientYamlTestSuiteIT.java index 294b1de4e7f99..b104f612c35c0 100644 --- a/plugins/examples/script-expert-scoring/src/yamlRestTest/java/org/opensearch/example/expertscript/ExpertScriptClientYamlTestSuiteIT.java +++ b/plugins/examples/script-expert-scoring/src/yamlRestTest/java/org/opensearch/example/expertscript/ExpertScriptClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/examples/script-expert-scoring/src/yamlRestTest/resources/rest-api-spec/test/script_expert_scoring/10_basic.yml b/plugins/examples/script-expert-scoring/src/yamlRestTest/resources/rest-api-spec/test/script_expert_scoring/10_basic.yml index 70842d5e767e5..d65943f0201b3 100644 --- a/plugins/examples/script-expert-scoring/src/yamlRestTest/resources/rest-api-spec/test/script_expert_scoring/10_basic.yml +++ b/plugins/examples/script-expert-scoring/src/yamlRestTest/resources/rest-api-spec/test/script_expert_scoring/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: script-expert-scoring } } + - contains: { nodes.$cluster_manager.plugins: { name: script-expert-scoring } } diff --git a/plugins/identity-shiro/build.gradle b/plugins/identity-shiro/build.gradle new file mode 100644 index 0000000000000..baa3464d0a98e --- /dev/null +++ b/plugins/identity-shiro/build.gradle @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +apply plugin: 'opensearch.internal-cluster-test' + +opensearchplugin { + description 'Plugin for identity features in OpenSearch.' + classname 'org.opensearch.identity.shiro.ShiroIdentityPlugin' + name project.name + licenseFile rootProject.file('licenses/APACHE-LICENSE-2.0.txt') + noticeFile rootProject.file('NOTICE.txt') +} + +dependencies { + implementation 'org.apache.shiro:shiro-core:1.11.0' + + // Needed for shiro + implementation "org.slf4j:slf4j-api:${versions.slf4j}" + + implementation 'commons-beanutils:commons-beanutils:1.9.4' + implementation 'commons-logging:commons-logging:1.2' + implementation 'commons-lang:commons-lang:2.6' + + implementation 'org.passay:passay:1.6.3' + + implementation "org.bouncycastle:bcprov-jdk15to18:${versions.bouncycastle}" + + testImplementation project(path: ':modules:transport-netty4') // for http + testImplementation project(path: ':plugins:transport-nio') // for http + testImplementation "org.mockito:mockito-core:${versions.mockito}" + testImplementation project(path: ':client:rest-high-level') + testImplementation 'junit:junit:4.13.2' +} + +/* + * We have to disable setting the number of available processors as tests in the same JVM randomize processors and will step on each + * other if we allow them to set the number of available processors as it's set-once in Netty. + */ +test { + systemProperty 'opensearch.set.netty.runtime.available.processors', 'false' +} + +internalClusterTest { + systemProperty 'opensearch.set.netty.runtime.available.processors', 'false' +} + +thirdPartyAudit.ignoreMissingClasses( + 'com.google.common.hash.BloomFilter', + 'javax.servlet.ServletContextEvent', + 'javax.servlet.ServletContextListener', + 'org.apache.avalon.framework.logger.Logger', + 'org.apache.commons.collections.Closure', + 'org.apache.commons.collections.FastHashMap', + 'org.apache.commons.collections.Predicate', + 'org.apache.commons.collections.Transformer', + 'org.apache.commons.collections.comparators.ComparableComparator', + 'org.apache.commons.collections.keyvalue.AbstractMapEntry', + 'org.apache.commons.configuration2.interpol.ConfigurationInterpolator', + 'org.apache.log.Hierarchy', + 'org.apache.log.Logger', + 'org.apache.log4j.Level', + 'org.apache.log4j.Logger', + 'org.apache.log4j.Priority', + 'org.cryptacular.bean.HashBean', + 'org.slf4j.impl.StaticLoggerBinder', + 'org.slf4j.impl.StaticMDCBinder', + 'org.slf4j.impl.StaticMarkerBinder', + 'org.springframework.context.MessageSource', + 'org.springframework.context.support.MessageSourceAccessor' +) diff --git a/plugins/identity-shiro/licenses/bcprov-jdk15to18-1.76.jar.sha1 b/plugins/identity-shiro/licenses/bcprov-jdk15to18-1.76.jar.sha1 new file mode 100644 index 0000000000000..2e96c404bef98 --- /dev/null +++ b/plugins/identity-shiro/licenses/bcprov-jdk15to18-1.76.jar.sha1 @@ -0,0 +1 @@ +0cb53f10290a634808555bc4b34328fdab1001f2 \ No newline at end of file diff --git a/plugins/identity-shiro/licenses/bcprov-jdk15to18-LICENSE.txt b/plugins/identity-shiro/licenses/bcprov-jdk15to18-LICENSE.txt new file mode 100644 index 0000000000000..9f27bafe96885 --- /dev/null +++ b/plugins/identity-shiro/licenses/bcprov-jdk15to18-LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2000 - 2013 The Legion of the Bouncy Castle Inc. + (http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/plugins/identity-shiro/licenses/bcprov-jdk15to18-NOTICE.txt b/plugins/identity-shiro/licenses/bcprov-jdk15to18-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/identity-shiro/licenses/commons-beanutils-1.9.4.jar.sha1 b/plugins/identity-shiro/licenses/commons-beanutils-1.9.4.jar.sha1 new file mode 100644 index 0000000000000..b91aa1e1d1f4f --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-beanutils-1.9.4.jar.sha1 @@ -0,0 +1 @@ +d52b9abcd97f38c81342bb7e7ae1eee9b73cba51 \ No newline at end of file diff --git a/plugins/identity-shiro/licenses/commons-beanutils-LICENSE.txt b/plugins/identity-shiro/licenses/commons-beanutils-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-beanutils-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/identity-shiro/licenses/commons-beanutils-NOTICE.txt b/plugins/identity-shiro/licenses/commons-beanutils-NOTICE.txt new file mode 100644 index 0000000000000..e1529d40c6bb6 --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-beanutils-NOTICE.txt @@ -0,0 +1,5 @@ +Apache Commons BeanUtils +Copyright 2000-2019 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/identity-shiro/licenses/commons-lang-2.6.jar.sha1 b/plugins/identity-shiro/licenses/commons-lang-2.6.jar.sha1 new file mode 100644 index 0000000000000..4ee9249d2b76f --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-lang-2.6.jar.sha1 @@ -0,0 +1 @@ +0ce1edb914c94ebc388f086c6827e8bdeec71ac2 \ No newline at end of file diff --git a/plugins/identity-shiro/licenses/commons-lang-LICENSE.txt b/plugins/identity-shiro/licenses/commons-lang-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-lang-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/identity-shiro/licenses/commons-lang-NOTICE.txt b/plugins/identity-shiro/licenses/commons-lang-NOTICE.txt new file mode 100644 index 0000000000000..780ac0edb3c94 --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-lang-NOTICE.txt @@ -0,0 +1,5 @@ +Apache Commons Lang +Copyright 2001-2022 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/). diff --git a/plugins/identity-shiro/licenses/commons-logging-1.2.jar.sha1 b/plugins/identity-shiro/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/identity-shiro/licenses/commons-logging-LICENSE.txt b/plugins/identity-shiro/licenses/commons-logging-LICENSE.txt new file mode 100644 index 0000000000000..57bc88a15a0ee --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-logging-LICENSE.txt @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/plugins/identity-shiro/licenses/commons-logging-NOTICE.txt b/plugins/identity-shiro/licenses/commons-logging-NOTICE.txt new file mode 100644 index 0000000000000..72eb32a902458 --- /dev/null +++ b/plugins/identity-shiro/licenses/commons-logging-NOTICE.txt @@ -0,0 +1,5 @@ +Apache Commons CLI +Copyright 2001-2009 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/identity-shiro/licenses/passay-1.6.3.jar.sha1 b/plugins/identity-shiro/licenses/passay-1.6.3.jar.sha1 new file mode 100644 index 0000000000000..52ac0b1a81469 --- /dev/null +++ b/plugins/identity-shiro/licenses/passay-1.6.3.jar.sha1 @@ -0,0 +1 @@ +bae4754c87297f5600e4071b2596c0b6625cf92b \ No newline at end of file diff --git a/plugins/identity-shiro/licenses/passay-LICENSE.txt b/plugins/identity-shiro/licenses/passay-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/identity-shiro/licenses/passay-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/identity-shiro/licenses/passay-NOTICE.txt b/plugins/identity-shiro/licenses/passay-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/identity-shiro/licenses/shiro-core-1.11.0.jar.sha1 b/plugins/identity-shiro/licenses/shiro-core-1.11.0.jar.sha1 new file mode 100644 index 0000000000000..67c33e15ec689 --- /dev/null +++ b/plugins/identity-shiro/licenses/shiro-core-1.11.0.jar.sha1 @@ -0,0 +1 @@ +033a70c87e91968a299f1ee00f4e95050312346d \ No newline at end of file diff --git a/plugins/identity-shiro/licenses/shiro-core-LICENSE.txt b/plugins/identity-shiro/licenses/shiro-core-LICENSE.txt new file mode 100644 index 0000000000000..261eeb9e9f8b2 --- /dev/null +++ b/plugins/identity-shiro/licenses/shiro-core-LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/identity-shiro/licenses/shiro-core-NOTICE.txt b/plugins/identity-shiro/licenses/shiro-core-NOTICE.txt new file mode 100644 index 0000000000000..1a356397fa88c --- /dev/null +++ b/plugins/identity-shiro/licenses/shiro-core-NOTICE.txt @@ -0,0 +1,15 @@ +Apache Shiro +Copyright 2008-2022 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +The implementation for org.apache.shiro.util.SoftHashMap is based +on initial ideas from Dr. Heinz Kabutz's publicly posted version +available at http://www.javaspecialists.eu/archive/Issue015.html, +with continued modifications. + +Certain parts (StringUtils, IpAddressMatcher, AntPathMatcherTests, etc.) of the +source code for this product was copied for simplicity and to reduce +dependencies from the source code developed by the Spring Framework Project +(http://www.springframework.org). diff --git a/plugins/identity-shiro/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/identity-shiro/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/identity-shiro/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/identity-shiro/licenses/slf4j-api-LICENSE.txt b/plugins/identity-shiro/licenses/slf4j-api-LICENSE.txt new file mode 100644 index 0000000000000..8fda22f4d72f6 --- /dev/null +++ b/plugins/identity-shiro/licenses/slf4j-api-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2004-2014 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/identity-shiro/licenses/slf4j-api-NOTICE.txt b/plugins/identity-shiro/licenses/slf4j-api-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java new file mode 100644 index 0000000000000..77cab13880c27 --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroIdentityPlugin.java @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.mgt.SecurityManager; +import org.opensearch.common.settings.Settings; +import org.opensearch.identity.Subject; +import org.opensearch.identity.tokens.TokenManager; +import org.opensearch.plugins.IdentityPlugin; +import org.opensearch.plugins.Plugin; + +/** + * Identity implementation with Shiro + * + * @opensearch.experimental + */ +public final class ShiroIdentityPlugin extends Plugin implements IdentityPlugin { + private Logger log = LogManager.getLogger(this.getClass()); + + private final Settings settings; + private final ShiroTokenManager authTokenHandler; + + /** + * Create a new instance of the Shiro Identity Plugin + * + * @param settings settings being used in the configuration + */ + public ShiroIdentityPlugin(final Settings settings) { + this.settings = settings; + authTokenHandler = new ShiroTokenManager(); + + SecurityManager securityManager = new ShiroSecurityManager(); + SecurityUtils.setSecurityManager(securityManager); + } + + /** + * Return a Shiro Subject based on the provided authTokenHandler and current subject + * + * @return The current subject + */ + @Override + public Subject getSubject() { + return new ShiroSubject(authTokenHandler, SecurityUtils.getSubject()); + } + + /** + * Return the Shiro Token Handler + * + * @return the Shiro Token Handler + */ + @Override + public TokenManager getTokenManager() { + return this.authTokenHandler; + } +} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSecurityManager.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSecurityManager.java new file mode 100644 index 0000000000000..96cf05ac53a1a --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSecurityManager.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +import org.apache.shiro.mgt.DefaultSecurityManager; +import org.apache.shiro.mgt.DefaultSessionStorageEvaluator; +import org.apache.shiro.mgt.DefaultSubjectDAO; +import org.opensearch.identity.shiro.realm.OpenSearchRealm; + +/** + * OpenSearch specific security manager implementation + * + * @opensearch.experimental + */ +public class ShiroSecurityManager extends DefaultSecurityManager { + + /** + * Creates the security manager using a default realm and no session storage + */ + public ShiroSecurityManager() { + super(OpenSearchRealm.INSTANCE); + + // By default shiro stores session information into a cache, there were performance + // issues with this sessions cache and so are defaulting to a stateless configuration + final DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator(); + evaluator.setSessionStorageEnabled(false); + + final DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); + subjectDAO.setSessionStorageEvaluator(evaluator); + setSubjectDAO(subjectDAO); + } +} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSubject.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSubject.java new file mode 100644 index 0000000000000..e55204593621c --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroSubject.java @@ -0,0 +1,91 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +import org.opensearch.identity.Subject; +import org.opensearch.identity.tokens.AuthToken; + +import java.security.Principal; +import java.util.Objects; + +/** + * Subject backed by Shiro + * + * @opensearch.experimental + */ +public class ShiroSubject implements Subject { + private final ShiroTokenManager authTokenHandler; + private final org.apache.shiro.subject.Subject shiroSubject; + + /** + * Creates a new shiro subject for use with the IdentityPlugin + * Cannot return null + * @param authTokenHandler Used to extract auth header info + * @param subject The specific subject being authc/z'd + */ + public ShiroSubject(final ShiroTokenManager authTokenHandler, final org.apache.shiro.subject.Subject subject) { + this.authTokenHandler = Objects.requireNonNull(authTokenHandler); + this.shiroSubject = Objects.requireNonNull(subject); + } + + /** + * Return the current principal + * + * @return The current principal + */ + @Override + public Principal getPrincipal() { + final Object o = shiroSubject.getPrincipal(); + if (o == null) return null; + if (o instanceof Principal) return (Principal) o; + return () -> o.toString(); + } + + /** + * Check if another object is equal to this object + * + * @param obj The object to be compared against this + * @return Whether the two objects are equal + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + final Subject that = (Subject) obj; + return Objects.equals(getPrincipal(), that.getPrincipal()); + } + + /** + * Return this Subject's principal as a hash + * @return An int hash code + */ + @Override + public int hashCode() { + return Objects.hash(getPrincipal()); + } + + /** + * Convert this ShiroSubject's principal to a string + * @return A string of the subject's principal + */ + @Override + public String toString() { + return "ShiroSubject(principal=" + getPrincipal() + ")"; + } + + /** + * Logs the user in via authenticating the user against current Shiro realm + * @param authenticationToken The authToken to be used for login + */ + public void authenticate(AuthToken authenticationToken) { + final org.apache.shiro.authc.AuthenticationToken authToken = authTokenHandler.translateAuthToken(authenticationToken) + .orElseThrow(() -> new UnsupportedAuthenticationToken()); + shiroSubject.login(authToken); + } +} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenManager.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenManager.java new file mode 100644 index 0000000000000..345b98d5cb423 --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/ShiroTokenManager.java @@ -0,0 +1,138 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.opensearch.common.Randomness; +import org.opensearch.identity.IdentityService; +import org.opensearch.identity.tokens.AuthToken; +import org.opensearch.identity.tokens.BasicAuthToken; +import org.opensearch.identity.tokens.TokenManager; + +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; + +import org.passay.CharacterRule; +import org.passay.EnglishCharacterData; +import org.passay.PasswordGenerator; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Extracts Shiro's {@link AuthenticationToken} from different types of auth headers + * + * @opensearch.experimental + */ +class ShiroTokenManager implements TokenManager { + + private static final Logger log = LogManager.getLogger(IdentityService.class); + + private static Map shiroTokenPasswordMap = new HashMap<>(); + + /** + * Translates into shiro auth token from the given header token + * @param authenticationToken the token from which to translate + * @return An optional of the shiro auth token for login + */ + public Optional translateAuthToken(org.opensearch.identity.tokens.AuthToken authenticationToken) { + if (authenticationToken instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) authenticationToken; + return Optional.of(new UsernamePasswordToken(basicAuthToken.getUser(), basicAuthToken.getPassword())); + } + + return Optional.empty(); + } + + @Override + public AuthToken issueToken(String audience) { + + String password = generatePassword(); + final byte[] rawEncoded = Base64.getEncoder().encode((audience + ":" + password).getBytes(UTF_8)); + final String usernamePassword = new String(rawEncoded, UTF_8); + final String header = "Basic " + usernamePassword; + BasicAuthToken token = new BasicAuthToken(header); + shiroTokenPasswordMap.put(token, password); + + return token; + } + + public boolean validateToken(AuthToken token) { + if (token instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) token; + return basicAuthToken.getUser().equals(SecurityUtils.getSubject().toString()) + && basicAuthToken.getPassword().equals(shiroTokenPasswordMap.get(basicAuthToken)); + } + return false; + } + + public String getTokenInfo(AuthToken token) { + if (token instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) token; + return basicAuthToken.toString(); + } + throw new UnsupportedAuthenticationToken(); + } + + public void revokeToken(AuthToken token) { + if (token instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) token; + basicAuthToken.revoke(); + return; + } + throw new UnsupportedAuthenticationToken(); + } + + public void resetToken(AuthToken token) { + if (token instanceof BasicAuthToken) { + final BasicAuthToken basicAuthToken = (BasicAuthToken) token; + basicAuthToken.revoke(); + } + } + + /** + * When the ShiroTokenManager is in use, a random password is generated for each token and is then output to the logs. + * The password is used for development only. + * @return A randomly generated password for development + */ + public String generatePassword() { + + CharacterRule lowercaseCharacterRule = new CharacterRule(EnglishCharacterData.LowerCase, 1); + CharacterRule uppercaseCharacterRule = new CharacterRule(EnglishCharacterData.UpperCase, 1); + CharacterRule numericCharacterRule = new CharacterRule(EnglishCharacterData.Digit, 1); + CharacterRule specialCharacterRule = new CharacterRule(EnglishCharacterData.Special, 1); + + List rules = Arrays.asList( + lowercaseCharacterRule, + uppercaseCharacterRule, + numericCharacterRule, + specialCharacterRule + ); + PasswordGenerator passwordGenerator = new PasswordGenerator(); + + Random random = Randomness.get(); + + String password = passwordGenerator.generatePassword(random.nextInt(8) + 8, rules); // Generate a 8 to 16 char password + log.info("Generated password: " + password); + return password; + } + + Map getShiroTokenPasswordMap() { + return shiroTokenPasswordMap; + } + +} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/UnsupportedAuthenticationToken.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/UnsupportedAuthenticationToken.java new file mode 100644 index 0000000000000..f216bbfefe940 --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/UnsupportedAuthenticationToken.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +/** Thrown if the authentication token was invalid */ +public class UnsupportedAuthenticationToken extends RuntimeException {} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/package-info.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/package-info.java new file mode 100644 index 0000000000000..49e276f11f660 --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Identity implementation using Shiro + */ +package org.opensearch.identity.shiro; diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcher.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcher.java new file mode 100644 index 0000000000000..a2cb78425929e --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcher.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro.realm; + +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authc.credential.CredentialsMatcher; +import org.bouncycastle.crypto.generators.OpenBSDBCrypt; + +/** + * Password matcher for BCrypt + * + * @opensearch.experimental + */ +public class BCryptPasswordMatcher implements CredentialsMatcher { + + /** + * Check if the provided authentication token and authentication info match one another + * @param token the {@code AuthenticationToken} submitted during the authentication attempt + * @param info the {@code AuthenticationInfo} stored in the system. + * @return A boolean showing whether the token credentials match the info or not. + */ + @Override + public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { + final UsernamePasswordToken userToken = (UsernamePasswordToken) token; + return OpenBSDBCrypt.checkPassword((String) info.getCredentials(), userToken.getPassword()); + } + +} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/OpenSearchRealm.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/OpenSearchRealm.java new file mode 100644 index 0000000000000..ef405a5637ae7 --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/OpenSearchRealm.java @@ -0,0 +1,142 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro.realm; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.IncorrectCredentialsException; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authc.UnknownAccountException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authc.pam.UnsupportedTokenException; +import org.apache.shiro.realm.AuthenticatingRealm; +import org.opensearch.identity.NamedPrincipal; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * Internal Realm is a custom realm using the internal OpenSearch IdP + * + * @opensearch.experimental + */ +public class OpenSearchRealm extends AuthenticatingRealm { + private static final String DEFAULT_REALM_NAME = "internal"; + + /** + * The instance of the realm to be constructed through the builder method + */ + public static final OpenSearchRealm INSTANCE = new OpenSearchRealm.Builder(DEFAULT_REALM_NAME).build(); + + private final String realmName; + + private Map internalUsers; + + /** + * Instantiate a new OpenSearchRealm + * + * @param realmName The name of the realm + * @param internalUsers A map of internal users + */ + private OpenSearchRealm(final String realmName, final Map internalUsers) { + super(new BCryptPasswordMatcher()); + this.realmName = realmName; + this.internalUsers = internalUsers; + setAuthenticationTokenClass(UsernamePasswordToken.class); + } + + /** + * An internal class representing a realm builder + * + */ + public static final class Builder { + private final String name; + + /** + * Instantiate a realm builder + * @param name The name of the realm builder + */ + public Builder(final String name) { + this.name = Objects.requireNonNull(name); + } + + /** + * Create a new OpenSearchRealm + * + * @return A new realm + */ + public OpenSearchRealm build() { + // TODO: Replace hardcoded admin user / user map with an external provider + final User adminUser = new User(); + adminUser.setUsername(new NamedPrincipal("admin")); + adminUser.setBcryptHash("$2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG"); // Password 'admin' + final Map internalUsers = Map.of("admin", adminUser); + return new OpenSearchRealm(name, internalUsers); + } + } + + /** + * Return an internal user given a principalIdentifier + * + * @param principalIdentifier The string of the identifier + * @return The associated user + * @throws UnknownAccountException when the principal identifier has no user match + */ + public User getInternalUser(final String principalIdentifier) throws UnknownAccountException { + final User userRecord = internalUsers.get(principalIdentifier); + if (userRecord == null) { + throw new UnknownAccountException(); + } + return userRecord; + } + + /** + * Gets the authentication info associated with a specific authentication token + * + * @param token the authentication token containing the user's principal and credentials. + * @return Authentication info associated with the auth token + * @throws AuthenticationException When the auth token has no valid info + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token) throws AuthenticationException { + if (token instanceof UsernamePasswordToken) { + final String username = ((UsernamePasswordToken) token).getUsername(); + // Look up the user by the provide username + final User userRecord = getInternalUser(username); + // TODO: Check for other things, like a locked account, expired password, etc. + + // Verify the user + final SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo( + userRecord.getUsername(), + userRecord.getBcryptHash(), + realmName + ); + + final boolean successfulAuthentication = getCredentialsMatcher().doCredentialsMatch(token, sai); + + if (successfulAuthentication) { + // TODO: Check for anything else that might prevent login (expired password, locked account, etc.) + // if (other problems) { + // throw new CredentialsException(); // Or something more specific + // } + // Success! + return sai; + } else { + // Bad password + throw new IncorrectCredentialsException(); + } + } + + // If the token was not handled, it was unsupported + final String tokenClassName = Optional.ofNullable(token).map(Object::getClass).map(Class::getName).orElse("null"); + throw new UnsupportedTokenException("Unable to support authentication token " + tokenClassName); + } +} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/User.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/User.java new file mode 100644 index 0000000000000..35b3348a955d7 --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/User.java @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro.realm; + +import org.opensearch.identity.NamedPrincipal; + +/** + * A non-volatile and immutable object in the storage. + * + * @opensearch.experimental + */ +public class User { + + private NamedPrincipal username; + private String bcryptHash; + + /** + * Get the User's username as a NamedPrincipal + * @return the named principal representing the username + */ + public NamedPrincipal getUsername() { + return username; + } + + /** + * Set the User's username + * @param username NamedPrincipal representing the username to be set + */ + public void setUsername(NamedPrincipal username) { + this.username = username; + } + + /** + * Get the User's hash + * @return The User's hash as a string + */ + public String getBcryptHash() { + return bcryptHash; + } + + /** + * Set the User's hash + * @param bcryptHash A string to be set as the User's hash + */ + public void setBcryptHash(String bcryptHash) { + this.bcryptHash = bcryptHash; + } +} diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/package-info.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/package-info.java new file mode 100644 index 0000000000000..888f54c6e37ea --- /dev/null +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Classes for the internal OpenSearch realm */ +package org.opensearch.identity.shiro.realm; diff --git a/plugins/identity-shiro/src/main/plugin-metadata/plugin-security.policy b/plugins/identity-shiro/src/main/plugin-metadata/plugin-security.policy new file mode 100644 index 0000000000000..120d1d8fbdb3a --- /dev/null +++ b/plugins/identity-shiro/src/main/plugin-metadata/plugin-security.policy @@ -0,0 +1,11 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +grant { + +}; diff --git a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java new file mode 100644 index 0000000000000..24a06bd9ac71a --- /dev/null +++ b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/AuthTokenHandlerTests.java @@ -0,0 +1,150 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.opensearch.identity.noop.NoopTokenManager; +import org.opensearch.identity.tokens.AuthToken; +import org.opensearch.identity.tokens.BasicAuthToken; +import org.opensearch.identity.tokens.BearerAuthToken; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; + +import java.util.Optional; + +import org.passay.CharacterCharacteristicsRule; +import org.passay.CharacterRule; +import org.passay.EnglishCharacterData; +import org.passay.LengthRule; +import org.passay.PasswordData; +import org.passay.PasswordValidator; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +public class AuthTokenHandlerTests extends OpenSearchTestCase { + + private ShiroTokenManager shiroAuthTokenHandler; + private NoopTokenManager noopTokenManager; + + @Before + public void testSetup() { + shiroAuthTokenHandler = new ShiroTokenManager(); + noopTokenManager = new NoopTokenManager(); + } + + public void testShouldExtractBasicAuthTokenSuccessfully() { + final BasicAuthToken authToken = new BasicAuthToken("Basic YWRtaW46YWRtaW4="); // admin:admin + + final AuthenticationToken translatedToken = shiroAuthTokenHandler.translateAuthToken(authToken).get(); + assertThat(translatedToken, is(instanceOf(UsernamePasswordToken.class))); + + final UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) translatedToken; + assertThat(usernamePasswordToken, notNullValue()); + assertThat(usernamePasswordToken.getUsername(), equalTo("admin")); + assertThat(new String(usernamePasswordToken.getPassword()), equalTo("admin")); + } + + public void testShouldExtractBasicAuthTokenSuccessfully_twoSemiColonPassword() { + final BasicAuthToken authToken = new BasicAuthToken("Basic dGVzdDp0ZTpzdA=="); // test:te:st + + final AuthenticationToken translatedToken = shiroAuthTokenHandler.translateAuthToken(authToken).get(); + assertThat(translatedToken, is(instanceOf(UsernamePasswordToken.class))); + + final UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) translatedToken; + assertThat(usernamePasswordToken, notNullValue()); + assertThat(usernamePasswordToken.getUsername(), equalTo("test")); + assertThat(new String(usernamePasswordToken.getPassword()), equalTo("te:st")); + } + + public void testShouldReturnNullWhenExtractingNullToken() { + final Optional translatedToken = shiroAuthTokenHandler.translateAuthToken(null); + + assertThat(translatedToken.isEmpty(), is(true)); + } + + public void testShouldRevokeTokenSuccessfully() { + final BasicAuthToken authToken = new BasicAuthToken("Basic dGVzdDp0ZTpzdA=="); + assertTrue(authToken.toString().equals("Basic auth token with user=test, password=te:st")); + shiroAuthTokenHandler.revokeToken(authToken); + assert (authToken.toString().equals("Basic auth token with user=, password=")); + } + + public void testShouldResetTokenSuccessfully() { + final BasicAuthToken authToken = new BasicAuthToken("Basic dGVzdDp0ZTpzdA=="); + assertTrue(authToken.toString().equals("Basic auth token with user=test, password=te:st")); + shiroAuthTokenHandler.resetToken(authToken); + assert (authToken.toString().equals("Basic auth token with user=, password=")); + } + + public void testShouldFailWhenRevokeToken() { + final BearerAuthToken bearerAuthToken = new BearerAuthToken("header.payload.signature"); + assert (bearerAuthToken.getTokenIdentifier().equals("Bearer")); + assertThrows(UnsupportedAuthenticationToken.class, () -> shiroAuthTokenHandler.revokeToken(bearerAuthToken)); + } + + public void testShouldFailGetTokenInfo() { + final BearerAuthToken bearerAuthToken = new BearerAuthToken("header.payload.signature"); + assert (bearerAuthToken.getTokenIdentifier().equals("Bearer")); + assertThrows(UnsupportedAuthenticationToken.class, () -> shiroAuthTokenHandler.getTokenInfo(bearerAuthToken)); + } + + public void testShouldFailValidateToken() { + final BearerAuthToken bearerAuthToken = new BearerAuthToken("header.payload.signature"); + assertFalse(shiroAuthTokenHandler.validateToken(bearerAuthToken)); + } + + public void testShoudPassMapLookupWithToken() { + final BasicAuthToken authToken = new BasicAuthToken("Basic dGVzdDp0ZTpzdA=="); + shiroAuthTokenHandler.getShiroTokenPasswordMap().put(authToken, "te:st"); + assertTrue(authToken.getPassword().equals(shiroAuthTokenHandler.getShiroTokenPasswordMap().get(authToken))); + } + + public void testShouldPassThrougbResetToken(AuthToken token) { + final BearerAuthToken bearerAuthToken = new BearerAuthToken("header.payload.signature"); + shiroAuthTokenHandler.resetToken(bearerAuthToken); + } + + public void testVerifyBearerTokenObject() { + BearerAuthToken testGoodToken = new BearerAuthToken("header.payload.signature"); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> new BearerAuthToken("asddfhadfasdfad")); + assert (exception.getMessage().contains("Illegally formed bearer authorization token ")); + assertEquals(testGoodToken.getCompleteToken(), "header.payload.signature"); + assertEquals(testGoodToken.getTokenIdentifier(), "Bearer"); + assertEquals(testGoodToken.getHeader(), "header"); + assertEquals(testGoodToken.getPayload(), "payload"); + assertEquals(testGoodToken.getSignature(), "signature"); + assertEquals(testGoodToken.toString(), "Bearer auth token with header=header, payload=payload, signature=signature"); + } + + public void testGeneratedPasswordContents() { + String password = shiroAuthTokenHandler.generatePassword(); + PasswordData data = new PasswordData(password); + + LengthRule lengthRule = new LengthRule(8, 16); + + CharacterCharacteristicsRule characteristicsRule = new CharacterCharacteristicsRule(); + + // Define M (3 in this case) + characteristicsRule.setNumberOfCharacteristics(3); + + // Define elements of N (upper, lower, digit, symbol) + characteristicsRule.getRules().add(new CharacterRule(EnglishCharacterData.UpperCase, 1)); + characteristicsRule.getRules().add(new CharacterRule(EnglishCharacterData.LowerCase, 1)); + characteristicsRule.getRules().add(new CharacterRule(EnglishCharacterData.Digit, 1)); + characteristicsRule.getRules().add(new CharacterRule(EnglishCharacterData.Special, 1)); + + PasswordValidator validator = new PasswordValidator(lengthRule, characteristicsRule); + validator.validate(data); + } + +} diff --git a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroIdentityPluginTests.java b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroIdentityPluginTests.java new file mode 100644 index 0000000000000..626cd44d13ec8 --- /dev/null +++ b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroIdentityPluginTests.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +import org.opensearch.OpenSearchException; +import org.opensearch.common.settings.Settings; +import org.opensearch.identity.IdentityService; +import org.opensearch.plugins.IdentityPlugin; +import org.opensearch.test.OpenSearchTestCase; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThrows; + +public class ShiroIdentityPluginTests extends OpenSearchTestCase { + + public void testSingleIdentityPluginSucceeds() { + IdentityPlugin identityPlugin1 = new ShiroIdentityPlugin(Settings.EMPTY); + List pluginList1 = List.of(identityPlugin1); + IdentityService identityService1 = new IdentityService(Settings.EMPTY, pluginList1); + assertThat(identityService1.getTokenManager(), is(instanceOf(ShiroTokenManager.class))); + } + + public void testMultipleIdentityPluginsFail() { + IdentityPlugin identityPlugin1 = new ShiroIdentityPlugin(Settings.EMPTY); + IdentityPlugin identityPlugin2 = new ShiroIdentityPlugin(Settings.EMPTY); + IdentityPlugin identityPlugin3 = new ShiroIdentityPlugin(Settings.EMPTY); + List pluginList = List.of(identityPlugin1, identityPlugin2, identityPlugin3); + Exception ex = assertThrows(OpenSearchException.class, () -> new IdentityService(Settings.EMPTY, pluginList)); + assert (ex.getMessage().contains("Multiple identity plugins are not supported,")); + } + +} diff --git a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroSubjectTests.java b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroSubjectTests.java new file mode 100644 index 0000000000000..ca896e1475120 --- /dev/null +++ b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/ShiroSubjectTests.java @@ -0,0 +1,73 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro; + +import org.opensearch.test.OpenSearchTestCase; +import org.junit.After; +import org.junit.Before; + +import java.security.Principal; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class ShiroSubjectTests extends OpenSearchTestCase { + + private org.apache.shiro.subject.Subject shiroSubject; + private ShiroTokenManager authTokenHandler; + private ShiroSubject subject; + + @Before + public void setup() { + shiroSubject = mock(org.apache.shiro.subject.Subject.class); + authTokenHandler = mock(ShiroTokenManager.class); + subject = new ShiroSubject(authTokenHandler, shiroSubject); + } + + @After + public void cleanup() { + verifyNoMoreInteractions(shiroSubject); + } + + public void testGetPrincipal_null() { + when(shiroSubject.getPrincipal()).thenReturn(null); + + final Principal result = subject.getPrincipal(); + + assertThat(result, nullValue()); + verify(shiroSubject).getPrincipal(); + } + + public void testGetPrincipal_principal() { + final Principal mockPrincipal = mock(Principal.class); + when(shiroSubject.getPrincipal()).thenReturn(mockPrincipal); + + final Principal result = subject.getPrincipal(); + + assertThat(result, equalTo(mockPrincipal)); + verify(shiroSubject).getPrincipal(); + } + + public void testGetPrincipal_otherType() { + final Object objPrincipal = mock(Object.class); + when(shiroSubject.getPrincipal()).thenReturn(objPrincipal); + when(objPrincipal.toString()).thenReturn("objectPrincipalString"); + + final Principal result = subject.getPrincipal(); + + // assertThat(result, equalTo("objectPrincipalString")); + verify(shiroSubject).getPrincipal(); + verifyNoMoreInteractions(objPrincipal); + } + +} diff --git a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcherTests.java b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcherTests.java new file mode 100644 index 0000000000000..91e88ed1bf701 --- /dev/null +++ b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcherTests.java @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro.realm; + +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.opensearch.test.OpenSearchTestCase; + +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BCryptPasswordMatcherTests extends OpenSearchTestCase { + + public void testCredentialMatch() { + final UsernamePasswordToken token = mock(UsernamePasswordToken.class); + when(token.getPassword()).thenReturn("admin".toCharArray()); + final AuthenticationInfo info = mock(AuthenticationInfo.class); + when(info.getCredentials()).thenReturn("$2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG"); + + final BCryptPasswordMatcher matcher = new BCryptPasswordMatcher(); + final boolean result = matcher.doCredentialsMatch(token, info); + + assertThat(result, equalTo(true)); + } + + public void testCredentialDoNotMatch() { + final UsernamePasswordToken token = mock(UsernamePasswordToken.class); + when(token.getPassword()).thenReturn("HashedPassword".toCharArray()); + final AuthenticationInfo info = mock(AuthenticationInfo.class); + when(info.getCredentials()).thenReturn("$2a$12$VcCDgh2NDk07JGN0rQGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG"); + + final BCryptPasswordMatcher matcher = new BCryptPasswordMatcher(); + final boolean result = matcher.doCredentialsMatch(token, info); + + assertThat(result, equalTo(false)); + } +} diff --git a/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/realm/OpenSearchRealmTests.java b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/realm/OpenSearchRealmTests.java new file mode 100644 index 0000000000000..db96a6d91a38e --- /dev/null +++ b/plugins/identity-shiro/src/test/java/org/opensearch/identity/shiro/realm/OpenSearchRealmTests.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.identity.shiro.realm; + +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.IncorrectCredentialsException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; + +public class OpenSearchRealmTests extends OpenSearchTestCase { + + private OpenSearchRealm realm; + + @Before + public void setup() { + realm = new OpenSearchRealm.Builder("test").build(); + } + + public void testGetAuthenticationInfoUserExists() { + final UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin"); + final User admin = realm.getInternalUser("admin"); + final AuthenticationInfo adminInfo = realm.getAuthenticationInfo(token); + assertNotNull(adminInfo); + } + + public void testGetAuthenticationInfoUserExistsWrongPassword() { + final UsernamePasswordToken token = new UsernamePasswordToken("admin", "wrong_password"); + final User admin = realm.getInternalUser("admin"); + + assertThrows(IncorrectCredentialsException.class, () -> realm.getAuthenticationInfo(token)); + } +} diff --git a/plugins/ingest-attachment/build.gradle b/plugins/ingest-attachment/build.gradle index 231ce40f3f38c..330a17c02bc7a 100644 --- a/plugins/ingest-attachment/build.gradle +++ b/plugins/ingest-attachment/build.gradle @@ -38,19 +38,26 @@ opensearchplugin { } versions << [ - 'tika' : '1.24.1', - 'pdfbox': '2.0.24', - 'poi' : '4.1.2', - 'mime4j': '0.8.3' + 'tika' : '2.6.0', + 'pdfbox': '2.0.27', + 'poi' : '5.2.3', + 'mime4j': '0.8.8' ] dependencies { // mandatory for tika api "org.apache.tika:tika-core:${versions.tika}" - // build against Jackson 2.9.5, but still works on our current version api "org.apache.tika:tika-parsers:${versions.tika}" - api 'org.tukaani:xz:1.8' - api 'commons-io:commons-io:2.7' + // Required for the various document parsers + api "org.apache.tika:tika-parsers-standard-package:${versions.tika}" + // Required for language detection + api "org.apache.tika:tika-langdetect-optimaize:${versions.tika}" + // Optimaize libraries/dependencies + runtimeOnly "com.optimaize.languagedetector:language-detector:0.6" + runtimeOnly "com.google.guava:guava:${versions.guava}" + // Other dependencies + api 'org.tukaani:xz:1.9' + api 'commons-io:commons-io:2.13.0' api "org.slf4j:slf4j-api:${versions.slf4j}" // character set detection @@ -62,31 +69,31 @@ dependencies { // Adobe PDF api "org.apache.pdfbox:pdfbox:${versions.pdfbox}" api "org.apache.pdfbox:fontbox:${versions.pdfbox}" - api "org.apache.pdfbox:jempbox:1.8.16" + api "org.apache.pdfbox:jempbox:1.8.17" api "commons-logging:commons-logging:${versions.commonslogging}" - api "org.bouncycastle:bcmail-jdk15on:${versions.bouncycastle}" - api "org.bouncycastle:bcprov-jdk15on:${versions.bouncycastle}" - api "org.bouncycastle:bcpkix-jdk15on:${versions.bouncycastle}" + api "org.bouncycastle:bcmail-jdk15to18:${versions.bouncycastle}" + api "org.bouncycastle:bcprov-jdk15to18:${versions.bouncycastle}" + api "org.bouncycastle:bcpkix-jdk15to18:${versions.bouncycastle}" // OpenOffice api "org.apache.poi:poi-ooxml:${versions.poi}" api "org.apache.poi:poi:${versions.poi}" - api "org.apache.poi:poi-ooxml-schemas:${versions.poi}" + api "org.apache.poi:poi-ooxml-lite:${versions.poi}" api "commons-codec:commons-codec:${versions.commonscodec}" - api 'org.apache.xmlbeans:xmlbeans:3.0.1' - api 'org.apache.commons:commons-collections4:4.1' + api 'org.apache.xmlbeans:xmlbeans:5.1.1' + api 'org.apache.commons:commons-collections4:4.4' // MS Office api "org.apache.poi:poi-scratchpad:${versions.poi}" // Apple iWork - api 'org.apache.commons:commons-compress:1.21' + api "org.apache.commons:commons-compress:${versions.commonscompress}" // Outlook documents api "org.apache.james:apache-mime4j-core:${versions.mime4j}" api "org.apache.james:apache-mime4j-dom:${versions.mime4j}" // EPUB books - api 'org.apache.commons:commons-lang3:3.9' + api "org.apache.commons:commons-lang3:${versions.commonslang}" // Microsoft Word files with visio diagrams api 'org.apache.commons:commons-math3:3.6.1' // POIs dependency - api 'com.zaxxer:SparseBitSet:1.2' + api 'com.zaxxer:SparseBitSet:1.3' } restResources { @@ -97,6 +104,8 @@ restResources { tasks.named("dependencyLicenses").configure { mapping from: /apache-mime4j-.*/, to: 'apache-mime4j' + mapping from: /tika-.*/, to: 'tika-parsers' + mapping from: /poi-ooxml-.*/, to: 'poi-ooxml' } forbiddenPatterns { @@ -109,6 +118,22 @@ forbiddenPatterns { thirdPartyAudit { ignoreMissingClasses() + ignoreViolations( + // uses internal java api: sun.misc.Unsafe + 'com.google.common.cache.Striped64', + 'com.google.common.cache.Striped64$1', + 'com.google.common.cache.Striped64$Cell', + 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray', + 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$1', + 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$2', + 'com.google.common.hash.Striped64', + 'com.google.common.hash.Striped64$1', + 'com.google.common.hash.Striped64$Cell', + 'com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator', + 'com.google.common.primitives.UnsignedBytes$LexicographicalComparatorHolder$UnsafeComparator$1', + 'com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper', + 'com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper$1' + ) } if (BuildParams.inFipsJvm) { diff --git a/plugins/ingest-attachment/licenses/SparseBitSet-1.2.jar.sha1 b/plugins/ingest-attachment/licenses/SparseBitSet-1.2.jar.sha1 deleted file mode 100644 index 5f1d015b87ac7..0000000000000 --- a/plugins/ingest-attachment/licenses/SparseBitSet-1.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8467c813d442837fcaeddbc42cf5c5359fab4933 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/SparseBitSet-1.3.jar.sha1 b/plugins/ingest-attachment/licenses/SparseBitSet-1.3.jar.sha1 new file mode 100644 index 0000000000000..2803db7c91e30 --- /dev/null +++ b/plugins/ingest-attachment/licenses/SparseBitSet-1.3.jar.sha1 @@ -0,0 +1 @@ +533eac055afe3d5f614ea95e333afd6c2bde8f26 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/apache-mime4j-core-0.8.3.jar.sha1 b/plugins/ingest-attachment/licenses/apache-mime4j-core-0.8.3.jar.sha1 deleted file mode 100644 index 464a34dd97643..0000000000000 --- a/plugins/ingest-attachment/licenses/apache-mime4j-core-0.8.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1179b56c9919c1a8e20d3a528ee4c6cee19bcbe0 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/apache-mime4j-core-0.8.8.jar.sha1 b/plugins/ingest-attachment/licenses/apache-mime4j-core-0.8.8.jar.sha1 new file mode 100644 index 0000000000000..77c36691d36b5 --- /dev/null +++ b/plugins/ingest-attachment/licenses/apache-mime4j-core-0.8.8.jar.sha1 @@ -0,0 +1 @@ +7330de23c52f71617cbec7f1d2760dae32e687cd \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/apache-mime4j-dom-0.8.3.jar.sha1 b/plugins/ingest-attachment/licenses/apache-mime4j-dom-0.8.3.jar.sha1 deleted file mode 100644 index 4f98753aa0af4..0000000000000 --- a/plugins/ingest-attachment/licenses/apache-mime4j-dom-0.8.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e80733714eb6a70895bfc74a9528c658504c2c83 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/apache-mime4j-dom-0.8.8.jar.sha1 b/plugins/ingest-attachment/licenses/apache-mime4j-dom-0.8.8.jar.sha1 new file mode 100644 index 0000000000000..fb9c5fed27162 --- /dev/null +++ b/plugins/ingest-attachment/licenses/apache-mime4j-dom-0.8.8.jar.sha1 @@ -0,0 +1 @@ +e76715563a6bd150f84ccb0adb920aec8faf4779 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcmail-jdk15on-1.70.jar.sha1 b/plugins/ingest-attachment/licenses/bcmail-jdk15on-1.70.jar.sha1 deleted file mode 100644 index 672e479eda8d7..0000000000000 --- a/plugins/ingest-attachment/licenses/bcmail-jdk15on-1.70.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -08f4aafad90f6cc7f16b9992279828ae848c9e0d \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcmail-jdk15to18-1.76.jar.sha1 b/plugins/ingest-attachment/licenses/bcmail-jdk15to18-1.76.jar.sha1 new file mode 100644 index 0000000000000..46010d64015ad --- /dev/null +++ b/plugins/ingest-attachment/licenses/bcmail-jdk15to18-1.76.jar.sha1 @@ -0,0 +1 @@ +23d8bcad6b57912e4633ca9955926ffcdf3c5c71 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcmail-jdk15on-LICENSE.txt b/plugins/ingest-attachment/licenses/bcmail-jdk15to18-LICENSE.txt similarity index 100% rename from plugins/ingest-attachment/licenses/bcmail-jdk15on-LICENSE.txt rename to plugins/ingest-attachment/licenses/bcmail-jdk15to18-LICENSE.txt diff --git a/plugins/ingest-attachment/licenses/bcmail-jdk15to18-NOTICE.txt b/plugins/ingest-attachment/licenses/bcmail-jdk15to18-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/ingest-attachment/licenses/bcpkix-jdk15on-1.70.jar.sha1 b/plugins/ingest-attachment/licenses/bcpkix-jdk15on-1.70.jar.sha1 deleted file mode 100644 index e348463a21257..0000000000000 --- a/plugins/ingest-attachment/licenses/bcpkix-jdk15on-1.70.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f81e5af49571a9d5a109a88f239a73ce87055417 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcpkix-jdk15to18-1.76.jar.sha1 b/plugins/ingest-attachment/licenses/bcpkix-jdk15to18-1.76.jar.sha1 new file mode 100644 index 0000000000000..a843d972ac681 --- /dev/null +++ b/plugins/ingest-attachment/licenses/bcpkix-jdk15to18-1.76.jar.sha1 @@ -0,0 +1 @@ +3ee440dfa1c557c1cc0c46b5dadf5ef3896ccebb \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcpkix-jdk15on-LICENSE.txt b/plugins/ingest-attachment/licenses/bcpkix-jdk15to18-LICENSE.txt similarity index 100% rename from plugins/ingest-attachment/licenses/bcpkix-jdk15on-LICENSE.txt rename to plugins/ingest-attachment/licenses/bcpkix-jdk15to18-LICENSE.txt diff --git a/plugins/ingest-attachment/licenses/bcpkix-jdk15to18-NOTICE.txt b/plugins/ingest-attachment/licenses/bcpkix-jdk15to18-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk15on-1.70.jar.sha1 b/plugins/ingest-attachment/licenses/bcprov-jdk15on-1.70.jar.sha1 deleted file mode 100644 index f5e89c0f5ed45..0000000000000 --- a/plugins/ingest-attachment/licenses/bcprov-jdk15on-1.70.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4636a0d01f74acaf28082fb62b317f1080118371 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk15on-LICENSE.txt b/plugins/ingest-attachment/licenses/bcprov-jdk15on-LICENSE.txt deleted file mode 100644 index e1fc4a1506db5..0000000000000 --- a/plugins/ingest-attachment/licenses/bcprov-jdk15on-LICENSE.txt +++ /dev/null @@ -1,23 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2000 - 2013 The Legion of the Bouncy Castle Inc. - (http://www.bouncycastle.org) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk15to18-1.76.jar.sha1 b/plugins/ingest-attachment/licenses/bcprov-jdk15to18-1.76.jar.sha1 new file mode 100644 index 0000000000000..2e96c404bef98 --- /dev/null +++ b/plugins/ingest-attachment/licenses/bcprov-jdk15to18-1.76.jar.sha1 @@ -0,0 +1 @@ +0cb53f10290a634808555bc4b34328fdab1001f2 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk15to18-LICENSE.txt b/plugins/ingest-attachment/licenses/bcprov-jdk15to18-LICENSE.txt new file mode 100644 index 0000000000000..9f27bafe96885 --- /dev/null +++ b/plugins/ingest-attachment/licenses/bcprov-jdk15to18-LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2000 - 2013 The Legion of the Bouncy Castle Inc. + (http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk15to18-NOTICE.txt b/plugins/ingest-attachment/licenses/bcprov-jdk15to18-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/ingest-attachment/licenses/commons-codec-1.13.jar.sha1 b/plugins/ingest-attachment/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/ingest-attachment/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-codec-1.15.jar.sha1 b/plugins/ingest-attachment/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/ingest-attachment/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-collections4-4.1.jar.sha1 b/plugins/ingest-attachment/licenses/commons-collections4-4.1.jar.sha1 deleted file mode 100644 index f054416580624..0000000000000 --- a/plugins/ingest-attachment/licenses/commons-collections4-4.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a4cf4688fe1c7e3a63aa636cc96d013af537768e \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-collections4-4.4.jar.sha1 b/plugins/ingest-attachment/licenses/commons-collections4-4.4.jar.sha1 new file mode 100644 index 0000000000000..6b4ed5ab62b44 --- /dev/null +++ b/plugins/ingest-attachment/licenses/commons-collections4-4.4.jar.sha1 @@ -0,0 +1 @@ +62ebe7544cb7164d87e0637a2a6a2bdc981395e8 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-compress-1.21.jar.sha1 b/plugins/ingest-attachment/licenses/commons-compress-1.21.jar.sha1 deleted file mode 100644 index 81ac609a1aa26..0000000000000 --- a/plugins/ingest-attachment/licenses/commons-compress-1.21.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4ec95b60d4e86b5c95a0e919cb172a0af98011ef \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-compress-1.24.0.jar.sha1 b/plugins/ingest-attachment/licenses/commons-compress-1.24.0.jar.sha1 new file mode 100644 index 0000000000000..23999d1bfbde4 --- /dev/null +++ b/plugins/ingest-attachment/licenses/commons-compress-1.24.0.jar.sha1 @@ -0,0 +1 @@ +b4b1b5a3d9573b2970fddab236102c0a4d27d35e \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-io-2.13.0.jar.sha1 b/plugins/ingest-attachment/licenses/commons-io-2.13.0.jar.sha1 new file mode 100644 index 0000000000000..c165136eb5822 --- /dev/null +++ b/plugins/ingest-attachment/licenses/commons-io-2.13.0.jar.sha1 @@ -0,0 +1 @@ +8bb2bc9b4df17e2411533a0708a69f983bf5e83b \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-io-2.7.jar.sha1 b/plugins/ingest-attachment/licenses/commons-io-2.7.jar.sha1 deleted file mode 100644 index bbb1b15dd1e1e..0000000000000 --- a/plugins/ingest-attachment/licenses/commons-io-2.7.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f2bd4ba11c4162733c13cc90ca7c7ea09967102 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-lang3-3.13.0.jar.sha1 b/plugins/ingest-attachment/licenses/commons-lang3-3.13.0.jar.sha1 new file mode 100644 index 0000000000000..d0c2f2486ee1f --- /dev/null +++ b/plugins/ingest-attachment/licenses/commons-lang3-3.13.0.jar.sha1 @@ -0,0 +1 @@ +b7263237aa89c1f99b327197c41d0669707a462e \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-lang3-3.9.jar.sha1 b/plugins/ingest-attachment/licenses/commons-lang3-3.9.jar.sha1 deleted file mode 100644 index 2adcfd377f87c..0000000000000 --- a/plugins/ingest-attachment/licenses/commons-lang3-3.9.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0122c7cee69b53ed4a7681c03d4ee4c0e2765da5 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-logging-1.1.3.jar.sha1 b/plugins/ingest-attachment/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index 5b8f029e58293..0000000000000 --- a/plugins/ingest-attachment/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-logging-1.2.jar.sha1 b/plugins/ingest-attachment/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/ingest-attachment/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/commons-logging-LICENSE.txt b/plugins/ingest-attachment/licenses/commons-logging-LICENSE.txt index d645695673349..57bc88a15a0ee 100644 --- a/plugins/ingest-attachment/licenses/commons-logging-LICENSE.txt +++ b/plugins/ingest-attachment/licenses/commons-logging-LICENSE.txt @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -200,3 +199,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + diff --git a/plugins/ingest-attachment/licenses/commons-logging-NOTICE.txt b/plugins/ingest-attachment/licenses/commons-logging-NOTICE.txt index d3d6e140ce4f3..72eb32a902458 100644 --- a/plugins/ingest-attachment/licenses/commons-logging-NOTICE.txt +++ b/plugins/ingest-attachment/licenses/commons-logging-NOTICE.txt @@ -1,5 +1,5 @@ -Apache Commons Logging -Copyright 2003-2014 The Apache Software Foundation +Apache Commons CLI +Copyright 2001-2009 The Apache Software Foundation -This product includes software developed at +This product includes software developed by The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/ingest-attachment/licenses/fontbox-2.0.24.jar.sha1 b/plugins/ingest-attachment/licenses/fontbox-2.0.24.jar.sha1 deleted file mode 100644 index 1f6388867917a..0000000000000 --- a/plugins/ingest-attachment/licenses/fontbox-2.0.24.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -df8ecb3006dfcd52355a5902096e5ec34f06112e \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/fontbox-2.0.27.jar.sha1 b/plugins/ingest-attachment/licenses/fontbox-2.0.27.jar.sha1 new file mode 100644 index 0000000000000..d578dffbfa3f6 --- /dev/null +++ b/plugins/ingest-attachment/licenses/fontbox-2.0.27.jar.sha1 @@ -0,0 +1 @@ +d08c064d18b2b149da937d15c0d1708cba03f29d \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/guava-32.1.1-jre.jar.sha1 b/plugins/ingest-attachment/licenses/guava-32.1.1-jre.jar.sha1 new file mode 100644 index 0000000000000..0d791b5d3f55b --- /dev/null +++ b/plugins/ingest-attachment/licenses/guava-32.1.1-jre.jar.sha1 @@ -0,0 +1 @@ +ad575652d84153075dd41ec6177ccb15251262b2 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/guava-LICENSE.txt b/plugins/ingest-attachment/licenses/guava-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/ingest-attachment/licenses/guava-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/ingest-attachment/licenses/guava-NOTICE.txt b/plugins/ingest-attachment/licenses/guava-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/ingest-attachment/licenses/jempbox-1.8.16.jar.sha1 b/plugins/ingest-attachment/licenses/jempbox-1.8.16.jar.sha1 deleted file mode 100644 index aba5a49037c48..0000000000000 --- a/plugins/ingest-attachment/licenses/jempbox-1.8.16.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1f41de81768ef84ca2d8cda4cb79e9272c8ee966 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/jempbox-1.8.17.jar.sha1 b/plugins/ingest-attachment/licenses/jempbox-1.8.17.jar.sha1 new file mode 100644 index 0000000000000..c0d190ad2b623 --- /dev/null +++ b/plugins/ingest-attachment/licenses/jempbox-1.8.17.jar.sha1 @@ -0,0 +1 @@ +388997fbd1b57f8e424c4447e3fc8ce5dd2fc665 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/language-detector-0.6.jar.sha1 b/plugins/ingest-attachment/licenses/language-detector-0.6.jar.sha1 new file mode 100644 index 0000000000000..5c069b9095db6 --- /dev/null +++ b/plugins/ingest-attachment/licenses/language-detector-0.6.jar.sha1 @@ -0,0 +1 @@ +52fee1eaa101f8d3e30b7095e1b6e0054d514dde \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/language-detector-LICENSE.txt b/plugins/ingest-attachment/licenses/language-detector-LICENSE.txt new file mode 100644 index 0000000000000..a62d849a22618 --- /dev/null +++ b/plugins/ingest-attachment/licenses/language-detector-LICENSE.txt @@ -0,0 +1,88 @@ +Apache License, Version 2.0 + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + You must give any other recipients of the Work or Derivative Works a copy of this License; and + You must cause any modified files to carry prominent notices stating that You changed the files; and + You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + + To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/ingest-attachment/licenses/language-detector-NOTICE.txt b/plugins/ingest-attachment/licenses/language-detector-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/ingest-attachment/licenses/pdfbox-2.0.24.jar.sha1 b/plugins/ingest-attachment/licenses/pdfbox-2.0.24.jar.sha1 deleted file mode 100644 index 2eb2e357cbf1c..0000000000000 --- a/plugins/ingest-attachment/licenses/pdfbox-2.0.24.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -cb562ee5f43e29415af4477e62fbe668ef88d18b \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/pdfbox-2.0.27.jar.sha1 b/plugins/ingest-attachment/licenses/pdfbox-2.0.27.jar.sha1 new file mode 100644 index 0000000000000..4f670b7f95e8c --- /dev/null +++ b/plugins/ingest-attachment/licenses/pdfbox-2.0.27.jar.sha1 @@ -0,0 +1 @@ +416a9dfce3714116bfdf793b15368df04266845f \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/poi-4.1.2.jar.sha1 b/plugins/ingest-attachment/licenses/poi-4.1.2.jar.sha1 deleted file mode 100644 index dae936314bde1..0000000000000 --- a/plugins/ingest-attachment/licenses/poi-4.1.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -964bf41cf68bce08e4ef6b2279b559fdf8d454f4 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/poi-5.2.3.jar.sha1 b/plugins/ingest-attachment/licenses/poi-5.2.3.jar.sha1 new file mode 100644 index 0000000000000..3d8b3daf606ad --- /dev/null +++ b/plugins/ingest-attachment/licenses/poi-5.2.3.jar.sha1 @@ -0,0 +1 @@ +2fb22ae74ad5aea6af1a9c64b9542f2ccf348604 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/poi-ooxml-4.1.2.jar.sha1 b/plugins/ingest-attachment/licenses/poi-ooxml-4.1.2.jar.sha1 deleted file mode 100644 index b42c94ac3b33e..0000000000000 --- a/plugins/ingest-attachment/licenses/poi-ooxml-4.1.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -87d9a22aa9a7dd26e80c360e709f7ee02e32ab3b \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/poi-ooxml-5.2.3.jar.sha1 b/plugins/ingest-attachment/licenses/poi-ooxml-5.2.3.jar.sha1 new file mode 100644 index 0000000000000..8371593cf0841 --- /dev/null +++ b/plugins/ingest-attachment/licenses/poi-ooxml-5.2.3.jar.sha1 @@ -0,0 +1 @@ +02efd11c940adb18c03eb9ce7ad88fc40ee6a196 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/poi-ooxml-lite-5.2.3.jar.sha1 b/plugins/ingest-attachment/licenses/poi-ooxml-lite-5.2.3.jar.sha1 new file mode 100644 index 0000000000000..5c6365876b7be --- /dev/null +++ b/plugins/ingest-attachment/licenses/poi-ooxml-lite-5.2.3.jar.sha1 @@ -0,0 +1 @@ +db113c8e9051b0ff967f4911fa20336c8325a7c5 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/poi-ooxml-schemas-4.1.2.jar.sha1 b/plugins/ingest-attachment/licenses/poi-ooxml-schemas-4.1.2.jar.sha1 deleted file mode 100644 index 852e018a5970d..0000000000000 --- a/plugins/ingest-attachment/licenses/poi-ooxml-schemas-4.1.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -550cc22a598c0b0a51d1f55f8371e83c1229802d \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/poi-ooxml-schemas-LICENSE.txt b/plugins/ingest-attachment/licenses/poi-ooxml-schemas-LICENSE.txt deleted file mode 100644 index dd2cbd5fbc180..0000000000000 --- a/plugins/ingest-attachment/licenses/poi-ooxml-schemas-LICENSE.txt +++ /dev/null @@ -1,463 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -APACHE POI SUBCOMPONENTS: - -Apache POI includes subcomponents with separate copyright notices and -license terms. Your use of these subcomponents is subject to the terms -and conditions of the following licenses: - - -Office Open XML schemas (ooxml-schemas-1.1.jar) - - The Office Open XML schema definitions used by Apache POI are - a part of the Office Open XML ECMA Specification (ECMA-376, [1]). - As defined in section 9.4 of the ECMA bylaws [2], this specification - is available to all interested parties without restriction: - - 9.4 All documents when approved shall be made available to - all interested parties without restriction. - - Furthermore, both Microsoft and Adobe have granted patent licenses - to this work [3,4,5]. - - [1] http://www.ecma-international.org/publications/standards/Ecma-376.htm - [2] http://www.ecma-international.org/memento/Ecmabylaws.htm - [3] http://www.microsoft.com/openspecifications/en/us/programs/osp/default.aspx - [4] http://www.ecma-international.org/publications/files/ECMA-ST/Ecma%20PATENT/Patent%20statements%20ok/ECMA-376%20Edition%202%20Microsoft%20Patent%20Declaration.pdf - [5] http://www.ecma-international.org/publications/files/ECMA-ST/Ecma%20PATENT/Patent%20statements%20ok/ECMA-376%20Adobe%20Patent%20Declaration.pdf - - -JUnit test library (junit-4.11.jar) - - Common Public License - v 1.0 - - THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON - PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION - OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - - 1. DEFINITIONS - - "Contribution" means: - - a) in the case of the initial Contributor, the initial code and - documentation distributed under this Agreement, and - - b) in the case of each subsequent Contributor: - - i) changes to the Program, and - - ii) additions to the Program; - - where such changes and/or additions to the Program originate from - and are distributed by that particular Contributor. A Contribution - 'originates' from a Contributor if it was added to the Program by - such Contributor itself or anyone acting on such Contributor's behalf. - Contributions do not include additions to the Program which: (i) are - separate modules of software distributed in conjunction with the - Program under their own license agreement, and (ii) are not derivative - works of the Program. - - "Contributor" means any person or entity that distributes the Program. - - "Licensed Patents " mean patent claims licensable by a Contributor which - are necessarily infringed by the use or sale of its Contribution alone - or when combined with the Program. - - "Program" means the Contributions distributed in accordance with this - Agreement. - - "Recipient" means anyone who receives the Program under this Agreement, - including all Contributors. - - 2. GRANT OF RIGHTS - - a) Subject to the terms of this Agreement, each Contributor hereby grants - Recipient a non-exclusive, worldwide, royalty-free copyright license - to reproduce, prepare derivative works of, publicly display, publicly - perform, distribute and sublicense the Contribution of such - Contributor, if any, and such derivative works, in source code and - object code form. - - b) Subject to the terms of this Agreement, each Contributor hereby grants - Recipient a non-exclusive, worldwide, royalty-free patent license under - Licensed Patents to make, use, sell, offer to sell, import and - otherwise transfer the Contribution of such Contributor, if any, in - source code and object code form. This patent license shall apply to - the combination of the Contribution and the Program if, at the time - the Contribution is added by the Contributor, such addition of the - Contribution causes such combination to be covered by the Licensed - Patents. The patent license shall not apply to any other combinations - which include the Contribution. No hardware per se is licensed - hereunder. - - c) Recipient understands that although each Contributor grants the - licenses to its Contributions set forth herein, no assurances are - provided by any Contributor that the Program does not infringe the - patent or other intellectual property rights of any other entity. - Each Contributor disclaims any liability to Recipient for claims - brought by any other entity based on infringement of intellectual - property rights or otherwise. As a condition to exercising the rights - and licenses granted hereunder, each Recipient hereby assumes sole - responsibility to secure any other intellectual property rights - needed, if any. For example, if a third party patent license is - required to allow Recipient to distribute the Program, it is - Recipient's responsibility to acquire that license before - distributing the Program. - - d) Each Contributor represents that to its knowledge it has sufficient - copyright rights in its Contribution, if any, to grant the copyright - license set forth in this Agreement. - - 3. REQUIREMENTS - - A Contributor may choose to distribute the Program in object code form - under its own license agreement, provided that: - - a) it complies with the terms and conditions of this Agreement; and - - b) its license agreement: - - i) effectively disclaims on behalf of all Contributors all warranties - and conditions, express and implied, including warranties or - conditions of title and non-infringement, and implied warranties - or conditions of merchantability and fitness for a particular - purpose; - - ii) effectively excludes on behalf of all Contributors all liability - for damages, including direct, indirect, special, incidental and - consequential damages, such as lost profits; - - iii) states that any provisions which differ from this Agreement are - offered by that Contributor alone and not by any other party; and - - iv) states that source code for the Program is available from such - Contributor, and informs licensees how to obtain it in a - reasonable manner on or through a medium customarily used for - software exchange. - - When the Program is made available in source code form: - - a) it must be made available under this Agreement; and - - b) a copy of this Agreement must be included with each copy of - the Program. - - Contributors may not remove or alter any copyright notices contained - within the Program. - - Each Contributor must identify itself as the originator of its - Contribution, if any, in a manner that reasonably allows subsequent - Recipients to identify the originator of the Contribution. - - 4. COMMERCIAL DISTRIBUTION - - Commercial distributors of software may accept certain responsibilities - with respect to end users, business partners and the like. While this - license is intended to facilitate the commercial use of the Program, - the Contributor who includes the Program in a commercial product offering - should do so in a manner which does not create potential liability for - other Contributors. Therefore, if a Contributor includes the Program - in a commercial product offering, such Contributor ("Commercial - Contributor") hereby agrees to defend and indemnify every other - Contributor ("Indemnified Contributor") against any losses, damages - and costs (collectively "Losses") arising from claims, lawsuits and - other legal actions brought by a third party against the Indemnified - Contributor to the extent caused by the acts or omissions of such - Commercial Contributor in connection with its distribution of the - Program in a commercial product offering. The obligations in this - section do not apply to any claims or Losses relating to any actual - or alleged intellectual property infringement. In order to qualify, - an Indemnified Contributor must: a) promptly notify the Commercial - Contributor in writing of such claim, and b) allow the Commercial - Contributor to control, and cooperate with the Commercial Contributor - in, the defense and any related settlement negotiations. The Indemnified - Contributor may participate in any such claim at its own expense. - - For example, a Contributor might include the Program in a commercial - product offering, Product X. That Contributor is then a Commercial - Contributor. If that Commercial Contributor then makes performance - claims, or offers warranties related to Product X, those performance - claims and warranties are such Commercial Contributor's responsibility - alone. Under this section, the Commercial Contributor would have to - defend claims against the other Contributors related to those - performance claims and warranties, and if a court requires any other - Contributor to pay any damages as a result, the Commercial Contributor - must pay those damages. - - 5. NO WARRANTY - - EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED - ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER - EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR - CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR - A PARTICULAR PURPOSE. Each Recipient is solely responsible for - determining the appropriateness of using and distributing the Program - and assumes all risks associated with its exercise of rights under this - Agreement, including but not limited to the risks and costs of program - errors, compliance with applicable laws, damage to or loss of data, - programs or equipment, and unavailability or interruption of operations. - - 6. DISCLAIMER OF LIABILITY - - EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR - ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING - WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR - DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED - HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - - 7. GENERAL - - If any provision of this Agreement is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this Agreement, and without further - action by the parties hereto, such provision shall be reformed to the - minimum extent necessary to make such provision valid and enforceable. - - If Recipient institutes patent litigation against a Contributor with - respect to a patent applicable to software (including a cross-claim or - counterclaim in a lawsuit), then any patent licenses granted by that - Contributor to such Recipient under this Agreement shall terminate as of - the date such litigation is filed. In addition, if Recipient institutes - patent litigation against any entity (including a cross-claim or - counterclaim in a lawsuit) alleging that the Program itself (excluding - combinations of the Program with other software or hardware) infringes - such Recipient's patent(s), then such Recipient's rights granted under - Section 2(b) shall terminate as of the date such litigation is filed. - - All Recipient's rights under this Agreement shall terminate if it fails - to comply with any of the material terms or conditions of this Agreement - and does not cure such failure in a reasonable period of time after - becoming aware of such noncompliance. If all Recipient's rights under - this Agreement terminate, Recipient agrees to cease use and distribution - of the Program as soon as reasonably practicable. However, Recipient's - obligations under this Agreement and any licenses granted by Recipient - relating to the Program shall continue and survive. - - Everyone is permitted to copy and distribute copies of this Agreement, - but in order to avoid inconsistency the Agreement is copyrighted and may - only be modified in the following manner. The Agreement Steward reserves - the right to publish new versions (including revisions) of this Agreement - from time to time. No one other than the Agreement Steward has the right - to modify this Agreement. IBM is the initial Agreement Steward. IBM may - assign the responsibility to serve as the Agreement Steward to a suitable - separate entity. Each new version of the Agreement will be given a - distinguishing version number. The Program (including Contributions) may - always be distributed subject to the version of the Agreement under which - it was received. In addition, after a new version of the Agreement is - published, Contributor may elect to distribute the Program (including - its Contributions) under the new version. Except as expressly stated in - Sections 2(a) and 2(b) above, Recipient receives no rights or licenses - to the intellectual property of any Contributor under this Agreement, - whether expressly, by implication, estoppel or otherwise. All rights in - the Program not expressly granted under this Agreement are reserved. - - This Agreement is governed by the laws of the State of New York and the - intellectual property laws of the United States of America. No party to - this Agreement will bring a legal action under this Agreement more than - one year after the cause of action arose. Each party waives its rights - to a jury trial in any resulting litigation. diff --git a/plugins/ingest-attachment/licenses/poi-ooxml-schemas-NOTICE.txt b/plugins/ingest-attachment/licenses/poi-ooxml-schemas-NOTICE.txt deleted file mode 100644 index 12ff265290de1..0000000000000 --- a/plugins/ingest-attachment/licenses/poi-ooxml-schemas-NOTICE.txt +++ /dev/null @@ -1,23 +0,0 @@ -Apache POI -Copyright 2003-2015 The Apache Software Foundation - -This product includes software developed by -The Apache Software Foundation (http://www.apache.org/). - -This product contains parts that were originally based on software from BEA. -Copyright (c) 2000-2003, BEA Systems, . - -This product contains W3C XML Schema documents. Copyright 2001-2003 (c) -World Wide Web Consortium (Massachusetts Institute of Technology, European -Research Consortium for Informatics and Mathematics, Keio University) - -This product contains the Piccolo XML Parser for Java -(http://piccolo.sourceforge.net/). Copyright 2002 Yuval Oren. - -This product contains the chunks_parse_cmds.tbl file from the vsdump program. -Copyright (C) 2006-2007 Valek Filippov (frob@df.ru) - -This product contains parts of the eID Applet project -(http://eid-applet.googlecode.com). Copyright (c) 2009-2014 -FedICT (federal ICT department of Belgium), e-Contract.be BVBA (https://www.e-contract.be), -Bart Hanssens from FedICT diff --git a/plugins/ingest-attachment/licenses/poi-scratchpad-4.1.2.jar.sha1 b/plugins/ingest-attachment/licenses/poi-scratchpad-4.1.2.jar.sha1 deleted file mode 100644 index ed3d36c955a9e..0000000000000 --- a/plugins/ingest-attachment/licenses/poi-scratchpad-4.1.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1be379e91d3d3fb0cd11425451acdbfb0d2264e7 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/poi-scratchpad-5.2.3.jar.sha1 b/plugins/ingest-attachment/licenses/poi-scratchpad-5.2.3.jar.sha1 new file mode 100644 index 0000000000000..3c8f92498f1a4 --- /dev/null +++ b/plugins/ingest-attachment/licenses/poi-scratchpad-5.2.3.jar.sha1 @@ -0,0 +1 @@ +2a7fce47e22b7fedb1b277347ff4fe36d6eda50d \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/slf4j-api-1.6.2.jar.sha1 b/plugins/ingest-attachment/licenses/slf4j-api-1.6.2.jar.sha1 deleted file mode 100644 index a2f93ea55802b..0000000000000 --- a/plugins/ingest-attachment/licenses/slf4j-api-1.6.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8619e95939167fb37245b5670135e4feb0ec7d50 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/ingest-attachment/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/ingest-attachment/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/slf4j-api-LICENSE.txt b/plugins/ingest-attachment/licenses/slf4j-api-LICENSE.txt index 52055e61de46f..8fda22f4d72f6 100644 --- a/plugins/ingest-attachment/licenses/slf4j-api-LICENSE.txt +++ b/plugins/ingest-attachment/licenses/slf4j-api-LICENSE.txt @@ -1,21 +1,21 @@ -Copyright (c) 2004-2014 QOS.ch -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Copyright (c) 2004-2014 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/ingest-attachment/licenses/tika-core-1.24.1.jar.sha1 b/plugins/ingest-attachment/licenses/tika-core-1.24.1.jar.sha1 deleted file mode 100644 index ba4f64ff8f078..0000000000000 --- a/plugins/ingest-attachment/licenses/tika-core-1.24.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -703e65fb300d1425d4ad7b68c21c7795bb7a95c3 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/tika-core-2.6.0.jar.sha1 b/plugins/ingest-attachment/licenses/tika-core-2.6.0.jar.sha1 new file mode 100644 index 0000000000000..c66c2f3f39401 --- /dev/null +++ b/plugins/ingest-attachment/licenses/tika-core-2.6.0.jar.sha1 @@ -0,0 +1 @@ +f6ed6356dd4a9bd269d873f65494376685e6192e \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/tika-core-LICENSE.txt b/plugins/ingest-attachment/licenses/tika-core-LICENSE.txt deleted file mode 100644 index 9537d733ea9bf..0000000000000 --- a/plugins/ingest-attachment/licenses/tika-core-LICENSE.txt +++ /dev/null @@ -1,372 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - -APACHE TIKA SUBCOMPONENTS - -Apache Tika includes a number of subcomponents with separate copyright notices -and license terms. Your use of these subcomponents is subject to the terms and -conditions of the following licenses. - -MIME type information from file-4.26.tar.gz (http://www.darwinsys.com/file/) - - Copyright (c) Ian F. Darwin 1986, 1987, 1989, 1990, 1991, 1992, 1994, 1995. - Software written by Ian F. Darwin and others; - maintained 1994- Christos Zoulas. - - This software is not subject to any export provision of the United States - Department of Commerce, and may be exported to any country or planet. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice immediately at the beginning of the file, without modification, - this list of conditions, and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. - -Charset detection code from ICU4J (http://site.icu-project.org/) - - Copyright (c) 1995-2009 International Business Machines Corporation - and others - - All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, and/or sell copies of the Software, and to permit persons - to whom the Software is furnished to do so, provided that the above - copyright notice(s) and this permission notice appear in all copies - of the Software and that both the above copyright notice(s) and this - permission notice appear in supporting documentation. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE - BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, - OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, - WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - SOFTWARE. - - Except as contained in this notice, the name of a copyright holder shall - not be used in advertising or otherwise to promote the sale, use or other - dealings in this Software without prior written authorization of the - copyright holder. - - -Parsing functionality provided by the NetCDF Java Library (http://www.unidata.ucar.edu/software/netcdf-java/) - - Copyright 1993-2010 University Corporation for Atmospheric Research/Unidata - - Portions of this software were developed by the Unidata Program at the University - Corporation for Atmospheric Research. - - Access and use of this software shall impose the following obligations and understandings - on the user. The user is granted the right, without any fee or cost, to use, copy, modify, - alter, enhance and distribute this software, and any derivative works thereof, and its - supporting documentation for any purpose whatsoever, provided that this entire notice - appears in all copies of the software, derivative works and supporting documentation. Further, - UCAR requests that the user credit UCAR/Unidata in any publications that result from the use - of this software or in any product that includes this software, although this is not an obligation. - The names UCAR and/or Unidata, however, may not be used in any advertising or publicity to endorse - or promote any products or commercial entity unless specific written permission is obtained from - UCAR/Unidata. The user also understands that UCAR/Unidata is not obligated to provide the user with - any support, consulting, training or assistance of any kind with regard to the use, operation and - performance of this software nor to provide the user with any updates, revisions, new versions or - "bug fixes." - - THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, - BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL - DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE ACCESS, - USE OR PERFORMANCE OF THIS SOFTWARE. - - -IPTC Photo Metadata descriptions are taken from the IPTC Photo Metadata -Standard, July 2010, Copyright 2010 International Press Telecommunications -Council. - - 1. The Specifications and Materials are licensed for use only on the condition that you agree to be bound by the terms of this license. Subject to this and other licensing requirements contained herein, you may, on a non-exclusive basis, use the Specifications and Materials. - 2. The IPTC openly provides the Specifications and Materials for voluntary use by individuals, partnerships, companies, corporations, organizations and any other entity for use at the entity's own risk. This disclaimer, license and release is intended to apply to the IPTC, its officers, directors, agents, representatives, members, contributors, affiliates, contractors, or co-venturers acting jointly or severally. - 3. The Document and translations thereof may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the copyright and license notices and references to the IPTC appearing in the Document and the terms of this Specifications License Agreement are included on all such copies and derivative works. Further, upon the receipt of written permission from the IPTC, the Document may be modified for the purpose of developing applications that use IPTC Specifications or as required to translate the Document into languages other than English. - 4. Any use, duplication, distribution, or exploitation of the Document and Specifications and Materials in any manner is at your own risk. - 5. NO WARRANTY, EXPRESSED OR IMPLIED, IS MADE REGARDING THE ACCURACY, ADEQUACY, COMPLETENESS, LEGALITY, RELIABILITY OR USEFULNESS OF ANY INFORMATION CONTAINED IN THE DOCUMENT OR IN ANY SPECIFICATION OR OTHER PRODUCT OR SERVICE PRODUCED OR SPONSORED BY THE IPTC. THE DOCUMENT AND THE INFORMATION CONTAINED HEREIN AND INCLUDED IN ANY SPECIFICATION OR OTHER PRODUCT OR SERVICE OF THE IPTC IS PROVIDED ON AN "AS IS" BASIS. THE IPTC DISCLAIMS ALL WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY ACTUAL OR ASSERTED WARRANTY OF NON-INFRINGEMENT OF PROPRIETARY RIGHTS, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE IPTC NOR ITS CONTRIBUTORS SHALL BE HELD LIABLE FOR ANY IMPROPER OR INCORRECT USE OF INFORMATION. NEITHER THE IPTC NOR ITS CONTRIBUTORS ASSUME ANY RESPONSIBILITY FOR ANYONE'S USE OF INFORMATION PROVIDED BY THE IPTC. IN NO EVENT SHALL THE IPTC OR ITS CONTRIBUTORS BE LIABLE TO ANYONE FOR DAMAGES OF ANY KIND, INCLUDING BUT NOT LIMITED TO, COMPENSATORY DAMAGES, LOST PROFITS, LOST DATA OR ANY FORM OF SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL OR PUNITIVE DAMAGES OF ANY KIND WHETHER BASED ON BREACH OF CONTRACT OR WARRANTY, TORT, PRODUCT LIABILITY OR OTHERWISE. - 6. The IPTC takes no position regarding the validity or scope of any Intellectual Property or other rights that might be claimed to pertain to the implementation or use of the technology described in the Document or the extent to which any license under such rights might or might not be available. The IPTC does not represent that it has made any effort to identify any such rights. Copies of claims of rights made available for publication, assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of the Specifications and Materials, can be obtained from the Managing Director of the IPTC. - 7. By using the Specifications and Materials including the Document in any manner or for any purpose, you release the IPTC from all liabilities, claims, causes of action, allegations, losses, injuries, damages, or detriments of any nature arising from or relating to the use of the Specifications, Materials or any portion thereof. You further agree not to file a lawsuit, make a claim, or take any other formal or informal legal action against the IPTC, resulting from your acquisition, use, duplication, distribution, or exploitation of the Specifications, Materials or any portion thereof. Finally, you hereby agree that the IPTC is not liable for any direct, indirect, special or consequential damages arising from or relating to your acquisition, use, duplication, distribution, or exploitation of the Specifications, Materials or any portion thereof. - 8. Specifications and Materials may be downloaded or copied provided that ALL copies retain the ownership, copyright and license notices. - 9. Materials may not be edited, modified, or presented in a context that creates a misleading or false impression or statement as to the positions, actions, or statements of the IPTC. - 10. The name and trademarks of the IPTC may not be used in advertising, publicity, or in relation to products or services and their names without the specific, written prior permission of the IPTC. Any permitted use of the trademarks of the IPTC, whether registered or not, shall be accompanied by an appropriate mark and attribution, as agreed with the IPTC. - 11. Specifications may be extended by both members and non-members to provide additional functionality (Extension Specifications) provided that there is a clear recognition of the IPTC IP and its ownership in the Extension Specifications and the related documentation and provided that the extensions are clearly identified and provided that a perpetual license is granted by the creator of the Extension Specifications for other members and non-members to use the Extension Specifications and to continue extensions of the Extension Specifications. The IPTC does not waive any of its rights in the Specifications and Materials in this context. The Extension Specifications may be considered the intellectual property of their creator. The IPTC expressly disclaims any responsibility for damage caused by an extension to the Specifications. - 12. Specifications and Materials may be included in derivative work of both members and non-members provided that there is a clear recognition of the IPTC IP and its ownership in the derivative work and its related documentation. The IPTC does not waive any of its rights in the Specifications and Materials in this context. Derivative work in its entirety may be considered the intellectual property of the creator of the work .The IPTC expressly disclaims any responsibility for damage caused when its IP is used in a derivative context. - 13. This Specifications License Agreement is perpetual subject to your conformance to the terms of this Agreement. The IPTC may terminate this Specifications License Agreement immediately upon your breach of this Agreement and, upon such termination you will cease all use, duplication, distribution, and/or exploitation in any manner of the Specifications and Materials. - 14. This Specifications License Agreement reflects the entire agreement of the parties regarding the subject matter hereof and supersedes all prior agreements or representations regarding such matters, whether written or oral. To the extent any portion or provision of this Specifications License Agreement is found to be illegal or unenforceable, then the remaining provisions of this Specifications License Agreement will remain in full force and effect and the illegal or unenforceable provision will be construed to give it such effect as it may properly have that is consistent with the intentions of the parties. - 15. This Specifications License Agreement may only be modified in writing signed by an authorized representative of the IPTC. - 16. This Specifications License Agreement is governed by the law of United Kingdom, as such law is applied to contracts made and fully performed in the United Kingdom. Any disputes arising from or relating to this Specifications License Agreement will be resolved in the courts of the United Kingdom. You consent to the jurisdiction of such courts over you and covenant not to assert before such courts any objection to proceeding in such forums. - - -JUnRAR (https://github.com/edmund-wagner/junrar/) - - JUnRAR is based on the UnRAR tool, and covered by the same license - It was formerly available from http://java-unrar.svn.sourceforge.net/ - - ****** ***** ****** UnRAR - free utility for RAR archives - ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ****** ******* ****** License for use and distribution of - ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ** ** ** ** ** ** FREE portable version - ~~~~~~~~~~~~~~~~~~~~~ - - The source code of UnRAR utility is freeware. This means: - - 1. All copyrights to RAR and the utility UnRAR are exclusively - owned by the author - Alexander Roshal. - - 2. The UnRAR sources may be used in any software to handle RAR - archives without limitations free of charge, but cannot be used - to re-create the RAR compression algorithm, which is proprietary. - Distribution of modified UnRAR sources in separate form or as a - part of other software is permitted, provided that it is clearly - stated in the documentation and source comments that the code may - not be used to develop a RAR (WinRAR) compatible archiver. - - 3. The UnRAR utility may be freely distributed. It is allowed - to distribute UnRAR inside of other software packages. - - 4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS". - NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT - YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS, - DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING - OR MISUSING THIS SOFTWARE. - - 5. Installing and using the UnRAR utility signifies acceptance of - these terms and conditions of the license. - - 6. If you don't agree with terms of the license you must remove - UnRAR files from your storage devices and cease to use the - utility. - - Thank you for your interest in RAR and UnRAR. Alexander L. Roshal - -Sqlite (bundled in org.xerial's sqlite-jdbc) - This product bundles Sqlite, which is in the Public Domain. For details - see: https://www.sqlite.org/copyright.html diff --git a/plugins/ingest-attachment/licenses/tika-core-NOTICE.txt b/plugins/ingest-attachment/licenses/tika-core-NOTICE.txt deleted file mode 100644 index 8e94f644b8112..0000000000000 --- a/plugins/ingest-attachment/licenses/tika-core-NOTICE.txt +++ /dev/null @@ -1,17 +0,0 @@ -Apache Tika -Copyright 2015 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). - -Copyright 1993-2010 University Corporation for Atmospheric Research/Unidata -This software contains code derived from UCAR/Unidata's NetCDF library. - -Tika-server component uses CDDL-licensed dependencies: jersey (http://jersey.java.net/) and -Grizzly (http://grizzly.java.net/) - -Tika-parsers component uses CDDL/LGPL dual-licensed dependency: jhighlight (https://github.com/codelibs/jhighlight) - -OpenCSV: Copyright 2005 Bytecode Pty Ltd. Licensed under the Apache License, Version 2.0 - -IPTC Photo Metadata descriptions Copyright 2010 International Press Telecommunications Council. diff --git a/plugins/ingest-attachment/licenses/tika-langdetect-optimaize-2.6.0.jar.sha1 b/plugins/ingest-attachment/licenses/tika-langdetect-optimaize-2.6.0.jar.sha1 new file mode 100644 index 0000000000000..e7bc59bb5ae49 --- /dev/null +++ b/plugins/ingest-attachment/licenses/tika-langdetect-optimaize-2.6.0.jar.sha1 @@ -0,0 +1 @@ +72b784a7bdab0ffde005fa64d15e3f077331d6fc \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/tika-parsers-1.24.1.jar.sha1 b/plugins/ingest-attachment/licenses/tika-parsers-1.24.1.jar.sha1 deleted file mode 100644 index 6ad33ac8eca37..0000000000000 --- a/plugins/ingest-attachment/licenses/tika-parsers-1.24.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -02ce8d709cef4ed6a1a51ff14ba15b2ba2e76f09 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/tika-parsers-standard-package-2.6.0.jar.sha1 b/plugins/ingest-attachment/licenses/tika-parsers-standard-package-2.6.0.jar.sha1 new file mode 100644 index 0000000000000..83c0777fcbe8a --- /dev/null +++ b/plugins/ingest-attachment/licenses/tika-parsers-standard-package-2.6.0.jar.sha1 @@ -0,0 +1 @@ +00980e70b1df13c1236b750f0ca1462edd5d7417 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/xmlbeans-3.0.1.jar.sha1 b/plugins/ingest-attachment/licenses/xmlbeans-3.0.1.jar.sha1 deleted file mode 100644 index e1c74c67f214d..0000000000000 --- a/plugins/ingest-attachment/licenses/xmlbeans-3.0.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -50d94da791ab1e799a11d6f82410fd7d49f402ca \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/xmlbeans-5.1.1.jar.sha1 b/plugins/ingest-attachment/licenses/xmlbeans-5.1.1.jar.sha1 new file mode 100644 index 0000000000000..4d1d2ad0807e7 --- /dev/null +++ b/plugins/ingest-attachment/licenses/xmlbeans-5.1.1.jar.sha1 @@ -0,0 +1 @@ +48a369df0eccb509d46203104e4df9cb00f0f68b \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/xz-1.8.jar.sha1 b/plugins/ingest-attachment/licenses/xz-1.8.jar.sha1 deleted file mode 100644 index 7455feac7983b..0000000000000 --- a/plugins/ingest-attachment/licenses/xz-1.8.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c4f7d054303948eb6a4066194253886c8af07128 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/xz-1.9.jar.sha1 b/plugins/ingest-attachment/licenses/xz-1.9.jar.sha1 new file mode 100644 index 0000000000000..c3e22d167212f --- /dev/null +++ b/plugins/ingest-attachment/licenses/xz-1.9.jar.sha1 @@ -0,0 +1 @@ +1ea4bec1a921180164852c65006d928617bd2caf \ No newline at end of file diff --git a/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/AttachmentProcessor.java b/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/AttachmentProcessor.java index 0eb864a2d9ac0..54d2e0c7832e6 100644 --- a/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/AttachmentProcessor.java +++ b/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/AttachmentProcessor.java @@ -33,12 +33,12 @@ package org.opensearch.ingest.attachment; import org.apache.tika.exception.ZeroByteFileException; -import org.apache.tika.language.LanguageIdentifier; +import org.apache.tika.langdetect.optimaize.OptimaizeLangDetector; +import org.apache.tika.language.detect.LanguageResult; import org.apache.tika.metadata.Metadata; import org.apache.tika.metadata.TikaCoreProperties; - import org.opensearch.OpenSearchParseException; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import org.opensearch.ingest.AbstractProcessor; import org.opensearch.ingest.IngestDocument; import org.opensearch.ingest.Processor; @@ -134,9 +134,10 @@ public IngestDocument execute(IngestDocument ingestDocument) { } if (properties.contains(Property.LANGUAGE) && Strings.hasLength(parsedContent)) { - // TODO: stop using LanguageIdentifier... - LanguageIdentifier identifier = new LanguageIdentifier(parsedContent); - String language = identifier.getLanguage(); + OptimaizeLangDetector langDetector = new OptimaizeLangDetector(); + langDetector.loadModels(); + LanguageResult result = langDetector.detect(parsedContent); + String language = result.getLanguage(); additionalFields.put(Property.LANGUAGE.toLowerCase(), language); } @@ -158,6 +159,12 @@ public IngestDocument execute(IngestDocument ingestDocument) { String author = metadata.get("Author"); if (Strings.hasLength(author)) { additionalFields.put(Property.AUTHOR.toLowerCase(), author); + } else { + // The MSOffice parser has deprecated "Author" in favor of "Creator" + author = metadata.get(TikaCoreProperties.CREATOR); + if (Strings.hasLength(author)) { + additionalFields.put(Property.AUTHOR.toLowerCase(), author); + } } } @@ -165,6 +172,12 @@ public IngestDocument execute(IngestDocument ingestDocument) { String keywords = metadata.get("Keywords"); if (Strings.hasLength(keywords)) { additionalFields.put(Property.KEYWORDS.toLowerCase(), keywords); + } else { + // Fallback - EPUBs put their keywords as multiple subject fields by convention + keywords = metadata.get(TikaCoreProperties.SUBJECT); + if (Strings.hasLength(keywords)) { + additionalFields.put(Property.KEYWORDS.toLowerCase(), keywords); + } } } diff --git a/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/IngestAttachmentPlugin.java b/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/IngestAttachmentPlugin.java index a7e51b21c3c1a..a1d1b07e73f5d 100644 --- a/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/IngestAttachmentPlugin.java +++ b/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/IngestAttachmentPlugin.java @@ -32,13 +32,13 @@ package org.opensearch.ingest.attachment; -import java.util.Collections; -import java.util.Map; - import org.opensearch.ingest.Processor; import org.opensearch.plugins.IngestPlugin; import org.opensearch.plugins.Plugin; +import java.util.Collections; +import java.util.Map; + public class IngestAttachmentPlugin extends Plugin implements IngestPlugin { @Override diff --git a/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/TikaImpl.java b/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/TikaImpl.java index 2451eee8e984b..ce7ceb5e3d776 100644 --- a/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/TikaImpl.java +++ b/plugins/ingest-attachment/src/main/java/org/opensearch/ingest/attachment/TikaImpl.java @@ -42,7 +42,6 @@ import org.opensearch.SpecialPermission; import org.opensearch.bootstrap.FilePermissionUtils; import org.opensearch.bootstrap.JarHell; -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.io.PathUtils; @@ -93,9 +92,9 @@ final class TikaImpl { private static final Parser PARSERS[] = new Parser[] { // documents new org.apache.tika.parser.html.HtmlParser(), - new org.apache.tika.parser.rtf.RTFParser(), new org.apache.tika.parser.pdf.PDFParser(), new org.apache.tika.parser.txt.TXTParser(), + new org.apache.tika.parser.microsoft.rtf.RTFParser(), new org.apache.tika.parser.microsoft.OfficeParser(), new org.apache.tika.parser.microsoft.OldExcelParser(), ParserDecorator.withoutTypes(new org.apache.tika.parser.microsoft.ooxml.OOXMLParser(), EXCLUDES), @@ -181,14 +180,6 @@ static PermissionCollection getRestrictedPermissions() { perms.add(new RuntimePermission("accessClassInPackage.sun.java2d.cmm.kcms")); // xmlbeans, use by POI, needs to get the context classloader perms.add(new RuntimePermission("getClassLoader")); - // ZipFile needs accessDeclaredMembers on JDK 10; cf. https://bugs.openjdk.java.net/browse/JDK-8187485 - if (JavaVersion.current().compareTo(JavaVersion.parse("10")) >= 0) { - if (JavaVersion.current().compareTo(JavaVersion.parse("11")) < 0) { - // TODO remove this and from plugin-security.policy when JDK 11 is the only one we support - // this is needed pre 11, but it's fixed in 11 : https://bugs.openjdk.java.net/browse/JDK-8187485 - perms.add(new RuntimePermission("accessDeclaredMembers")); - } - } perms.setReadOnly(); return perms; } diff --git a/plugins/ingest-attachment/src/main/plugin-metadata/plugin-security.policy b/plugins/ingest-attachment/src/main/plugin-metadata/plugin-security.policy index 0fa85f6f040f6..4b90f9a21aae4 100644 --- a/plugins/ingest-attachment/src/main/plugin-metadata/plugin-security.policy +++ b/plugins/ingest-attachment/src/main/plugin-metadata/plugin-security.policy @@ -35,9 +35,6 @@ grant { // needed to apply additional sandboxing to tika parsing permission java.security.SecurityPermission "createAccessControlContext"; - // TODO: fix PDFBox not to actually install bouncy castle like this - permission java.security.SecurityPermission "putProviderProperty.BC"; - permission java.security.SecurityPermission "insertProvider"; // TODO: fix POI XWPF to not do this: https://bz.apache.org/bugzilla/show_bug.cgi?id=58597 permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; // needed by xmlbeans, as part of POI for MS xml docs diff --git a/plugins/ingest-attachment/src/test/java/org/opensearch/ingest/attachment/AttachmentProcessorTests.java b/plugins/ingest-attachment/src/test/java/org/opensearch/ingest/attachment/AttachmentProcessorTests.java index e9c87ae3a6109..17d73a74b6073 100644 --- a/plugins/ingest-attachment/src/test/java/org/opensearch/ingest/attachment/AttachmentProcessorTests.java +++ b/plugins/ingest-attachment/src/test/java/org/opensearch/ingest/attachment/AttachmentProcessorTests.java @@ -33,13 +33,11 @@ package org.opensearch.ingest.attachment; import org.apache.commons.io.IOUtils; - import org.opensearch.OpenSearchParseException; import org.opensearch.ingest.IngestDocument; import org.opensearch.ingest.Processor; import org.opensearch.ingest.RandomDocumentPicks; import org.opensearch.test.OpenSearchTestCase; - import org.junit.Before; import java.io.InputStream; @@ -138,8 +136,8 @@ public void testUnknownLanguageDocument() throws Exception { Map attachmentData = parseDocument("text-gibberish.txt", processor); assertThat(attachmentData.keySet(), hasItem("language")); - // lt seems some standard for not detected - assertThat(attachmentData.get("language"), is("lt")); + // Note - this output is non-deterministic across library versions + assertThat(attachmentData.get("language"), is("sl")); } public void testEmptyTextDocument() throws Exception { @@ -378,8 +376,9 @@ public void testIndexedChars() throws Exception { attachmentData = parseDocument("text-in-english.txt", processor, Collections.singletonMap("max_length", 10)); + // Language detection is not accurate when the text to analyze is short assertThat(attachmentData.keySet(), containsInAnyOrder("language", "content", "content_type", "content_length")); - assertThat(attachmentData.get("language"), is("sk")); + assertThat(attachmentData.get("language"), not(is("en"))); assertThat(attachmentData.get("content"), is("\"God Save")); assertThat(attachmentData.get("content_type").toString(), containsString("text/plain")); assertThat(attachmentData.get("content_length"), is(10L)); diff --git a/plugins/ingest-attachment/src/test/resources/org/opensearch/ingest/attachment/test/sample-files/asciidoc.asciidoc b/plugins/ingest-attachment/src/test/resources/org/opensearch/ingest/attachment/test/sample-files/asciidoc.asciidoc index dc06d4e83dd30..4a2b2c388cfc1 100644 --- a/plugins/ingest-attachment/src/test/resources/org/opensearch/ingest/attachment/test/sample-files/asciidoc.asciidoc +++ b/plugins/ingest-attachment/src/test/resources/org/opensearch/ingest/attachment/test/sample-files/asciidoc.asciidoc @@ -2,4 +2,3 @@ = AsciiDoc test Here is a test of the asciidoc format. - diff --git a/plugins/ingest-attachment/src/yamlRestTest/resources/rest-api-spec/test/ingest_attachment/10_basic.yml b/plugins/ingest-attachment/src/yamlRestTest/resources/rest-api-spec/test/ingest_attachment/10_basic.yml index 607fa5bf8b781..09075e0573fbe 100644 --- a/plugins/ingest-attachment/src/yamlRestTest/resources/rest-api-spec/test/ingest_attachment/10_basic.yml +++ b/plugins/ingest-attachment/src/yamlRestTest/resources/rest-api-spec/test/ingest_attachment/10_basic.yml @@ -5,11 +5,10 @@ - do: cluster.state: {} - - set: {master_node: master} + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { 'nodes.$master.plugins': { name: ingest-attachment } } - - contains: { 'nodes.$master.ingest.processors': { type: attachment } } - + - contains: { 'nodes.$cluster_manager.plugins': { name: ingest-attachment } } + - contains: { 'nodes.$cluster_manager.ingest.processors': { type: attachment } } diff --git a/plugins/ingest-attachment/src/yamlRestTest/resources/rest-api-spec/test/ingest_attachment/30_files_supported.yml b/plugins/ingest-attachment/src/yamlRestTest/resources/rest-api-spec/test/ingest_attachment/30_files_supported.yml index 7407c2a9326f6..58833b037b8cc 100644 --- a/plugins/ingest-attachment/src/yamlRestTest/resources/rest-api-spec/test/ingest_attachment/30_files_supported.yml +++ b/plugins/ingest-attachment/src/yamlRestTest/resources/rest-api-spec/test/ingest_attachment/30_files_supported.yml @@ -29,7 +29,7 @@ id: 1 - length: { _source.attachment: 6 } - match: { _source.attachment.content: "Test opensearch" } - - match: { _source.attachment.language: "pl" } + - match: { _source.attachment.language: "en" } - match: { _source.attachment.author: "OpenSearch" } - match: { _source.attachment.date: "2021-03-27T04:12:00Z" } - match: { _source.attachment.content_length: 16 } @@ -67,7 +67,7 @@ id: 1 - length: { _source.attachment: 6 } - match: { _source.attachment.content: "Test opensearch" } - - match: { _source.attachment.language: "pl" } + - match: { _source.attachment.language: "en" } - match: { _source.attachment.author: "OpenSearch" } - match: { _source.attachment.date: "2021-03-27T04:12:00Z" } - match: { _source.attachment.content_length: 16 } diff --git a/plugins/mapper-annotated-text/src/internalClusterTest/java/org/opensearch/index/mapper/annotatedtext/AnnotatedTextFieldMapperTests.java b/plugins/mapper-annotated-text/src/internalClusterTest/java/org/opensearch/index/mapper/annotatedtext/AnnotatedTextFieldMapperTests.java index 87933ab3df6be..9bfd5669e731b 100644 --- a/plugins/mapper-annotated-text/src/internalClusterTest/java/org/opensearch/index/mapper/annotatedtext/AnnotatedTextFieldMapperTests.java +++ b/plugins/mapper-annotated-text/src/internalClusterTest/java/org/opensearch/index/mapper/annotatedtext/AnnotatedTextFieldMapperTests.java @@ -47,10 +47,9 @@ import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AnalyzerScope; import org.opensearch.index.analysis.CharFilterFactory; @@ -99,7 +98,7 @@ protected void writeFieldValue(XContentBuilder builder) throws IOException { @Override protected void assertParseMaximalWarnings() { - assertWarnings("Parameter [boost] on field [field] is deprecated and will be removed in 8.0"); + assertWarnings("Parameter [boost] on field [field] is deprecated and will be removed in 3.0"); } @Override @@ -396,92 +395,87 @@ public void testPositionIncrementGap() throws IOException { } public void testSearchAnalyzerSerialization() throws IOException { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("_doc") - .startObject("properties") - .startObject("field") - .field("type", "annotated_text") - .field("analyzer", "standard") - .field("search_analyzer", "keyword") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("field") + .field("type", "annotated_text") + .field("analyzer", "standard") + .field("search_analyzer", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); DocumentMapper mapper = createDocumentMapper("_doc", mapping); assertEquals(mapping, mapper.mappingSource().toString()); // special case: default index analyzer - mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("_doc") - .startObject("properties") - .startObject("field") - .field("type", "annotated_text") - .field("analyzer", "default") - .field("search_analyzer", "keyword") - .endObject() - .endObject() - .endObject() - .endObject() - ); + mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("field") + .field("type", "annotated_text") + .field("analyzer", "default") + .field("search_analyzer", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); mapper = createDocumentMapper("_doc", mapping); assertEquals(mapping, mapper.mappingSource().toString()); - mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("_doc") - .startObject("properties") - .startObject("field") - .field("type", "annotated_text") - .field("analyzer", "keyword") - .endObject() - .endObject() - .endObject() - .endObject() - ); + mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("field") + .field("type", "annotated_text") + .field("analyzer", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); mapper = createDocumentMapper("_doc", mapping); assertEquals(mapping, mapper.mappingSource().toString()); // special case: default search analyzer - mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("_doc") - .startObject("properties") - .startObject("field") - .field("type", "annotated_text") - .field("analyzer", "keyword") - .field("search_analyzer", "default") - .endObject() - .endObject() - .endObject() - .endObject() - ); + mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("field") + .field("type", "annotated_text") + .field("analyzer", "keyword") + .field("search_analyzer", "default") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); mapper = createDocumentMapper("_doc", mapping); assertEquals(mapping, mapper.mappingSource().toString()); - mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("_doc") - .startObject("properties") - .startObject("field") - .field("type", "annotated_text") - .field("analyzer", "keyword") - .endObject() - .endObject() - .endObject() - .endObject() - ); + mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("field") + .field("type", "annotated_text") + .field("analyzer", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); mapper = createDocumentMapper("_doc", mapping); XContentBuilder builder = XContentFactory.jsonBuilder(); @@ -489,48 +483,46 @@ public void testSearchAnalyzerSerialization() throws IOException { mapper.toXContent(builder, new ToXContent.MapParams(Collections.singletonMap("include_defaults", "true"))); builder.endObject(); - String mappingString = Strings.toString(builder); + String mappingString = builder.toString(); assertTrue(mappingString.contains("analyzer")); assertTrue(mappingString.contains("search_analyzer")); assertTrue(mappingString.contains("search_quote_analyzer")); } public void testSearchQuoteAnalyzerSerialization() throws IOException { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("_doc") - .startObject("properties") - .startObject("field") - .field("type", "annotated_text") - .field("analyzer", "standard") - .field("search_analyzer", "standard") - .field("search_quote_analyzer", "keyword") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("field") + .field("type", "annotated_text") + .field("analyzer", "standard") + .field("search_analyzer", "standard") + .field("search_quote_analyzer", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); DocumentMapper mapper = createDocumentMapper("_doc", mapping); assertEquals(mapping, mapper.mappingSource().toString()); // special case: default index/search analyzer - mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("_doc") - .startObject("properties") - .startObject("field") - .field("type", "annotated_text") - .field("analyzer", "default") - .field("search_analyzer", "default") - .field("search_quote_analyzer", "keyword") - .endObject() - .endObject() - .endObject() - .endObject() - ); + mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("field") + .field("type", "annotated_text") + .field("analyzer", "default") + .field("search_analyzer", "default") + .field("search_quote_analyzer", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); mapper = createDocumentMapper("_doc", mapping); assertEquals(mapping, mapper.mappingSource().toString()); diff --git a/plugins/mapper-annotated-text/src/main/java/org/opensearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java b/plugins/mapper-annotated-text/src/main/java/org/opensearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java index e377a7c030f37..935887261dcc9 100644 --- a/plugins/mapper-annotated-text/src/main/java/org/opensearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java +++ b/plugins/mapper-annotated-text/src/main/java/org/opensearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java @@ -44,7 +44,6 @@ import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; import org.apache.lucene.index.IndexOptions; - import org.opensearch.OpenSearchParseException; import org.opensearch.index.analysis.AnalyzerScope; import org.opensearch.index.analysis.IndexAnalyzers; diff --git a/plugins/mapper-annotated-text/src/test/java/org/opensearch/search/fetch/subphase/highlight/AnnotatedTextHighlighterTests.java b/plugins/mapper-annotated-text/src/test/java/org/opensearch/search/fetch/subphase/highlight/AnnotatedTextHighlighterTests.java index d2d009f1b28b8..34851cf9bcbae 100644 --- a/plugins/mapper-annotated-text/src/test/java/org/opensearch/search/fetch/subphase/highlight/AnnotatedTextHighlighterTests.java +++ b/plugins/mapper-annotated-text/src/test/java/org/opensearch/search/fetch/subphase/highlight/AnnotatedTextHighlighterTests.java @@ -41,7 +41,6 @@ import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexWriterConfig; -import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; @@ -56,7 +55,8 @@ import org.apache.lucene.search.uhighlight.Snippet; import org.apache.lucene.search.uhighlight.SplittingBreakIterator; import org.apache.lucene.store.Directory; -import org.opensearch.common.Strings; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.opensearch.core.common.Strings; import org.opensearch.index.mapper.annotatedtext.AnnotatedTextFieldMapper.AnnotatedHighlighterAnalyzer; import org.opensearch.index.mapper.annotatedtext.AnnotatedTextFieldMapper.AnnotatedText; import org.opensearch.index.mapper.annotatedtext.AnnotatedTextFieldMapper.AnnotationAnalyzerWrapper; @@ -67,8 +67,8 @@ import java.util.ArrayList; import java.util.Locale; -import static org.apache.lucene.search.uhighlight.CustomUnifiedHighlighter.MULTIVAL_SEP_CHAR; import static org.hamcrest.CoreMatchers.equalTo; +import static org.apache.lucene.search.uhighlight.CustomUnifiedHighlighter.MULTIVAL_SEP_CHAR; public class AnnotatedTextHighlighterTests extends OpenSearchTestCase { @@ -136,7 +136,8 @@ private void assertHighlightOneDoc( noMatchSize, expectedPassages.length, name -> "text".equals(name), - Integer.MAX_VALUE + Integer.MAX_VALUE, + null ); highlighter.setFieldMatcher((name) -> "text".equals(name)); final Snippet[] snippets = highlighter.highlightField(getOnlyLeafReader(reader), topDocs.scoreDocs[0].doc, () -> rawValue); diff --git a/plugins/mapper-annotated-text/src/yamlRestTest/resources/rest-api-spec/test/mapper_annotatedtext/10_basic.yml b/plugins/mapper-annotated-text/src/yamlRestTest/resources/rest-api-spec/test/mapper_annotatedtext/10_basic.yml index b4acccf36879d..0e43e9e40b619 100644 --- a/plugins/mapper-annotated-text/src/yamlRestTest/resources/rest-api-spec/test/mapper_annotatedtext/10_basic.yml +++ b/plugins/mapper-annotated-text/src/yamlRestTest/resources/rest-api-spec/test/mapper_annotatedtext/10_basic.yml @@ -142,4 +142,3 @@ request_cache: false body: { "query" : {"match_phrase" : { "my_field" : {"query": "~MARK0", "analyzer": "whitespace"} } }, "highlight" : { "type" : "annotated", "fields" : { "my_field" : {} } } } - match: {_shards.failed: 0} - diff --git a/plugins/mapper-murmur3/src/test/java/org/opensearch/index/mapper/murmur3/Murmur3FieldMapperTests.java b/plugins/mapper-murmur3/src/test/java/org/opensearch/index/mapper/murmur3/Murmur3FieldMapperTests.java index 5cfbc0cde1050..04d46db50592c 100644 --- a/plugins/mapper-murmur3/src/test/java/org/opensearch/index/mapper/murmur3/Murmur3FieldMapperTests.java +++ b/plugins/mapper-murmur3/src/test/java/org/opensearch/index/mapper/murmur3/Murmur3FieldMapperTests.java @@ -35,7 +35,7 @@ import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.MapperTestCase; import org.opensearch.index.mapper.ParsedDocument; @@ -45,6 +45,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.List; public class Murmur3FieldMapperTests extends MapperTestCase { @@ -55,7 +56,7 @@ protected void writeFieldValue(XContentBuilder builder) throws IOException { @Override protected Collection getPlugins() { - return org.opensearch.common.collect.List.of(new MapperMurmur3Plugin()); + return List.of(new MapperMurmur3Plugin()); } @Override diff --git a/plugins/mapper-size/src/internalClusterTest/java/org/opensearch/index/mapper/size/SizeMappingIT.java b/plugins/mapper-size/src/internalClusterTest/java/org/opensearch/index/mapper/size/SizeMappingIT.java index 3a430331167f6..51e0979324623 100644 --- a/plugins/mapper-size/src/internalClusterTest/java/org/opensearch/index/mapper/size/SizeMappingIT.java +++ b/plugins/mapper-size/src/internalClusterTest/java/org/opensearch/index/mapper/size/SizeMappingIT.java @@ -34,8 +34,8 @@ import org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.opensearch.action.get.GetResponse; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugin.mapper.MapperSizePlugin; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; @@ -123,7 +123,7 @@ private void assertSizeMappingEnabled(String index, boolean enabled) throws IOEx public void testBasic() throws Exception { assertAcked(prepareCreate("test").setMapping("_size", "enabled=true")); final String source = "{\"f\":10}"; - indexRandom(true, client().prepareIndex("test").setId("1").setSource(source, XContentType.JSON)); + indexRandom(true, client().prepareIndex("test").setId("1").setSource(source, MediaTypeRegistry.JSON)); GetResponse getResponse = client().prepareGet("test", "1").setStoredFields("_size").get(); assertNotNull(getResponse.getField("_size")); assertEquals(source.length(), (int) getResponse.getField("_size").getValue()); diff --git a/plugins/mapper-size/src/internalClusterTest/java/org/opensearch/index/mapper/size/SizeMappingTests.java b/plugins/mapper-size/src/internalClusterTest/java/org/opensearch/index/mapper/size/SizeMappingTests.java index 4e4648a87fbfc..e7e8d92cee65a 100644 --- a/plugins/mapper-size/src/internalClusterTest/java/org/opensearch/index/mapper/size/SizeMappingTests.java +++ b/plugins/mapper-size/src/internalClusterTest/java/org/opensearch/index/mapper/size/SizeMappingTests.java @@ -32,29 +32,27 @@ package org.opensearch.index.mapper.size; -import java.util.Collection; - -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; +import org.apache.lucene.index.IndexableField; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; -import org.opensearch.index.mapper.MapperService; import org.opensearch.index.mapper.DocumentMapper; +import org.opensearch.index.mapper.MapperService; import org.opensearch.index.mapper.ParsedDocument; import org.opensearch.index.mapper.SourceToParse; import org.opensearch.plugin.mapper.MapperSizePlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchSingleNodeTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchSingleNodeTestCase; + +import java.util.Collection; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -import org.apache.lucene.index.IndexableField; - public class SizeMappingTests extends OpenSearchSingleNodeTestCase { @Override protected Collection> getPlugins() { @@ -66,7 +64,7 @@ public void testSizeEnabled() throws Exception { DocumentMapper docMapper = service.mapperService().documentMapper(); BytesReference source = BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", "value").endObject()); - ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1", source, XContentType.JSON)); + ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1", source, MediaTypeRegistry.JSON)); boolean stored = false; boolean points = false; @@ -83,7 +81,7 @@ public void testSizeDisabled() throws Exception { DocumentMapper docMapper = service.mapperService().documentMapper(); BytesReference source = BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", "value").endObject()); - ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1", source, XContentType.JSON)); + ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1", source, MediaTypeRegistry.JSON)); assertThat(doc.rootDoc().getField("_size"), nullValue()); } @@ -93,7 +91,7 @@ public void testSizeNotSet() throws Exception { DocumentMapper docMapper = service.mapperService().documentMapper(); BytesReference source = BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field("field", "value").endObject()); - ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1", source, XContentType.JSON)); + ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1", source, MediaTypeRegistry.JSON)); assertThat(doc.rootDoc().getField("_size"), nullValue()); } @@ -103,16 +101,15 @@ public void testThatDisablingWorksWhenMerging() throws Exception { DocumentMapper docMapper = service.mapperService().documentMapper(); assertThat(docMapper.metadataMapper(SizeFieldMapper.class).enabled(), is(true)); - String disabledMapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("type") - .startObject("_size") - .field("enabled", false) - .endObject() - .endObject() - .endObject() - ); + String disabledMapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("type") + .startObject("_size") + .field("enabled", false) + .endObject() + .endObject() + .endObject() + .toString(); docMapper = service.mapperService() .merge("type", new CompressedXContent(disabledMapping), MapperService.MergeReason.MAPPING_UPDATE); diff --git a/plugins/repository-azure/build.gradle b/plugins/repository-azure/build.gradle index 60fb99f459454..2695d3f99576d 100644 --- a/plugins/repository-azure/build.gradle +++ b/plugins/repository-azure/build.gradle @@ -44,9 +44,10 @@ opensearchplugin { } dependencies { - api 'com.azure:azure-core:1.26.0' - api 'com.azure:azure-storage-common:12.15.0' - api 'com.azure:azure-core-http-netty:1.11.8' + api 'com.azure:azure-core:1.39.0' + api 'com.azure:azure-json:1.0.1' + api 'com.azure:azure-storage-common:12.21.1' + api 'com.azure:azure-core-http-netty:1.12.8' api "io.netty:netty-codec-dns:${versions.netty}" api "io.netty:netty-codec-socks:${versions.netty}" api "io.netty:netty-codec-http2:${versions.netty}" @@ -54,22 +55,22 @@ dependencies { api "io.netty:netty-resolver-dns:${versions.netty}" api "io.netty:netty-transport-native-unix-common:${versions.netty}" implementation project(':modules:transport-netty4') - api 'com.azure:azure-storage-blob:12.14.4' + api 'com.azure:azure-storage-blob:12.23.0' api 'org.reactivestreams:reactive-streams:1.0.3' - api 'io.projectreactor:reactor-core:3.4.15' - api 'io.projectreactor.netty:reactor-netty:1.0.16' - api 'io.projectreactor.netty:reactor-netty-core:1.0.16' - api 'io.projectreactor.netty:reactor-netty-http:1.0.16' + api 'io.projectreactor:reactor-core:3.5.6' + api 'io.projectreactor.netty:reactor-netty:1.1.8' + api 'io.projectreactor.netty:reactor-netty-core:1.1.8' + api 'io.projectreactor.netty:reactor-netty-http:1.1.9' api "org.slf4j:slf4j-api:${versions.slf4j}" api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" - api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}" api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:${versions.jackson}" api "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:${versions.jackson}" api "com.fasterxml.jackson.module:jackson-module-jaxb-annotations:${versions.jackson}" api 'org.codehaus.woodstox:stax2-api:4.2.1' - implementation 'com.fasterxml.woodstox:woodstox-core:6.2.8' - runtimeOnly 'com.google.guava:guava:31.1-jre' - api 'org.apache.commons:commons-lang3:3.12.0' + implementation "com.fasterxml.woodstox:woodstox-core:${versions.woodstox}" + runtimeOnly "com.google.guava:guava:${versions.guava}" + api "org.apache.commons:commons-lang3:${versions.commonslang}" testImplementation project(':test:fixtures:azure-fixture') } @@ -137,7 +138,7 @@ thirdPartyAudit { 'javax.xml.bind.annotation.XmlAccessOrder', 'javax.xml.bind.annotation.XmlAccessType', 'javax.xml.bind.annotation.XmlAccessorOrder', - 'javax.xml.bind.annotation.XmlAccessorType', + 'javax.xml.bind.annotation.XmlAccessorType', 'javax.xml.bind.annotation.XmlAttribute', 'javax.xml.bind.annotation.XmlElement', 'javax.xml.bind.annotation.XmlElement$DEFAULT', @@ -158,24 +159,41 @@ thirdPartyAudit { 'javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter', 'javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter$DEFAULT', 'javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters', - 'kotlin.collections.ArraysKt', - 'kotlin.jvm.JvmClassMappingKt', - 'kotlin.jvm.functions.Function0', - 'kotlin.jvm.functions.Function1', - 'kotlin.jvm.internal.FunctionReference', - 'kotlin.jvm.internal.Intrinsics', - 'kotlin.jvm.internal.Reflection', - 'kotlin.jvm.internal.markers.KMappedMarker', - 'kotlin.reflect.KClass', - 'kotlin.reflect.KDeclarationContainer', - 'kotlin.sequences.Sequence', 'org.osgi.framework.BundleActivator', 'org.osgi.framework.BundleContext', 'org.slf4j.impl.StaticLoggerBinder', 'org.slf4j.impl.StaticMDCBinder', 'org.slf4j.impl.StaticMarkerBinder', 'reactor.blockhound.BlockHound$Builder', - 'reactor.blockhound.integration.BlockHoundIntegration' + 'reactor.blockhound.integration.BlockHoundIntegration', + 'io.micrometer.context.ContextRegistry', + 'io.micrometer.context.ContextSnapshot', + 'io.micrometer.context.ContextSnapshot$Scope', + 'io.micrometer.context.ThreadLocalAccessor', + 'io.micrometer.common.KeyValue', + 'io.micrometer.common.KeyValues', + 'io.micrometer.common.docs.KeyName', + 'io.micrometer.core.instrument.Meter$Type', + 'io.micrometer.core.instrument.docs.MeterDocumentation', + 'io.micrometer.observation.Observation', + 'io.micrometer.observation.Observation$Context', + 'io.micrometer.observation.docs.ObservationDocumentation', + 'io.micrometer.observation.transport.ReceiverContext', + 'io.micrometer.observation.transport.RequestReplyReceiverContext', + 'io.micrometer.observation.transport.RequestReplySenderContext', + 'io.micrometer.observation.transport.SenderContext', + 'io.micrometer.tracing.Span', + 'io.micrometer.tracing.Tracer', + 'io.micrometer.tracing.docs.SpanDocumentation', + 'io.micrometer.tracing.handler.PropagatingReceiverTracingObservationHandler', + 'io.micrometer.tracing.handler.PropagatingSenderTracingObservationHandler', + 'io.micrometer.tracing.propagation.Propagator', + 'io.micrometer.context.ContextAccessor', + 'io.micrometer.core.instrument.observation.MeterObservationHandler', + 'io.micrometer.observation.ObservationHandler', + 'io.micrometer.observation.ObservationRegistry', + 'io.micrometer.observation.ObservationRegistry$ObservationConfig', + 'io.micrometer.tracing.handler.DefaultTracingObservationHandler' ) ignoreViolations( @@ -188,7 +206,6 @@ thirdPartyAudit { 'com.google.common.hash.Striped64$Cell', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$1', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$2', - 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$3', 'com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper', 'com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper$1', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray', diff --git a/plugins/repository-azure/licenses/azure-core-1.26.0.jar.sha1 b/plugins/repository-azure/licenses/azure-core-1.26.0.jar.sha1 deleted file mode 100644 index 693c6a721959c..0000000000000 --- a/plugins/repository-azure/licenses/azure-core-1.26.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -461b89dcf8948a0c4a97d4f1d876f778d0cac7aa \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-core-1.39.0.jar.sha1 b/plugins/repository-azure/licenses/azure-core-1.39.0.jar.sha1 new file mode 100644 index 0000000000000..c91498a464b3d --- /dev/null +++ b/plugins/repository-azure/licenses/azure-core-1.39.0.jar.sha1 @@ -0,0 +1 @@ +39765fb88a90174628b31ddf6ff9f8d63462e080 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-core-http-netty-1.11.8.jar.sha1 b/plugins/repository-azure/licenses/azure-core-http-netty-1.11.8.jar.sha1 deleted file mode 100644 index df7d7ae4ce285..0000000000000 --- a/plugins/repository-azure/licenses/azure-core-http-netty-1.11.8.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0ea66d4531fb41cb3b5ab55e2e7b7f301e7f8503 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-core-http-netty-1.12.8.jar.sha1 b/plugins/repository-azure/licenses/azure-core-http-netty-1.12.8.jar.sha1 new file mode 100644 index 0000000000000..e6ee1dec64641 --- /dev/null +++ b/plugins/repository-azure/licenses/azure-core-http-netty-1.12.8.jar.sha1 @@ -0,0 +1 @@ +511ed2d02afb0f43f029df3d10ff80d2d3539f05 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-json-1.0.1.jar.sha1 b/plugins/repository-azure/licenses/azure-json-1.0.1.jar.sha1 new file mode 100644 index 0000000000000..128a82717fef9 --- /dev/null +++ b/plugins/repository-azure/licenses/azure-json-1.0.1.jar.sha1 @@ -0,0 +1 @@ +abdfdb0c49eebe75ed8532d047dea0c9f13c30ac \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-storage-blob-12.14.4.jar.sha1 b/plugins/repository-azure/licenses/azure-storage-blob-12.14.4.jar.sha1 deleted file mode 100644 index 5333f8fa90ada..0000000000000 --- a/plugins/repository-azure/licenses/azure-storage-blob-12.14.4.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2b92020693d09e4980b96d278e8038a1087afea0 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-storage-blob-12.23.0.jar.sha1 b/plugins/repository-azure/licenses/azure-storage-blob-12.23.0.jar.sha1 new file mode 100644 index 0000000000000..5f32d64b00918 --- /dev/null +++ b/plugins/repository-azure/licenses/azure-storage-blob-12.23.0.jar.sha1 @@ -0,0 +1 @@ +3eeb49d5109e812343fb436e4bbb2eecac8fe386 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-storage-common-12.15.0.jar.sha1 b/plugins/repository-azure/licenses/azure-storage-common-12.15.0.jar.sha1 deleted file mode 100644 index 1f3adfc161c7f..0000000000000 --- a/plugins/repository-azure/licenses/azure-storage-common-12.15.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4d63ce8bbd20379c5e5262b1204ceac7b31a7743 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/azure-storage-common-12.21.1.jar.sha1 b/plugins/repository-azure/licenses/azure-storage-common-12.21.1.jar.sha1 new file mode 100644 index 0000000000000..97baa70f28f05 --- /dev/null +++ b/plugins/repository-azure/licenses/azure-storage-common-12.21.1.jar.sha1 @@ -0,0 +1 @@ +3dd1e186f4e0815eb9f5c1a5bf3090ff4467a03a \ No newline at end of file diff --git a/plugins/repository-azure/licenses/commons-lang3-3.12.0.jar.sha1 b/plugins/repository-azure/licenses/commons-lang3-3.12.0.jar.sha1 deleted file mode 100644 index 9273d8c01aaba..0000000000000 --- a/plugins/repository-azure/licenses/commons-lang3-3.12.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c6842c86792ff03b9f1d1fe2aab8dc23aa6c6f0e \ No newline at end of file diff --git a/plugins/repository-azure/licenses/commons-lang3-3.13.0.jar.sha1 b/plugins/repository-azure/licenses/commons-lang3-3.13.0.jar.sha1 new file mode 100644 index 0000000000000..d0c2f2486ee1f --- /dev/null +++ b/plugins/repository-azure/licenses/commons-lang3-3.13.0.jar.sha1 @@ -0,0 +1 @@ +b7263237aa89c1f99b327197c41d0669707a462e \ No newline at end of file diff --git a/plugins/repository-azure/licenses/commons-lang3-NOTICE.txt b/plugins/repository-azure/licenses/commons-lang3-NOTICE.txt index 078282451b679..13a3140897472 100644 --- a/plugins/repository-azure/licenses/commons-lang3-NOTICE.txt +++ b/plugins/repository-azure/licenses/commons-lang3-NOTICE.txt @@ -1,8 +1,5 @@ Apache Commons Lang -Copyright 2001-2014 The Apache Software Foundation +Copyright 2001-2019 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). - -This product includes software from the Spring Framework, -under the Apache License 2.0 (see: StringUtils.containsWhitespace()) diff --git a/plugins/repository-azure/licenses/guava-31.1-jre.jar.sha1 b/plugins/repository-azure/licenses/guava-31.1-jre.jar.sha1 deleted file mode 100644 index e57390ebe1299..0000000000000 --- a/plugins/repository-azure/licenses/guava-31.1-jre.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -60458f877d055d0c9114d9e1a2efb737b4bc282c \ No newline at end of file diff --git a/plugins/repository-azure/licenses/guava-32.1.1-jre.jar.sha1 b/plugins/repository-azure/licenses/guava-32.1.1-jre.jar.sha1 new file mode 100644 index 0000000000000..0d791b5d3f55b --- /dev/null +++ b/plugins/repository-azure/licenses/guava-32.1.1-jre.jar.sha1 @@ -0,0 +1 @@ +ad575652d84153075dd41ec6177ccb15251262b2 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-LICENSE.txt b/plugins/repository-azure/licenses/jackson-LICENSE.txt new file mode 100644 index 0000000000000..f5f45d26a49d6 --- /dev/null +++ b/plugins/repository-azure/licenses/jackson-LICENSE.txt @@ -0,0 +1,8 @@ +This copy of Jackson JSON processor streaming parser/generator is licensed under the +Apache (Software) License, version 2.0 ("the License"). +See the License for details about distribution rights, and the +specific rights regarding derivate works. + +You may obtain a copy of the License at: + +http://www.apache.org/licenses/LICENSE-2.0 diff --git a/plugins/repository-azure/licenses/jackson-NOTICE.txt b/plugins/repository-azure/licenses/jackson-NOTICE.txt new file mode 100644 index 0000000000000..4c976b7b4cc58 --- /dev/null +++ b/plugins/repository-azure/licenses/jackson-NOTICE.txt @@ -0,0 +1,20 @@ +# Jackson JSON processor + +Jackson is a high-performance, Free/Open Source JSON processing library. +It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has +been in development since 2007. +It is currently developed by a community of developers, as well as supported +commercially by FasterXML.com. + +## Licensing + +Jackson core and extension components may licensed under different licenses. +To find the details that apply to this artifact see the accompanying LICENSE file. +For more information, including possible other licensing options, contact +FasterXML.com (http://fasterxml.com). + +## Credits + +A list of contributors may be found from CREDITS file, which is included +in some artifacts (usually source distributions); but is always available +from the source code management (SCM) system project uses. diff --git a/plugins/repository-azure/licenses/jackson-annotations-2.13.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-annotations-2.13.2.jar.sha1 deleted file mode 100644 index ecd3fb49d5b12..0000000000000 --- a/plugins/repository-azure/licenses/jackson-annotations-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -ec18851f1976d5b810ae1a5fcc32520d2d38f77a \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-annotations-2.15.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-annotations-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f63416ddb8ceb --- /dev/null +++ b/plugins/repository-azure/licenses/jackson-annotations-2.15.2.jar.sha1 @@ -0,0 +1 @@ +4724a65ac8e8d156a24898d50fd5dbd3642870b8 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-databind-2.13.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-databind-2.13.2.jar.sha1 deleted file mode 100644 index 5d356f3fd045f..0000000000000 --- a/plugins/repository-azure/licenses/jackson-databind-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -926e48c451166a291f1ce6c6276d9abbefa7c00f \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-databind-2.15.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-databind-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f16d80af8dce6 --- /dev/null +++ b/plugins/repository-azure/licenses/jackson-databind-2.15.2.jar.sha1 @@ -0,0 +1 @@ +9353b021f10c307c00328f52090de2bdb4b6ff9c \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-dataformat-xml-2.13.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-dataformat-xml-2.13.2.jar.sha1 deleted file mode 100644 index 7d020f81a91ba..0000000000000 --- a/plugins/repository-azure/licenses/jackson-dataformat-xml-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -cb6a722f128ff0ce2494384d419b6ff20fad25ab \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-dataformat-xml-2.15.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-dataformat-xml-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..18c388b84f333 --- /dev/null +++ b/plugins/repository-azure/licenses/jackson-dataformat-xml-2.15.2.jar.sha1 @@ -0,0 +1 @@ +e7e9038dee5c1adb1ebd07d3669e0e1182ac5b60 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-datatype-jsr310-2.13.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-datatype-jsr310-2.13.2.jar.sha1 deleted file mode 100644 index 979d38bb38784..0000000000000 --- a/plugins/repository-azure/licenses/jackson-datatype-jsr310-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -cddd9380efd4b81ea01e98be8fbdc9765a81793b \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-datatype-jsr310-2.15.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-datatype-jsr310-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..6aa4f9b99c274 --- /dev/null +++ b/plugins/repository-azure/licenses/jackson-datatype-jsr310-2.15.2.jar.sha1 @@ -0,0 +1 @@ +30d16ec2aef6d8094c5e2dce1d95034ca8b6cb42 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-module-jaxb-annotations-2.13.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-module-jaxb-annotations-2.13.2.jar.sha1 deleted file mode 100644 index c71c4fe5ee90c..0000000000000 --- a/plugins/repository-azure/licenses/jackson-module-jaxb-annotations-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e2f198c512f0f0ccbd6d618baecc9dde9975eadf \ No newline at end of file diff --git a/plugins/repository-azure/licenses/jackson-module-jaxb-annotations-2.15.2.jar.sha1 b/plugins/repository-azure/licenses/jackson-module-jaxb-annotations-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..80da08928f855 --- /dev/null +++ b/plugins/repository-azure/licenses/jackson-module-jaxb-annotations-2.15.2.jar.sha1 @@ -0,0 +1 @@ +6a22fd1c7b0f9788e81eea32c11dc8c1ba421f18 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-dns-4.1.73.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-dns-4.1.73.Final.jar.sha1 deleted file mode 100644 index 320ae18c98bda..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-dns-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -46137a5b01a5202059324cf4300443e53f11a38d \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-dns-4.1.99.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-dns-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..6c1112ed49775 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-dns-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +21c76a42a468faafac6c84f8aca775073fc8e345 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-http2-4.1.73.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-http2-4.1.73.Final.jar.sha1 deleted file mode 100644 index d7f5a464bcc00..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-http2-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0eb145bc31fd32a20fd2a3e8b30736d2e0248b0c \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-http2-4.1.99.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-http2-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..f9bdefc6dd965 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-http2-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +c5a3481c4bb9732a3a94fb63cf916141a1a14669 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-socks-4.1.73.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-socks-4.1.73.Final.jar.sha1 deleted file mode 100644 index 6ba41c576c93d..0000000000000 --- a/plugins/repository-azure/licenses/netty-codec-socks-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -cefa44d8f5dcaab21179d945f12b6c6d7325cce9 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-codec-socks-4.1.99.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-codec-socks-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..717703c36e1ab --- /dev/null +++ b/plugins/repository-azure/licenses/netty-codec-socks-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +259bf1c5178c3e23bb89a2fab59b6d22846e3fa6 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.73.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.73.Final.jar.sha1 deleted file mode 100644 index f50c9abf023cf..0000000000000 --- a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d1afa6876c3d3bdbdbe5127ddd495e6514d6e600 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-handler-proxy-4.1.99.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..3f69ae54c5d4a --- /dev/null +++ b/plugins/repository-azure/licenses/netty-handler-proxy-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +8c8a89ea89b06e120c57bdb3db14b9a47ca30bb3 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.73.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.73.Final.jar.sha1 deleted file mode 100644 index 817fa4cc0d86f..0000000000000 --- a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -97cdf5fb97f8d961cfa3ffb05175009b90e5cfee \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-resolver-dns-4.1.99.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..adef44a4e7da7 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-resolver-dns-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +804d8b752847923d3bb81f24de604597047c9b2e \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.73.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.73.Final.jar.sha1 deleted file mode 100644 index 22b8f58bd5103..0000000000000 --- a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4701063d36f390e02da6da85c13e32a0e78349d2 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..6b7b66ea768e3 --- /dev/null +++ b/plugins/repository-azure/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +cb0fc6c31c387404212949c57950b5d72ce908b9 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-core-3.4.15.jar.sha1 b/plugins/repository-azure/licenses/reactor-core-3.4.15.jar.sha1 deleted file mode 100644 index a89de48b20b51..0000000000000 --- a/plugins/repository-azure/licenses/reactor-core-3.4.15.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -28ccf513fe64709c8ded30ea3f387fc718db9626 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-core-3.5.6.jar.sha1 b/plugins/repository-azure/licenses/reactor-core-3.5.6.jar.sha1 new file mode 100644 index 0000000000000..ad9b7263e7b38 --- /dev/null +++ b/plugins/repository-azure/licenses/reactor-core-3.5.6.jar.sha1 @@ -0,0 +1 @@ +027fdc551537b349389176a23a192f11a7a3d7de \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-netty-1.0.16.jar.sha1 b/plugins/repository-azure/licenses/reactor-netty-1.0.16.jar.sha1 deleted file mode 100644 index 582380e449a1d..0000000000000 --- a/plugins/repository-azure/licenses/reactor-netty-1.0.16.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d90829f6127966b0c35c4a3e8e23ca9ed29cd8a5 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-netty-1.1.8.jar.sha1 b/plugins/repository-azure/licenses/reactor-netty-1.1.8.jar.sha1 new file mode 100644 index 0000000000000..6b6bf1903b16c --- /dev/null +++ b/plugins/repository-azure/licenses/reactor-netty-1.1.8.jar.sha1 @@ -0,0 +1 @@ +d53a9d7d0395285f4c81664494fcd61477626e32 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-netty-core-1.0.16.jar.sha1 b/plugins/repository-azure/licenses/reactor-netty-core-1.0.16.jar.sha1 deleted file mode 100644 index 0d1a0cb20c80f..0000000000000 --- a/plugins/repository-azure/licenses/reactor-netty-core-1.0.16.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8f842a912677f2bc614ff60fb9e786d4fa429c34 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-netty-core-1.1.8.jar.sha1 b/plugins/repository-azure/licenses/reactor-netty-core-1.1.8.jar.sha1 new file mode 100644 index 0000000000000..707631f4dfe0c --- /dev/null +++ b/plugins/repository-azure/licenses/reactor-netty-core-1.1.8.jar.sha1 @@ -0,0 +1 @@ +48999c4ae27cdcee5eaff9dfd150a8b64624f0f5 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-netty-http-1.0.16.jar.sha1 b/plugins/repository-azure/licenses/reactor-netty-http-1.0.16.jar.sha1 deleted file mode 100644 index d737315b06b62..0000000000000 --- a/plugins/repository-azure/licenses/reactor-netty-http-1.0.16.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -93edb9a1dc774d843551a616e0f316e11ffa81ed \ No newline at end of file diff --git a/plugins/repository-azure/licenses/reactor-netty-http-1.1.9.jar.sha1 b/plugins/repository-azure/licenses/reactor-netty-http-1.1.9.jar.sha1 new file mode 100644 index 0000000000000..96deead2c75d1 --- /dev/null +++ b/plugins/repository-azure/licenses/reactor-netty-http-1.1.9.jar.sha1 @@ -0,0 +1 @@ +408b3037133f2e8ab0f195ccd3f807026be9b860 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/slf4j-api-1.6.2.jar.sha1 b/plugins/repository-azure/licenses/slf4j-api-1.6.2.jar.sha1 deleted file mode 100644 index a2f93ea55802b..0000000000000 --- a/plugins/repository-azure/licenses/slf4j-api-1.6.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8619e95939167fb37245b5670135e4feb0ec7d50 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/repository-azure/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/repository-azure/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/woodstox-core-6.2.8.jar.sha1 b/plugins/repository-azure/licenses/woodstox-core-6.2.8.jar.sha1 deleted file mode 100644 index ae65cdebf26de..0000000000000 --- a/plugins/repository-azure/licenses/woodstox-core-6.2.8.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -670748292899c53b1963730d9eb7f8ab71314e90 \ No newline at end of file diff --git a/plugins/repository-azure/licenses/woodstox-core-6.4.0.jar.sha1 b/plugins/repository-azure/licenses/woodstox-core-6.4.0.jar.sha1 new file mode 100644 index 0000000000000..cac5f37205956 --- /dev/null +++ b/plugins/repository-azure/licenses/woodstox-core-6.4.0.jar.sha1 @@ -0,0 +1 @@ +c47579857bbf12c85499f431d4ecf27d77976b7c \ No newline at end of file diff --git a/plugins/repository-azure/src/internalClusterTest/java/org/opensearch/repositories/azure/AzureBlobStoreRepositoryTests.java b/plugins/repository-azure/src/internalClusterTest/java/org/opensearch/repositories/azure/AzureBlobStoreRepositoryTests.java index 4b11f2e3305e6..82364d1b7b3c1 100644 --- a/plugins/repository-azure/src/internalClusterTest/java/org/opensearch/repositories/azure/AzureBlobStoreRepositoryTests.java +++ b/plugins/repository-azure/src/internalClusterTest/java/org/opensearch/repositories/azure/AzureBlobStoreRepositoryTests.java @@ -31,25 +31,23 @@ package org.opensearch.repositories.azure; -import com.azure.storage.blob.models.ParallelTransferOptions; -import com.azure.storage.common.implementation.Constants; -import com.azure.storage.common.policy.RequestRetryOptions; -import com.azure.storage.common.policy.RetryPolicyType; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import fixture.azure.AzureHttpHandler; -import reactor.core.scheduler.Schedulers; -import org.junit.AfterClass; +import com.azure.storage.blob.models.ParallelTransferOptions; +import com.azure.storage.common.implementation.Constants; +import com.azure.storage.common.policy.RequestRetryOptions; +import com.azure.storage.common.policy.RetryPolicyType; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.rest.RestStatus; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.blobstore.OpenSearchMockAPIBasedRepositoryIntegTestCase; -import org.opensearch.rest.RestStatus; +import org.junit.AfterClass; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -59,6 +57,9 @@ import java.util.Map; import java.util.regex.Pattern; +import fixture.azure.AzureHttpHandler; +import reactor.core.scheduler.Schedulers; + @SuppressForbidden(reason = "this test uses a HttpServer to emulate an Azure endpoint") public class AzureBlobStoreRepositoryTests extends OpenSearchMockAPIBasedRepositoryIntegTestCase { @AfterClass diff --git a/plugins/repository-azure/src/internalClusterTest/java/org/opensearch/repositories/azure/AzureStorageCleanupThirdPartyTests.java b/plugins/repository-azure/src/internalClusterTest/java/org/opensearch/repositories/azure/AzureStorageCleanupThirdPartyTests.java index 6d71a65a35a4c..176e60a667aef 100644 --- a/plugins/repository-azure/src/internalClusterTest/java/org/opensearch/repositories/azure/AzureStorageCleanupThirdPartyTests.java +++ b/plugins/repository-azure/src/internalClusterTest/java/org/opensearch/repositories/azure/AzureStorageCleanupThirdPartyTests.java @@ -32,30 +32,29 @@ package org.opensearch.repositories.azure; -import reactor.core.scheduler.Schedulers; - import com.azure.core.util.Context; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; import com.azure.storage.blob.models.BlobStorageException; - -import org.junit.AfterClass; import org.opensearch.action.ActionRunnable; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.common.Strings; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.SecureSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.AbstractThirdPartyRepositoryTestCase; import org.opensearch.repositories.blobstore.BlobStoreRepository; +import org.junit.AfterClass; import java.net.HttpURLConnection; import java.util.Collection; import java.util.function.Supplier; +import reactor.core.scheduler.Schedulers; + import static org.hamcrest.Matchers.blankOrNullString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; diff --git a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureBlobContainer.java b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureBlobContainer.java index d6fa72221f408..2b4654d220061 100644 --- a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureBlobContainer.java +++ b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureBlobContainer.java @@ -35,10 +35,8 @@ import com.azure.storage.blob.models.BlobStorageException; import com.azure.storage.blob.specialized.BlobInputStream; import com.azure.storage.common.implementation.Constants; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.support.GroupedActionListener; import org.opensearch.action.support.PlainActionFuture; @@ -48,6 +46,7 @@ import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.DeleteResult; import org.opensearch.common.blobstore.support.AbstractBlobContainer; +import org.opensearch.core.action.ActionListener; import org.opensearch.threadpool.ThreadPool; import java.io.FileInputStream; diff --git a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureBlobStore.java b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureBlobStore.java index b540dd83c95a2..e76a6bdd16764 100644 --- a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureBlobStore.java +++ b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureBlobStore.java @@ -50,7 +50,6 @@ import com.azure.storage.blob.models.ListBlobsOptions; import com.azure.storage.blob.options.BlobParallelUploadOptions; import com.azure.storage.common.implementation.Constants; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.util.Throwables; @@ -442,7 +441,7 @@ private static class Stats { private final AtomicLong putBlockListOperations = new AtomicLong(); private Map toMap() { - return org.opensearch.common.collect.Map.of( + return Map.of( "GetBlob", getOperations.get(), "ListBlobs", diff --git a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureRepository.java b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureRepository.java index a7799fef475f3..47a5536a6cd8a 100644 --- a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureRepository.java +++ b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureRepository.java @@ -37,16 +37,18 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.BlobStore; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.repositories.blobstore.MeteredBlobStoreRepository; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -100,8 +102,6 @@ public static final class Repository { MAX_CHUNK_SIZE, Property.NodeScope ); - public static final Setting COMPRESS_SETTING = Setting.boolSetting("compress", false, Property.NodeScope); - public static final Setting READONLY_SETTING = Setting.boolSetting("readonly", false, Property.NodeScope); } private final BlobPath basePath; @@ -116,14 +116,7 @@ public AzureRepository( final ClusterService clusterService, final RecoverySettings recoverySettings ) { - super( - metadata, - Repository.COMPRESS_SETTING.get(metadata.settings()), - namedXContentRegistry, - clusterService, - recoverySettings, - buildLocation(metadata) - ); + super(metadata, namedXContentRegistry, clusterService, recoverySettings, buildLocation(metadata)); this.chunkSize = Repository.CHUNK_SIZE_SETTING.get(metadata.settings()); this.storageService = storageService; @@ -142,15 +135,15 @@ public AzureRepository( // If the user explicitly did not define a readonly value, we set it by ourselves depending on the location mode setting. // For secondary_only setting, the repository should be read only final LocationMode locationMode = Repository.LOCATION_MODE_SETTING.get(metadata.settings()); - if (Repository.READONLY_SETTING.exists(metadata.settings())) { - this.readonly = Repository.READONLY_SETTING.get(metadata.settings()); + if (READONLY_SETTING.exists(metadata.settings())) { + this.readonly = READONLY_SETTING.get(metadata.settings()); } else { this.readonly = locationMode == LocationMode.SECONDARY_ONLY; } } private static Map buildLocation(RepositoryMetadata metadata) { - return org.opensearch.common.collect.Map.of( + return Map.of( "base_path", Repository.BASE_PATH_SETTING.get(metadata.settings()), "container", @@ -194,4 +187,13 @@ protected ByteSizeValue chunkSize() { public boolean isReadOnly() { return readonly; } + + @Override + public List> getRestrictedSystemRepositorySettings() { + List> restrictedSettings = new ArrayList<>(); + restrictedSettings.addAll(super.getRestrictedSystemRepositorySettings()); + restrictedSettings.add(Repository.BASE_PATH_SETTING); + restrictedSettings.add(Repository.LOCATION_MODE_SETTING); + return restrictedSettings; + } } diff --git a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureRepositoryPlugin.java b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureRepositoryPlugin.java index 82ab5243a09aa..78db7cb2d0ea7 100644 --- a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureRepositoryPlugin.java +++ b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureRepositoryPlugin.java @@ -37,7 +37,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; diff --git a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureStorageService.java b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureStorageService.java index 3800be7c2d27d..9dcc312f8f5a7 100644 --- a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureStorageService.java +++ b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureStorageService.java @@ -32,11 +32,6 @@ package org.opensearch.repositories.azure; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.util.concurrent.Future; -import reactor.core.publisher.Mono; - import com.azure.core.http.HttpPipelineCallContext; import com.azure.core.http.HttpPipelineNextPolicy; import com.azure.core.http.HttpPipelinePosition; @@ -56,14 +51,13 @@ import com.azure.storage.common.implementation.connectionstring.StorageEndpoint; import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.common.policy.RetryPolicyType; - import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import java.net.Authenticator; import java.net.PasswordAuthentication; @@ -79,6 +73,11 @@ import java.util.function.BiConsumer; import java.util.function.Supplier; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.util.concurrent.Future; +import reactor.core.publisher.Mono; + import static java.util.Collections.emptyMap; public class AzureStorageService implements AutoCloseable { @@ -250,7 +249,7 @@ private BlobServiceClientBuilder applyLocationMode(final BlobServiceClientBuilde private static BlobServiceClientBuilder createClientBuilder(AzureStorageSettings settings) throws InvalidKeyException, URISyntaxException { - return new BlobServiceClientBuilder().connectionString(settings.getConnectString()); + return SocketAccess.doPrivilegedException(() -> new BlobServiceClientBuilder().connectionString(settings.getConnectString())); } /** diff --git a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureStorageSettings.java b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureStorageSettings.java index c9a031451bccd..e73ded679cf2b 100644 --- a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureStorageSettings.java +++ b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/AzureStorageSettings.java @@ -33,16 +33,17 @@ package org.opensearch.repositories.azure; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.settings.SecureSetting; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.AffixSetting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.settings.SecureString; + import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collections; @@ -91,8 +92,7 @@ final class AzureStorageSettings { AZURE_CLIENT_PREFIX_KEY, "endpoint_suffix", key -> Setting.simpleString(key, Property.NodeScope), - () -> ACCOUNT_SETTING, - () -> KEY_SETTING + () -> ACCOUNT_SETTING ); // The overall operation timeout diff --git a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/ProxySettings.java b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/ProxySettings.java index df8c95e69acf2..03097626c9741 100644 --- a/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/ProxySettings.java +++ b/plugins/repository-azure/src/main/java/org/opensearch/repositories/azure/ProxySettings.java @@ -9,8 +9,8 @@ package org.opensearch.repositories.azure; import com.azure.core.http.ProxyOptions; -import org.opensearch.common.Strings; import org.opensearch.common.settings.SettingsException; +import org.opensearch.core.common.Strings; import java.net.InetAddress; import java.net.InetSocketAddress; diff --git a/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureBlobContainerRetriesTests.java b/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureBlobContainerRetriesTests.java index c9e6e299c7120..5117768aa3351 100644 --- a/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureBlobContainerRetriesTests.java +++ b/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureBlobContainerRetriesTests.java @@ -31,23 +31,18 @@ package org.opensearch.repositories.azure; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; + import com.azure.storage.blob.BlobClient; import com.azure.storage.blob.models.ParallelTransferOptions; import com.azure.storage.common.policy.RequestRetryOptions; import com.azure.storage.common.policy.RetryPolicyType; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpServer; -import fixture.azure.AzureHttpHandler; -import reactor.core.scheduler.Schedulers; - import org.apache.http.HttpStatus; - import org.opensearch.cluster.metadata.RepositoryMetadata; -import org.opensearch.common.Strings; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.io.Streams; import org.opensearch.common.lucene.store.ByteArrayIndexInput; @@ -55,15 +50,16 @@ import org.opensearch.common.network.InetAddresses; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.CountDown; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.rest.RestStatus; import org.opensearch.rest.RestUtils; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; - import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -91,6 +87,9 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import fixture.azure.AzureHttpHandler; +import reactor.core.scheduler.Schedulers; + import static java.nio.charset.StandardCharsets.UTF_8; import static org.opensearch.repositories.azure.AzureRepository.Repository.CONTAINER_SETTING; import static org.opensearch.repositories.azure.AzureStorageSettings.ACCOUNT_SETTING; @@ -122,6 +121,10 @@ public class AzureBlobContainerRetriesTests extends OpenSearchTestCase { public void setUp() throws Exception { threadPool = new TestThreadPool(getTestClass().getName(), AzureRepositoryPlugin.executorBuilder()); httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0); + httpServer.createContext("/", (exchange) -> { + exchange.sendResponseHeaders(404, 0L); + exchange.close(); + }); httpServer.start(); super.setUp(); } @@ -231,6 +234,7 @@ public void testReadBlobWithRetries() throws Exception { exchange.getResponseHeaders().add("Content-Type", "application/octet-stream"); exchange.getResponseHeaders().add("Content-Length", String.valueOf(length)); exchange.getResponseHeaders().add("x-ms-blob-type", "blockblob"); + exchange.getResponseHeaders().add("Content-Range", "bytes " + rangeStart + "-" + bytes.length + "/" + bytes.length); exchange.sendResponseHeaders(RestStatus.OK.getStatus(), length); exchange.getResponseBody().write(bytes, rangeStart, length); return; @@ -247,7 +251,8 @@ public void testReadBlobWithRetries() throws Exception { final BlobContainer blobContainer = createBlobContainer(maxRetries); try (InputStream inputStream = blobContainer.readBlob("read_blob_max_retries")) { assertArrayEquals(bytes, BytesReference.toBytes(Streams.readFully(inputStream))); - assertThat(countDownHead.isCountedDown(), is(true)); + // No more getProperties() calls in BlobClientBase::openInputStream(), HEAD should not be invoked + assertThat(countDownHead.isCountedDown(), is(false)); assertThat(countDownGet.isCountedDown(), is(true)); } } @@ -278,6 +283,8 @@ public void testReadRangeBlobWithRetries() throws Exception { assertThat(length, lessThanOrEqualTo(bytes.length - rangeStart)); exchange.getResponseHeaders().add("Content-Type", "application/octet-stream"); exchange.getResponseHeaders().add("Content-Length", String.valueOf(length)); + exchange.getResponseHeaders() + .add("Content-Range", "bytes " + rangeStart + "-" + rangeEnd.get() + "/" + bytes.length); exchange.getResponseHeaders().add("x-ms-blob-type", "blockblob"); exchange.sendResponseHeaders(RestStatus.OK.getStatus(), length); exchange.getResponseBody().write(bytes, rangeStart, length); diff --git a/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureRepositorySettingsTests.java b/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureRepositorySettingsTests.java index 01235d6193d9c..3356e5174592a 100644 --- a/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureRepositorySettingsTests.java +++ b/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureRepositorySettingsTests.java @@ -32,19 +32,23 @@ package org.opensearch.repositories.azure; -import reactor.core.scheduler.Schedulers; - -import org.junit.AfterClass; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; +import org.opensearch.repositories.blobstore.BlobStoreRepository; import org.opensearch.repositories.blobstore.BlobStoreTestUtil; import org.opensearch.test.OpenSearchTestCase; +import org.junit.AfterClass; + +import java.util.List; + +import reactor.core.scheduler.Schedulers; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -179,4 +183,21 @@ public void testChunkSize() { ); } + public void testSystemRepositoryDefault() { + assertThat(azureRepository(Settings.EMPTY).isSystemRepository(), is(false)); + } + + public void testSystemRepositoryOn() { + assertThat(azureRepository(Settings.builder().put("system_repository", true).build()).isSystemRepository(), is(true)); + } + + public void testRestrictedSettingsDefault() { + List> restrictedSettings = azureRepository(Settings.EMPTY).getRestrictedSystemRepositorySettings(); + assertThat(restrictedSettings.size(), is(5)); + assertTrue(restrictedSettings.contains(BlobStoreRepository.SYSTEM_REPOSITORY_SETTING)); + assertTrue(restrictedSettings.contains(BlobStoreRepository.READONLY_SETTING)); + assertTrue(restrictedSettings.contains(BlobStoreRepository.REMOTE_STORE_INDEX_SHALLOW_COPY)); + assertTrue(restrictedSettings.contains(AzureRepository.Repository.BASE_PATH_SETTING)); + assertTrue(restrictedSettings.contains(AzureRepository.Repository.LOCATION_MODE_SETTING)); + } } diff --git a/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureStorageServiceTests.java b/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureStorageServiceTests.java index 7f5ca73a507ad..264888bb7da3a 100644 --- a/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureStorageServiceTests.java +++ b/plugins/repository-azure/src/test/java/org/opensearch/repositories/azure/AzureStorageServiceTests.java @@ -32,20 +32,17 @@ package org.opensearch.repositories.azure; -import org.opensearch.common.Strings; -import reactor.core.scheduler.Schedulers; - import com.azure.core.http.policy.HttpPipelinePolicy; import com.azure.storage.blob.BlobServiceClient; import com.azure.storage.common.policy.RequestRetryPolicy; - -import org.junit.AfterClass; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; import org.opensearch.common.settings.SettingsModule; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; import org.opensearch.test.OpenSearchTestCase; +import org.junit.AfterClass; import java.io.IOException; import java.io.UncheckedIOException; @@ -60,6 +57,8 @@ import java.util.Collections; import java.util.Map; +import reactor.core.scheduler.Schedulers; + import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; diff --git a/plugins/repository-azure/src/yamlRestTest/java/org/opensearch/repositories/azure/RepositoryAzureClientYamlTestSuiteIT.java b/plugins/repository-azure/src/yamlRestTest/java/org/opensearch/repositories/azure/RepositoryAzureClientYamlTestSuiteIT.java index 7ce28855417c3..31ecd60602aa7 100644 --- a/plugins/repository-azure/src/yamlRestTest/java/org/opensearch/repositories/azure/RepositoryAzureClientYamlTestSuiteIT.java +++ b/plugins/repository-azure/src/yamlRestTest/java/org/opensearch/repositories/azure/RepositoryAzureClientYamlTestSuiteIT.java @@ -34,6 +34,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/plugins/repository-azure/src/yamlRestTest/resources/rest-api-spec/test/repository_azure/10_basic.yml b/plugins/repository-azure/src/yamlRestTest/resources/rest-api-spec/test/repository_azure/10_basic.yml index fe21a295e37bb..caec0a89101b4 100644 --- a/plugins/repository-azure/src/yamlRestTest/resources/rest-api-spec/test/repository_azure/10_basic.yml +++ b/plugins/repository-azure/src/yamlRestTest/resources/rest-api-spec/test/repository_azure/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: repository-azure } } + - contains: { nodes.$cluster_manager.plugins: { name: repository-azure } } diff --git a/plugins/repository-azure/src/yamlRestTest/resources/rest-api-spec/test/repository_azure/20_repository.yml b/plugins/repository-azure/src/yamlRestTest/resources/rest-api-spec/test/repository_azure/20_repository.yml index beaa95b732d52..04ff4e8c34033 100644 --- a/plugins/repository-azure/src/yamlRestTest/resources/rest-api-spec/test/repository_azure/20_repository.yml +++ b/plugins/repository-azure/src/yamlRestTest/resources/rest-api-spec/test/repository_azure/20_repository.yml @@ -29,6 +29,9 @@ setup: --- "Snapshot/Restore with repository-azure": + - skip: + features: allowed_warnings + # Get repository - do: snapshot.get_repository: @@ -169,12 +172,16 @@ setup: # Remove the snapshots - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." snapshot.delete: repository: repository snapshot: snapshot-two master_timeout: 5m - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." snapshot.delete: repository: repository snapshot: snapshot-one diff --git a/plugins/repository-gcs/build.gradle b/plugins/repository-gcs/build.gradle index e1ecf3c65a0f9..95070d04b3bf9 100644 --- a/plugins/repository-gcs/build.gradle +++ b/plugins/repository-gcs/build.gradle @@ -9,14 +9,12 @@ * GitHub history for details. */ -import java.nio.file.Files -import java.security.KeyPair -import java.security.KeyPairGenerator + import org.opensearch.gradle.MavenFilteringHack import org.opensearch.gradle.info.BuildParams +import org.opensearch.gradle.test.InternalClusterTestPlugin import org.opensearch.gradle.test.RestIntegTestTask import org.opensearch.gradle.test.rest.YamlRestTestPlugin -import org.opensearch.gradle.test.InternalClusterTestPlugin import java.nio.file.Files import java.security.KeyPair @@ -49,36 +47,48 @@ opensearchplugin { classname 'org.opensearch.repositories.gcs.GoogleCloudStoragePlugin' } +versions << [ + 'google_auth': '1.7.0' +] + dependencies { - api 'com.google.cloud:google-cloud-storage:1.113.1' + api 'com.google.api:api-common:1.8.1' + api 'com.google.api:gax:2.27.0' + api 'com.google.api:gax-httpjson:0.103.1' + + api 'com.google.apis:google-api-services-storage:v1-rev20230617-2.0.0' + + api 'com.google.api-client:google-api-client:2.2.0' + + api 'com.google.api.grpc:proto-google-common-protos:2.25.0' + api 'com.google.api.grpc:proto-google-iam-v1:0.12.0' + + api "com.google.auth:google-auth-library-credentials:${versions.google_auth}" + api "com.google.auth:google-auth-library-oauth2-http:${versions.google_auth}" + api 'com.google.cloud:google-cloud-core:2.5.10' - runtimeOnly 'com.google.guava:guava:30.1.1-jre' + api 'com.google.cloud:google-cloud-core-http:2.23.0' + api 'com.google.cloud:google-cloud-storage:1.113.1' + + api 'com.google.code.gson:gson:2.10.1' + + runtimeOnly "com.google.guava:guava:${versions.guava}" api 'com.google.guava:failureaccess:1.0.1' - api 'com.google.http-client:google-http-client:1.35.0' + + api 'com.google.http-client:google-http-client:1.43.2' + api 'com.google.http-client:google-http-client-appengine:1.43.2' + api 'com.google.http-client:google-http-client-gson:1.43.3' + api 'com.google.http-client:google-http-client-jackson2:1.43.2' + + api 'com.google.oauth-client:google-oauth-client:1.34.1' + api "commons-logging:commons-logging:${versions.commonslogging}" api "org.apache.logging.log4j:log4j-1.2-api:${versions.log4j}" api "commons-codec:commons-codec:${versions.commonscodec}" - api 'com.google.api:api-common:1.8.1' - api 'com.google.api:gax:1.54.0' api 'org.threeten:threetenbp:1.4.4' - api 'com.google.protobuf:protobuf-java-util:3.19.3' - api 'com.google.protobuf:protobuf-java:3.19.3' - api 'com.google.code.gson:gson:2.9.0' - api 'com.google.api.grpc:proto-google-common-protos:1.16.0' - api 'com.google.api.grpc:proto-google-iam-v1:0.12.0' - api 'com.google.cloud:google-cloud-core-http:1.93.3' - api 'com.google.auth:google-auth-library-credentials:0.20.0' - api 'com.google.auth:google-auth-library-oauth2-http:0.20.0' - api 'com.google.oauth-client:google-oauth-client:1.31.0' - api 'com.google.api-client:google-api-client:1.30.10' - api 'com.google.http-client:google-http-client-appengine:1.35.0' - api 'com.google.http-client:google-http-client-jackson2:1.35.0' - api 'com.google.http-client:google-http-client-gson:1.41.4' - api 'com.google.api:gax-httpjson:0.62.0' - api 'io.grpc:grpc-context:1.29.0' - api 'io.opencensus:opencensus-api:0.18.0' - api 'io.opencensus:opencensus-contrib-http-util:0.18.0' - api 'com.google.apis:google-api-services-storage:v1-rev20200814-1.30.10' + api 'io.grpc:grpc-api:1.57.2' + api 'io.opencensus:opencensus-api:0.31.1' + api 'io.opencensus:opencensus-contrib-http-util:0.31.1' testImplementation project(':test:fixtures:gcs-fixture') } @@ -101,13 +111,6 @@ tasks.named("dependencyLicenses").configure { thirdPartyAudit { ignoreViolations( // uses internal java api: sun.misc.Unsafe - 'com.google.protobuf.UnsafeUtil', - 'com.google.protobuf.UnsafeUtil$1', - 'com.google.protobuf.UnsafeUtil$JvmMemoryAccessor', - 'com.google.protobuf.UnsafeUtil$MemoryAccessor', - 'com.google.protobuf.MessageSchema', - 'com.google.protobuf.UnsafeUtil$Android32MemoryAccessor', - 'com.google.protobuf.UnsafeUtil$Android64MemoryAccessor', 'com.google.common.cache.Striped64', 'com.google.common.cache.Striped64$1', 'com.google.common.cache.Striped64$Cell', @@ -117,7 +120,6 @@ thirdPartyAudit { 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$1', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$2', - 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$3', 'com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper', 'com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper$1', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray', @@ -126,6 +128,7 @@ thirdPartyAudit { ) ignoreMissingClasses( + 'com.google.api.client.http.apache.v2.ApacheHttpTransport', 'com.google.appengine.api.datastore.Blob', 'com.google.appengine.api.datastore.DatastoreService', 'com.google.appengine.api.datastore.DatastoreServiceFactory', @@ -145,6 +148,10 @@ thirdPartyAudit { 'com.google.appengine.api.urlfetch.HTTPResponse', 'com.google.appengine.api.urlfetch.URLFetchService', 'com.google.appengine.api.urlfetch.URLFetchServiceFactory', + 'com.google.protobuf.util.JsonFormat', + 'com.google.protobuf.util.JsonFormat$Parser', + 'com.google.protobuf.util.JsonFormat$Printer', + 'com.google.protobuf.util.Timestamps', // commons-logging optional dependencies 'org.apache.avalon.framework.logger.Logger', 'org.apache.log.Hierarchy', @@ -168,8 +175,8 @@ thirdPartyAudit { 'org.apache.http.client.UserTokenHandler', 'org.apache.http.client.methods.HttpEntityEnclosingRequestBase', 'org.apache.http.client.methods.HttpRequestBase', - 'org.apache.http.config.SocketConfig', - 'org.apache.http.config.SocketConfig$Builder', + 'org.apache.http.config.Registry', + 'org.apache.http.config.RegistryBuilder', 'org.apache.http.conn.ClientConnectionManager', 'org.apache.http.conn.ConnectionKeepAliveStrategy', 'org.apache.http.conn.params.ConnManagerParams', @@ -177,6 +184,7 @@ thirdPartyAudit { 'org.apache.http.conn.routing.HttpRoutePlanner', 'org.apache.http.conn.scheme.PlainSocketFactory', 'org.apache.http.conn.scheme.SchemeRegistry', + 'org.apache.http.conn.socket.PlainConnectionSocketFactory', 'org.apache.http.conn.ssl.SSLSocketFactory', 'org.apache.http.conn.ssl.X509HostnameVerifier', 'org.apache.http.entity.AbstractHttpEntity', @@ -189,10 +197,15 @@ thirdPartyAudit { 'org.apache.http.protocol.HttpContext', 'org.apache.http.protocol.HttpProcessor', 'org.apache.http.protocol.HttpRequestExecutor', + 'org.graalvm.nativeimage.hosted.Feature', + 'org.graalvm.nativeimage.hosted.Feature$BeforeAnalysisAccess', + 'org.graalvm.nativeimage.hosted.Feature$DuringAnalysisAccess', + 'org.graalvm.nativeimage.hosted.Feature$FeatureAccess', + 'org.graalvm.nativeimage.hosted.RuntimeReflection', // commons-logging provided dependencies 'javax.jms.Message', 'javax.servlet.ServletContextEvent', - 'javax.servlet.ServletContextListener', + 'javax.servlet.ServletContextListener' ) } diff --git a/plugins/repository-gcs/licenses/commons-codec-1.13.jar.sha1 b/plugins/repository-gcs/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/repository-gcs/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/commons-codec-1.15.jar.sha1 b/plugins/repository-gcs/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/repository-gcs/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/commons-logging-1.1.3.jar.sha1 b/plugins/repository-gcs/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index 5b8f029e58293..0000000000000 --- a/plugins/repository-gcs/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/commons-logging-1.2.jar.sha1 b/plugins/repository-gcs/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/repository-gcs/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/commons-logging-LICENSE.txt b/plugins/repository-gcs/licenses/commons-logging-LICENSE.txt index 57bc88a15a0ee..d645695673349 100644 --- a/plugins/repository-gcs/licenses/commons-logging-LICENSE.txt +++ b/plugins/repository-gcs/licenses/commons-logging-LICENSE.txt @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -199,4 +200,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - diff --git a/plugins/repository-gcs/licenses/commons-logging-NOTICE.txt b/plugins/repository-gcs/licenses/commons-logging-NOTICE.txt index 72eb32a902458..d3d6e140ce4f3 100644 --- a/plugins/repository-gcs/licenses/commons-logging-NOTICE.txt +++ b/plugins/repository-gcs/licenses/commons-logging-NOTICE.txt @@ -1,5 +1,5 @@ -Apache Commons CLI -Copyright 2001-2009 The Apache Software Foundation +Apache Commons Logging +Copyright 2003-2014 The Apache Software Foundation -This product includes software developed by +This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/repository-gcs/licenses/gax-1.54.0.jar.sha1 b/plugins/repository-gcs/licenses/gax-1.54.0.jar.sha1 deleted file mode 100644 index ed63c084f4edc..0000000000000 --- a/plugins/repository-gcs/licenses/gax-1.54.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1f1668868b8b3fd5fc248d80c16dd9f09afc9180 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/gax-2.27.0.jar.sha1 b/plugins/repository-gcs/licenses/gax-2.27.0.jar.sha1 new file mode 100644 index 0000000000000..1813a3aa94404 --- /dev/null +++ b/plugins/repository-gcs/licenses/gax-2.27.0.jar.sha1 @@ -0,0 +1 @@ +04a27757c9240da71f896be39f47aaa6e23ef989 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/gax-httpjson-0.103.1.jar.sha1 b/plugins/repository-gcs/licenses/gax-httpjson-0.103.1.jar.sha1 new file mode 100644 index 0000000000000..11315004e233d --- /dev/null +++ b/plugins/repository-gcs/licenses/gax-httpjson-0.103.1.jar.sha1 @@ -0,0 +1 @@ +041d99172fda933bc879bdfd8de9420c5c34107e \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/gax-httpjson-0.62.0.jar.sha1 b/plugins/repository-gcs/licenses/gax-httpjson-0.62.0.jar.sha1 deleted file mode 100644 index 161ca85ccfc0c..0000000000000 --- a/plugins/repository-gcs/licenses/gax-httpjson-0.62.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -05a1a4736acd1c4f30304be953532be6aecdc2c9 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-api-client-1.30.10.jar.sha1 b/plugins/repository-gcs/licenses/google-api-client-1.30.10.jar.sha1 deleted file mode 100644 index 62c51887ee1ea..0000000000000 --- a/plugins/repository-gcs/licenses/google-api-client-1.30.10.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2de98417199785982e1f037fb8b52613f57175ae \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-api-client-2.2.0.jar.sha1 b/plugins/repository-gcs/licenses/google-api-client-2.2.0.jar.sha1 new file mode 100644 index 0000000000000..f9604d6837ca9 --- /dev/null +++ b/plugins/repository-gcs/licenses/google-api-client-2.2.0.jar.sha1 @@ -0,0 +1 @@ +10e53fd4d987e37190432e896bdaa62e8ea2c628 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-api-services-storage-v1-rev20200814-1.30.10.jar.sha1 b/plugins/repository-gcs/licenses/google-api-services-storage-v1-rev20200814-1.30.10.jar.sha1 deleted file mode 100644 index e399aa5865413..0000000000000 --- a/plugins/repository-gcs/licenses/google-api-services-storage-v1-rev20200814-1.30.10.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -fe3b480958961fc7144da10ce3653065d5eb5490 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-api-services-storage-v1-rev20230617-2.0.0.jar.sha1 b/plugins/repository-gcs/licenses/google-api-services-storage-v1-rev20230617-2.0.0.jar.sha1 new file mode 100644 index 0000000000000..1a1452f773b96 --- /dev/null +++ b/plugins/repository-gcs/licenses/google-api-services-storage-v1-rev20230617-2.0.0.jar.sha1 @@ -0,0 +1 @@ +fc3f225b405303fe7cb760d578348b6b07e7ea8b \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-auth-library-credentials-0.20.0.jar.sha1 b/plugins/repository-gcs/licenses/google-auth-library-credentials-0.20.0.jar.sha1 deleted file mode 100644 index 14cc742737eed..0000000000000 --- a/plugins/repository-gcs/licenses/google-auth-library-credentials-0.20.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -87a91a373e64ba5c3cdf8cc5cf54b189dd1492f8 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-auth-library-credentials-1.7.0.jar.sha1 b/plugins/repository-gcs/licenses/google-auth-library-credentials-1.7.0.jar.sha1 new file mode 100644 index 0000000000000..f2e9a4f7283bf --- /dev/null +++ b/plugins/repository-gcs/licenses/google-auth-library-credentials-1.7.0.jar.sha1 @@ -0,0 +1 @@ +b29af5a9ea94e9e7f86bded11e39f5afda5b17e8 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-auth-library-oauth2-http-0.20.0.jar.sha1 b/plugins/repository-gcs/licenses/google-auth-library-oauth2-http-0.20.0.jar.sha1 deleted file mode 100644 index 7911c34780cbe..0000000000000 --- a/plugins/repository-gcs/licenses/google-auth-library-oauth2-http-0.20.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f33d4d6c91a68826816606a2208990eea93fcb2a \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-auth-library-oauth2-http-1.7.0.jar.sha1 b/plugins/repository-gcs/licenses/google-auth-library-oauth2-http-1.7.0.jar.sha1 new file mode 100644 index 0000000000000..738645d6b8c7b --- /dev/null +++ b/plugins/repository-gcs/licenses/google-auth-library-oauth2-http-1.7.0.jar.sha1 @@ -0,0 +1 @@ +985d183303dbd4b7ceb348056e41e59677f6f74f \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-cloud-core-http-1.93.3.jar.sha1 b/plugins/repository-gcs/licenses/google-cloud-core-http-1.93.3.jar.sha1 deleted file mode 100644 index 0518072447569..0000000000000 --- a/plugins/repository-gcs/licenses/google-cloud-core-http-1.93.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2589aa6a4b6c49811c08ec2803c8e9c79c410bc5 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-cloud-core-http-2.23.0.jar.sha1 b/plugins/repository-gcs/licenses/google-cloud-core-http-2.23.0.jar.sha1 new file mode 100644 index 0000000000000..9db3cbcbec35b --- /dev/null +++ b/plugins/repository-gcs/licenses/google-cloud-core-http-2.23.0.jar.sha1 @@ -0,0 +1 @@ +9913d0806fcfbfbc4a775f29865126ed8465464b \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-http-client-1.35.0.jar.sha1 b/plugins/repository-gcs/licenses/google-http-client-1.35.0.jar.sha1 deleted file mode 100644 index 802a6ab3a8d04..0000000000000 --- a/plugins/repository-gcs/licenses/google-http-client-1.35.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f2348dd57d5417c29388bd430f5055dca863c600 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-http-client-1.43.2.jar.sha1 b/plugins/repository-gcs/licenses/google-http-client-1.43.2.jar.sha1 new file mode 100644 index 0000000000000..a576a74c62542 --- /dev/null +++ b/plugins/repository-gcs/licenses/google-http-client-1.43.2.jar.sha1 @@ -0,0 +1 @@ +2520469ebd8c0675f0d2aeafd2da665228320fcf \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-http-client-appengine-1.35.0.jar.sha1 b/plugins/repository-gcs/licenses/google-http-client-appengine-1.35.0.jar.sha1 deleted file mode 100644 index 8bf444887d30f..0000000000000 --- a/plugins/repository-gcs/licenses/google-http-client-appengine-1.35.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -394d1e1376538931ec3d4eeed654f9da911b95eb \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-http-client-appengine-1.43.2.jar.sha1 b/plugins/repository-gcs/licenses/google-http-client-appengine-1.43.2.jar.sha1 new file mode 100644 index 0000000000000..d8a9dba20070b --- /dev/null +++ b/plugins/repository-gcs/licenses/google-http-client-appengine-1.43.2.jar.sha1 @@ -0,0 +1 @@ +9fb548c5264227813fd83991b94a705b0841c15f \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-http-client-gson-1.41.4.jar.sha1 b/plugins/repository-gcs/licenses/google-http-client-gson-1.41.4.jar.sha1 deleted file mode 100644 index 17960a99abea2..0000000000000 --- a/plugins/repository-gcs/licenses/google-http-client-gson-1.41.4.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -fa665c1c573765dd858bc34931ad747e4ed11efe \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-http-client-gson-1.43.3.jar.sha1 b/plugins/repository-gcs/licenses/google-http-client-gson-1.43.3.jar.sha1 new file mode 100644 index 0000000000000..43f4fe4a127e1 --- /dev/null +++ b/plugins/repository-gcs/licenses/google-http-client-gson-1.43.3.jar.sha1 @@ -0,0 +1 @@ +252e267acf720ef6333488740a696a1d5e204639 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-http-client-jackson2-1.35.0.jar.sha1 b/plugins/repository-gcs/licenses/google-http-client-jackson2-1.35.0.jar.sha1 deleted file mode 100644 index 0342f57779315..0000000000000 --- a/plugins/repository-gcs/licenses/google-http-client-jackson2-1.35.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c1c2a08792b935f3345590783ada872f4a0997f1 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-http-client-jackson2-1.43.2.jar.sha1 b/plugins/repository-gcs/licenses/google-http-client-jackson2-1.43.2.jar.sha1 new file mode 100644 index 0000000000000..7b606a07651ed --- /dev/null +++ b/plugins/repository-gcs/licenses/google-http-client-jackson2-1.43.2.jar.sha1 @@ -0,0 +1 @@ +5e52a9967ebd8246fc4cca64df5f03608db5ac6e \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-oauth-client-1.31.0.jar.sha1 b/plugins/repository-gcs/licenses/google-oauth-client-1.31.0.jar.sha1 deleted file mode 100644 index 942dbb5d167a4..0000000000000 --- a/plugins/repository-gcs/licenses/google-oauth-client-1.31.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -bf1cfbbaa2497d0a841ea0363df4a61170d5823b \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/google-oauth-client-1.34.1.jar.sha1 b/plugins/repository-gcs/licenses/google-oauth-client-1.34.1.jar.sha1 new file mode 100644 index 0000000000000..a8434bd380761 --- /dev/null +++ b/plugins/repository-gcs/licenses/google-oauth-client-1.34.1.jar.sha1 @@ -0,0 +1 @@ +4a4f88c5e13143f882268c98239fb85c3b2c6cb2 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/grpc-api-1.57.2.jar.sha1 b/plugins/repository-gcs/licenses/grpc-api-1.57.2.jar.sha1 new file mode 100644 index 0000000000000..8b320fdd2f9cc --- /dev/null +++ b/plugins/repository-gcs/licenses/grpc-api-1.57.2.jar.sha1 @@ -0,0 +1 @@ +c71a006b81ddae7bc4b7cb1d2da78c1b173761f4 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/grpc-api-LICENSE.txt b/plugins/repository-gcs/licenses/grpc-api-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-gcs/licenses/grpc-api-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-gcs/licenses/grpc-api-NOTICE.txt b/plugins/repository-gcs/licenses/grpc-api-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/repository-gcs/licenses/grpc-context-1.29.0.jar.sha1 b/plugins/repository-gcs/licenses/grpc-context-1.29.0.jar.sha1 deleted file mode 100644 index a549827edd283..0000000000000 --- a/plugins/repository-gcs/licenses/grpc-context-1.29.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1d8a441110f86f8927543dc3007639080441ea3c \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/gson-2.10.1.jar.sha1 b/plugins/repository-gcs/licenses/gson-2.10.1.jar.sha1 new file mode 100644 index 0000000000000..9810309d1013a --- /dev/null +++ b/plugins/repository-gcs/licenses/gson-2.10.1.jar.sha1 @@ -0,0 +1 @@ +b3add478d4382b78ea20b1671390a858002feb6c \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/gson-2.9.0.jar.sha1 b/plugins/repository-gcs/licenses/gson-2.9.0.jar.sha1 deleted file mode 100644 index 8e9626b0c949b..0000000000000 --- a/plugins/repository-gcs/licenses/gson-2.9.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8a1167e089096758b49f9b34066ef98b2f4b37aa \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/guava-30.1.1-jre.jar.sha1 b/plugins/repository-gcs/licenses/guava-30.1.1-jre.jar.sha1 deleted file mode 100644 index 39e641fc7834f..0000000000000 --- a/plugins/repository-gcs/licenses/guava-30.1.1-jre.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -87e0fd1df874ea3cbe577702fe6f17068b790fd8 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/guava-32.1.1-jre.jar.sha1 b/plugins/repository-gcs/licenses/guava-32.1.1-jre.jar.sha1 new file mode 100644 index 0000000000000..0d791b5d3f55b --- /dev/null +++ b/plugins/repository-gcs/licenses/guava-32.1.1-jre.jar.sha1 @@ -0,0 +1 @@ +ad575652d84153075dd41ec6177ccb15251262b2 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/log4j-1.2-api-2.17.1.jar.sha1 b/plugins/repository-gcs/licenses/log4j-1.2-api-2.17.1.jar.sha1 deleted file mode 100644 index 23aa5c60bd596..0000000000000 --- a/plugins/repository-gcs/licenses/log4j-1.2-api-2.17.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -db3a7e7f07e878b92ac4a8f1100bee8325d5713a \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/log4j-1.2-api-2.20.0.jar.sha1 b/plugins/repository-gcs/licenses/log4j-1.2-api-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..9829576d38ce0 --- /dev/null +++ b/plugins/repository-gcs/licenses/log4j-1.2-api-2.20.0.jar.sha1 @@ -0,0 +1 @@ +689151374756cb809cb029f2501015bdc7733179 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/opencensus-api-0.18.0.jar.sha1 b/plugins/repository-gcs/licenses/opencensus-api-0.18.0.jar.sha1 deleted file mode 100644 index 8b95ab4e4c49c..0000000000000 --- a/plugins/repository-gcs/licenses/opencensus-api-0.18.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -b89a8f8dfd1e1e0d68d83c82a855624814b19a6e \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/opencensus-api-0.31.1.jar.sha1 b/plugins/repository-gcs/licenses/opencensus-api-0.31.1.jar.sha1 new file mode 100644 index 0000000000000..03760848f76ef --- /dev/null +++ b/plugins/repository-gcs/licenses/opencensus-api-0.31.1.jar.sha1 @@ -0,0 +1 @@ +66a60c7201c2b8b20ce495f0295b32bb0ccbbc57 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/opencensus-contrib-http-util-0.18.0.jar.sha1 b/plugins/repository-gcs/licenses/opencensus-contrib-http-util-0.18.0.jar.sha1 deleted file mode 100644 index 1757e00591110..0000000000000 --- a/plugins/repository-gcs/licenses/opencensus-contrib-http-util-0.18.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -76a37e4a931d5801a9e25b0c0353e5f37c4d1e8e \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/opencensus-contrib-http-util-0.31.1.jar.sha1 b/plugins/repository-gcs/licenses/opencensus-contrib-http-util-0.31.1.jar.sha1 new file mode 100644 index 0000000000000..4e123da3ab45f --- /dev/null +++ b/plugins/repository-gcs/licenses/opencensus-contrib-http-util-0.31.1.jar.sha1 @@ -0,0 +1 @@ +3c13fc5715231fadb16a9b74a44d9d59c460cfa8 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/proto-google-common-protos-1.16.0.jar.sha1 b/plugins/repository-gcs/licenses/proto-google-common-protos-1.16.0.jar.sha1 deleted file mode 100644 index 7762b7a3ebdc3..0000000000000 --- a/plugins/repository-gcs/licenses/proto-google-common-protos-1.16.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2c5f022ea3b8e8df6a619c4cd8faf9af86022daa \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/proto-google-common-protos-2.25.0.jar.sha1 b/plugins/repository-gcs/licenses/proto-google-common-protos-2.25.0.jar.sha1 new file mode 100644 index 0000000000000..b5ef7ee78e794 --- /dev/null +++ b/plugins/repository-gcs/licenses/proto-google-common-protos-2.25.0.jar.sha1 @@ -0,0 +1 @@ +adfa7c3d9b806969db75cf35fe4b286b3b8b1ce0 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/protobuf-java-3.19.3.jar.sha1 b/plugins/repository-gcs/licenses/protobuf-java-3.19.3.jar.sha1 deleted file mode 100644 index 655ecd1f1c1c9..0000000000000 --- a/plugins/repository-gcs/licenses/protobuf-java-3.19.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4b57f1b1b9e281231c3fcfc039ce3021e29ff570 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/protobuf-java-util-3.19.3.jar.sha1 b/plugins/repository-gcs/licenses/protobuf-java-util-3.19.3.jar.sha1 deleted file mode 100644 index 9ba36d444c541..0000000000000 --- a/plugins/repository-gcs/licenses/protobuf-java-util-3.19.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3e6812cbbb7e6faffa7b56438740dec510e1fc1a \ No newline at end of file diff --git a/plugins/repository-gcs/src/internalClusterTest/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java b/plugins/repository-gcs/src/internalClusterTest/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java index 4bc59b6ae6553..f8e1a1cc39ae0 100644 --- a/plugins/repository-gcs/src/internalClusterTest/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java +++ b/plugins/repository-gcs/src/internalClusterTest/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStoreRepositoryTests.java @@ -32,33 +32,31 @@ package org.opensearch.repositories.gcs; -import com.google.api.gax.retrying.RetrySettings; -import com.google.cloud.http.HttpTransportOptions; -import com.google.cloud.storage.StorageOptions; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import fixture.gcs.FakeOAuth2HttpHandler; -import fixture.gcs.GoogleCloudStorageHttpHandler; + +import com.google.api.gax.retrying.RetrySettings; +import com.google.cloud.http.HttpTransportOptions; +import com.google.cloud.storage.StorageOptions; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; import org.opensearch.action.ActionRunnable; import org.opensearch.action.support.PlainActionFuture; -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.BlobStore; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.io.Streams; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; @@ -67,9 +65,6 @@ import org.opensearch.repositories.blobstore.BlobStoreRepository; import org.opensearch.repositories.blobstore.OpenSearchMockAPIBasedRepositoryIntegTestCase; -import org.junit.BeforeClass; -import org.threeten.bp.Duration; - import java.io.IOException; import java.io.InputStream; import java.util.Arrays; @@ -80,6 +75,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import fixture.gcs.FakeOAuth2HttpHandler; +import fixture.gcs.GoogleCloudStorageHttpHandler; +import org.threeten.bp.Duration; + import static org.opensearch.repositories.gcs.GoogleCloudStorageClientSettings.CREDENTIALS_FILE_SETTING; import static org.opensearch.repositories.gcs.GoogleCloudStorageClientSettings.ENDPOINT_SETTING; import static org.opensearch.repositories.gcs.GoogleCloudStorageClientSettings.TOKEN_URI_SETTING; @@ -88,22 +87,6 @@ @SuppressForbidden(reason = "this test uses a HttpServer to emulate a Google Cloud Storage endpoint") public class GoogleCloudStorageBlobStoreRepositoryTests extends OpenSearchMockAPIBasedRepositoryIntegTestCase { - - public static void assumeNotJava8() { - assumeFalse( - "This test is flaky on jdk8 - we suspect a JDK bug to trigger some assertion in the HttpServer implementation used " - + "to emulate the server side logic of Google Cloud Storage. See https://bugs.openjdk.java.net/browse/JDK-8180754, " - + "https://github.com/elastic/elasticsearch/pull/51933 and https://github.com/elastic/elasticsearch/issues/52906 " - + "for more background on this issue.", - JavaVersion.current().equals(JavaVersion.parse("8")) - ); - } - - @BeforeClass - public static void skipJava8() { - assumeNotJava8(); - } - @Override protected String repositoryType() { return GoogleCloudStorageRepository.TYPE; @@ -148,7 +131,7 @@ protected Settings nodeSettings(int nodeOrdinal) { public void testDeleteSingleItem() { final String repoName = createRepository(randomName()); - final RepositoriesService repositoriesService = internalCluster().getMasterNodeInstance(RepositoriesService.class); + final RepositoriesService repositoriesService = internalCluster().getClusterManagerNodeInstance(RepositoriesService.class); final BlobStoreRepository repository = (BlobStoreRepository) repositoriesService.repository(repoName); PlainActionFuture.get( f -> repository.threadPool() diff --git a/plugins/repository-gcs/src/internalClusterTest/java/org/opensearch/repositories/gcs/GoogleCloudStorageThirdPartyTests.java b/plugins/repository-gcs/src/internalClusterTest/java/org/opensearch/repositories/gcs/GoogleCloudStorageThirdPartyTests.java index f1b2f78a37380..1e11b1d111d8f 100644 --- a/plugins/repository-gcs/src/internalClusterTest/java/org/opensearch/repositories/gcs/GoogleCloudStorageThirdPartyTests.java +++ b/plugins/repository-gcs/src/internalClusterTest/java/org/opensearch/repositories/gcs/GoogleCloudStorageThirdPartyTests.java @@ -33,10 +33,10 @@ package org.opensearch.repositories.gcs; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.common.Strings; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.SecureSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.AbstractThirdPartyRepositoryTestCase; diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStore.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStore.java index cd6eb5357ea97..f5c20003ea7b6 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStore.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStore.java @@ -55,8 +55,8 @@ import org.opensearch.common.blobstore.support.PlainBlobMetadata; import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.io.Streams; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -295,7 +295,7 @@ private void writeBlobResumable(BlobInfo blobInfo, InputStream inputStream, long * It is not enough to wrap the call to Streams#copy, we have to wrap the privileged calls too; this is because Streams#copy * is in the stacktrace and is not granted the permissions needed to close and write the channel. */ - org.opensearch.core.internal.io.Streams.copy(inputStream, Channels.newOutputStream(new WritableByteChannel() { + org.opensearch.common.util.io.Streams.copy(inputStream, Channels.newOutputStream(new WritableByteChannel() { @SuppressForbidden(reason = "channel is based on a socket") @Override diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageClientSettings.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageClientSettings.java index e8700570d2801..7463bd4ff26fe 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageClientSettings.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageClientSettings.java @@ -33,14 +33,13 @@ import com.google.api.services.storage.StorageScopes; import com.google.auth.oauth2.ServiceAccountCredentials; - -import org.opensearch.common.Strings; import org.opensearch.common.settings.SecureSetting; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.settings.SecureString; import java.io.IOException; import java.io.InputStream; diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageHttpStatsCollector.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageHttpStatsCollector.java index 7375d4edb9030..2f48de94a4830 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageHttpStatsCollector.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageHttpStatsCollector.java @@ -36,8 +36,8 @@ import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpResponseInterceptor; -import org.opensearch.common.collect.List; +import java.util.List; import java.util.Locale; import java.util.function.Consumer; import java.util.function.Function; diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStoragePlugin.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStoragePlugin.java index 4908b26649b1b..c0fda11b98467 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStoragePlugin.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStoragePlugin.java @@ -35,7 +35,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageRepository.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageRepository.java index 909dc066a493b..f6d078868b875 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageRepository.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageRepository.java @@ -36,21 +36,22 @@ import org.apache.logging.log4j.Logger; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.settings.Setting; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.repositories.RepositoryException; import org.opensearch.repositories.blobstore.MeteredBlobStoreRepository; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.function.Function; import static org.opensearch.common.settings.Setting.Property; -import static org.opensearch.common.settings.Setting.boolSetting; import static org.opensearch.common.settings.Setting.byteSizeSetting; import static org.opensearch.common.settings.Setting.simpleString; @@ -70,7 +71,6 @@ class GoogleCloudStorageRepository extends MeteredBlobStoreRepository { static final Setting BUCKET = simpleString("bucket", Property.NodeScope, Property.Dynamic); static final Setting BASE_PATH = simpleString("base_path", Property.NodeScope, Property.Dynamic); - static final Setting COMPRESS = boolSetting("compress", false, Property.NodeScope, Property.Dynamic); static final Setting CHUNK_SIZE = byteSizeSetting( "chunk_size", MAX_CHUNK_SIZE, @@ -94,7 +94,7 @@ class GoogleCloudStorageRepository extends MeteredBlobStoreRepository { final ClusterService clusterService, final RecoverySettings recoverySettings ) { - super(metadata, getSetting(COMPRESS, metadata), namedXContentRegistry, clusterService, recoverySettings, buildLocation(metadata)); + super(metadata, namedXContentRegistry, clusterService, recoverySettings, buildLocation(metadata)); this.storageService = storageService; String basePath = BASE_PATH.get(metadata.settings()); @@ -115,12 +115,7 @@ class GoogleCloudStorageRepository extends MeteredBlobStoreRepository { } private static Map buildLocation(RepositoryMetadata metadata) { - return org.opensearch.common.collect.Map.of( - "base_path", - BASE_PATH.get(metadata.settings()), - "bucket", - getSetting(BUCKET, metadata) - ); + return Map.of("base_path", BASE_PATH.get(metadata.settings()), "bucket", getSetting(BUCKET, metadata)); } @Override @@ -138,6 +133,15 @@ protected ByteSizeValue chunkSize() { return chunkSize; } + @Override + public List> getRestrictedSystemRepositorySettings() { + List> restrictedSettings = new ArrayList<>(); + restrictedSettings.addAll(super.getRestrictedSystemRepositorySettings()); + restrictedSettings.add(BUCKET); + restrictedSettings.add(BASE_PATH); + return restrictedSettings; + } + /** * Get a given setting from the repository settings, throwing a {@link RepositoryException} if the setting does not exist or is empty. */ diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageRetryingInputStream.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageRetryingInputStream.java index 72d3e37466d09..22926199cd6a2 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageRetryingInputStream.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageRetryingInputStream.java @@ -45,7 +45,7 @@ import org.opensearch.LegacyESVersion; import org.opensearch.SpecialPermission; import org.opensearch.common.SuppressForbidden; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import java.io.FilterInputStream; import java.io.IOException; diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageService.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageService.java index f4b501327d52c..445e1d65f3d3e 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageService.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/GoogleCloudStorageService.java @@ -41,13 +41,12 @@ import com.google.cloud.http.HttpTransportOptions; import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageOptions; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.common.Strings; import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; import java.io.IOException; import java.net.Authenticator; diff --git a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/ProxySettings.java b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/ProxySettings.java index ddc6446d2c8c5..11f0cbd83e62e 100644 --- a/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/ProxySettings.java +++ b/plugins/repository-gcs/src/main/java/org/opensearch/repositories/gcs/ProxySettings.java @@ -8,7 +8,7 @@ package org.opensearch.repositories.gcs; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import java.net.InetAddress; import java.net.InetSocketAddress; diff --git a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobContainerRetriesTests.java b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobContainerRetriesTests.java index 6a589126a9466..7ad1c6cf31477 100644 --- a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobContainerRetriesTests.java +++ b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobContainerRetriesTests.java @@ -31,23 +31,18 @@ package org.opensearch.repositories.gcs; +import com.sun.net.httpserver.HttpHandler; + import com.google.api.gax.retrying.RetrySettings; import com.google.cloud.http.HttpTransportOptions; import com.google.cloud.storage.StorageException; import com.google.cloud.storage.StorageOptions; -import com.sun.net.httpserver.HttpHandler; -import fixture.gcs.FakeOAuth2HttpHandler; import org.apache.http.HttpStatus; - -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.UUIDs; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.io.Streams; import org.opensearch.common.lucene.store.ByteArrayIndexInput; @@ -55,15 +50,16 @@ import org.opensearch.common.network.InetAddresses; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.CountDown; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.rest.RestStatus; import org.opensearch.repositories.blobstore.AbstractBlobContainerRetriesTestCase; import org.opensearch.repositories.blobstore.OpenSearchMockAPIBasedRepositoryIntegTestCase; -import org.opensearch.rest.RestStatus; import org.opensearch.rest.RestUtils; -import org.junit.BeforeClass; -import org.threeten.bp.Duration; import java.io.IOException; import java.io.InputStream; @@ -79,10 +75,9 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static fixture.gcs.GoogleCloudStorageHttpHandler.getContentRangeEnd; -import static fixture.gcs.GoogleCloudStorageHttpHandler.getContentRangeLimit; -import static fixture.gcs.GoogleCloudStorageHttpHandler.getContentRangeStart; -import static fixture.gcs.GoogleCloudStorageHttpHandler.parseMultipartRequestBody; +import fixture.gcs.FakeOAuth2HttpHandler; +import org.threeten.bp.Duration; + import static java.nio.charset.StandardCharsets.UTF_8; import static org.opensearch.repositories.blobstore.OpenSearchBlobStoreRepositoryIntegTestCase.randomBytes; import static org.opensearch.repositories.gcs.GoogleCloudStorageClientSettings.CREDENTIALS_FILE_SETTING; @@ -97,6 +92,10 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; +import static fixture.gcs.GoogleCloudStorageHttpHandler.getContentRangeEnd; +import static fixture.gcs.GoogleCloudStorageHttpHandler.getContentRangeLimit; +import static fixture.gcs.GoogleCloudStorageHttpHandler.getContentRangeStart; +import static fixture.gcs.GoogleCloudStorageHttpHandler.parseMultipartRequestBody; @SuppressForbidden(reason = "use a http server") public class GoogleCloudStorageBlobContainerRetriesTests extends AbstractBlobContainerRetriesTestCase { @@ -107,21 +106,6 @@ private String httpServerUrl() { return "http://" + InetAddresses.toUriString(address.getAddress()) + ":" + address.getPort(); } - public static void assumeNotJava8() { - assumeFalse( - "This test is flaky on jdk8 - we suspect a JDK bug to trigger some assertion in the HttpServer implementation used " - + "to emulate the server side logic of Google Cloud Storage. See https://bugs.openjdk.java.net/browse/JDK-8180754, " - + "https://github.com/elastic/elasticsearch/pull/51933 and https://github.com/elastic/elasticsearch/issues/52906 " - + "for more background on this issue.", - JavaVersion.current().equals(JavaVersion.parse("8")) - ); - } - - @BeforeClass - public static void skipJava8() { - assumeNotJava8(); - } - @Override protected String downloadStorageEndpoint(String blob) { return "/download/storage/v1/b/bucket/o/" + blob; diff --git a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStoreContainerTests.java b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStoreContainerTests.java index 183793049fb8e..1c4e0e4a5a400 100644 --- a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStoreContainerTests.java +++ b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageBlobStoreContainerTests.java @@ -50,11 +50,11 @@ import static org.hamcrest.Matchers.instanceOf; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageClientSettingsTests.java b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageClientSettingsTests.java index abf63e5525d4d..dc12456c641ae 100644 --- a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageClientSettingsTests.java +++ b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageClientSettingsTests.java @@ -33,7 +33,6 @@ import com.google.api.services.storage.StorageScopes; import com.google.auth.oauth2.ServiceAccountCredentials; - import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Setting; diff --git a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageServiceTests.java b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageServiceTests.java index c5a3a26be082f..a531555debefb 100644 --- a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageServiceTests.java +++ b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/GoogleCloudStorageServiceTests.java @@ -35,14 +35,14 @@ import com.google.auth.Credentials; import com.google.cloud.http.HttpTransportOptions; import com.google.cloud.storage.Storage; -import org.hamcrest.Matchers; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchTestCase; +import org.hamcrest.Matchers; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -50,9 +50,9 @@ import java.util.Locale; import java.util.UUID; +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; public class GoogleCloudStorageServiceTests extends OpenSearchTestCase { diff --git a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/TestUtils.java b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/TestUtils.java index 44e90fb6b9fea..648955c079b3e 100644 --- a/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/TestUtils.java +++ b/plugins/repository-gcs/src/test/java/org/opensearch/repositories/gcs/TestUtils.java @@ -31,8 +31,8 @@ package org.opensearch.repositories.gcs; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.ByteArrayOutputStream; import java.security.KeyPairGenerator; @@ -54,7 +54,7 @@ static byte[] createServiceAccount(final Random random) { final String privateKey = Base64.getEncoder().encodeToString(keyPairGenerator.generateKeyPair().getPrivate().getEncoded()); final ByteArrayOutputStream out = new ByteArrayOutputStream(); - try (XContentBuilder builder = new XContentBuilder(XContentType.JSON.xContent(), out)) { + try (XContentBuilder builder = new XContentBuilder(MediaTypeRegistry.JSON.xContent(), out)) { builder.startObject(); { builder.field("type", "service_account"); diff --git a/plugins/repository-gcs/src/yamlRestTest/resources/rest-api-spec/test/repository_gcs/10_basic.yml b/plugins/repository-gcs/src/yamlRestTest/resources/rest-api-spec/test/repository_gcs/10_basic.yml index 072836280b3bc..265b4e2fecfc7 100644 --- a/plugins/repository-gcs/src/yamlRestTest/resources/rest-api-spec/test/repository_gcs/10_basic.yml +++ b/plugins/repository-gcs/src/yamlRestTest/resources/rest-api-spec/test/repository_gcs/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: repository-gcs } } + - contains: { nodes.$cluster_manager.plugins: { name: repository-gcs } } diff --git a/plugins/repository-hdfs/build.gradle b/plugins/repository-hdfs/build.gradle index dc1f55b686044..a5c5015a89a62 100644 --- a/plugins/repository-hdfs/build.gradle +++ b/plugins/repository-hdfs/build.gradle @@ -48,7 +48,7 @@ opensearchplugin { } versions << [ - 'hadoop3': '3.3.1' + 'hadoop3': '3.3.6' ] testFixtures.useFixture ":test:fixtures:krb5kdc-fixture", "hdfs" @@ -60,30 +60,30 @@ configurations { dependencies { api "org.apache.hadoop:hadoop-client-api:${versions.hadoop3}" runtimeOnly "org.apache.hadoop:hadoop-client-runtime:${versions.hadoop3}" - api "org.apache.hadoop:hadoop-hdfs:${versions.hadoop3}" - api 'org.apache.htrace:htrace-core4:4.1.0-incubating' + api("org.apache.hadoop:hadoop-hdfs:${versions.hadoop3}") { + exclude module: 'jetty-server' + exclude group: 'org.codehaus.jackson' + } + api 'org.apache.htrace:htrace-core4:4.2.0-incubating' api "org.apache.logging.log4j:log4j-core:${versions.log4j}" - api 'org.apache.avro:avro:1.10.2' - api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" - api 'com.google.code.gson:gson:2.9.0' - runtimeOnly 'com.google.guava:guava:30.1.1-jre' - api 'com.google.protobuf:protobuf-java:3.19.3' + api 'org.apache.avro:avro:1.11.2' + api 'com.google.code.gson:gson:2.10.1' + runtimeOnly "com.google.guava:guava:${versions.guava}" api "commons-logging:commons-logging:${versions.commonslogging}" api 'commons-cli:commons-cli:1.2' api "commons-codec:commons-codec:${versions.commonscodec}" api 'commons-collections:commons-collections:3.2.2' - api 'org.apache.commons:commons-compress:1.21' - api 'org.apache.commons:commons-configuration2:2.7' - api 'commons-io:commons-io:2.11.0' - api 'org.apache.commons:commons-lang3:3.12.0' - implementation 'com.google.re2j:re2j:1.1' + api "org.apache.commons:commons-compress:${versions.commonscompress}" + api 'org.apache.commons:commons-configuration2:2.8.0' + api 'commons-io:commons-io:2.12.0' + api 'org.apache.commons:commons-lang3:3.13.0' + implementation 'com.google.re2j:re2j:1.6' api 'javax.servlet:servlet-api:2.5' api "org.slf4j:slf4j-api:${versions.slf4j}" api "org.apache.logging.log4j:log4j-slf4j-impl:${versions.log4j}" - api 'net.minidev:json-smart:2.4.7' - api 'org.apache.zookeeper:zookeeper:3.7.0' + api 'net.minidev:json-smart:2.4.11' api "io.netty:netty-all:${versions.netty}" - implementation 'com.fasterxml.woodstox:woodstox-core:6.2.8' + implementation "com.fasterxml.woodstox:woodstox-core:${versions.woodstox}" implementation 'org.codehaus.woodstox:stax2-api:4.2.1' hdfsFixture project(':test:fixtures:hdfs-fixture') @@ -113,19 +113,6 @@ tasks.named("dependencyLicenses").configure { mapping from: /hadoop-.*/, to: 'hadoop' } -thirdPartyAudit { - ignoreViolations( - // uses internal java api: sun.misc.Unsafe - 'com.google.protobuf.MessageSchema', - 'com.google.protobuf.UnsafeUtil', - 'com.google.protobuf.UnsafeUtil$1', - 'com.google.protobuf.UnsafeUtil$Android32MemoryAccessor', - 'com.google.protobuf.UnsafeUtil$Android64MemoryAccessor', - 'com.google.protobuf.UnsafeUtil$JvmMemoryAccessor', - 'com.google.protobuf.UnsafeUtil$MemoryAccessor' - ) -} - tasks.named("integTest").configure { it.dependsOn(project.tasks.named("bundlePlugin")) } @@ -226,7 +213,8 @@ for (String integTestTaskName : ['integTestHa', 'integTestSecure', 'integTestSec .resolve("ports") } nonInputProperties.systemProperty "test.hdfs-fixture.ports", file("$portsFileDir/ports") - classpath += files(portsFileDir) + // See please https://docs.gradle.org/8.1/userguide/upgrading_version_8.html#test_task_default_classpath + classpath = testing.suites.test.sources.runtimeClasspath + files(portsFileDir) // Copy ports file to separate location which is placed on the test classpath doFirst { mkdir(portsFileDir) @@ -371,7 +359,6 @@ thirdPartyAudit { 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$1', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$2', - 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$3', 'com.google.common.hash.Striped64', 'com.google.common.hash.Striped64$1', 'com.google.common.hash.Striped64$Cell', @@ -438,10 +425,6 @@ thirdPartyAudit { 'org.apache.hadoop.shaded.org.apache.curator.shaded.com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper', 'org.apache.hadoop.shaded.org.apache.curator.shaded.com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper$1', 'org.apache.hadoop.shaded.org.xbill.DNS.spi.DNSJavaNameServiceDescriptor', - 'org.apache.hadoop.shaded.org.xerial.snappy.pure.PureJavaSnappy', - 'org.apache.hadoop.shaded.org.xerial.snappy.pure.SnappyRawCompressor', - 'org.apache.hadoop.shaded.org.xerial.snappy.pure.SnappyRawDecompressor', - 'org.apache.hadoop.shaded.org.xerial.snappy.pure.UnsafeUtil', 'org.apache.avro.reflect.FieldAccessUnsafe', 'org.apache.avro.reflect.FieldAccessUnsafe$UnsafeBooleanField', @@ -457,3 +440,7 @@ thirdPartyAudit { 'org.apache.avro.reflect.FieldAccessUnsafe$UnsafeShortField', ) } + +tasks.withType(JavaForkOptions) { + systemProperty "java.util.concurrent.ForkJoinPool.common.threadFactory", "org.opensearch.secure_sm.SecuredForkJoinWorkerThreadFactory" +} diff --git a/plugins/repository-hdfs/licenses/avro-1.10.2.jar.sha1 b/plugins/repository-hdfs/licenses/avro-1.10.2.jar.sha1 deleted file mode 100644 index eae1c5116ff0f..0000000000000 --- a/plugins/repository-hdfs/licenses/avro-1.10.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a65aaa91c1aeceb3dd4859dbb9765d1c2063f5a2 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/avro-1.11.2.jar.sha1 b/plugins/repository-hdfs/licenses/avro-1.11.2.jar.sha1 new file mode 100644 index 0000000000000..ce1a894e0ce6d --- /dev/null +++ b/plugins/repository-hdfs/licenses/avro-1.11.2.jar.sha1 @@ -0,0 +1 @@ +97e62e8be2b37e849f1bdb5a4f08121d47cc9806 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-codec-1.13.jar.sha1 b/plugins/repository-hdfs/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/repository-hdfs/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-codec-1.15.jar.sha1 b/plugins/repository-hdfs/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/repository-hdfs/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-compress-1.21.jar.sha1 b/plugins/repository-hdfs/licenses/commons-compress-1.21.jar.sha1 deleted file mode 100644 index 81ac609a1aa26..0000000000000 --- a/plugins/repository-hdfs/licenses/commons-compress-1.21.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4ec95b60d4e86b5c95a0e919cb172a0af98011ef \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-compress-1.24.0.jar.sha1 b/plugins/repository-hdfs/licenses/commons-compress-1.24.0.jar.sha1 new file mode 100644 index 0000000000000..23999d1bfbde4 --- /dev/null +++ b/plugins/repository-hdfs/licenses/commons-compress-1.24.0.jar.sha1 @@ -0,0 +1 @@ +b4b1b5a3d9573b2970fddab236102c0a4d27d35e \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-configuration2-2.7.jar.sha1 b/plugins/repository-hdfs/licenses/commons-configuration2-2.7.jar.sha1 deleted file mode 100644 index 31e16840e2c4f..0000000000000 --- a/plugins/repository-hdfs/licenses/commons-configuration2-2.7.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -593326399e5fb5e1f986607f06f63c1250ab36b4 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-configuration2-2.8.0.jar.sha1 b/plugins/repository-hdfs/licenses/commons-configuration2-2.8.0.jar.sha1 new file mode 100644 index 0000000000000..0f782489a571d --- /dev/null +++ b/plugins/repository-hdfs/licenses/commons-configuration2-2.8.0.jar.sha1 @@ -0,0 +1 @@ +6a76acbe14d2c01d4758a57171f3f6a150dbd462 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-io-2.11.0.jar.sha1 b/plugins/repository-hdfs/licenses/commons-io-2.11.0.jar.sha1 deleted file mode 100644 index 8adec30bade49..0000000000000 --- a/plugins/repository-hdfs/licenses/commons-io-2.11.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -a2503f302b11ebde7ebc3df41daebe0e4eea3689 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-io-2.12.0.jar.sha1 b/plugins/repository-hdfs/licenses/commons-io-2.12.0.jar.sha1 new file mode 100644 index 0000000000000..5225b130fb817 --- /dev/null +++ b/plugins/repository-hdfs/licenses/commons-io-2.12.0.jar.sha1 @@ -0,0 +1 @@ +e5e3eb2ff05b494287f51476bc715161412c525f \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-lang3-3.12.0.jar.sha1 b/plugins/repository-hdfs/licenses/commons-lang3-3.12.0.jar.sha1 deleted file mode 100644 index 9273d8c01aaba..0000000000000 --- a/plugins/repository-hdfs/licenses/commons-lang3-3.12.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -c6842c86792ff03b9f1d1fe2aab8dc23aa6c6f0e \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-lang3-3.13.0.jar.sha1 b/plugins/repository-hdfs/licenses/commons-lang3-3.13.0.jar.sha1 new file mode 100644 index 0000000000000..d0c2f2486ee1f --- /dev/null +++ b/plugins/repository-hdfs/licenses/commons-lang3-3.13.0.jar.sha1 @@ -0,0 +1 @@ +b7263237aa89c1f99b327197c41d0669707a462e \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-lang3-NOTICE.txt b/plugins/repository-hdfs/licenses/commons-lang3-NOTICE.txt index 8dfa22157abc3..13a3140897472 100644 --- a/plugins/repository-hdfs/licenses/commons-lang3-NOTICE.txt +++ b/plugins/repository-hdfs/licenses/commons-lang3-NOTICE.txt @@ -1,9 +1,5 @@ Apache Commons Lang -Copyright 2001-2015 The Apache Software Foundation +Copyright 2001-2019 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). - -This product includes software from the Spring Framework, -under the Apache License 2.0 (see: StringUtils.containsWhitespace()) - diff --git a/plugins/repository-hdfs/licenses/commons-logging-1.1.3.jar.sha1 b/plugins/repository-hdfs/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index 5b8f029e58293..0000000000000 --- a/plugins/repository-hdfs/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-logging-1.2.jar.sha1 b/plugins/repository-hdfs/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/repository-hdfs/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/commons-logging-NOTICE.txt b/plugins/repository-hdfs/licenses/commons-logging-NOTICE.txt index 556bd03951d4b..d3d6e140ce4f3 100644 --- a/plugins/repository-hdfs/licenses/commons-logging-NOTICE.txt +++ b/plugins/repository-hdfs/licenses/commons-logging-NOTICE.txt @@ -3,4 +3,3 @@ Copyright 2003-2014 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). - diff --git a/plugins/repository-hdfs/licenses/gson-2.10.1.jar.sha1 b/plugins/repository-hdfs/licenses/gson-2.10.1.jar.sha1 new file mode 100644 index 0000000000000..9810309d1013a --- /dev/null +++ b/plugins/repository-hdfs/licenses/gson-2.10.1.jar.sha1 @@ -0,0 +1 @@ +b3add478d4382b78ea20b1671390a858002feb6c \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/gson-2.9.0.jar.sha1 b/plugins/repository-hdfs/licenses/gson-2.9.0.jar.sha1 deleted file mode 100644 index 8e9626b0c949b..0000000000000 --- a/plugins/repository-hdfs/licenses/gson-2.9.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8a1167e089096758b49f9b34066ef98b2f4b37aa \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/guava-30.1.1-jre.jar.sha1 b/plugins/repository-hdfs/licenses/guava-30.1.1-jre.jar.sha1 deleted file mode 100644 index 39e641fc7834f..0000000000000 --- a/plugins/repository-hdfs/licenses/guava-30.1.1-jre.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -87e0fd1df874ea3cbe577702fe6f17068b790fd8 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/guava-32.1.1-jre.jar.sha1 b/plugins/repository-hdfs/licenses/guava-32.1.1-jre.jar.sha1 new file mode 100644 index 0000000000000..0d791b5d3f55b --- /dev/null +++ b/plugins/repository-hdfs/licenses/guava-32.1.1-jre.jar.sha1 @@ -0,0 +1 @@ +ad575652d84153075dd41ec6177ccb15251262b2 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/hadoop-client-api-3.3.1.jar.sha1 b/plugins/repository-hdfs/licenses/hadoop-client-api-3.3.1.jar.sha1 deleted file mode 100644 index dc2f20e310d30..0000000000000 --- a/plugins/repository-hdfs/licenses/hadoop-client-api-3.3.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4b9c9cdd9967495838fb521001699c4c9dddf183 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/hadoop-client-api-3.3.6.jar.sha1 b/plugins/repository-hdfs/licenses/hadoop-client-api-3.3.6.jar.sha1 new file mode 100644 index 0000000000000..d99793bc56522 --- /dev/null +++ b/plugins/repository-hdfs/licenses/hadoop-client-api-3.3.6.jar.sha1 @@ -0,0 +1 @@ +12ac6f103a0ff29fce17a078c7c64d25320b6165 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/hadoop-client-runtime-3.3.1.jar.sha1 b/plugins/repository-hdfs/licenses/hadoop-client-runtime-3.3.1.jar.sha1 deleted file mode 100644 index feb37ecc90255..0000000000000 --- a/plugins/repository-hdfs/licenses/hadoop-client-runtime-3.3.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f3a55d882328ee87a1054f99d62ba987fa9029a4 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/hadoop-client-runtime-3.3.6.jar.sha1 b/plugins/repository-hdfs/licenses/hadoop-client-runtime-3.3.6.jar.sha1 new file mode 100644 index 0000000000000..ea22d763b7bfa --- /dev/null +++ b/plugins/repository-hdfs/licenses/hadoop-client-runtime-3.3.6.jar.sha1 @@ -0,0 +1 @@ +81065531e63fccbe85fb04a3274709593fb00d3c \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/hadoop-hdfs-3.3.1.jar.sha1 b/plugins/repository-hdfs/licenses/hadoop-hdfs-3.3.1.jar.sha1 deleted file mode 100644 index 66c98cf7ec291..0000000000000 --- a/plugins/repository-hdfs/licenses/hadoop-hdfs-3.3.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -5da7f270cb6564e099e0d2d424285a24fca62bd2 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/hadoop-hdfs-3.3.6.jar.sha1 b/plugins/repository-hdfs/licenses/hadoop-hdfs-3.3.6.jar.sha1 new file mode 100644 index 0000000000000..fe60968056eb7 --- /dev/null +++ b/plugins/repository-hdfs/licenses/hadoop-hdfs-3.3.6.jar.sha1 @@ -0,0 +1 @@ +ba40aca60f39599d5b1f1d32b35295bfde1f3c8b \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/htrace-core4-4.1.0-incubating.jar.sha1 b/plugins/repository-hdfs/licenses/htrace-core4-4.1.0-incubating.jar.sha1 deleted file mode 100644 index 806c624c02cf0..0000000000000 --- a/plugins/repository-hdfs/licenses/htrace-core4-4.1.0-incubating.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -12b3e2adda95e8c41d9d45d33db075137871d2e2 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/htrace-core4-4.2.0-incubating.jar.sha1 b/plugins/repository-hdfs/licenses/htrace-core4-4.2.0-incubating.jar.sha1 new file mode 100644 index 0000000000000..e2eafb09dba00 --- /dev/null +++ b/plugins/repository-hdfs/licenses/htrace-core4-4.2.0-incubating.jar.sha1 @@ -0,0 +1 @@ +94b3f1966922bc45d0f8a86a2aa867a4b0df288b \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/jackson-databind-2.13.2.jar.sha1 b/plugins/repository-hdfs/licenses/jackson-databind-2.13.2.jar.sha1 deleted file mode 100644 index 5d356f3fd045f..0000000000000 --- a/plugins/repository-hdfs/licenses/jackson-databind-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -926e48c451166a291f1ce6c6276d9abbefa7c00f \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/json-smart-2.4.11.jar.sha1 b/plugins/repository-hdfs/licenses/json-smart-2.4.11.jar.sha1 new file mode 100644 index 0000000000000..04627ab2baace --- /dev/null +++ b/plugins/repository-hdfs/licenses/json-smart-2.4.11.jar.sha1 @@ -0,0 +1 @@ +cc5888f14a5768f254b97bafe8b9fd29b31e872e \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/json-smart-2.4.7.jar.sha1 b/plugins/repository-hdfs/licenses/json-smart-2.4.7.jar.sha1 deleted file mode 100644 index 16f9a4431485a..0000000000000 --- a/plugins/repository-hdfs/licenses/json-smart-2.4.7.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8d7f4c1530c07c54930935f3da85f48b83b3c109 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/log4j-slf4j-impl-2.17.1.jar.sha1 b/plugins/repository-hdfs/licenses/log4j-slf4j-impl-2.17.1.jar.sha1 deleted file mode 100644 index 894ed8d886c3f..0000000000000 --- a/plugins/repository-hdfs/licenses/log4j-slf4j-impl-2.17.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -84692d456bcce689355d33d68167875e486954dd \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/log4j-slf4j-impl-2.20.0.jar.sha1 b/plugins/repository-hdfs/licenses/log4j-slf4j-impl-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..800a4aa87ba0e --- /dev/null +++ b/plugins/repository-hdfs/licenses/log4j-slf4j-impl-2.20.0.jar.sha1 @@ -0,0 +1 @@ +7ab4f082fd162f60afcaf2b8744a3d959feab3e8 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/netty-all-4.1.73.Final.jar.sha1 b/plugins/repository-hdfs/licenses/netty-all-4.1.73.Final.jar.sha1 deleted file mode 100644 index 52d6f22e73013..0000000000000 --- a/plugins/repository-hdfs/licenses/netty-all-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -75c5a0ddb28adcc9e4991c75678d4a85dfe4a0b3 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/netty-all-4.1.99.Final.jar.sha1 b/plugins/repository-hdfs/licenses/netty-all-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..0756635018837 --- /dev/null +++ b/plugins/repository-hdfs/licenses/netty-all-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +a45aa70bc50d0500da5cdcd595cc838d87ada987 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/protobuf-java-3.19.3.jar.sha1 b/plugins/repository-hdfs/licenses/protobuf-java-3.19.3.jar.sha1 deleted file mode 100644 index 655ecd1f1c1c9..0000000000000 --- a/plugins/repository-hdfs/licenses/protobuf-java-3.19.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4b57f1b1b9e281231c3fcfc039ce3021e29ff570 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/protobuf-java-LICENSE.txt b/plugins/repository-hdfs/licenses/protobuf-java-LICENSE.txt deleted file mode 100644 index 49e7019ac5a1b..0000000000000 --- a/plugins/repository-hdfs/licenses/protobuf-java-LICENSE.txt +++ /dev/null @@ -1,10 +0,0 @@ -Copyright (c) , -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/repository-hdfs/licenses/protobuf-java-NOTICE.txt b/plugins/repository-hdfs/licenses/protobuf-java-NOTICE.txt deleted file mode 100644 index 139597f9cb07c..0000000000000 --- a/plugins/repository-hdfs/licenses/protobuf-java-NOTICE.txt +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/plugins/repository-hdfs/licenses/re2j-1.1.jar.sha1 b/plugins/repository-hdfs/licenses/re2j-1.1.jar.sha1 deleted file mode 100644 index d8a100dafd8c4..0000000000000 --- a/plugins/repository-hdfs/licenses/re2j-1.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d716952ab58aa4369ea15126505a36544d50a333 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/re2j-1.6.jar.sha1 b/plugins/repository-hdfs/licenses/re2j-1.6.jar.sha1 new file mode 100644 index 0000000000000..854bd3a225b92 --- /dev/null +++ b/plugins/repository-hdfs/licenses/re2j-1.6.jar.sha1 @@ -0,0 +1 @@ +a13e879fd7971738d06020fefeb108cc14e14169 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/slf4j-api-1.6.2.jar.sha1 b/plugins/repository-hdfs/licenses/slf4j-api-1.6.2.jar.sha1 deleted file mode 100644 index a2f93ea55802b..0000000000000 --- a/plugins/repository-hdfs/licenses/slf4j-api-1.6.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -8619e95939167fb37245b5670135e4feb0ec7d50 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/repository-hdfs/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/repository-hdfs/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/woodstox-core-6.2.8.jar.sha1 b/plugins/repository-hdfs/licenses/woodstox-core-6.2.8.jar.sha1 deleted file mode 100644 index ae65cdebf26de..0000000000000 --- a/plugins/repository-hdfs/licenses/woodstox-core-6.2.8.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -670748292899c53b1963730d9eb7f8ab71314e90 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/woodstox-core-6.4.0.jar.sha1 b/plugins/repository-hdfs/licenses/woodstox-core-6.4.0.jar.sha1 new file mode 100644 index 0000000000000..cac5f37205956 --- /dev/null +++ b/plugins/repository-hdfs/licenses/woodstox-core-6.4.0.jar.sha1 @@ -0,0 +1 @@ +c47579857bbf12c85499f431d4ecf27d77976b7c \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/zookeeper-3.7.0.jar.sha1 b/plugins/repository-hdfs/licenses/zookeeper-3.7.0.jar.sha1 deleted file mode 100644 index 88a6106a68710..0000000000000 --- a/plugins/repository-hdfs/licenses/zookeeper-3.7.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1360048c7ca057df627b7267ff7360870e987ab0 \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/zookeeper-LICENSE.txt b/plugins/repository-hdfs/licenses/zookeeper-LICENSE.txt deleted file mode 100644 index 7a4a3ea2424c0..0000000000000 --- a/plugins/repository-hdfs/licenses/zookeeper-LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/plugins/repository-hdfs/licenses/zookeeper-NOTICE.txt b/plugins/repository-hdfs/licenses/zookeeper-NOTICE.txt deleted file mode 100644 index b853f3e85f32f..0000000000000 --- a/plugins/repository-hdfs/licenses/zookeeper-NOTICE.txt +++ /dev/null @@ -1,11 +0,0 @@ -Apache ZooKeeper -Copyright 2009-2021 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). - -This product includes software components originally -developed for Airlift (https://github.com/airlift/airlift), -licensed under the Apache 2.0 license. The licensing terms -for Airlift code can be found at: -https://github.com/airlift/airlift/blob/master/LICENSE \ No newline at end of file diff --git a/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsBlobContainer.java b/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsBlobContainer.java index dcbd52d311230..669190f4e2490 100644 --- a/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsBlobContainer.java +++ b/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsBlobContainer.java @@ -32,6 +32,7 @@ package org.opensearch.repositories.hdfs; import org.apache.hadoop.fs.CreateFlag; +import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileContext; import org.apache.hadoop.fs.FileStatus; @@ -46,6 +47,7 @@ import org.opensearch.common.blobstore.fs.FsBlobContainer; import org.opensearch.common.blobstore.support.AbstractBlobContainer; import org.opensearch.common.blobstore.support.PlainBlobMetadata; +import org.opensearch.common.io.Streams; import org.opensearch.repositories.hdfs.HdfsBlobStore.Operation; import java.io.FileNotFoundException; @@ -125,8 +127,23 @@ public InputStream readBlob(String blobName) throws IOException { } @Override - public InputStream readBlob(String blobName, long position, long length) { - throw new UnsupportedOperationException(); + public InputStream readBlob(String blobName, long position, long length) throws IOException { + return store.execute(fileContext -> { + final FSDataInputStream stream; + try { + stream = fileContext.open(new Path(path, blobName), bufferSize); + } catch (FileNotFoundException fnfe) { + throw new NoSuchFileException("[" + blobName + "] blob not found"); + } + // Seek to the desired start position, closing the stream if any error occurs + try { + stream.seek(position); + } catch (Exception e) { + stream.close(); + throw e; + } + return Streams.limitStream(new HDFSPrivilegedInputSteam(stream, securityContext), length); + }); } @Override diff --git a/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsPlugin.java b/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsPlugin.java index 4b715a509a195..119d060374be2 100644 --- a/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsPlugin.java +++ b/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsPlugin.java @@ -31,27 +31,27 @@ package org.opensearch.repositories.hdfs; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Collections; -import java.util.Map; - import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB; import org.apache.hadoop.security.KerberosInfo; import org.apache.hadoop.security.SecurityUtil; import org.opensearch.SpecialPermission; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.RepositoryPlugin; import org.opensearch.repositories.Repository; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Map; + public final class HdfsPlugin extends Plugin implements RepositoryPlugin { // initialize some problematic classes with elevated privileges diff --git a/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsRepository.java b/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsRepository.java index 3be6fba5322db..f0ffec5713c1d 100644 --- a/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsRepository.java +++ b/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsRepository.java @@ -45,12 +45,12 @@ import org.opensearch.SpecialPermission; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.repositories.blobstore.BlobStoreRepository; @@ -83,7 +83,7 @@ public HdfsRepository( final ClusterService clusterService, final RecoverySettings recoverySettings ) { - super(metadata, metadata.settings().getAsBoolean("compress", false), namedXContentRegistry, clusterService, recoverySettings); + super(metadata, namedXContentRegistry, clusterService, recoverySettings); this.environment = environment; this.chunkSize = metadata.settings().getAsBytesSize("chunk_size", null); diff --git a/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsSecurityContext.java b/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsSecurityContext.java index 9078e2b76cc6d..2e62ae48d1a06 100644 --- a/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsSecurityContext.java +++ b/plugins/repository-hdfs/src/main/java/org/opensearch/repositories/hdfs/HdfsSecurityContext.java @@ -31,6 +31,14 @@ package org.opensearch.repositories.hdfs; +import org.apache.hadoop.security.UserGroupInformation; +import org.opensearch.SpecialPermission; +import org.opensearch.env.Environment; + +import javax.security.auth.AuthPermission; +import javax.security.auth.PrivateCredentialPermission; +import javax.security.auth.kerberos.ServicePermission; + import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.ReflectPermission; @@ -42,13 +50,6 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Arrays; -import javax.security.auth.AuthPermission; -import javax.security.auth.PrivateCredentialPermission; -import javax.security.auth.kerberos.ServicePermission; - -import org.apache.hadoop.security.UserGroupInformation; -import org.opensearch.SpecialPermission; -import org.opensearch.env.Environment; /** * Oversees all the security specific logic for the HDFS Repository plugin. diff --git a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HaHdfsFailoverTestSuiteIT.java b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HaHdfsFailoverTestSuiteIT.java index e12b24f27dcdc..d0b63f17e3887 100644 --- a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HaHdfsFailoverTestSuiteIT.java +++ b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HaHdfsFailoverTestSuiteIT.java @@ -75,9 +75,9 @@ public void testHAFailoverWithRepository() throws Exception { String nn2Port = "10002"; if (ports.length() > 0) { final Path path = PathUtils.get(ports); - final List lines = AccessController.doPrivileged( - (PrivilegedExceptionAction>) () -> { return Files.readAllLines(path); } - ); + final List lines = AccessController.doPrivileged((PrivilegedExceptionAction>) () -> { + return Files.readAllLines(path); + }); nn1Port = lines.get(0); nn2Port = lines.get(1); } diff --git a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsBlobStoreContainerTests.java b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsBlobStoreContainerTests.java index 66677b0327191..3a6eb0e205ccb 100644 --- a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsBlobStoreContainerTests.java +++ b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsBlobStoreContainerTests.java @@ -33,6 +33,7 @@ package org.opensearch.repositories.hdfs; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; + import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.AbstractFileSystem; import org.apache.hadoop.fs.FileContext; @@ -41,7 +42,7 @@ import org.opensearch.common.SuppressForbidden; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; -import org.opensearch.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesArray; import org.opensearch.test.OpenSearchTestCase; import javax.security.auth.Subject; diff --git a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsBlobStoreRepositoryTests.java b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsBlobStoreRepositoryTests.java index 9196a8f2b0558..6ff18b20036a8 100644 --- a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsBlobStoreRepositoryTests.java +++ b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsBlobStoreRepositoryTests.java @@ -32,6 +32,7 @@ package org.opensearch.repositories.hdfs; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; + import org.opensearch.common.settings.Settings; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.blobstore.OpenSearchBlobStoreRepositoryIntegTestCase; @@ -65,4 +66,8 @@ protected Settings repositorySettings() { protected Collection> nodePlugins() { return Collections.singletonList(HdfsPlugin.class); } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9513") + @Override + public void testReadRange() {} } diff --git a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsClientThreadLeakFilter.java b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsClientThreadLeakFilter.java index b9b0e9e87dd0c..2758bd020e979 100644 --- a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsClientThreadLeakFilter.java +++ b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsClientThreadLeakFilter.java @@ -43,6 +43,9 @@ * to ignore the offending thread until a version of Hadoop is released that addresses the incorrect * interrupt handling. * + * In Hadoop 3.3.6, the org.apache.hadoop.fs.statistics.impl.EvaluatingStatisticsMap uses ForkJoinPool + * to perform statistics calculation, leaving dangling workers. + * * @see https://issues.apache.org/jira/browse/HADOOP-12829 * @see "org.apache.hadoop.fs.FileSystem.Statistics.StatisticsDataReferenceCleaner" * @see "org.apache.hadoop.fs.FileSystem.Statistics" @@ -53,6 +56,6 @@ public final class HdfsClientThreadLeakFilter implements ThreadFilter { @Override public boolean reject(Thread t) { - return t.getName().equals(OFFENDING_THREAD_NAME); + return t.getName().equals(OFFENDING_THREAD_NAME) || t.getName().startsWith("ForkJoinPool.commonPool-"); } } diff --git a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsRepositoryTests.java b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsRepositoryTests.java index 4e12de7cce212..ab10691240649 100644 --- a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsRepositoryTests.java +++ b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsRepositoryTests.java @@ -32,8 +32,8 @@ package org.opensearch.repositories.hdfs; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; -import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryResponse; +import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryResponse; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.SecureSettings; diff --git a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsTests.java b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsTests.java index 46d97f41b604f..ce456f26af3a4 100644 --- a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsTests.java +++ b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/HdfsTests.java @@ -32,11 +32,10 @@ package org.opensearch.repositories.hdfs; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; + import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; - import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.client.Client; import org.opensearch.cluster.ClusterState; import org.opensearch.common.settings.Settings; @@ -63,7 +62,6 @@ protected Collection> getPlugins() { } public void testSimpleWorkflow() { - assumeFalse("https://github.com/elastic/elasticsearch/issues/31498", JavaVersion.current().equals(JavaVersion.parse("11"))); Client client = client(); AcknowledgedResponse putRepositoryResponse = client.admin() diff --git a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/TestingFs.java b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/TestingFs.java index 8fca4f9afd771..faa8d1d891c1f 100644 --- a/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/TestingFs.java +++ b/plugins/repository-hdfs/src/test/java/org/opensearch/repositories/hdfs/TestingFs.java @@ -43,11 +43,11 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.spi.FileSystemProvider; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; /** * Extends LFS to improve some operations to keep the security permissions at diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/10_basic.yml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/10_basic.yml index bc419d75ba773..eedcaf099ccef 100644 --- a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/10_basic.yml +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/10_basic.yml @@ -9,13 +9,13 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: repository-hdfs } } + - contains: { nodes.$cluster_manager.plugins: { name: repository-hdfs } } --- # # Check that we can't use file:// repositories or anything like that diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot.yml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot.yml index 20019686d3db1..7e76024dd2368 100644 --- a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot.yml +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot.yml @@ -36,7 +36,7 @@ - match: { snapshot.shards.failed : 0 } # Remove our snapshot - - do: + - do: snapshot.delete: repository: test_snapshot_repository snapshot: test_snapshot @@ -45,4 +45,3 @@ - do: snapshot.delete_repository: repository: test_snapshot_repository - diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot_get.yml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot_get.yml index f38f4783b195b..3e61ef62f2a7d 100644 --- a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot_get.yml +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/hdfs_repository/30_snapshot_get.yml @@ -59,7 +59,7 @@ - match: { snapshots.0.snapshot : test_snapshot_get } # Remove our snapshot - - do: + - do: snapshot.delete: repository: test_snapshot_get_repository snapshot: test_snapshot_get diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yml index bc419d75ba773..eedcaf099ccef 100644 --- a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yml +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/10_basic.yml @@ -9,13 +9,13 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: repository-hdfs } } + - contains: { nodes.$cluster_manager.plugins: { name: repository-hdfs } } --- # # Check that we can't use file:// repositories or anything like that diff --git a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yml b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yml index 44f29fe0341a6..821110dc52bed 100644 --- a/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yml +++ b/plugins/repository-hdfs/src/test/resources/rest-api-spec/test/secure_hdfs_repository/30_snapshot.yml @@ -47,4 +47,3 @@ - do: snapshot.delete_repository: repository: test_snapshot_repository - diff --git a/plugins/repository-s3/README.md b/plugins/repository-s3/README.md new file mode 100644 index 0000000000000..03007e03b633e --- /dev/null +++ b/plugins/repository-s3/README.md @@ -0,0 +1,25 @@ +# repository-s3 + +The repository-s3 plugin enables the use of S3 as a place to store snapshots. + +## Testing + +### Unit Tests + +``` +./gradlew :plugins:repository-s3:test +``` + +### Integration Tests + +Integration tests require several environment variables. + +- `amazon_s3_bucket`: Name of the S3 bucket to use. +- `amazon_s3_access_key`: The access key ID (`AWS_ACCESS_KEY_ID`) with r/w access to the S3 bucket. +- `amazon_s3_secret_key`: The secret access key (`AWS_SECRET_ACCESS_KEY`). +- `amazon_s3_base_path`: A relative path inside the S3 bucket, e.g. `opensearch`. +- `AWS_REGION`: The region in which the S3 bucket was created. While S3 buckets are global, credentials must scoped to a specific region and cross-region access is not allowed. (TODO: rename this to `amazon_s3_region` in https://github.com/opensearch-project/opensearch-build/issues/3615 and https://github.com/opensearch-project/OpenSearch/pull/7974.) + +``` +AWS_REGION=us-west-2 amazon_s3_access_key=$AWS_ACCESS_KEY_ID amazon_s3_secret_key=$AWS_SECRET_ACCESS_KEY amazon_s3_base_path=path amazon_s3_bucket=dblock-opensearch ./gradlew :plugins:repository-s3:s3ThirdPartyTest +``` diff --git a/plugins/repository-s3/build.gradle b/plugins/repository-s3/build.gradle index c5939958c816a..44fd45b265e82 100644 --- a/plugins/repository-s3/build.gradle +++ b/plugins/repository-s3/build.gradle @@ -31,6 +31,7 @@ import org.opensearch.gradle.MavenFilteringHack import org.opensearch.gradle.info.BuildParams import org.opensearch.gradle.test.RestIntegTestTask +import org.opensearch.gradle.test.TestTask import org.opensearch.gradle.test.rest.YamlRestTestPlugin import org.opensearch.gradle.test.InternalClusterTestPlugin @@ -44,24 +45,55 @@ opensearchplugin { classname 'org.opensearch.repositories.s3.S3RepositoryPlugin' } -versions << [ - 'aws': '1.11.749' -] - dependencies { - api "com.amazonaws:aws-java-sdk-s3:${versions.aws}" - api "com.amazonaws:aws-java-sdk-core:${versions.aws}" - api "com.amazonaws:jmespath-java:${versions.aws}" + // aws sdk v2 stack + api "software.amazon.awssdk:sdk-core:${versions.aws}" + api "software.amazon.awssdk:annotations:${versions.aws}" + api "software.amazon.awssdk:aws-core:${versions.aws}" + api "software.amazon.awssdk:auth:${versions.aws}" + api "software.amazon.awssdk:endpoints-spi:${versions.aws}" + api "software.amazon.awssdk:http-client-spi:${versions.aws}" + api "software.amazon.awssdk:apache-client:${versions.aws}" + api "software.amazon.awssdk:metrics-spi:${versions.aws}" + api "software.amazon.awssdk:profiles:${versions.aws}" + api "software.amazon.awssdk:regions:${versions.aws}" + api "software.amazon.awssdk:utils:${versions.aws}" + api "software.amazon.awssdk:aws-json-protocol:${versions.aws}" + api "software.amazon.awssdk:protocol-core:${versions.aws}" + api "software.amazon.awssdk:json-utils:${versions.aws}" + api "software.amazon.awssdk:third-party-jackson-core:${versions.aws}" + api "software.amazon.awssdk:s3:${versions.aws}" + api "software.amazon.awssdk:signer:${versions.aws}" + api "software.amazon.awssdk:aws-xml-protocol:${versions.aws}" + api "software.amazon.awssdk:aws-json-protocol:${versions.aws}" + api "software.amazon.awssdk:aws-query-protocol:${versions.aws}" + api "software.amazon.awssdk:sts:${versions.aws}" + api "software.amazon.awssdk:netty-nio-client:${versions.aws}" + + api "org.reactivestreams:reactive-streams:${versions.reactivestreams}" api "org.apache.httpcomponents:httpclient:${versions.httpclient}" api "org.apache.httpcomponents:httpcore:${versions.httpcore}" api "commons-logging:commons-logging:${versions.commonslogging}" api "org.apache.logging.log4j:log4j-1.2-api:${versions.log4j}" api "commons-codec:commons-codec:${versions.commonscodec}" api "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" - api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}" api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" api "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:${versions.jackson}" api "joda-time:joda-time:${versions.joda}" + api "org.slf4j:slf4j-api:${versions.slf4j}" + + // network stack + api "io.netty:netty-buffer:${versions.netty}" + api "io.netty:netty-codec:${versions.netty}" + api "io.netty:netty-codec-http:${versions.netty}" + api "io.netty:netty-codec-http2:${versions.netty}" + api "io.netty:netty-common:${versions.netty}" + api "io.netty:netty-handler:${versions.netty}" + api "io.netty:netty-resolver:${versions.netty}" + api "io.netty:netty-transport:${versions.netty}" + api "io.netty:netty-transport-native-unix-common:${versions.netty}" + api "io.netty:netty-transport-classes-epoll:${versions.netty}" // HACK: javax.xml.bind was removed from default modules in java 9, so we pull the api in here, // and allowlist this hack in JarHell @@ -77,10 +109,9 @@ restResources { } tasks.named("dependencyLicenses").configure { - mapping from: /aws-java-sdk-.*/, to: 'aws-java-sdk' - mapping from: /jmespath-java.*/, to: 'aws-java-sdk' mapping from: /jackson-.*/, to: 'jackson' mapping from: /jaxb-.*/, to: 'jaxb' + mapping from: /netty-.*/, to: 'netty' } bundlePlugin { @@ -89,7 +120,7 @@ bundlePlugin { } } -task testRepositoryCreds(type: Test) { +task testRepositoryCreds(type: TestTask) { include '**/RepositoryCredentialsTests.class' systemProperty 'opensearch.allow_insecure_settings', 'true' } @@ -117,12 +148,14 @@ String s3PermanentAccessKey = System.getenv("amazon_s3_access_key") String s3PermanentSecretKey = System.getenv("amazon_s3_secret_key") String s3PermanentBucket = System.getenv("amazon_s3_bucket") String s3PermanentBasePath = System.getenv("amazon_s3_base_path") +String s3PermanentRegion = System.getenv("amazon_s3_region") String s3TemporaryAccessKey = System.getenv("amazon_s3_access_key_temporary") String s3TemporarySecretKey = System.getenv("amazon_s3_secret_key_temporary") String s3TemporarySessionToken = System.getenv("amazon_s3_session_token_temporary") String s3TemporaryBucket = System.getenv("amazon_s3_bucket_temporary") String s3TemporaryBasePath = System.getenv("amazon_s3_base_path_temporary") +String s3TemporaryRegion = System.getenv("amazon_s3_region_temporary") String s3EC2Bucket = System.getenv("amazon_s3_bucket_ec2") String s3EC2BasePath = System.getenv("amazon_s3_base_path_ec2") @@ -130,55 +163,76 @@ String s3EC2BasePath = System.getenv("amazon_s3_base_path_ec2") String s3ECSBucket = System.getenv("amazon_s3_bucket_ecs") String s3ECSBasePath = System.getenv("amazon_s3_base_path_ecs") +String s3EKSBucket = System.getenv("amazon_s3_bucket_eks") +String s3EKSBasePath = System.getenv("amazon_s3_base_path_eks") + boolean s3DisableChunkedEncoding = (new Random(Long.parseUnsignedLong(BuildParams.testSeed.tokenize(':').get(0), 16))).nextBoolean() +// TODO: remove after https://github.com/opensearch-project/opensearch-build/issues/3615 +if (s3PermanentBucket && !s3PermanentRegion) { + s3PermanentRegion = "us-west-2" +} + +if (s3TemporaryBucket && !s3TemporaryRegion) { + s3TemporaryRegion = "us-west-2" +} +// ---- + // If all these variables are missing then we are testing against the internal fixture instead, which has the following // credentials hard-coded in. -if (!s3PermanentAccessKey && !s3PermanentSecretKey && !s3PermanentBucket && !s3PermanentBasePath) { +if (!s3PermanentAccessKey && !s3PermanentSecretKey && !s3PermanentBucket && !s3PermanentBasePath && !s3PermanentRegion) { s3PermanentAccessKey = 'access_key' s3PermanentSecretKey = 'secret_key' s3PermanentBucket = 'bucket' s3PermanentBasePath = 'base_path' + s3PermanentRegion = 'region' apply plugin: 'opensearch.test.fixtures' useFixture = true -} else if (!s3PermanentAccessKey || !s3PermanentSecretKey || !s3PermanentBucket || !s3PermanentBasePath) { +} else if (!s3PermanentAccessKey || !s3PermanentSecretKey || !s3PermanentBucket || !s3PermanentBasePath || !s3PermanentRegion) { throw new IllegalArgumentException("not all options specified to run against external S3 service as permanent credentials are present") } -if (!s3TemporaryAccessKey && !s3TemporarySecretKey && !s3TemporaryBucket && !s3TemporaryBasePath && !s3TemporarySessionToken) { +if (!s3TemporaryAccessKey && !s3TemporarySecretKey && !s3TemporaryBucket && !s3TemporaryBasePath && !s3TemporarySessionToken && !s3TemporaryRegion) { s3TemporaryAccessKey = 'session_token_access_key' s3TemporarySecretKey = 'session_token_secret_key' s3TemporaryBucket = 'session_token_bucket' s3TemporaryBasePath = 'session_token_base_path' s3TemporarySessionToken = 'session_token' + s3TemporaryRegion = 'session_token_region' } else if (!s3TemporaryAccessKey || !s3TemporarySecretKey || !s3TemporaryBucket || !s3TemporaryBasePath || !s3TemporarySessionToken) { throw new IllegalArgumentException("not all options specified to run against external S3 service as temporary credentials are present") } -if (!s3EC2Bucket && !s3EC2BasePath && !s3ECSBucket && !s3ECSBasePath) { +if (!s3EC2Bucket && !s3EC2BasePath && !s3ECSBucket && !s3ECSBasePath && !s3EKSBucket && !s3EKSBasePath) { s3EC2Bucket = 'ec2_bucket' s3EC2BasePath = 'ec2_base_path' s3ECSBucket = 'ecs_bucket' s3ECSBasePath = 'ecs_base_path' -} else if (!s3EC2Bucket || !s3EC2BasePath || !s3ECSBucket || !s3ECSBasePath) { - throw new IllegalArgumentException("not all options specified to run EC2/ECS tests are present") + s3EKSBucket = 'eks_bucket' + s3EKSBasePath = 'eks_base_path' +} else if (!s3EC2Bucket || !s3EC2BasePath || !s3ECSBucket || !s3ECSBasePath || !s3EKSBucket || !s3EKSBasePath) { + throw new IllegalArgumentException("not all options specified to run EC2/ECS/EKS tests are present") } processYamlRestTestResources { Map expansions = [ 'permanent_bucket': s3PermanentBucket, - 'permanent_base_path': s3PermanentBasePath + "_integration_tests", + 'permanent_base_path': s3PermanentBasePath + "_integration_tests_" + BuildParams.testSeed, + 'permanent_region': s3PermanentRegion, 'temporary_bucket': s3TemporaryBucket, - 'temporary_base_path': s3TemporaryBasePath + "_integration_tests", + 'temporary_base_path': s3TemporaryBasePath + "_integration_tests_" + BuildParams.testSeed, + 'temporary_region': s3TemporaryRegion, 'ec2_bucket': s3EC2Bucket, 'ec2_base_path': s3EC2BasePath, 'ecs_bucket': s3ECSBucket, 'ecs_base_path': s3ECSBasePath, - 'disable_chunked_encoding': s3DisableChunkedEncoding, + 'eks_bucket': s3EKSBucket, + 'eks_base_path': s3EKSBasePath, + 'disable_chunked_encoding': s3DisableChunkedEncoding ] inputs.properties(expansions) MavenFilteringHack.filter(it, expansions) @@ -190,14 +244,15 @@ internalClusterTest { } yamlRestTest { - systemProperty 'tests.rest.blacklist', ( + systemProperty 'tests.rest.denylist', ( useFixture ? ['repository_s3/50_repository_ecs_credentials/*'] : [ 'repository_s3/30_repository_temporary_credentials/*', 'repository_s3/40_repository_ec2_credentials/*', - 'repository_s3/50_repository_ecs_credentials/*' + 'repository_s3/50_repository_ecs_credentials/*', + 'repository_s3/60_repository_eks_credentials/*' ] ).join(",") } @@ -214,6 +269,7 @@ testClusters.yamlRestTest { testFixtures.useFixture(':test:fixtures:s3-fixture', 's3-fixture') testFixtures.useFixture(':test:fixtures:s3-fixture', 's3-fixture-with-session-token') testFixtures.useFixture(':test:fixtures:s3-fixture', 's3-fixture-with-ec2') + testFixtures.useFixture(':test:fixtures:s3-fixture', 's3-fixture-with-eks') normalization { runtimeClasspath { @@ -222,12 +278,21 @@ testClusters.yamlRestTest { } } + keystore 's3.client.integration_test_eks.role_arn', "arn:aws:iam::000000000000:role/test" + keystore 's3.client.integration_test_eks.role_session_name', "s3-test" + keystore 's3.client.integration_test_eks.access_key', "access_key" + keystore 's3.client.integration_test_eks.secret_key', "secret_key" + setting 's3.client.integration_test_permanent.endpoint', { "${-> fixtureAddress('s3-fixture', 's3-fixture', '80')}" }, IGNORE_VALUE setting 's3.client.integration_test_temporary.endpoint', { "${-> fixtureAddress('s3-fixture', 's3-fixture-with-session-token', '80')}" }, IGNORE_VALUE setting 's3.client.integration_test_ec2.endpoint', { "${-> fixtureAddress('s3-fixture', 's3-fixture-with-ec2', '80')}" }, IGNORE_VALUE + setting 's3.client.integration_test_eks.endpoint', { "${-> fixtureAddress('s3-fixture', 's3-fixture-with-eks', '80')}" }, IGNORE_VALUE + setting 's3.client.integration_test_eks.region', { "us-east-2" }, IGNORE_VALUE // to redirect InstanceProfileCredentialsProvider to custom auth point - systemProperty "com.amazonaws.sdk.ec2MetadataServiceEndpointOverride", { "${-> fixtureAddress('s3-fixture', 's3-fixture-with-ec2', '80')}" }, IGNORE_VALUE + systemProperty "aws.ec2MetadataServiceEndpointOverride", { "${-> fixtureAddress('s3-fixture', 's3-fixture-with-ec2', '80')}" }, IGNORE_VALUE + // to redirect AWSSecurityTokenServiceClient to custom auth point + systemProperty "aws.stsEndpointOverride", { "${-> fixtureAddress('s3-fixture', 's3-fixture-with-eks', '80')}/eks_credentials_endpoint" }, IGNORE_VALUE } else { println "Using an external service to test the repository-s3 plugin" } @@ -246,10 +311,11 @@ if (useFixture) { setClasspath(yamlRestTestSourceSet.getRuntimeClasspath()) // Minio only supports a single access key, see https://github.com/minio/minio/pull/5968 - systemProperty 'tests.rest.blacklist', [ + systemProperty 'tests.rest.denylist', [ 'repository_s3/30_repository_temporary_credentials/*', 'repository_s3/40_repository_ec2_credentials/*', - 'repository_s3/50_repository_ecs_credentials/*' + 'repository_s3/50_repository_ecs_credentials/*', + 'repository_s3/60_repository_eks_credentials/*' ].join(",") } check.dependsOn(yamlRestTestMinio) @@ -272,11 +338,12 @@ if (useFixture) { SourceSet yamlRestTestSourceSet = sourceSets.getByName(YamlRestTestPlugin.SOURCE_SET_NAME) setTestClassesDirs(yamlRestTestSourceSet.getOutput().getClassesDirs()) setClasspath(yamlRestTestSourceSet.getRuntimeClasspath()) - systemProperty 'tests.rest.blacklist', [ + systemProperty 'tests.rest.denylist', [ 'repository_s3/10_basic/*', 'repository_s3/20_repository_permanent_credentials/*', 'repository_s3/30_repository_temporary_credentials/*', - 'repository_s3/40_repository_ec2_credentials/*' + 'repository_s3/40_repository_ec2_credentials/*', + 'repository_s3/60_repository_eks_credentials/*' ].join(",") } check.dependsOn(yamlRestTestECS) @@ -288,6 +355,41 @@ if (useFixture) { } } +// EKS +if (useFixture) { + testFixtures.useFixture(':test:fixtures:s3-fixture', 's3-fixture-with-eks') + task yamlRestTestEKS(type: RestIntegTestTask.class) { + description = "Runs tests using the EKS repository." + dependsOn('bundlePlugin') + SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + SourceSet yamlRestTestSourceSet = sourceSets.getByName(YamlRestTestPlugin.SOURCE_SET_NAME) + setTestClassesDirs(yamlRestTestSourceSet.getOutput().getClassesDirs()) + setClasspath(yamlRestTestSourceSet.getRuntimeClasspath()) + systemProperty 'tests.rest.denylist', [ + 'repository_s3/10_basic/*', + 'repository_s3/20_repository_permanent_credentials/*', + 'repository_s3/30_repository_temporary_credentials/*', + 'repository_s3/40_repository_ec2_credentials/*', + 'repository_s3/50_repository_ecs_credentials/*' + ].join(",") + } + check.dependsOn(yamlRestTestEKS) + + testClusters.yamlRestTestEKS { + keystore 's3.client.integration_test_eks.role_arn', "arn:aws:iam::000000000000:role/test" + keystore 's3.client.integration_test_eks.role_session_name', "s3-test" + keystore 's3.client.integration_test_eks.access_key', "access_key" + keystore 's3.client.integration_test_eks.secret_key', "secret_key" + + setting 's3.client.integration_test_eks.endpoint', { "${-> fixtureAddress('s3-fixture', 's3-fixture-with-eks', '80')}" }, IGNORE_VALUE + setting 's3.client.integration_test_eks.region', { "us-east-2" }, IGNORE_VALUE + plugin tasks.bundlePlugin.archiveFile + + // to redirect AWSSecurityTokenServiceClient to custom auth point + systemProperty "aws.stsEndpointOverride", { "${-> fixtureAddress('s3-fixture', 's3-fixture-with-eks', '80')}/eks_credentials_endpoint" }, IGNORE_VALUE + } +} + // 3rd Party Tests TaskProvider s3ThirdPartyTest = tasks.register("s3ThirdPartyTest", Test) { SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); @@ -298,6 +400,7 @@ TaskProvider s3ThirdPartyTest = tasks.register("s3ThirdPartyTest", Test) { systemProperty 'test.s3.account', s3PermanentAccessKey systemProperty 'test.s3.key', s3PermanentSecretKey systemProperty 'test.s3.bucket', s3PermanentBucket + systemProperty 'test.s3.region', s3PermanentRegion nonInputProperties.systemProperty 'test.s3.base', s3PermanentBasePath + "_third_party_tests_" + BuildParams.testSeed if (useFixture) { nonInputProperties.systemProperty 'test.s3.endpoint', "${-> fixtureAddress('minio-fixture', 'minio-fixture', '9000') }" @@ -305,33 +408,183 @@ TaskProvider s3ThirdPartyTest = tasks.register("s3ThirdPartyTest", Test) { } tasks.named("check").configure { dependsOn(s3ThirdPartyTest) } -thirdPartyAudit.ignoreMissingClasses( - // classes are missing - 'javax.jms.Message', - 'javax.servlet.ServletContextEvent', - 'javax.servlet.ServletContextListener', - 'org.apache.avalon.framework.logger.Logger', - 'org.apache.log.Hierarchy', - 'org.apache.log.Logger', - 'software.amazon.ion.IonReader', - 'software.amazon.ion.IonSystem', - 'software.amazon.ion.IonType', - 'software.amazon.ion.IonWriter', - 'software.amazon.ion.Timestamp', - 'software.amazon.ion.system.IonBinaryWriterBuilder', - 'software.amazon.ion.system.IonSystemBuilder', - 'software.amazon.ion.system.IonTextWriterBuilder', - 'software.amazon.ion.system.IonWriterBuilder', - // We don't use the kms dependency - 'com.amazonaws.services.kms.AWSKMS', - 'com.amazonaws.services.kms.AWSKMSClient', - 'com.amazonaws.services.kms.model.DecryptRequest', - 'com.amazonaws.services.kms.model.DecryptResult', - 'com.amazonaws.services.kms.model.EncryptRequest', - 'com.amazonaws.services.kms.model.EncryptResult', - 'com.amazonaws.services.kms.model.GenerateDataKeyRequest', - 'com.amazonaws.services.kms.model.GenerateDataKeyResult' -) +thirdPartyAudit { + ignoreMissingClasses( + // classes are missing + 'javax.jms.Message', + 'javax.servlet.ServletContextEvent', + 'javax.servlet.ServletContextListener', + 'org.apache.avalon.framework.logger.Logger', + 'org.apache.log.Hierarchy', + 'org.apache.log.Logger', + + // from io.netty.handler.ssl.OpenSslEngine (netty) + 'io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod', + 'io.netty.internal.tcnative.AsyncTask', + 'io.netty.internal.tcnative.Buffer', + 'io.netty.internal.tcnative.CertificateCompressionAlgo', + 'io.netty.internal.tcnative.Library', + 'io.netty.internal.tcnative.ResultCallback', + 'io.netty.internal.tcnative.SSL', + 'io.netty.internal.tcnative.SSLContext', + 'io.netty.internal.tcnative.SSLPrivateKeyMethod', + 'io.netty.internal.tcnative.SSLSession', + 'io.netty.internal.tcnative.SSLSessionCache', + 'io.netty.internal.tcnative.CertificateCallback', + 'io.netty.internal.tcnative.CertificateVerifier', + 'io.netty.internal.tcnative.SessionTicketKey', + 'io.netty.internal.tcnative.SniHostNameMatcher', + + 'com.aayushatharva.brotli4j.Brotli4jLoader', + 'com.aayushatharva.brotli4j.decoder.DecoderJNI$Status', + 'com.aayushatharva.brotli4j.decoder.DecoderJNI$Wrapper', + 'com.aayushatharva.brotli4j.encoder.BrotliEncoderChannel', + 'com.aayushatharva.brotli4j.encoder.Encoder$Mode', + 'com.aayushatharva.brotli4j.encoder.Encoder$Parameters', + + 'com.google.protobuf.nano.CodedOutputByteBufferNano', + 'com.google.protobuf.nano.MessageNano', + + 'com.ning.compress.BufferRecycler', + 'com.ning.compress.lzf.ChunkDecoder', + 'com.ning.compress.lzf.ChunkEncoder', + 'com.ning.compress.lzf.LZFChunk', + 'com.ning.compress.lzf.LZFEncoder', + 'com.ning.compress.lzf.util.ChunkDecoderFactory', + 'com.ning.compress.lzf.util.ChunkEncoderFactory', + + 'io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod', + 'io.netty.internal.tcnative.AsyncTask', + + 'io.netty.internal.tcnative.CertificateCallback', + 'io.netty.internal.tcnative.CertificateVerifier', + 'io.netty.internal.tcnative.ResultCallback', + 'io.netty.internal.tcnative.SessionTicketKey', + 'io.netty.internal.tcnative.SniHostNameMatcher', + 'io.netty.internal.tcnative.SSL', + 'io.netty.internal.tcnative.SSLSession', + 'io.netty.internal.tcnative.SSLSessionCache', + + 'lzma.sdk.lzma.Encoder', + 'net.jpountz.lz4.LZ4Compressor', + 'net.jpountz.lz4.LZ4Factory', + 'net.jpountz.lz4.LZ4FastDecompressor', + 'net.jpountz.xxhash.XXHash32', + 'net.jpountz.xxhash.XXHashFactory', + + // from io.netty.handler.ssl.util.BouncyCastleSelfSignedCertGenerator (netty) + 'org.bouncycastle.cert.X509v3CertificateBuilder', + 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', + 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', + 'org.bouncycastle.openssl.PEMEncryptedKeyPair', + 'org.bouncycastle.openssl.PEMParser', + 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', + 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', + 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', + 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', + + 'org.conscrypt.AllocatedBuffer', + 'org.conscrypt.BufferAllocator', + 'org.conscrypt.Conscrypt', + 'org.conscrypt.HandshakeListener', + + 'org.eclipse.jetty.alpn.ALPN$ClientProvider', + 'org.eclipse.jetty.alpn.ALPN$ServerProvider', + 'org.eclipse.jetty.alpn.ALPN', + + // from io.netty.handler.ssl.JettyNpnSslEngine (netty) + 'org.eclipse.jetty.npn.NextProtoNego$ClientProvider', + 'org.eclipse.jetty.npn.NextProtoNego$ServerProvider', + 'org.eclipse.jetty.npn.NextProtoNego', + + // from io.netty.handler.codec.marshalling.ChannelBufferByteInput (netty) + 'org.jboss.marshalling.ByteInput', + + // from io.netty.handler.codec.marshalling.ChannelBufferByteOutput (netty) + 'org.jboss.marshalling.ByteOutput', + + // from io.netty.handler.codec.marshalling.CompatibleMarshallingEncoder (netty) + 'org.jboss.marshalling.Marshaller', + + // from io.netty.handler.codec.marshalling.ContextBoundUnmarshallerProvider (netty) + 'org.jboss.marshalling.MarshallerFactory', + 'org.jboss.marshalling.MarshallingConfiguration', + 'org.jboss.marshalling.Unmarshaller', + + 'org.slf4j.impl.StaticLoggerBinder', + 'org.slf4j.impl.StaticMDCBinder', + 'org.slf4j.impl.StaticMarkerBinder', + 'reactor.blockhound.BlockHound$Builder', + 'reactor.blockhound.integration.BlockHoundIntegration', + + 'software.amazon.awssdk.arns.Arn', + 'software.amazon.awssdk.arns.ArnResource', + 'software.amazon.awssdk.crt.CRT', + 'software.amazon.awssdk.crt.auth.credentials.Credentials', + 'software.amazon.awssdk.crt.auth.credentials.CredentialsProvider', + 'software.amazon.awssdk.crt.auth.credentials.DelegateCredentialsProvider$DelegateCredentialsProviderBuilder', + 'software.amazon.awssdk.crt.http.HttpHeader', + 'software.amazon.awssdk.crt.http.HttpMonitoringOptions', + 'software.amazon.awssdk.crt.http.HttpProxyOptions', + 'software.amazon.awssdk.crt.http.HttpRequest', + 'software.amazon.awssdk.crt.http.HttpRequestBodyStream', + 'software.amazon.awssdk.crt.io.ClientBootstrap', + 'software.amazon.awssdk.crt.io.ExponentialBackoffRetryOptions', + 'software.amazon.awssdk.crt.io.StandardRetryOptions', + 'software.amazon.awssdk.crt.io.TlsCipherPreference', + 'software.amazon.awssdk.crt.io.TlsContext', + 'software.amazon.awssdk.crt.io.TlsContextOptions', + 'software.amazon.awssdk.crt.s3.ChecksumAlgorithm', + 'software.amazon.awssdk.crt.s3.ChecksumConfig', + 'software.amazon.awssdk.crt.s3.ChecksumConfig$ChecksumLocation', + 'software.amazon.awssdk.crt.s3.ResumeToken', + 'software.amazon.awssdk.crt.s3.S3Client', + 'software.amazon.awssdk.crt.s3.S3ClientOptions', + 'software.amazon.awssdk.crt.s3.S3FinishedResponseContext', + 'software.amazon.awssdk.crt.s3.S3MetaRequest', + 'software.amazon.awssdk.crt.s3.S3MetaRequestOptions', + 'software.amazon.awssdk.crt.s3.S3MetaRequestOptions$MetaRequestType', + 'software.amazon.awssdk.crt.s3.S3MetaRequestResponseHandler', + 'software.amazon.awssdk.crtcore.CrtConfigurationUtils', + 'software.amazon.awssdk.crtcore.CrtConnectionHealthConfiguration', + 'software.amazon.awssdk.crtcore.CrtConnectionHealthConfiguration$Builder', + 'software.amazon.awssdk.crtcore.CrtConnectionHealthConfiguration$DefaultBuilder', + 'software.amazon.awssdk.crtcore.CrtProxyConfiguration', + 'software.amazon.awssdk.crtcore.CrtProxyConfiguration$Builder', + 'software.amazon.awssdk.crtcore.CrtProxyConfiguration$DefaultBuilder', + 'software.amazon.eventstream.HeaderValue', + 'software.amazon.eventstream.Message', + 'software.amazon.eventstream.MessageDecoder' + ) + + ignoreViolations ( + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$1', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$2', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$3', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$4', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$5', + + 'io.netty.util.internal.PlatformDependent0', + 'io.netty.util.internal.PlatformDependent0$1', + 'io.netty.util.internal.PlatformDependent0$2', + 'io.netty.util.internal.PlatformDependent0$3', + 'io.netty.util.internal.PlatformDependent0$4', + 'io.netty.util.internal.PlatformDependent0$6', + + 'io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueConsumerNodeRef', + 'io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueProducerNodeRef', + 'io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields', + 'io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields', + 'io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields', + 'io.netty.util.internal.shaded.org.jctools.queues.LinkedQueueNode', + 'io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField', + 'io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField', + 'io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField', + 'io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess', + 'io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess', + ) +} // jarhell with jdk (intentionally, because jaxb was removed from default modules in java 9) if (BuildParams.runtimeJavaVersion <= JavaVersion.VERSION_1_8) { diff --git a/plugins/repository-s3/config/repository-s3/log4j2.properties b/plugins/repository-s3/config/repository-s3/log4j2.properties index 7263bbcba521c..ceb9a546b9b08 100644 --- a/plugins/repository-s3/config/repository-s3/log4j2.properties +++ b/plugins/repository-s3/config/repository-s3/log4j2.properties @@ -9,17 +9,17 @@ # GitHub history for details. # -logger.com_amazonaws.name = com.amazonaws +logger.com_amazonaws.name = software.amazon.awssdk logger.com_amazonaws.level = warn -logger.com_amazonaws_jmx_SdkMBeanRegistrySupport.name = com.amazonaws.jmx.SdkMBeanRegistrySupport +logger.com_amazonaws_jmx_SdkMBeanRegistrySupport.name = software.amazon.awssdk.jmx.SdkMBeanRegistrySupport logger.com_amazonaws_jmx_SdkMBeanRegistrySupport.level = error -logger.com_amazonaws_metrics_AwsSdkMetrics.name = com.amazonaws.metrics.AwsSdkMetrics +logger.com_amazonaws_metrics_AwsSdkMetrics.name = software.amazon.awssdk.metrics.AwsSdkMetrics logger.com_amazonaws_metrics_AwsSdkMetrics.level = error -logger.com_amazonaws_auth_profile_internal_BasicProfileConfigFileLoader.name = com.amazonaws.auth.profile.internal.BasicProfileConfigFileLoader +logger.com_amazonaws_auth_profile_internal_BasicProfileConfigFileLoader.name = software.amazon.awssdk.auth.profile.internal.BasicProfileConfigFileLoader logger.com_amazonaws_auth_profile_internal_BasicProfileConfigFileLoader.level = error -logger.com_amazonaws_services_s3_internal_UseArnRegionResolver.name = com.amazonaws.services.s3.internal.UseArnRegionResolver +logger.com_amazonaws_services_s3_internal_UseArnRegionResolver.name = software.amazon.awssdk.services.s3.internal.UseArnRegionResolver logger.com_amazonaws_services_s3_internal_UseArnRegionResolver.level = error diff --git a/plugins/repository-s3/licenses/annotations-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/annotations-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5a626eeb5725b --- /dev/null +++ b/plugins/repository-s3/licenses/annotations-2.20.55.jar.sha1 @@ -0,0 +1 @@ +330e9d0e5f2401fffba5afe30f3740f400e8308d \ No newline at end of file diff --git a/plugins/repository-s3/licenses/annotations-LICENSE.txt b/plugins/repository-s3/licenses/annotations-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/annotations-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/annotations-NOTICE.txt b/plugins/repository-s3/licenses/annotations-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/annotations-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/apache-client-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/apache-client-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..3ee96bb6e4076 --- /dev/null +++ b/plugins/repository-s3/licenses/apache-client-2.20.55.jar.sha1 @@ -0,0 +1 @@ +5c149885667d41a306769505cfa481cfddf6f113 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/apache-client-LICENSE.txt b/plugins/repository-s3/licenses/apache-client-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/apache-client-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/apache-client-NOTICE.txt b/plugins/repository-s3/licenses/apache-client-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/apache-client-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/auth-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/auth-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..010464bdf9fd1 --- /dev/null +++ b/plugins/repository-s3/licenses/auth-2.20.55.jar.sha1 @@ -0,0 +1 @@ +e21f00a8a2096d5044f3eff176944256e01a175e \ No newline at end of file diff --git a/plugins/repository-s3/licenses/auth-LICENSE.txt b/plugins/repository-s3/licenses/auth-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/auth-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/auth-NOTICE.txt b/plugins/repository-s3/licenses/auth-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/auth-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/aws-core-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/aws-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..4b4ee1db864a8 --- /dev/null +++ b/plugins/repository-s3/licenses/aws-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +734427c2cece98a8cb90871b78d2311e4a7ef746 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/aws-core-LICENSE.txt b/plugins/repository-s3/licenses/aws-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/aws-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/aws-core-NOTICE.txt b/plugins/repository-s3/licenses/aws-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/aws-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/aws-java-sdk-LICENSE.txt b/plugins/repository-s3/licenses/aws-java-sdk-LICENSE.txt deleted file mode 100644 index 98d1f9319f374..0000000000000 --- a/plugins/repository-s3/licenses/aws-java-sdk-LICENSE.txt +++ /dev/null @@ -1,63 +0,0 @@ -Apache License -Version 2.0, January 2004 - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and - 2. You must cause any modified files to carry prominent notices stating that You changed the files; and - 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -Note: Other license terms may apply to certain, identified software files contained within or distributed with the accompanying software if such terms are included in the directory containing the accompanying software. Such other license terms will then apply in lieu of the terms of the software license above. - -JSON processing code subject to the JSON License from JSON.org: - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -The Software shall be used for Good, not Evil. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/repository-s3/licenses/aws-java-sdk-NOTICE.txt b/plugins/repository-s3/licenses/aws-java-sdk-NOTICE.txt deleted file mode 100644 index 565bd6085c71a..0000000000000 --- a/plugins/repository-s3/licenses/aws-java-sdk-NOTICE.txt +++ /dev/null @@ -1,15 +0,0 @@ -AWS SDK for Java -Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -This product includes software developed by -Amazon Technologies, Inc (http://www.amazon.com/). - -********************** -THIRD PARTY COMPONENTS -********************** -This software includes third party software subject to the following copyrights: -- XML parsing and utility functions from JetS3t - Copyright 2006-2009 James Murty. -- JSON parsing and utility functions from JSON.org - Copyright 2002 JSON.org. -- PKCS#1 PEM encoded private key parsing and utility functions from oauth.googlecode.com - Copyright 1998-2010 AOL Inc. - -The licenses for these third party components are included in LICENSE.txt diff --git a/plugins/repository-s3/licenses/aws-java-sdk-core-1.11.749.jar.sha1 b/plugins/repository-s3/licenses/aws-java-sdk-core-1.11.749.jar.sha1 deleted file mode 100644 index 7bc18d6d4f681..0000000000000 --- a/plugins/repository-s3/licenses/aws-java-sdk-core-1.11.749.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1da5c1549295cfeebc67fc1c7539785a9441755b \ No newline at end of file diff --git a/plugins/repository-s3/licenses/aws-java-sdk-s3-1.11.749.jar.sha1 b/plugins/repository-s3/licenses/aws-java-sdk-s3-1.11.749.jar.sha1 deleted file mode 100644 index af794dc59dd7f..0000000000000 --- a/plugins/repository-s3/licenses/aws-java-sdk-s3-1.11.749.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -7d069f82723907ccdbd0c91ef0ac76046f5c9652 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/aws-json-protocol-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/aws-json-protocol-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..45a88305c1928 --- /dev/null +++ b/plugins/repository-s3/licenses/aws-json-protocol-2.20.55.jar.sha1 @@ -0,0 +1 @@ +a52731c86b974aefa5bbb1c545f407811a0163b1 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/aws-json-protocol-LICENSE.txt b/plugins/repository-s3/licenses/aws-json-protocol-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/aws-json-protocol-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/aws-json-protocol-NOTICE.txt b/plugins/repository-s3/licenses/aws-json-protocol-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/aws-json-protocol-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/aws-query-protocol-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/aws-query-protocol-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..ba5f43378730c --- /dev/null +++ b/plugins/repository-s3/licenses/aws-query-protocol-2.20.55.jar.sha1 @@ -0,0 +1 @@ +ac116215cc85366f0bdffee53b4c21e7a7fe03ef \ No newline at end of file diff --git a/plugins/repository-s3/licenses/aws-query-protocol-LICENSE.txt b/plugins/repository-s3/licenses/aws-query-protocol-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/aws-query-protocol-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/aws-query-protocol-NOTICE.txt b/plugins/repository-s3/licenses/aws-query-protocol-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/aws-query-protocol-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/aws-xml-protocol-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/aws-xml-protocol-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..fc65ee07c40c6 --- /dev/null +++ b/plugins/repository-s3/licenses/aws-xml-protocol-2.20.55.jar.sha1 @@ -0,0 +1 @@ +6a3b5f607ece38536f17d869b82c669c6339f9ad \ No newline at end of file diff --git a/plugins/repository-s3/licenses/aws-xml-protocol-LICENSE.txt b/plugins/repository-s3/licenses/aws-xml-protocol-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/aws-xml-protocol-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/aws-xml-protocol-NOTICE.txt b/plugins/repository-s3/licenses/aws-xml-protocol-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/aws-xml-protocol-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/commons-codec-1.13.jar.sha1 b/plugins/repository-s3/licenses/commons-codec-1.13.jar.sha1 deleted file mode 100644 index 66b72c414d63a..0000000000000 --- a/plugins/repository-s3/licenses/commons-codec-1.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3f18e1aa31031d89db6f01ba05d501258ce69d2c \ No newline at end of file diff --git a/plugins/repository-s3/licenses/commons-codec-1.15.jar.sha1 b/plugins/repository-s3/licenses/commons-codec-1.15.jar.sha1 new file mode 100644 index 0000000000000..62d99837b87e1 --- /dev/null +++ b/plugins/repository-s3/licenses/commons-codec-1.15.jar.sha1 @@ -0,0 +1 @@ +49d94806b6e3dc933dacbd8acb0fdbab8ebd1e5d \ No newline at end of file diff --git a/plugins/repository-s3/licenses/commons-logging-1.1.3.jar.sha1 b/plugins/repository-s3/licenses/commons-logging-1.1.3.jar.sha1 deleted file mode 100644 index c8756c438320f..0000000000000 --- a/plugins/repository-s3/licenses/commons-logging-1.1.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f diff --git a/plugins/repository-s3/licenses/commons-logging-1.2.jar.sha1 b/plugins/repository-s3/licenses/commons-logging-1.2.jar.sha1 new file mode 100644 index 0000000000000..f40f0242448e8 --- /dev/null +++ b/plugins/repository-s3/licenses/commons-logging-1.2.jar.sha1 @@ -0,0 +1 @@ +4bfc12adfe4842bf07b657f0369c4cb522955686 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/commons-logging-LICENSE.txt b/plugins/repository-s3/licenses/commons-logging-LICENSE.txt index 57bc88a15a0ee..d645695673349 100644 --- a/plugins/repository-s3/licenses/commons-logging-LICENSE.txt +++ b/plugins/repository-s3/licenses/commons-logging-LICENSE.txt @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -199,4 +200,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - diff --git a/plugins/repository-s3/licenses/commons-logging-NOTICE.txt b/plugins/repository-s3/licenses/commons-logging-NOTICE.txt index 72eb32a902458..d3d6e140ce4f3 100644 --- a/plugins/repository-s3/licenses/commons-logging-NOTICE.txt +++ b/plugins/repository-s3/licenses/commons-logging-NOTICE.txt @@ -1,5 +1,5 @@ -Apache Commons CLI -Copyright 2001-2009 The Apache Software Foundation +Apache Commons Logging +Copyright 2003-2014 The Apache Software Foundation -This product includes software developed by +This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/plugins/repository-s3/licenses/endpoints-spi-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/endpoints-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5bc0e31166c77 --- /dev/null +++ b/plugins/repository-s3/licenses/endpoints-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +085f82038ee86a7d6cd568fe7edd842978d92de3 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/endpoints-spi-LICENSE.txt b/plugins/repository-s3/licenses/endpoints-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/endpoints-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/endpoints-spi-NOTICE.txt b/plugins/repository-s3/licenses/endpoints-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/endpoints-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/http-client-spi-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/http-client-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..523cf43dcb2e9 --- /dev/null +++ b/plugins/repository-s3/licenses/http-client-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +34f9b10c1a46038a0ceebdd750ba3a413a862ceb \ No newline at end of file diff --git a/plugins/repository-s3/licenses/http-client-spi-LICENSE.txt b/plugins/repository-s3/licenses/http-client-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/http-client-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/http-client-spi-NOTICE.txt b/plugins/repository-s3/licenses/http-client-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/http-client-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/httpclient-4.5.13.jar.sha1 b/plugins/repository-s3/licenses/httpclient-4.5.13.jar.sha1 deleted file mode 100644 index 3281e21595b39..0000000000000 --- a/plugins/repository-s3/licenses/httpclient-4.5.13.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e5f6cae5ca7ecaac1ec2827a9e2d65ae2869cada \ No newline at end of file diff --git a/plugins/repository-s3/licenses/httpclient-4.5.14.jar.sha1 b/plugins/repository-s3/licenses/httpclient-4.5.14.jar.sha1 new file mode 100644 index 0000000000000..66e05851c2e3c --- /dev/null +++ b/plugins/repository-s3/licenses/httpclient-4.5.14.jar.sha1 @@ -0,0 +1 @@ +1194890e6f56ec29177673f2f12d0b8e627dec98 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/httpcore-4.4.12.jar.sha1 b/plugins/repository-s3/licenses/httpcore-4.4.12.jar.sha1 deleted file mode 100644 index 3c046171b30da..0000000000000 --- a/plugins/repository-s3/licenses/httpcore-4.4.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -21ebaf6d532bc350ba95bd81938fa5f0e511c132 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/httpcore-4.4.16.jar.sha1 b/plugins/repository-s3/licenses/httpcore-4.4.16.jar.sha1 new file mode 100644 index 0000000000000..172110694b5bd --- /dev/null +++ b/plugins/repository-s3/licenses/httpcore-4.4.16.jar.sha1 @@ -0,0 +1 @@ +51cf043c87253c9f58b539c9f7e44c8894223850 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/jackson-annotations-2.13.2.jar.sha1 b/plugins/repository-s3/licenses/jackson-annotations-2.13.2.jar.sha1 deleted file mode 100644 index ecd3fb49d5b12..0000000000000 --- a/plugins/repository-s3/licenses/jackson-annotations-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -ec18851f1976d5b810ae1a5fcc32520d2d38f77a \ No newline at end of file diff --git a/plugins/repository-s3/licenses/jackson-annotations-2.15.2.jar.sha1 b/plugins/repository-s3/licenses/jackson-annotations-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f63416ddb8ceb --- /dev/null +++ b/plugins/repository-s3/licenses/jackson-annotations-2.15.2.jar.sha1 @@ -0,0 +1 @@ +4724a65ac8e8d156a24898d50fd5dbd3642870b8 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/jackson-databind-2.13.2.jar.sha1 b/plugins/repository-s3/licenses/jackson-databind-2.13.2.jar.sha1 deleted file mode 100644 index 5d356f3fd045f..0000000000000 --- a/plugins/repository-s3/licenses/jackson-databind-2.13.2.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -926e48c451166a291f1ce6c6276d9abbefa7c00f \ No newline at end of file diff --git a/plugins/repository-s3/licenses/jackson-databind-2.15.2.jar.sha1 b/plugins/repository-s3/licenses/jackson-databind-2.15.2.jar.sha1 new file mode 100644 index 0000000000000..f16d80af8dce6 --- /dev/null +++ b/plugins/repository-s3/licenses/jackson-databind-2.15.2.jar.sha1 @@ -0,0 +1 @@ +9353b021f10c307c00328f52090de2bdb4b6ff9c \ No newline at end of file diff --git a/plugins/repository-s3/licenses/jmespath-java-1.11.749.jar.sha1 b/plugins/repository-s3/licenses/jmespath-java-1.11.749.jar.sha1 deleted file mode 100644 index 3467802d074c7..0000000000000 --- a/plugins/repository-s3/licenses/jmespath-java-1.11.749.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -778866bc557dba508ee0eab2a0c5bfde468e49e6 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/json-utils-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/json-utils-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..a19b00e62f8b5 --- /dev/null +++ b/plugins/repository-s3/licenses/json-utils-2.20.55.jar.sha1 @@ -0,0 +1 @@ +cd6710900e3190eac4c4496ae529ce08680dd320 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/json-utils-LICENSE.txt b/plugins/repository-s3/licenses/json-utils-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/json-utils-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/json-utils-NOTICE.txt b/plugins/repository-s3/licenses/json-utils-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/json-utils-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/log4j-1.2-api-2.17.1.jar.sha1 b/plugins/repository-s3/licenses/log4j-1.2-api-2.17.1.jar.sha1 deleted file mode 100644 index 23aa5c60bd596..0000000000000 --- a/plugins/repository-s3/licenses/log4j-1.2-api-2.17.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -db3a7e7f07e878b92ac4a8f1100bee8325d5713a \ No newline at end of file diff --git a/plugins/repository-s3/licenses/log4j-1.2-api-2.20.0.jar.sha1 b/plugins/repository-s3/licenses/log4j-1.2-api-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..9829576d38ce0 --- /dev/null +++ b/plugins/repository-s3/licenses/log4j-1.2-api-2.20.0.jar.sha1 @@ -0,0 +1 @@ +689151374756cb809cb029f2501015bdc7733179 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/metrics-spi-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/metrics-spi-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..db6701d87892a --- /dev/null +++ b/plugins/repository-s3/licenses/metrics-spi-2.20.55.jar.sha1 @@ -0,0 +1 @@ +8a0eae705b27465516f3b09cc9918e40963d534d \ No newline at end of file diff --git a/plugins/repository-s3/licenses/metrics-spi-LICENSE.txt b/plugins/repository-s3/licenses/metrics-spi-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/metrics-spi-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/metrics-spi-NOTICE.txt b/plugins/repository-s3/licenses/metrics-spi-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/metrics-spi-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/netty-LICENSE.txt b/plugins/repository-s3/licenses/netty-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/netty-NOTICE.txt b/plugins/repository-s3/licenses/netty-NOTICE.txt new file mode 100644 index 0000000000000..5bbf91a14de23 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-NOTICE.txt @@ -0,0 +1,116 @@ + + The Netty Project + ================= + +Please visit the Netty web site for more information: + + * http://netty.io/ + +Copyright 2011 The Netty Project + +The Netty Project 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. + +Also, please refer to each LICENSE..txt file, which is located in +the 'license' directory of the distribution file, for the license terms of the +components that this product depends on. + +------------------------------------------------------------------------------- +This product contains the extensions to Java Collections Framework which has +been derived from the works by JSR-166 EG, Doug Lea, and Jason T. Greene: + + * LICENSE: + * license/LICENSE.jsr166y.txt (Public Domain) + * HOMEPAGE: + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/ + * http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/ + +This product contains a modified version of Robert Harder's Public Domain +Base64 Encoder and Decoder, which can be obtained at: + + * LICENSE: + * license/LICENSE.base64.txt (Public Domain) + * HOMEPAGE: + * http://iharder.sourceforge.net/current/java/base64/ + +This product contains a modified version of 'JZlib', a re-implementation of +zlib in pure Java, which can be obtained at: + + * LICENSE: + * license/LICENSE.jzlib.txt (BSD Style License) + * HOMEPAGE: + * http://www.jcraft.com/jzlib/ + +This product contains a modified version of 'Webbit', a Java event based +WebSocket and HTTP server: + + * LICENSE: + * license/LICENSE.webbit.txt (BSD License) + * HOMEPAGE: + * https://github.com/joewalnes/webbit + +This product optionally depends on 'Protocol Buffers', Google's data +interchange format, which can be obtained at: + + * LICENSE: + * license/LICENSE.protobuf.txt (New BSD License) + * HOMEPAGE: + * http://code.google.com/p/protobuf/ + +This product optionally depends on 'Bouncy Castle Crypto APIs' to generate +a temporary self-signed X.509 certificate when the JVM does not provide the +equivalent functionality. It can be obtained at: + + * LICENSE: + * license/LICENSE.bouncycastle.txt (MIT License) + * HOMEPAGE: + * http://www.bouncycastle.org/ + +This product optionally depends on 'SLF4J', a simple logging facade for Java, +which can be obtained at: + + * LICENSE: + * license/LICENSE.slf4j.txt (MIT License) + * HOMEPAGE: + * http://www.slf4j.org/ + +This product optionally depends on 'Apache Commons Logging', a logging +framework, which can be obtained at: + + * LICENSE: + * license/LICENSE.commons-logging.txt (Apache License 2.0) + * HOMEPAGE: + * http://commons.apache.org/logging/ + +This product optionally depends on 'Apache Log4J', a logging framework, +which can be obtained at: + + * LICENSE: + * license/LICENSE.log4j.txt (Apache License 2.0) + * HOMEPAGE: + * http://logging.apache.org/log4j/ + +This product optionally depends on 'JBoss Logging', a logging framework, +which can be obtained at: + + * LICENSE: + * license/LICENSE.jboss-logging.txt (GNU LGPL 2.1) + * HOMEPAGE: + * http://anonsvn.jboss.org/repos/common/common-logging-spi/ + +This product optionally depends on 'Apache Felix', an open source OSGi +framework implementation, which can be obtained at: + + * LICENSE: + * license/LICENSE.felix.txt (Apache License 2.0) + * HOMEPAGE: + * http://felix.apache.org/ diff --git a/plugins/repository-s3/licenses/netty-buffer-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-buffer-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..5b393be40e945 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-buffer-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9f02dcb9b15a647a56af210dffdc294a57922fb0 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-codec-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-codec-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..45ea27d29a183 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-codec-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9984cbd6e5d55c768f198e975d8aaf7fd42a4602 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-codec-http-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-codec-http-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..6bb7fcd68b272 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-codec-http-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +7142095066eaebd5f29b88c41af7b383b6a953f6 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-codec-http2-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-codec-http2-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..f9bdefc6dd965 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-codec-http2-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +c5a3481c4bb9732a3a94fb63cf916141a1a14669 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-common-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-common-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..d53adfa649f5f --- /dev/null +++ b/plugins/repository-s3/licenses/netty-common-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +278f6dfa49d6bd75c40ae1470eb165716f87dce0 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-handler-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-handler-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..258f7c957dda0 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-handler-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +742693761d7ea4c038bccfda96bb38194720b80d \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-nio-client-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/netty-nio-client-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..489f18e0bceaa --- /dev/null +++ b/plugins/repository-s3/licenses/netty-nio-client-2.20.55.jar.sha1 @@ -0,0 +1 @@ +4c269571ad2fb19851ebd7c7856aa2975fe0bab3 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-resolver-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-resolver-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..b8bc0a4370f58 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-resolver-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +080e45397d9d5b134477de3ffd0f94283b908621 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-transport-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-transport-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..247975e0a64c7 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-transport-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9ca2e3ae19a6713b749df154622115f480b6716c \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-transport-classes-epoll-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-transport-classes-epoll-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..75b64ad4197d8 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-transport-classes-epoll-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +961bd5b8d97ea6a07168176462f398089a24b5c8 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 b/plugins/repository-s3/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..6b7b66ea768e3 --- /dev/null +++ b/plugins/repository-s3/licenses/netty-transport-native-unix-common-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +cb0fc6c31c387404212949c57950b5d72ce908b9 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/profiles-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/profiles-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..b7104cf0939e6 --- /dev/null +++ b/plugins/repository-s3/licenses/profiles-2.20.55.jar.sha1 @@ -0,0 +1 @@ +959aad08b2f24057bf286c761b49e3af31a0a623 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/profiles-LICENSE.txt b/plugins/repository-s3/licenses/profiles-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/profiles-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/profiles-NOTICE.txt b/plugins/repository-s3/licenses/profiles-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/profiles-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/protocol-core-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/protocol-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..4dee45f4d9dd3 --- /dev/null +++ b/plugins/repository-s3/licenses/protocol-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +0935e3ab32962a890f1d13bf39ba2167d9d692f9 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/protocol-core-LICENSE.txt b/plugins/repository-s3/licenses/protocol-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/protocol-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/protocol-core-NOTICE.txt b/plugins/repository-s3/licenses/protocol-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/protocol-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/reactive-streams-1.0.4.jar.sha1 b/plugins/repository-s3/licenses/reactive-streams-1.0.4.jar.sha1 new file mode 100644 index 0000000000000..45a80e3f7e361 --- /dev/null +++ b/plugins/repository-s3/licenses/reactive-streams-1.0.4.jar.sha1 @@ -0,0 +1 @@ +3864a1320d97d7b045f729a326e1e077661f31b7 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/reactive-streams-LICENSE.txt b/plugins/repository-s3/licenses/reactive-streams-LICENSE.txt new file mode 100644 index 0000000000000..1e3c7e7c77495 --- /dev/null +++ b/plugins/repository-s3/licenses/reactive-streams-LICENSE.txt @@ -0,0 +1,21 @@ +MIT No Attribution + +Copyright 2014 Reactive Streams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/plugins/repository-s3/licenses/reactive-streams-NOTICE.txt b/plugins/repository-s3/licenses/reactive-streams-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/repository-s3/licenses/regions-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/regions-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..993fc2f97de62 --- /dev/null +++ b/plugins/repository-s3/licenses/regions-2.20.55.jar.sha1 @@ -0,0 +1 @@ +a117c19b4a30e902f4f1cc4bef6b5c10cc9aef31 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/regions-LICENSE.txt b/plugins/repository-s3/licenses/regions-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/regions-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/regions-NOTICE.txt b/plugins/repository-s3/licenses/regions-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/regions-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/s3-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/s3-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..b7f3157995aa6 --- /dev/null +++ b/plugins/repository-s3/licenses/s3-2.20.55.jar.sha1 @@ -0,0 +1 @@ +69e7df4c7c170867dc246c0205c5e0b6099e8a6f \ No newline at end of file diff --git a/plugins/repository-s3/licenses/s3-LICENSE.txt b/plugins/repository-s3/licenses/s3-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/s3-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/s3-NOTICE.txt b/plugins/repository-s3/licenses/s3-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/s3-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/sdk-core-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/sdk-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..5f12be9c08c5b --- /dev/null +++ b/plugins/repository-s3/licenses/sdk-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +8f2347feaf2575560ca89a2caa8d0243dbeb17a9 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/sdk-core-LICENSE.txt b/plugins/repository-s3/licenses/sdk-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/sdk-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/sdk-core-NOTICE.txt b/plugins/repository-s3/licenses/sdk-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/sdk-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/signer-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/signer-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..ec53fa0db623e --- /dev/null +++ b/plugins/repository-s3/licenses/signer-2.20.55.jar.sha1 @@ -0,0 +1 @@ +a44e55775ae429931287f81a634eeb67bd607a9f \ No newline at end of file diff --git a/plugins/repository-s3/licenses/signer-LICENSE.txt b/plugins/repository-s3/licenses/signer-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/signer-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/signer-NOTICE.txt b/plugins/repository-s3/licenses/signer-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/signer-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/slf4j-api-1.7.36.jar.sha1 b/plugins/repository-s3/licenses/slf4j-api-1.7.36.jar.sha1 new file mode 100644 index 0000000000000..77b9917528382 --- /dev/null +++ b/plugins/repository-s3/licenses/slf4j-api-1.7.36.jar.sha1 @@ -0,0 +1 @@ +6c62681a2f655b49963a5983b8b0950a6120ae14 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/slf4j-api-LICENSE.txt b/plugins/repository-s3/licenses/slf4j-api-LICENSE.txt new file mode 100644 index 0000000000000..8fda22f4d72f6 --- /dev/null +++ b/plugins/repository-s3/licenses/slf4j-api-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright (c) 2004-2014 QOS.ch +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/repository-s3/licenses/slf4j-api-NOTICE.txt b/plugins/repository-s3/licenses/slf4j-api-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/repository-s3/licenses/sts-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/sts-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..9f4bbdd0f22ad --- /dev/null +++ b/plugins/repository-s3/licenses/sts-2.20.55.jar.sha1 @@ -0,0 +1 @@ +adc350996b6f8481a32c8e73598138fc32826584 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/sts-LICENSE.txt b/plugins/repository-s3/licenses/sts-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/sts-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/sts-NOTICE.txt b/plugins/repository-s3/licenses/sts-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/sts-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/third-party-jackson-core-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/third-party-jackson-core-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..e7eebbb98f1fe --- /dev/null +++ b/plugins/repository-s3/licenses/third-party-jackson-core-2.20.55.jar.sha1 @@ -0,0 +1 @@ +956912f26056fc7d46b2db566362fe5f7a8c0e14 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/third-party-jackson-core-LICENSE.txt b/plugins/repository-s3/licenses/third-party-jackson-core-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/third-party-jackson-core-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/third-party-jackson-core-NOTICE.txt b/plugins/repository-s3/licenses/third-party-jackson-core-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/third-party-jackson-core-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/licenses/utils-2.20.55.jar.sha1 b/plugins/repository-s3/licenses/utils-2.20.55.jar.sha1 new file mode 100644 index 0000000000000..fc4cde604e33c --- /dev/null +++ b/plugins/repository-s3/licenses/utils-2.20.55.jar.sha1 @@ -0,0 +1 @@ +d3e1bbbc19795eadbeb4dd963a94647576644097 \ No newline at end of file diff --git a/plugins/repository-s3/licenses/utils-LICENSE.txt b/plugins/repository-s3/licenses/utils-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/repository-s3/licenses/utils-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/repository-s3/licenses/utils-NOTICE.txt b/plugins/repository-s3/licenses/utils-NOTICE.txt new file mode 100644 index 0000000000000..d2bc5723e9aea --- /dev/null +++ b/plugins/repository-s3/licenses/utils-NOTICE.txt @@ -0,0 +1,14 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors + +This product includes software developed by +Elasticsearch (http://www.elastic.co). +Copyright 2009-2018 Elasticsearch + +This product includes software developed by The Apache Software +Foundation (http://www.apache.org/). + +This product includes software developed by +Joda.org (http://www.joda.org/). + + diff --git a/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3BlobStoreRepositoryTests.java b/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3BlobStoreRepositoryTests.java index e31a9f8cf3856..4bd67e66ebcbd 100644 --- a/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3BlobStoreRepositoryTests.java +++ b/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3BlobStoreRepositoryTests.java @@ -31,35 +31,36 @@ package org.opensearch.repositories.s3; -import com.amazonaws.http.AmazonHttpClient; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; -import fixture.s3.S3HttpHandler; + +import software.amazon.awssdk.core.internal.http.pipeline.stages.ApplyTransactionIdStage; + import org.opensearch.action.ActionRunnable; import org.opensearch.action.support.PlainActionFuture; - import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.BlobStore; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.RepositoriesService; import org.opensearch.repositories.RepositoryData; import org.opensearch.repositories.blobstore.BlobStoreRepository; import org.opensearch.repositories.blobstore.OpenSearchMockAPIBasedRepositoryIntegTestCase; +import org.opensearch.repositories.s3.utils.AwsRequestSigner; import org.opensearch.snapshots.SnapshotId; import org.opensearch.snapshots.SnapshotsService; import org.opensearch.snapshots.mockstore.BlobStoreWrapper; @@ -68,16 +69,18 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import fixture.s3.S3HttpHandler; + import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; -import static org.hamcrest.Matchers.startsWith; @SuppressForbidden(reason = "this test uses a HttpServer to emulate an S3 endpoint") // Need to set up a new cluster for each test because cluster settings use randomized authentication settings @@ -86,22 +89,27 @@ public class S3BlobStoreRepositoryTests extends OpenSearchMockAPIBasedRepository private static final TimeValue TEST_COOLDOWN_PERIOD = TimeValue.timeValueSeconds(10L); - private String region; + private final String region = "test-region"; private String signerOverride; + private String previousOpenSearchPathConf; @Override public void setUp() throws Exception { - if (randomBoolean()) { - region = "test-region"; - } - if (region != null && randomBoolean()) { - signerOverride = randomFrom("AWS3SignerType", "AWS4SignerType"); - } else if (randomBoolean()) { - signerOverride = "AWS3SignerType"; - } + signerOverride = AwsRequestSigner.VERSION_FOUR_SIGNER.getName(); + previousOpenSearchPathConf = SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", "config")); super.setUp(); } + @Override + public void tearDown() throws Exception { + if (previousOpenSearchPathConf != null) { + SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", previousOpenSearchPathConf)); + } else { + SocketAccess.doPrivileged(() -> System.clearProperty("opensearch.path.conf")); + } + super.tearDown(); + } + @Override protected String repositoryType() { return S3Repository.TYPE; @@ -146,15 +154,15 @@ protected Settings nodeSettings(int nodeOrdinal) { .put(S3ClientSettings.DISABLE_CHUNKED_ENCODING.getConcreteSettingForNamespace("test").getKey(), true) // Disable request throttling because some random values in tests might generate too many failures for the S3 client .put(S3ClientSettings.USE_THROTTLE_RETRIES_SETTING.getConcreteSettingForNamespace("test").getKey(), false) + .put(S3ClientSettings.PROXY_TYPE_SETTING.getConcreteSettingForNamespace("test").getKey(), ProxySettings.ProxyType.DIRECT) .put(super.nodeSettings(nodeOrdinal)) .setSecureSettings(secureSettings); if (signerOverride != null) { builder.put(S3ClientSettings.SIGNER_OVERRIDE.getConcreteSettingForNamespace("test").getKey(), signerOverride); } - if (region != null) { - builder.put(S3ClientSettings.REGION.getConcreteSettingForNamespace("test").getKey(), region); - } + + builder.put(S3ClientSettings.REGION.getConcreteSettingForNamespace("test").getKey(), region); return builder.build(); } @@ -172,7 +180,7 @@ public void testEnforcedCooldownPeriod() throws IOException { .get() .getSnapshotInfo() .snapshotId(); - final RepositoriesService repositoriesService = internalCluster().getCurrentMasterNodeInstance(RepositoriesService.class); + final RepositoriesService repositoriesService = internalCluster().getCurrentClusterManagerNodeInstance(RepositoriesService.class); final BlobStoreRepository repository = (BlobStoreRepository) repositoriesService.repository(repoName); final RepositoryData repositoryData = getRepositoryData(repository); final RepositoryData modifiedRepositoryData = repositoryData.withVersions( @@ -213,8 +221,8 @@ public void testEnforcedCooldownPeriod() throws IOException { */ public static class TestS3RepositoryPlugin extends S3RepositoryPlugin { - public TestS3RepositoryPlugin(final Settings settings) { - super(settings); + public TestS3RepositoryPlugin(final Settings settings, final Path configPath) { + super(settings, configPath); } @Override @@ -231,7 +239,7 @@ protected S3Repository createRepository( ClusterService clusterService, RecoverySettings recoverySettings ) { - return new S3Repository(metadata, registry, service, clusterService, recoverySettings) { + return new S3Repository(metadata, registry, service, clusterService, recoverySettings, null, null, null, null, false) { @Override public BlobStore blobStore() { @@ -269,14 +277,11 @@ public void handle(final HttpExchange exchange) throws IOException { private void validateAuthHeader(HttpExchange exchange) { final String authorizationHeaderV4 = exchange.getRequestHeaders().getFirst("Authorization"); - final String authorizationHeaderV3 = exchange.getRequestHeaders().getFirst("X-amzn-authorization"); - if ("AWS3SignerType".equals(signerOverride)) { - assertThat(authorizationHeaderV3, startsWith("AWS3")); - } else if ("AWS4SignerType".equals(signerOverride)) { + if ("AWS4SignerType".equals(signerOverride)) { assertThat(authorizationHeaderV4, containsString("aws4_request")); } - if (region != null && authorizationHeaderV4 != null) { + if (authorizationHeaderV4 != null) { assertThat(authorizationHeaderV4, containsString("/" + region + "/s3/")); } } @@ -298,7 +303,7 @@ private static class S3ErroneousHttpHandler extends ErroneousHttpHandler { @Override protected String requestUniqueId(final HttpExchange exchange) { // Amazon SDK client provides a unique ID per request - return exchange.getRequestHeaders().getFirst(AmazonHttpClient.HEADER_SDK_TRANSACTION_ID); + return exchange.getRequestHeaders().getFirst(ApplyTransactionIdStage.HEADER_SDK_TRANSACTION_ID); } } @@ -314,7 +319,7 @@ private static class S3StatsCollectorHttpHandler extends HttpStatsCollectorHandl @Override public void maybeTrack(final String request, Headers requestHeaders) { - if (Regex.simpleMatch("GET /*/?prefix=*", request)) { + if (Regex.simpleMatch("GET /*?list-type=*", request)) { trackRequest("ListObjects"); } else if (Regex.simpleMatch("GET /*/*", request)) { trackRequest("GetObject"); diff --git a/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3RepositoryThirdPartyTests.java b/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3RepositoryThirdPartyTests.java index bc2839d066092..f7a84864a8569 100644 --- a/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3RepositoryThirdPartyTests.java +++ b/plugins/repository-s3/src/internalClusterTest/java/org/opensearch/repositories/s3/S3RepositoryThirdPartyTests.java @@ -31,7 +31,10 @@ package org.opensearch.repositories.s3; +import software.amazon.awssdk.services.s3.model.StorageClass; + import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.common.SuppressForbidden; import org.opensearch.common.blobstore.BlobMetadata; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.settings.MockSecureSettings; @@ -40,6 +43,7 @@ import org.opensearch.plugins.Plugin; import org.opensearch.repositories.AbstractThirdPartyRepositoryTestCase; import org.opensearch.repositories.blobstore.BlobStoreRepository; +import org.junit.Before; import java.util.Collection; import java.util.Map; @@ -52,6 +56,21 @@ public class S3RepositoryThirdPartyTests extends AbstractThirdPartyRepositoryTestCase { + @Override + @Before + @SuppressForbidden(reason = "Need to set system property here for AWS SDK v2") + public void setUp() throws Exception { + SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", "config")); + super.setUp(); + } + + @Override + @SuppressForbidden(reason = "Need to reset system property here for AWS SDK v2") + public void tearDown() throws Exception { + SocketAccess.doPrivileged(() -> System.clearProperty("opensearch.path.conf")); + super.tearDown(); + } + @Override protected Collection> getPlugins() { return pluginList(S3RepositoryPlugin.class); @@ -73,6 +92,7 @@ protected SecureSettings credentials() { protected void createRepository(String repoName) { Settings.Builder settings = Settings.builder() .put("bucket", System.getProperty("test.s3.bucket")) + .put("region", System.getProperty("test.s3.region", "us-west-2")) .put("base_path", System.getProperty("test.s3.base", "testpath")); final String endpoint = System.getProperty("test.s3.endpoint"); if (endpoint != null) { @@ -81,12 +101,12 @@ protected void createRepository(String repoName) { // only test different storage classes when running against the default endpoint, i.e. a genuine S3 service if (randomBoolean()) { final String storageClass = randomFrom( - "standard", - "reduced_redundancy", - "standard_ia", - "onezone_ia", - "intelligent_tiering" - ); + StorageClass.STANDARD, + StorageClass.REDUCED_REDUNDANCY, + StorageClass.STANDARD_IA, + StorageClass.ONEZONE_IA, + StorageClass.INTELLIGENT_TIERING + ).toString(); logger.info("--> using storage_class [{}]", storageClass); settings.put("storage_class", storageClass); } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonAsyncS3Reference.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonAsyncS3Reference.java new file mode 100644 index 0000000000000..0b5fcb6df280e --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonAsyncS3Reference.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.common.concurrent.RefCountedReleasable; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Handles the shutdown of the wrapped {@link software.amazon.awssdk.services.s3.S3AsyncClient} using reference + * counting. + */ +public class AmazonAsyncS3Reference extends RefCountedReleasable { + + private static final Logger logger = LogManager.getLogger(AmazonAsyncS3Reference.class); + + AmazonAsyncS3Reference(AmazonAsyncS3WithCredentials client) { + super("AWS_S3_CLIENT", client, () -> { + client.client().close(); + client.priorityClient().close(); + AwsCredentialsProvider credentials = client.credentials(); + if (credentials instanceof Closeable) { + try { + ((Closeable) credentials).close(); + } catch (IOException e) { + logger.error("Exception while closing AwsCredentialsProvider", e); + } + } + }); + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonAsyncS3WithCredentials.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonAsyncS3WithCredentials.java new file mode 100644 index 0000000000000..fa2db83729d25 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonAsyncS3WithCredentials.java @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.services.s3.S3AsyncClient; + +import org.opensearch.common.Nullable; + +/** + * The holder of the AmazonS3 and AWSCredentialsProvider + */ +final class AmazonAsyncS3WithCredentials { + private final S3AsyncClient client; + private final S3AsyncClient priorityClient; + private final AwsCredentialsProvider credentials; + + private AmazonAsyncS3WithCredentials( + final S3AsyncClient client, + final S3AsyncClient priorityClient, + @Nullable final AwsCredentialsProvider credentials + ) { + this.client = client; + this.credentials = credentials; + this.priorityClient = priorityClient; + } + + S3AsyncClient client() { + return client; + } + + S3AsyncClient priorityClient() { + return priorityClient; + } + + AwsCredentialsProvider credentials() { + return credentials; + } + + static AmazonAsyncS3WithCredentials create( + final S3AsyncClient client, + final S3AsyncClient priorityClient, + @Nullable final AwsCredentialsProvider credentials + ) { + return new AmazonAsyncS3WithCredentials(client, priorityClient, credentials); + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonS3Reference.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonS3Reference.java index 62e415705a011..21554147d6f7c 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonS3Reference.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonS3Reference.java @@ -32,17 +32,35 @@ package org.opensearch.repositories.s3; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.services.s3.S3Client; + +import org.opensearch.common.Nullable; import org.opensearch.common.concurrent.RefCountedReleasable; /** - * Handles the shutdown of the wrapped {@link AmazonS3Client} using reference + * Handles the shutdown of the wrapped {@link S3Client} using reference * counting. */ -public class AmazonS3Reference extends RefCountedReleasable { +public class AmazonS3Reference extends RefCountedReleasable { + AmazonS3Reference(S3Client client) { + this(client, null); + } + + AmazonS3Reference(AmazonS3WithCredentials client) { + this(client.client(), client.credentials()); + } - AmazonS3Reference(AmazonS3 client) { - super("AWS_S3_CLIENT", client, client::shutdown); + AmazonS3Reference(S3Client client, @Nullable AwsCredentialsProvider credentials) { + super("AWS_S3_CLIENT", client, () -> { + client.close(); + if (credentials instanceof AutoCloseable) { + try { + ((AutoCloseable) credentials).close(); + } catch (Exception e) { + /* Do nothing here */ + } + } + }); } } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonS3WithCredentials.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonS3WithCredentials.java new file mode 100644 index 0000000000000..5e7459d532779 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/AmazonS3WithCredentials.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.services.s3.S3Client; + +import org.opensearch.common.Nullable; + +/** + * The holder of the AmazonS3 and AwsCredentialsProvider + */ +final class AmazonS3WithCredentials { + private final S3Client client; + private final AwsCredentialsProvider credentials; + + private AmazonS3WithCredentials(final S3Client client, @Nullable final AwsCredentialsProvider credentials) { + this.client = client; + this.credentials = credentials; + } + + S3Client client() { + return client; + } + + AwsCredentialsProvider credentials() { + return credentials; + } + + static AmazonS3WithCredentials create(final S3Client client, @Nullable final AwsCredentialsProvider credentials) { + return new AmazonS3WithCredentials(client, credentials); + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/ProxySettings.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/ProxySettings.java index 430af0096d8b5..94327872bdd72 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/ProxySettings.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/ProxySettings.java @@ -8,9 +8,11 @@ package org.opensearch.repositories.s3; -import com.amazonaws.Protocol; -import org.opensearch.common.Strings; +import software.amazon.awssdk.core.exception.SdkException; + import org.opensearch.common.settings.SettingsException; +import org.opensearch.core.common.Strings; +import org.opensearch.repositories.s3.utils.Protocol; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -28,7 +30,7 @@ public static enum ProxyType { private final String name; - private ProxyType(String name) { + ProxyType(String name) { this.name = name; } @@ -84,7 +86,7 @@ public InetSocketAddress getAddress() { return new InetSocketAddress(InetAddress.getByName(host), port); } catch (UnknownHostException e) { // this error won't be thrown since validation of the host name is in the S3ClientSettings - throw new RuntimeException(e); + throw SdkException.create("Unknown host", e); } } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3AsyncService.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3AsyncService.java new file mode 100644 index 0000000000000..08215ebdd45e0 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3AsyncService.java @@ -0,0 +1,431 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientAsyncConfiguration; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption; +import software.amazon.awssdk.core.retry.RetryPolicy; +import software.amazon.awssdk.core.retry.backoff.BackoffStrategy; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.ProxyConfiguration; +import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; +import software.amazon.awssdk.services.sts.StsClient; +import software.amazon.awssdk.services.sts.StsClientBuilder; +import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; +import software.amazon.awssdk.services.sts.auth.StsWebIdentityTokenFileCredentialsProvider; +import software.amazon.awssdk.services.sts.model.AssumeRoleRequest; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.common.Nullable; +import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.collect.MapBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; +import org.opensearch.repositories.s3.S3ClientSettings.IrsaCredentials; +import org.opensearch.repositories.s3.async.AsyncExecutorContainer; +import org.opensearch.repositories.s3.async.AsyncTransferEventLoopGroup; + +import java.io.Closeable; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.time.Duration; +import java.util.Map; + +import static java.util.Collections.emptyMap; + +class S3AsyncService implements Closeable { + private static final Logger logger = LogManager.getLogger(S3AsyncService.class); + + private static final String STS_ENDPOINT_OVERRIDE_SYSTEM_PROPERTY = "aws.stsEndpointOverride"; + + private static final String DEFAULT_S3_ENDPOINT = "s3.amazonaws.com"; + + private volatile Map clientsCache = emptyMap(); + + /** + * Client settings calculated from static configuration and settings in the keystore. + */ + private volatile Map staticClientSettings; + + /** + * Client settings derived from those in {@link #staticClientSettings} by combining them with settings + * in the {@link RepositoryMetadata}. + */ + private volatile Map derivedClientSettings = emptyMap(); + + S3AsyncService(final Path configPath) { + staticClientSettings = MapBuilder.newMapBuilder() + .put("default", S3ClientSettings.getClientSettings(Settings.EMPTY, "default", configPath)) + .immutableMap(); + } + + /** + * Refreshes the settings for the AmazonS3 clients and clears the cache of + * existing clients. New clients will be build using these new settings. Old + * clients are usable until released. On release they will be destroyed instead + * of being returned to the cache. + */ + public synchronized void refreshAndClearCache(Map clientsSettings) { + // shutdown all unused clients + // others will shutdown on their respective release + releaseCachedClients(); + this.staticClientSettings = MapBuilder.newMapBuilder(clientsSettings).immutableMap(); + derivedClientSettings = emptyMap(); + assert this.staticClientSettings.containsKey("default") : "always at least have 'default'"; + // clients are built lazily by {@link client} + } + + /** + * Attempts to retrieve a client by its repository metadata and settings from the cache. + * If the client does not exist it will be created. + */ + public AmazonAsyncS3Reference client( + RepositoryMetadata repositoryMetadata, + AsyncExecutorContainer priorityExecutorBuilder, + AsyncExecutorContainer normalExecutorBuilder + ) { + final S3ClientSettings clientSettings = settings(repositoryMetadata); + { + final AmazonAsyncS3Reference clientReference = clientsCache.get(clientSettings); + if (clientReference != null && clientReference.tryIncRef()) { + return clientReference; + } + } + synchronized (this) { + final AmazonAsyncS3Reference existing = clientsCache.get(clientSettings); + if (existing != null && existing.tryIncRef()) { + return existing; + } + final AmazonAsyncS3Reference clientReference = new AmazonAsyncS3Reference( + buildClient(clientSettings, priorityExecutorBuilder, normalExecutorBuilder) + ); + clientReference.incRef(); + clientsCache = MapBuilder.newMapBuilder(clientsCache).put(clientSettings, clientReference).immutableMap(); + return clientReference; + } + } + + /** + * Either fetches {@link S3ClientSettings} for a given {@link RepositoryMetadata} from cached settings or creates them + * by overriding static client settings from {@link #staticClientSettings} with settings found in the repository metadata. + * @param repositoryMetadata Repository Metadata + * @return S3ClientSettings + */ + S3ClientSettings settings(RepositoryMetadata repositoryMetadata) { + final Settings settings = repositoryMetadata.settings(); + { + final S3ClientSettings existing = derivedClientSettings.get(settings); + if (existing != null) { + return existing; + } + } + final String clientName = S3Repository.CLIENT_NAME.get(settings); + final S3ClientSettings staticSettings = staticClientSettings.get(clientName); + if (staticSettings != null) { + synchronized (this) { + final S3ClientSettings existing = derivedClientSettings.get(settings); + if (existing != null) { + return existing; + } + final S3ClientSettings newSettings = staticSettings.refine(settings); + derivedClientSettings = MapBuilder.newMapBuilder(derivedClientSettings).put(settings, newSettings).immutableMap(); + return newSettings; + } + } + throw new IllegalArgumentException( + "Unknown s3 client name [" + + clientName + + "]. Existing client configs: " + + Strings.collectionToDelimitedString(staticClientSettings.keySet(), ",") + ); + } + + // proxy for testing + synchronized AmazonAsyncS3WithCredentials buildClient( + final S3ClientSettings clientSettings, + AsyncExecutorContainer priorityExecutorBuilder, + AsyncExecutorContainer normalExecutorBuilder + ) { + setDefaultAwsProfilePath(); + final S3AsyncClientBuilder builder = S3AsyncClient.builder(); + builder.overrideConfiguration(buildOverrideConfiguration(clientSettings)); + final AwsCredentialsProvider credentials = buildCredentials(logger, clientSettings); + builder.credentialsProvider(credentials); + + String endpoint = Strings.hasLength(clientSettings.endpoint) ? clientSettings.endpoint : DEFAULT_S3_ENDPOINT; + if ((endpoint.startsWith("http://") || endpoint.startsWith("https://")) == false) { + // Manually add the schema to the endpoint to work around https://github.com/aws/aws-sdk-java/issues/2274 + endpoint = clientSettings.protocol.toString() + "://" + endpoint; + } + logger.debug("using endpoint [{}] and region [{}]", endpoint, clientSettings.region); + + // If the endpoint configuration isn't set on the builder then the default behaviour is to try + // and work out what region we are in and use an appropriate endpoint - see AwsClientBuilder#setRegion. + // In contrast, directly-constructed clients use s3.amazonaws.com unless otherwise instructed. We currently + // use a directly-constructed client, and need to keep the existing behaviour to avoid a breaking change, + // so to move to using the builder we must set it explicitly to keep the existing behaviour. + // + // We do this because directly constructing the client is deprecated (was already deprecated in 1.1.223 too) + // so this change removes that usage of a deprecated API. + builder.endpointOverride(URI.create(endpoint)); + builder.region(Region.of(clientSettings.region)); + if (clientSettings.pathStyleAccess) { + builder.forcePathStyle(true); + } + + builder.httpClient(buildHttpClient(clientSettings, priorityExecutorBuilder.getAsyncTransferEventLoopGroup())); + builder.asyncConfiguration( + ClientAsyncConfiguration.builder() + .advancedOption( + SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, + priorityExecutorBuilder.getFutureCompletionExecutor() + ) + .build() + ); + final S3AsyncClient priorityClient = SocketAccess.doPrivileged(builder::build); + + builder.httpClient(buildHttpClient(clientSettings, normalExecutorBuilder.getAsyncTransferEventLoopGroup())); + builder.asyncConfiguration( + ClientAsyncConfiguration.builder() + .advancedOption( + SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, + normalExecutorBuilder.getFutureCompletionExecutor() + ) + .build() + ); + final S3AsyncClient client = SocketAccess.doPrivileged(builder::build); + + return AmazonAsyncS3WithCredentials.create(client, priorityClient, credentials); + } + + static ClientOverrideConfiguration buildOverrideConfiguration(final S3ClientSettings clientSettings) { + return ClientOverrideConfiguration.builder() + .retryPolicy( + RetryPolicy.builder() + .numRetries(clientSettings.maxRetries) + .throttlingBackoffStrategy( + clientSettings.throttleRetries ? BackoffStrategy.defaultThrottlingStrategy() : BackoffStrategy.none() + ) + .build() + ) + .apiCallAttemptTimeout(Duration.ofMillis(clientSettings.requestTimeoutMillis)) + .build(); + } + + // pkg private for tests + static SdkAsyncHttpClient buildHttpClient(S3ClientSettings clientSettings, AsyncTransferEventLoopGroup asyncTransferEventLoopGroup) { + // the response metadata cache is only there for diagnostics purposes, + // but can force objects from every response to the old generation. + NettyNioAsyncHttpClient.Builder clientBuilder = NettyNioAsyncHttpClient.builder(); + + if (clientSettings.proxySettings.getType() != ProxySettings.ProxyType.DIRECT) { + ProxyConfiguration.Builder proxyConfiguration = ProxyConfiguration.builder(); + proxyConfiguration.scheme(clientSettings.proxySettings.getType().toProtocol().toString()); + proxyConfiguration.host(clientSettings.proxySettings.getHostName()); + proxyConfiguration.port(clientSettings.proxySettings.getPort()); + proxyConfiguration.username(clientSettings.proxySettings.getUsername()); + proxyConfiguration.password(clientSettings.proxySettings.getPassword()); + clientBuilder.proxyConfiguration(proxyConfiguration.build()); + } + + // TODO: add max retry and UseThrottleRetry. Replace values with settings and put these in default settings + clientBuilder.connectionTimeout(Duration.ofMillis(clientSettings.connectionTimeoutMillis)); + clientBuilder.connectionAcquisitionTimeout(Duration.ofMillis(clientSettings.connectionAcquisitionTimeoutMillis)); + clientBuilder.maxPendingConnectionAcquires(10_000); + clientBuilder.maxConcurrency(clientSettings.maxConnections); + clientBuilder.eventLoopGroup(SdkEventLoopGroup.create(asyncTransferEventLoopGroup.getEventLoopGroup())); + clientBuilder.tcpKeepAlive(true); + + return clientBuilder.build(); + } + + // pkg private for tests + static AwsCredentialsProvider buildCredentials(Logger logger, S3ClientSettings clientSettings) { + final AwsCredentials basicCredentials = clientSettings.credentials; + final IrsaCredentials irsaCredentials = buildFromEnvironment(clientSettings.irsaCredentials); + + // If IAM Roles for Service Accounts (IRSA) credentials are configured, start with them first + if (irsaCredentials != null) { + logger.debug("Using IRSA credentials"); + + final Region region = Region.of(clientSettings.region); + StsClient stsClient = SocketAccess.doPrivileged(() -> { + StsClientBuilder builder = StsClient.builder().region(region); + + final String stsEndpoint = System.getProperty(STS_ENDPOINT_OVERRIDE_SYSTEM_PROPERTY); + if (stsEndpoint != null) { + builder = builder.endpointOverride(URI.create(stsEndpoint)); + } + + if (basicCredentials != null) { + builder = builder.credentialsProvider(StaticCredentialsProvider.create(basicCredentials)); + } else { + builder = builder.credentialsProvider(DefaultCredentialsProvider.create()); + } + + return builder.build(); + }); + + if (irsaCredentials.getIdentityTokenFile() == null) { + final StsAssumeRoleCredentialsProvider.Builder stsCredentialsProviderBuilder = StsAssumeRoleCredentialsProvider.builder() + .stsClient(stsClient) + .refreshRequest( + AssumeRoleRequest.builder() + .roleArn(irsaCredentials.getRoleArn()) + .roleSessionName(irsaCredentials.getRoleSessionName()) + .build() + ); + + final StsAssumeRoleCredentialsProvider stsCredentialsProvider = SocketAccess.doPrivileged( + stsCredentialsProviderBuilder::build + ); + + return new PrivilegedSTSAssumeRoleSessionCredentialsProvider<>(stsClient, stsCredentialsProvider); + } else { + final StsWebIdentityTokenFileCredentialsProvider.Builder stsCredentialsProviderBuilder = + StsWebIdentityTokenFileCredentialsProvider.builder() + .stsClient(stsClient) + .roleArn(irsaCredentials.getRoleArn()) + .roleSessionName(irsaCredentials.getRoleSessionName()) + .webIdentityTokenFile(Path.of(irsaCredentials.getIdentityTokenFile())); + + final StsWebIdentityTokenFileCredentialsProvider stsCredentialsProvider = SocketAccess.doPrivileged( + stsCredentialsProviderBuilder::build + ); + + return new PrivilegedSTSAssumeRoleSessionCredentialsProvider<>(stsClient, stsCredentialsProvider); + } + } else if (basicCredentials != null) { + logger.debug("Using basic key/secret credentials"); + return StaticCredentialsProvider.create(basicCredentials); + } else { + logger.debug("Using instance profile credentials"); + return new PrivilegedInstanceProfileCredentialsProvider(); + } + } + + // Aws v2 sdk tries to load a default profile from home path which is restricted. Hence, setting these to random + // valid paths. + @SuppressForbidden(reason = "Need to provide this override to v2 SDK so that path does not default to home path") + private static void setDefaultAwsProfilePath() { + if (ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.getStringValue().isEmpty()) { + System.setProperty(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property(), System.getProperty("opensearch.path.conf")); + } + if (ProfileFileSystemSetting.AWS_CONFIG_FILE.getStringValue().isEmpty()) { + System.setProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), System.getProperty("opensearch.path.conf")); + } + } + + private static IrsaCredentials buildFromEnvironment(IrsaCredentials defaults) { + if (defaults == null) { + return null; + } + + String webIdentityTokenFile = defaults.getIdentityTokenFile(); + if (webIdentityTokenFile == null) { + webIdentityTokenFile = System.getenv(SdkSystemSetting.AWS_WEB_IDENTITY_TOKEN_FILE.environmentVariable()); + } + + String roleArn = defaults.getRoleArn(); + if (roleArn == null) { + roleArn = System.getenv(SdkSystemSetting.AWS_ROLE_ARN.environmentVariable()); + } + + String roleSessionName = defaults.getRoleSessionName(); + if (roleSessionName == null) { + roleSessionName = System.getenv(SdkSystemSetting.AWS_ROLE_SESSION_NAME.environmentVariable()); + } + + return new IrsaCredentials(webIdentityTokenFile, roleArn, roleSessionName); + } + + private synchronized void releaseCachedClients() { + // the clients will shutdown when they will not be used anymore + for (final AmazonAsyncS3Reference clientReference : clientsCache.values()) { + clientReference.decRef(); + } + + // clear previously cached clients, they will be build lazily + clientsCache = emptyMap(); + derivedClientSettings = emptyMap(); + } + + static class PrivilegedInstanceProfileCredentialsProvider implements AwsCredentialsProvider { + private final AwsCredentialsProvider credentials; + + private PrivilegedInstanceProfileCredentialsProvider() { + this.credentials = initializeProvider(); + } + + private AwsCredentialsProvider initializeProvider() { + if (SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.getStringValue().isPresent() + || SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_FULL_URI.getStringValue().isPresent()) { + + return ContainerCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build(); + } + // InstanceProfileCredentialsProvider as last item of chain + return InstanceProfileCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build(); + } + + @Override + public AwsCredentials resolveCredentials() { + return SocketAccess.doPrivileged(credentials::resolveCredentials); + } + } + + static class PrivilegedSTSAssumeRoleSessionCredentialsProvider

    + implements + AwsCredentialsProvider, + Closeable { + private final P credentials; + private final StsClient stsClient; + + private PrivilegedSTSAssumeRoleSessionCredentialsProvider(@Nullable final StsClient stsClient, final P credentials) { + this.stsClient = stsClient; + this.credentials = credentials; + } + + @Override + public void close() throws IOException { + SocketAccess.doPrivilegedIOException(() -> { + credentials.close(); + if (stsClient != null) { + stsClient.close(); + } + return null; + }); + } + + @Override + public AwsCredentials resolveCredentials() { + return SocketAccess.doPrivileged(credentials::resolveCredentials); + } + } + + @Override + public void close() { + releaseCachedClients(); + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BasicCredentials.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BasicCredentials.java deleted file mode 100644 index 5f00236c671eb..0000000000000 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BasicCredentials.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.repositories.s3; - -import com.amazonaws.auth.AWSCredentials; - -import java.util.Objects; - -class S3BasicCredentials implements AWSCredentials { - - private final String accessKey; - - private final String secretKey; - - S3BasicCredentials(String accessKey, String secretKey) { - this.accessKey = accessKey; - this.secretKey = secretKey; - } - - @Override - public final String getAWSAccessKeyId() { - return accessKey; - } - - @Override - public final String getAWSSecretKey() { - return secretKey; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final S3BasicCredentials that = (S3BasicCredentials) o; - return accessKey.equals(that.accessKey) && secretKey.equals(that.secretKey); - } - - @Override - public int hashCode() { - return Objects.hash(accessKey, secretKey); - } -} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BasicSessionCredentials.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BasicSessionCredentials.java deleted file mode 100644 index 2264dd6cde297..0000000000000 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BasicSessionCredentials.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.repositories.s3; - -import com.amazonaws.auth.AWSSessionCredentials; - -import java.util.Objects; - -final class S3BasicSessionCredentials extends S3BasicCredentials implements AWSSessionCredentials { - - private final String sessionToken; - - S3BasicSessionCredentials(String accessKey, String secretKey, String sessionToken) { - super(accessKey, secretKey); - this.sessionToken = sessionToken; - } - - @Override - public String getSessionToken() { - return sessionToken; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final S3BasicSessionCredentials that = (S3BasicSessionCredentials) o; - return sessionToken.equals(that.sessionToken) - && getAWSAccessKeyId().equals(that.getAWSAccessKeyId()) - && getAWSSecretKey().equals(that.getAWSSecretKey()); - } - - @Override - public int hashCode() { - return Objects.hash(sessionToken, getAWSAccessKeyId(), getAWSSecretKey()); - } -} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BlobContainer.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BlobContainer.java index 678be7c6f13f2..fcfccf50ad326 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BlobContainer.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BlobContainer.java @@ -32,45 +32,75 @@ package org.opensearch.repositories.s3; -import com.amazonaws.AmazonClientException; -import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; -import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; -import com.amazonaws.services.s3.model.DeleteObjectsRequest; -import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; -import com.amazonaws.services.s3.model.ListObjectsRequest; -import com.amazonaws.services.s3.model.MultiObjectDeleteException; -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PartETag; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.UploadPartRequest; -import com.amazonaws.services.s3.model.UploadPartResult; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CommonPrefix; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompletedMultipartUpload; +import software.amazon.awssdk.services.s3.model.CompletedPart; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.Delete; +import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse; +import software.amazon.awssdk.services.s3.model.GetObjectAttributesRequest; +import software.amazon.awssdk.services.s3.model.GetObjectAttributesResponse; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.HeadObjectRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.NoSuchKeyException; +import software.amazon.awssdk.services.s3.model.ObjectAttributes; +import software.amazon.awssdk.services.s3.model.ObjectIdentifier; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.S3Error; +import software.amazon.awssdk.services.s3.model.ServerSideEncryption; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.model.UploadPartResponse; +import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.lucene.util.SetOnce; import org.opensearch.ExceptionsHelper; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; +import org.opensearch.common.SetOnce; +import org.opensearch.common.StreamContext; +import org.opensearch.common.annotation.ExperimentalApi; +import org.opensearch.common.blobstore.AsyncMultiStreamBlobContainer; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobMetadata; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.BlobStoreException; import org.opensearch.common.blobstore.DeleteResult; +import org.opensearch.common.blobstore.stream.read.ReadContext; +import org.opensearch.common.blobstore.stream.write.WriteContext; +import org.opensearch.common.blobstore.stream.write.WritePriority; import org.opensearch.common.blobstore.support.AbstractBlobContainer; import org.opensearch.common.blobstore.support.PlainBlobMetadata; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.common.io.InputStreamContainer; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.repositories.s3.async.UploadRequest; +import org.opensearch.repositories.s3.utils.HttpRangeUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.stream.Collectors; @@ -79,12 +109,13 @@ import static org.opensearch.repositories.s3.S3Repository.MAX_FILE_SIZE_USING_MULTIPART; import static org.opensearch.repositories.s3.S3Repository.MIN_PART_SIZE_USING_MULTIPART; -class S3BlobContainer extends AbstractBlobContainer { +class S3BlobContainer extends AbstractBlobContainer implements AsyncMultiStreamBlobContainer { private static final Logger logger = LogManager.getLogger(S3BlobContainer.class); /** * Maximum number of deletes in a {@link DeleteObjectsRequest}. + * * @see S3 Documentation. */ private static final int MAX_BULK_DELETES = 1000; @@ -101,7 +132,13 @@ class S3BlobContainer extends AbstractBlobContainer { @Override public boolean blobExists(String blobName) { try (AmazonS3Reference clientReference = blobStore.clientReference()) { - return SocketAccess.doPrivileged(() -> clientReference.get().doesObjectExist(blobStore.bucket(), buildKey(blobName))); + SocketAccess.doPrivileged( + () -> clientReference.get() + .headObject(HeadObjectRequest.builder().bucket(blobStore.bucket()).key(buildKey(blobName)).build()) + ); + return true; + } catch (NoSuchKeyException e) { + return false; } catch (final Exception e) { throw new BlobStoreException("Failed to check if blob [" + blobName + "] exists", e); } @@ -149,6 +186,88 @@ public void writeBlob(String blobName, InputStream inputStream, long blobSize, b }); } + @Override + public void asyncBlobUpload(WriteContext writeContext, ActionListener completionListener) throws IOException { + UploadRequest uploadRequest = new UploadRequest( + blobStore.bucket(), + buildKey(writeContext.getFileName()), + writeContext.getFileSize(), + writeContext.getWritePriority(), + writeContext.getUploadFinalizer(), + writeContext.doRemoteDataIntegrityCheck(), + writeContext.getExpectedChecksum() + ); + try { + long partSize = blobStore.getAsyncTransferManager().calculateOptimalPartSize(writeContext.getFileSize()); + StreamContext streamContext = SocketAccess.doPrivileged(() -> writeContext.getStreamProvider(partSize)); + try (AmazonAsyncS3Reference amazonS3Reference = SocketAccess.doPrivileged(blobStore::asyncClientReference)) { + + S3AsyncClient s3AsyncClient = writeContext.getWritePriority() == WritePriority.HIGH + ? amazonS3Reference.get().priorityClient() + : amazonS3Reference.get().client(); + CompletableFuture completableFuture = blobStore.getAsyncTransferManager() + .uploadObject(s3AsyncClient, uploadRequest, streamContext); + completableFuture.whenComplete((response, throwable) -> { + if (throwable == null) { + completionListener.onResponse(response); + } else { + Exception ex = throwable instanceof Error ? new Exception(throwable) : (Exception) throwable; + completionListener.onFailure(ex); + } + }); + } + } catch (Exception e) { + logger.info("exception error from blob container for file {}", writeContext.getFileName()); + throw new IOException(e); + } + } + + @ExperimentalApi + @Override + public void readBlobAsync(String blobName, ActionListener listener) { + try (AmazonAsyncS3Reference amazonS3Reference = SocketAccess.doPrivileged(blobStore::asyncClientReference)) { + final S3AsyncClient s3AsyncClient = amazonS3Reference.get().client(); + final String bucketName = blobStore.bucket(); + final String blobKey = buildKey(blobName); + + final CompletableFuture blobMetadataFuture = getBlobMetadata(s3AsyncClient, bucketName, blobKey); + + blobMetadataFuture.whenComplete((blobMetadata, throwable) -> { + if (throwable != null) { + Exception ex = throwable.getCause() instanceof Exception + ? (Exception) throwable.getCause() + : new Exception(throwable.getCause()); + listener.onFailure(ex); + return; + } + + final List blobPartInputStreamFutures = new ArrayList<>(); + final long blobSize = blobMetadata.objectSize(); + final Integer numberOfParts = blobMetadata.objectParts() == null ? null : blobMetadata.objectParts().totalPartsCount(); + final String blobChecksum = blobMetadata.checksum().checksumCRC32(); + + if (numberOfParts == null) { + blobPartInputStreamFutures.add(() -> getBlobPartInputStreamContainer(s3AsyncClient, bucketName, blobKey, null)); + } else { + // S3 multipart files use 1 to n indexing + for (int partNumber = 1; partNumber <= numberOfParts; partNumber++) { + final int innerPartNumber = partNumber; + blobPartInputStreamFutures.add( + () -> getBlobPartInputStreamContainer(s3AsyncClient, bucketName, blobKey, innerPartNumber) + ); + } + } + listener.onResponse(new ReadContext(blobSize, blobPartInputStreamFutures, blobChecksum)); + }); + } catch (Exception ex) { + listener.onFailure(SdkException.create("Error occurred while fetching blob parts from the repository", ex)); + } + } + + public boolean remoteIntegrityCheckSupported() { + return true; + } + // package private for testing long getLargeBlobThresholdInBytes() { return blobStore.bufferSizeInBytes(); @@ -164,38 +283,39 @@ public DeleteResult delete() throws IOException { final AtomicLong deletedBlobs = new AtomicLong(); final AtomicLong deletedBytes = new AtomicLong(); try (AmazonS3Reference clientReference = blobStore.clientReference()) { - ObjectListing prevListing = null; - while (true) { - ObjectListing list; - if (prevListing != null) { - final ObjectListing finalPrevListing = prevListing; - list = SocketAccess.doPrivileged(() -> clientReference.get().listNextBatchOfObjects(finalPrevListing)); - } else { - final ListObjectsRequest listObjectsRequest = new ListObjectsRequest(); - listObjectsRequest.setBucketName(blobStore.bucket()); - listObjectsRequest.setPrefix(keyPath); - listObjectsRequest.setRequestMetricCollector(blobStore.listMetricCollector); - list = SocketAccess.doPrivileged(() -> clientReference.get().listObjects(listObjectsRequest)); - } - final List blobsToDelete = new ArrayList<>(); - list.getObjectSummaries().forEach(s3ObjectSummary -> { + ListObjectsV2Iterable listObjectsIterable = SocketAccess.doPrivileged( + () -> clientReference.get() + .listObjectsV2Paginator( + ListObjectsV2Request.builder() + .bucket(blobStore.bucket()) + .prefix(keyPath) + .overrideConfiguration( + o -> o.addMetricPublisher(blobStore.getStatsMetricPublisher().listObjectsMetricPublisher) + ) + .build() + ) + ); + + Iterator listObjectsResponseIterator = listObjectsIterable.iterator(); + while (listObjectsResponseIterator.hasNext()) { + ListObjectsV2Response listObjectsResponse = SocketAccess.doPrivileged(listObjectsResponseIterator::next); + List blobsToDelete = listObjectsResponse.contents().stream().map(s3Object -> { deletedBlobs.incrementAndGet(); - deletedBytes.addAndGet(s3ObjectSummary.getSize()); - blobsToDelete.add(s3ObjectSummary.getKey()); - }); - if (list.isTruncated()) { - doDeleteBlobs(blobsToDelete, false); - prevListing = list; - } else { - final List lastBlobsToDelete = new ArrayList<>(blobsToDelete); - lastBlobsToDelete.add(keyPath); - doDeleteBlobs(lastBlobsToDelete, false); - break; + deletedBytes.addAndGet(s3Object.size()); + + return s3Object.key(); + }).collect(Collectors.toList()); + + if (!listObjectsResponseIterator.hasNext()) { + blobsToDelete.add(keyPath); } + + doDeleteBlobs(blobsToDelete, false); } - } catch (final AmazonClientException e) { + } catch (SdkException e) { throw new IOException("Exception when deleting blob container [" + keyPath + "]", e); } + return new DeleteResult(deletedBlobs.get(), deletedBytes.get()); } @@ -229,34 +349,29 @@ private void doDeleteBlobs(List blobNames, boolean relative) throws IOEx deleteRequests.add(bulkDelete(blobStore.bucket(), partition)); } SocketAccess.doPrivilegedVoid(() -> { - AmazonClientException aex = null; + SdkException aex = null; for (DeleteObjectsRequest deleteRequest : deleteRequests) { - List keysInRequest = deleteRequest.getKeys() + List keysInRequest = deleteRequest.delete() + .objects() .stream() - .map(DeleteObjectsRequest.KeyVersion::getKey) + .map(ObjectIdentifier::key) .collect(Collectors.toList()); try { - clientReference.get().deleteObjects(deleteRequest); + DeleteObjectsResponse deleteObjectsResponse = clientReference.get().deleteObjects(deleteRequest); outstanding.removeAll(keysInRequest); - } catch (MultiObjectDeleteException e) { - // We are sending quiet mode requests so we can't use the deleted keys entry on the exception and instead - // first remove all keys that were sent in the request and then add back those that ran into an exception. - outstanding.removeAll(keysInRequest); - outstanding.addAll( - e.getErrors().stream().map(MultiObjectDeleteException.DeleteError::getKey).collect(Collectors.toSet()) - ); - logger.warn( - () -> new ParameterizedMessage( - "Failed to delete some blobs {}", - e.getErrors() - .stream() - .map(err -> "[" + err.getKey() + "][" + err.getCode() + "][" + err.getMessage() + "]") - .collect(Collectors.toList()) - ), - e - ); - aex = ExceptionsHelper.useOrSuppress(aex, e); - } catch (AmazonClientException e) { + outstanding.addAll(deleteObjectsResponse.errors().stream().map(S3Error::key).collect(Collectors.toSet())); + if (!deleteObjectsResponse.errors().isEmpty()) { + logger.warn( + () -> new ParameterizedMessage( + "Failed to delete some blobs {}", + deleteObjectsResponse.errors() + .stream() + .map(s3Error -> "[" + s3Error.key() + "][" + s3Error.code() + "][" + s3Error.message() + "]") + .collect(Collectors.toList()) + ) + ); + } + } catch (SdkException e) { // The AWS client threw any unexpected exception and did not execute the request at all so we do not // remove any keys from the outstanding deletes set. aex = ExceptionsHelper.useOrSuppress(aex, e); @@ -273,18 +388,52 @@ private void doDeleteBlobs(List blobNames, boolean relative) throws IOEx } private static DeleteObjectsRequest bulkDelete(String bucket, List blobs) { - return new DeleteObjectsRequest(bucket).withKeys(blobs.toArray(Strings.EMPTY_ARRAY)).withQuiet(true); + return DeleteObjectsRequest.builder() + .bucket(bucket) + .delete( + Delete.builder() + .objects(blobs.stream().map(blob -> ObjectIdentifier.builder().key(blob).build()).collect(Collectors.toList())) + .quiet(true) + .build() + ) + .build(); + } + + @Override + public List listBlobsByPrefixInSortedOrder(String blobNamePrefix, int limit, BlobNameSortOrder blobNameSortOrder) + throws IOException { + // As AWS S3 returns list of keys in Lexicographic order, we don't have to fetch all the keys in order to sort them + // We fetch only keys as per the given limit to optimize the fetch. If provided sort order is not Lexicographic, + // we fall-back to default implementation of fetching all the keys and sorting them. + if (blobNameSortOrder != BlobNameSortOrder.LEXICOGRAPHIC) { + return super.listBlobsByPrefixInSortedOrder(blobNamePrefix, limit, blobNameSortOrder); + } else { + if (limit < 0) { + throw new IllegalArgumentException("limit should not be a negative value"); + } + String prefix = blobNamePrefix == null ? keyPath : buildKey(blobNamePrefix); + try (AmazonS3Reference clientReference = blobStore.clientReference()) { + List blobs = executeListing(clientReference, listObjectsRequest(prefix, limit), limit).stream() + .flatMap(listing -> listing.contents().stream()) + .map(s3Object -> new PlainBlobMetadata(s3Object.key().substring(keyPath.length()), s3Object.size())) + .collect(Collectors.toList()); + return blobs.subList(0, Math.min(limit, blobs.size())); + } catch (final Exception e) { + throw new IOException("Exception when listing blobs by prefix [" + prefix + "]", e); + } + } } @Override public Map listBlobsByPrefix(@Nullable String blobNamePrefix) throws IOException { + String prefix = blobNamePrefix == null ? keyPath : buildKey(blobNamePrefix); try (AmazonS3Reference clientReference = blobStore.clientReference()) { - return executeListing(clientReference, listObjectsRequest(blobNamePrefix == null ? keyPath : buildKey(blobNamePrefix))).stream() - .flatMap(listing -> listing.getObjectSummaries().stream()) - .map(summary -> new PlainBlobMetadata(summary.getKey().substring(keyPath.length()), summary.getSize())) + return executeListing(clientReference, listObjectsRequest(prefix)).stream() + .flatMap(listing -> listing.contents().stream()) + .map(s3Object -> new PlainBlobMetadata(s3Object.key().substring(keyPath.length()), s3Object.size())) .collect(Collectors.toMap(PlainBlobMetadata::name, Function.identity())); - } catch (final AmazonClientException e) { - throw new IOException("Exception when listing blobs by prefix [" + blobNamePrefix + "]", e); + } catch (final SdkException e) { + throw new IOException("Exception when listing blobs by prefix [" + prefix + "]", e); } } @@ -296,53 +445,62 @@ public Map listBlobs() throws IOException { @Override public Map children() throws IOException { try (AmazonS3Reference clientReference = blobStore.clientReference()) { - return executeListing(clientReference, listObjectsRequest(keyPath)).stream().flatMap(listing -> { - assert listing.getObjectSummaries().stream().noneMatch(s -> { - for (String commonPrefix : listing.getCommonPrefixes()) { - if (s.getKey().substring(keyPath.length()).startsWith(commonPrefix)) { + return executeListing(clientReference, listObjectsRequest(keyPath)).stream().flatMap(listObjectsResponse -> { + assert listObjectsResponse.contents().stream().noneMatch(s -> { + for (CommonPrefix commonPrefix : listObjectsResponse.commonPrefixes()) { + if (s.key().substring(keyPath.length()).startsWith(commonPrefix.prefix())) { return true; } } return false; }) : "Response contained children for listed common prefixes."; - return listing.getCommonPrefixes().stream(); + return listObjectsResponse.commonPrefixes().stream(); }) - .map(prefix -> prefix.substring(keyPath.length())) + .map(commonPrefix -> commonPrefix.prefix().substring(keyPath.length())) .filter(name -> name.isEmpty() == false) // Stripping the trailing slash off of the common prefix .map(name -> name.substring(0, name.length() - 1)) .collect(Collectors.toMap(Function.identity(), name -> blobStore.blobContainer(path().add(name)))); - } catch (final AmazonClientException e) { + } catch (final SdkException e) { throw new IOException("Exception when listing children of [" + path().buildAsString() + ']', e); } } - private static List executeListing(AmazonS3Reference clientReference, ListObjectsRequest listObjectsRequest) { - final List results = new ArrayList<>(); - ObjectListing prevListing = null; - while (true) { - ObjectListing list; - if (prevListing != null) { - final ObjectListing finalPrevListing = prevListing; - list = SocketAccess.doPrivileged(() -> clientReference.get().listNextBatchOfObjects(finalPrevListing)); - } else { - list = SocketAccess.doPrivileged(() -> clientReference.get().listObjects(listObjectsRequest)); - } - results.add(list); - if (list.isTruncated()) { - prevListing = list; - } else { - break; + private static List executeListing(AmazonS3Reference clientReference, ListObjectsV2Request listObjectsRequest) { + return executeListing(clientReference, listObjectsRequest, -1); + } + + private static List executeListing( + AmazonS3Reference clientReference, + ListObjectsV2Request listObjectsRequest, + int limit + ) { + return SocketAccess.doPrivileged(() -> { + final List results = new ArrayList<>(); + int totalObjects = 0; + ListObjectsV2Iterable listObjectsIterable = clientReference.get().listObjectsV2Paginator(listObjectsRequest); + for (ListObjectsV2Response listObjectsV2Response : listObjectsIterable) { + results.add(listObjectsV2Response); + totalObjects += listObjectsV2Response.contents().size(); + if (limit != -1 && totalObjects > limit) { + break; + } } - } - return results; + return results; + }); + } + + private ListObjectsV2Request listObjectsRequest(String keyPath) { + return ListObjectsV2Request.builder() + .bucket(blobStore.bucket()) + .prefix(keyPath) + .delimiter("/") + .overrideConfiguration(o -> o.addMetricPublisher(blobStore.getStatsMetricPublisher().listObjectsMetricPublisher)) + .build(); } - private ListObjectsRequest listObjectsRequest(String keyPath) { - return new ListObjectsRequest().withBucketName(blobStore.bucket()) - .withPrefix(keyPath) - .withDelimiter("/") - .withRequestMetricCollector(blobStore.listMetricCollector); + private ListObjectsV2Request listObjectsRequest(String keyPath, int limit) { + return listObjectsRequest(keyPath).toBuilder().maxKeys(Math.min(limit, 1000)).build(); } private String buildKey(String blobName) { @@ -363,19 +521,23 @@ void executeSingleUpload(final S3BlobStore blobStore, final String blobName, fin throw new IllegalArgumentException("Upload request size [" + blobSize + "] can't be larger than buffer size"); } - final ObjectMetadata md = new ObjectMetadata(); - md.setContentLength(blobSize); + PutObjectRequest.Builder putObjectRequestBuilder = PutObjectRequest.builder() + .bucket(blobStore.bucket()) + .key(blobName) + .contentLength(blobSize) + .storageClass(blobStore.getStorageClass()) + .acl(blobStore.getCannedACL()) + .overrideConfiguration(o -> o.addMetricPublisher(blobStore.getStatsMetricPublisher().putObjectMetricPublisher)); if (blobStore.serverSideEncryption()) { - md.setSSEAlgorithm(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION); + putObjectRequestBuilder.serverSideEncryption(ServerSideEncryption.AES256); } - final PutObjectRequest putRequest = new PutObjectRequest(blobStore.bucket(), blobName, input, md); - putRequest.setStorageClass(blobStore.getStorageClass()); - putRequest.setCannedAcl(blobStore.getCannedACL()); - putRequest.setRequestMetricCollector(blobStore.putMetricCollector); + PutObjectRequest putObjectRequest = putObjectRequestBuilder.build(); try (AmazonS3Reference clientReference = blobStore.clientReference()) { - SocketAccess.doPrivilegedVoid(() -> { clientReference.get().putObject(putRequest); }); - } catch (final AmazonClientException e) { + SocketAccess.doPrivilegedVoid( + () -> clientReference.get().putObject(putObjectRequest, RequestBody.fromInputStream(input, blobSize)) + ); + } catch (final SdkException e) { throw new IOException("Unable to upload object [" + blobName + "] using a single upload", e); } } @@ -402,45 +564,46 @@ void executeMultipartUpload(final S3BlobStore blobStore, final String blobName, final String bucketName = blobStore.bucket(); boolean success = false; - final InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, blobName); - initRequest.setStorageClass(blobStore.getStorageClass()); - initRequest.setCannedACL(blobStore.getCannedACL()); - initRequest.setRequestMetricCollector(blobStore.multiPartUploadMetricCollector); + CreateMultipartUploadRequest.Builder createMultipartUploadRequestBuilder = CreateMultipartUploadRequest.builder() + .bucket(bucketName) + .key(blobName) + .storageClass(blobStore.getStorageClass()) + .acl(blobStore.getCannedACL()) + .overrideConfiguration(o -> o.addMetricPublisher(blobStore.getStatsMetricPublisher().multipartUploadMetricCollector)); + if (blobStore.serverSideEncryption()) { - final ObjectMetadata md = new ObjectMetadata(); - md.setSSEAlgorithm(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION); - initRequest.setObjectMetadata(md); + createMultipartUploadRequestBuilder.serverSideEncryption(ServerSideEncryption.AES256); } - try (AmazonS3Reference clientReference = blobStore.clientReference()) { - uploadId.set(SocketAccess.doPrivileged(() -> clientReference.get().initiateMultipartUpload(initRequest).getUploadId())); + CreateMultipartUploadRequest createMultipartUploadRequest = createMultipartUploadRequestBuilder.build(); + try (AmazonS3Reference clientReference = blobStore.clientReference()) { + uploadId.set( + SocketAccess.doPrivileged(() -> clientReference.get().createMultipartUpload(createMultipartUploadRequest).uploadId()) + ); if (Strings.isEmpty(uploadId.get())) { throw new IOException("Failed to initialize multipart upload " + blobName); } - final List parts = new ArrayList<>(); + final List parts = new ArrayList<>(); long bytesCount = 0; for (int i = 1; i <= nbParts; i++) { - final UploadPartRequest uploadRequest = new UploadPartRequest(); - uploadRequest.setBucketName(bucketName); - uploadRequest.setKey(blobName); - uploadRequest.setUploadId(uploadId.get()); - uploadRequest.setPartNumber(i); - uploadRequest.setInputStream(input); - uploadRequest.setRequestMetricCollector(blobStore.multiPartUploadMetricCollector); - - if (i < nbParts) { - uploadRequest.setPartSize(partSize); - uploadRequest.setLastPart(false); - } else { - uploadRequest.setPartSize(lastPartSize); - uploadRequest.setLastPart(true); - } - bytesCount += uploadRequest.getPartSize(); - - final UploadPartResult uploadResponse = SocketAccess.doPrivileged(() -> clientReference.get().uploadPart(uploadRequest)); - parts.add(uploadResponse.getPartETag()); + final UploadPartRequest uploadPartRequest = UploadPartRequest.builder() + .bucket(bucketName) + .key(blobName) + .uploadId(uploadId.get()) + .partNumber(i) + .contentLength((i < nbParts) ? partSize : lastPartSize) + .overrideConfiguration(o -> o.addMetricPublisher(blobStore.getStatsMetricPublisher().multipartUploadMetricCollector)) + .build(); + + bytesCount += uploadPartRequest.contentLength(); + + final UploadPartResponse uploadResponse = SocketAccess.doPrivileged( + () -> clientReference.get() + .uploadPart(uploadPartRequest, RequestBody.fromInputStream(input, uploadPartRequest.contentLength())) + ); + parts.add(CompletedPart.builder().partNumber(uploadPartRequest.partNumber()).eTag(uploadResponse.eTag()).build()); } if (bytesCount != blobSize) { @@ -449,21 +612,26 @@ void executeMultipartUpload(final S3BlobStore blobStore, final String blobName, ); } - final CompleteMultipartUploadRequest complRequest = new CompleteMultipartUploadRequest( - bucketName, - blobName, - uploadId.get(), - parts - ); - complRequest.setRequestMetricCollector(blobStore.multiPartUploadMetricCollector); - SocketAccess.doPrivilegedVoid(() -> clientReference.get().completeMultipartUpload(complRequest)); + CompleteMultipartUploadRequest completeMultipartUploadRequest = CompleteMultipartUploadRequest.builder() + .bucket(bucketName) + .key(blobName) + .uploadId(uploadId.get()) + .multipartUpload(CompletedMultipartUpload.builder().parts(parts).build()) + .overrideConfiguration(o -> o.addMetricPublisher(blobStore.getStatsMetricPublisher().multipartUploadMetricCollector)) + .build(); + + SocketAccess.doPrivilegedVoid(() -> clientReference.get().completeMultipartUpload(completeMultipartUploadRequest)); success = true; - } catch (final AmazonClientException e) { + } catch (final SdkException e) { throw new IOException("Unable to upload object [" + blobName + "] using multipart upload", e); } finally { if ((success == false) && Strings.hasLength(uploadId.get())) { - final AbortMultipartUploadRequest abortRequest = new AbortMultipartUploadRequest(bucketName, blobName, uploadId.get()); + AbortMultipartUploadRequest abortRequest = AbortMultipartUploadRequest.builder() + .bucket(bucketName) + .key(blobName) + .uploadId(uploadId.get()) + .build(); try (AmazonS3Reference clientReference = blobStore.clientReference()) { SocketAccess.doPrivilegedVoid(() -> clientReference.get().abortMultipartUpload(abortRequest)); } @@ -512,4 +680,71 @@ static Tuple numberOfMultiparts(final long totalSize, final long par return Tuple.tuple(parts + 1, remaining); } } + + /** + * Fetches a part of the blob from the S3 bucket and transforms it to an {@link InputStreamContainer}, which holds + * the stream and its related metadata. + * @param s3AsyncClient Async client to be utilized to fetch the object part + * @param bucketName Name of the S3 bucket + * @param blobKey Identifier of the blob for which the parts will be fetched + * @param partNumber Optional part number for the blob to be retrieved + * @return A future of {@link InputStreamContainer} containing the stream and stream metadata. + */ + CompletableFuture getBlobPartInputStreamContainer( + S3AsyncClient s3AsyncClient, + String bucketName, + String blobKey, + @Nullable Integer partNumber + ) { + final boolean isMultipartObject = partNumber != null; + final GetObjectRequest.Builder getObjectRequestBuilder = GetObjectRequest.builder().bucket(bucketName).key(blobKey); + + if (isMultipartObject) { + getObjectRequestBuilder.partNumber(partNumber); + } + + return SocketAccess.doPrivileged( + () -> s3AsyncClient.getObject(getObjectRequestBuilder.build(), AsyncResponseTransformer.toBlockingInputStream()) + .thenApply(response -> transformResponseToInputStreamContainer(response, isMultipartObject)) + ); + } + + /** + * Transforms the stream response object from S3 into an {@link InputStreamContainer} + * @param streamResponse Response stream object from S3 + * @param isMultipartObject Flag to denote a multipart object response + * @return {@link InputStreamContainer} containing the stream and stream metadata + */ + // Package-Private for testing. + static InputStreamContainer transformResponseToInputStreamContainer( + ResponseInputStream streamResponse, + boolean isMultipartObject + ) { + final GetObjectResponse getObjectResponse = streamResponse.response(); + final String contentRange = getObjectResponse.contentRange(); + final Long contentLength = getObjectResponse.contentLength(); + if ((isMultipartObject && contentRange == null) || contentLength == null) { + throw SdkException.builder().message("Failed to fetch required metadata for blob part").build(); + } + final long offset = isMultipartObject ? HttpRangeUtils.getStartOffsetFromRangeHeader(getObjectResponse.contentRange()) : 0L; + return new InputStreamContainer(streamResponse, getObjectResponse.contentLength(), offset); + } + + /** + * Retrieves the metadata like checksum, object size and parts for the provided blob within the S3 bucket. + * @param s3AsyncClient Async client to be utilized to fetch the metadata + * @param bucketName Name of the S3 bucket + * @param blobName Identifier of the blob for which the metadata will be fetched + * @return A future containing the metadata within {@link GetObjectAttributesResponse} + */ + CompletableFuture getBlobMetadata(S3AsyncClient s3AsyncClient, String bucketName, String blobName) { + // Fetch blob metadata - part info, size, checksum + final GetObjectAttributesRequest getObjectAttributesRequest = GetObjectAttributesRequest.builder() + .bucket(bucketName) + .key(blobName) + .objectAttributes(ObjectAttributes.CHECKSUM, ObjectAttributes.OBJECT_SIZE, ObjectAttributes.OBJECT_PARTS) + .build(); + + return SocketAccess.doPrivileged(() -> s3AsyncClient.getObjectAttributes(getObjectAttributesRequest)); + } } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BlobStore.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BlobStore.java index 90f8bbb6612d4..3dd373b5b9f32 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BlobStore.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3BlobStore.java @@ -32,12 +32,9 @@ package org.opensearch.repositories.s3; -import com.amazonaws.Request; -import com.amazonaws.Response; -import com.amazonaws.metrics.RequestMetricCollector; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.StorageClass; -import com.amazonaws.util.AWSRequestMetrics; +import software.amazon.awssdk.services.s3.model.ObjectCannedACL; +import software.amazon.awssdk.services.s3.model.StorageClass; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.cluster.metadata.RepositoryMetadata; @@ -45,13 +42,19 @@ import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.BlobStore; import org.opensearch.common.blobstore.BlobStoreException; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.repositories.s3.async.AsyncExecutorContainer; +import org.opensearch.repositories.s3.async.AsyncTransferManager; import java.io.IOException; -import java.util.HashMap; import java.util.Locale; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; + +import static org.opensearch.repositories.s3.S3Repository.BUCKET_SETTING; +import static org.opensearch.repositories.s3.S3Repository.BUFFER_SIZE_SETTING; +import static org.opensearch.repositories.s3.S3Repository.CANNED_ACL_SETTING; +import static org.opensearch.repositories.s3.S3Repository.SERVER_SIDE_ENCRYPTION_SETTING; +import static org.opensearch.repositories.s3.S3Repository.STORAGE_CLASS_SETTING; class S3BlobStore implements BlobStore { @@ -59,78 +62,63 @@ class S3BlobStore implements BlobStore { private final S3Service service; - private final String bucket; + private final S3AsyncService s3AsyncService; - private final ByteSizeValue bufferSize; + private volatile String bucket; - private final boolean serverSideEncryption; + private volatile ByteSizeValue bufferSize; - private final CannedAccessControlList cannedACL; + private volatile boolean serverSideEncryption; - private final StorageClass storageClass; + private volatile ObjectCannedACL cannedACL; - private final RepositoryMetadata repositoryMetadata; + private volatile StorageClass storageClass; - private final Stats stats = new Stats(); + private volatile RepositoryMetadata repositoryMetadata; - final RequestMetricCollector getMetricCollector; - final RequestMetricCollector listMetricCollector; - final RequestMetricCollector putMetricCollector; - final RequestMetricCollector multiPartUploadMetricCollector; + private final StatsMetricPublisher statsMetricPublisher = new StatsMetricPublisher(); + + private final AsyncTransferManager asyncTransferManager; + private final AsyncExecutorContainer priorityExecutorBuilder; + private final AsyncExecutorContainer normalExecutorBuilder; + private final boolean multipartUploadEnabled; S3BlobStore( S3Service service, + S3AsyncService s3AsyncService, + boolean multipartUploadEnabled, String bucket, boolean serverSideEncryption, ByteSizeValue bufferSize, String cannedACL, String storageClass, - RepositoryMetadata repositoryMetadata + RepositoryMetadata repositoryMetadata, + AsyncTransferManager asyncTransferManager, + AsyncExecutorContainer priorityExecutorBuilder, + AsyncExecutorContainer normalExecutorBuilder ) { this.service = service; + this.s3AsyncService = s3AsyncService; + this.multipartUploadEnabled = multipartUploadEnabled; this.bucket = bucket; this.serverSideEncryption = serverSideEncryption; this.bufferSize = bufferSize; this.cannedACL = initCannedACL(cannedACL); this.storageClass = initStorageClass(storageClass); this.repositoryMetadata = repositoryMetadata; - this.getMetricCollector = new RequestMetricCollector() { - @Override - public void collectMetrics(Request request, Response response) { - assert request.getHttpMethod().name().equals("GET"); - stats.getCount.addAndGet(getRequestCount(request)); - } - }; - this.listMetricCollector = new RequestMetricCollector() { - @Override - public void collectMetrics(Request request, Response response) { - assert request.getHttpMethod().name().equals("GET"); - stats.listCount.addAndGet(getRequestCount(request)); - } - }; - this.putMetricCollector = new RequestMetricCollector() { - @Override - public void collectMetrics(Request request, Response response) { - assert request.getHttpMethod().name().equals("PUT"); - stats.putCount.addAndGet(getRequestCount(request)); - } - }; - this.multiPartUploadMetricCollector = new RequestMetricCollector() { - @Override - public void collectMetrics(Request request, Response response) { - assert request.getHttpMethod().name().equals("PUT") || request.getHttpMethod().name().equals("POST"); - stats.postCount.addAndGet(getRequestCount(request)); - } - }; + this.asyncTransferManager = asyncTransferManager; + this.normalExecutorBuilder = normalExecutorBuilder; + this.priorityExecutorBuilder = priorityExecutorBuilder; } - private long getRequestCount(Request request) { - Number requestCount = request.getAWSRequestMetrics().getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.name()); - if (requestCount == null) { - logger.warn("Expected request count to be tracked for request [{}] but found not count.", request); - return 0L; - } - return requestCount.longValue(); + @Override + public void reload(RepositoryMetadata repositoryMetadata) { + this.repositoryMetadata = repositoryMetadata; + this.bucket = BUCKET_SETTING.get(repositoryMetadata.settings()); + this.serverSideEncryption = SERVER_SIDE_ENCRYPTION_SETTING.get(repositoryMetadata.settings()); + this.bufferSize = BUFFER_SIZE_SETTING.get(repositoryMetadata.settings()); + this.cannedACL = initCannedACL(CANNED_ACL_SETTING.get(repositoryMetadata.settings())); + this.storageClass = initStorageClass(STORAGE_CLASS_SETTING.get(repositoryMetadata.settings())); } @Override @@ -142,6 +130,10 @@ public AmazonS3Reference clientReference() { return service.client(repositoryMetadata); } + public AmazonAsyncS3Reference asyncClientReference() { + return s3AsyncService.client(repositoryMetadata, priorityExecutorBuilder, normalExecutorBuilder); + } + int getMaxRetries() { return service.settings(repositoryMetadata).maxRetries; } @@ -165,15 +157,20 @@ public BlobContainer blobContainer(BlobPath path) { @Override public void close() throws IOException { - this.service.close(); + if (service != null) { + this.service.close(); + } + if (s3AsyncService != null) { + this.s3AsyncService.close(); + } } @Override public Map stats() { - return stats.toMap(); + return statsMetricPublisher.getStats().toMap(); } - public CannedAccessControlList getCannedACL() { + public ObjectCannedACL getCannedACL() { return cannedACL; } @@ -181,32 +178,36 @@ public StorageClass getStorageClass() { return storageClass; } - public static StorageClass initStorageClass(String storageClass) { - if ((storageClass == null) || storageClass.equals("")) { - return StorageClass.Standard; + public StatsMetricPublisher getStatsMetricPublisher() { + return statsMetricPublisher; + } + + public static StorageClass initStorageClass(String storageClassStringValue) { + if ((storageClassStringValue == null) || storageClassStringValue.equals("")) { + return StorageClass.STANDARD; } - try { - final StorageClass _storageClass = StorageClass.fromValue(storageClass.toUpperCase(Locale.ENGLISH)); - if (_storageClass.equals(StorageClass.Glacier)) { - throw new BlobStoreException("Glacier storage class is not supported"); - } + final StorageClass storageClass = StorageClass.fromValue(storageClassStringValue.toUpperCase(Locale.ENGLISH)); + if (storageClass.equals(StorageClass.GLACIER)) { + throw new BlobStoreException("Glacier storage class is not supported"); + } - return _storageClass; - } catch (final IllegalArgumentException illegalArgumentException) { - throw new BlobStoreException("`" + storageClass + "` is not a valid S3 Storage Class."); + if (storageClass == StorageClass.UNKNOWN_TO_SDK_VERSION) { + throw new BlobStoreException("`" + storageClassStringValue + "` is not a valid S3 Storage Class."); } + + return storageClass; } /** * Constructs canned acl from string */ - public static CannedAccessControlList initCannedACL(String cannedACL) { + public static ObjectCannedACL initCannedACL(String cannedACL) { if ((cannedACL == null) || cannedACL.equals("")) { - return CannedAccessControlList.Private; + return ObjectCannedACL.PRIVATE; } - for (final CannedAccessControlList cur : CannedAccessControlList.values()) { + for (final ObjectCannedACL cur : ObjectCannedACL.values()) { if (cur.toString().equalsIgnoreCase(cannedACL)) { return cur; } @@ -215,23 +216,7 @@ public static CannedAccessControlList initCannedACL(String cannedACL) { throw new BlobStoreException("cannedACL is not valid: [" + cannedACL + "]"); } - static class Stats { - - final AtomicLong listCount = new AtomicLong(); - - final AtomicLong getCount = new AtomicLong(); - - final AtomicLong putCount = new AtomicLong(); - - final AtomicLong postCount = new AtomicLong(); - - Map toMap() { - final Map results = new HashMap<>(); - results.put("GetObject", getCount.get()); - results.put("ListObjects", listCount.get()); - results.put("PutObject", putCount.get()); - results.put("PutMultipartObject", postCount.get()); - return results; - } + public AsyncTransferManager getAsyncTransferManager() { + return asyncTransferManager; } } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3ClientSettings.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3ClientSettings.java index e02c7cae89378..933136228b1bb 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3ClientSettings.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3ClientSettings.java @@ -32,20 +32,26 @@ package org.opensearch.repositories.s3; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.Protocol; -import org.opensearch.common.Strings; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; + +import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.io.PathUtils; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.settings.SecureSetting; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.settings.SecureString; +import org.opensearch.repositories.s3.utils.Protocol; import java.net.InetAddress; import java.net.UnknownHostException; +import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -67,6 +73,29 @@ final class S3ClientSettings { /** Placeholder client name for normalizing client settings in the repository settings. */ private static final String PLACEHOLDER_CLIENT = "placeholder"; + // Properties to support using IAM Roles for Service Accounts (IRSA) + + /** The identity token file for connecting to s3. */ + static final Setting.AffixSetting IDENTITY_TOKEN_FILE_SETTING = Setting.affixKeySetting( + PREFIX, + "identity_token_file", + key -> SecureSetting.simpleString(key, Property.NodeScope) + ); + + /** The role ARN (Amazon Resource Name) for connecting to s3. */ + static final Setting.AffixSetting ROLE_ARN_SETTING = Setting.affixKeySetting( + PREFIX, + "role_arn", + key -> SecureSetting.secureString(key, null) + ); + + /** The role session name for connecting to s3. */ + static final Setting.AffixSetting ROLE_SESSION_NAME_SETTING = Setting.affixKeySetting( + PREFIX, + "role_session_name", + key -> SecureSetting.secureString(key, null) + ); + /** The access key (ie login id) for connecting to s3. */ static final Setting.AffixSetting ACCESS_KEY_SETTING = Setting.affixKeySetting( PREFIX, @@ -141,21 +170,63 @@ final class S3ClientSettings { static final Setting.AffixSetting READ_TIMEOUT_SETTING = Setting.affixKeySetting( PREFIX, "read_timeout", - key -> Setting.timeSetting(key, TimeValue.timeValueMillis(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT), Property.NodeScope) + key -> Setting.timeSetting(key, TimeValue.timeValueMillis(50_000), Property.NodeScope) + ); + + /** The request timeout for connecting to s3. */ + static final Setting.AffixSetting REQUEST_TIMEOUT_SETTING = Setting.affixKeySetting( + PREFIX, + "request_timeout", + key -> Setting.timeSetting(key, TimeValue.timeValueMinutes(2), Property.NodeScope) + ); + + /** The connection timeout for connecting to s3. */ + static final Setting.AffixSetting CONNECTION_TIMEOUT_SETTING = Setting.affixKeySetting( + PREFIX, + "connection_timeout", + key -> Setting.timeSetting(key, TimeValue.timeValueSeconds(10), Property.NodeScope) + ); + + /** The connection TTL for connecting to s3. */ + static final Setting.AffixSetting CONNECTION_TTL_SETTING = Setting.affixKeySetting( + PREFIX, + "connection_ttl", + key -> Setting.timeSetting(key, TimeValue.timeValueMillis(5000), Property.NodeScope) + ); + + /** The maximum connections to s3. */ + static final Setting.AffixSetting MAX_CONNECTIONS_SETTING = Setting.affixKeySetting( + PREFIX, + "max_connections", + key -> Setting.intSetting(key, 100, Property.NodeScope) + ); + + /** Connection acquisition timeout for new connections to S3. */ + static final Setting.AffixSetting CONNECTION_ACQUISITION_TIMEOUT = Setting.affixKeySetting( + PREFIX, + "connection_acquisition_timeout", + key -> Setting.timeSetting(key, TimeValue.timeValueMinutes(2), Property.NodeScope) + ); + + /** The maximum pending connections to S3. */ + static final Setting.AffixSetting MAX_PENDING_CONNECTION_ACQUIRES = Setting.affixKeySetting( + PREFIX, + "max_pending_connection_acquires", + key -> Setting.intSetting(key, 10_000, Property.NodeScope) ); /** The number of retries to use when an s3 request fails. */ static final Setting.AffixSetting MAX_RETRIES_SETTING = Setting.affixKeySetting( PREFIX, "max_retries", - key -> Setting.intSetting(key, ClientConfiguration.DEFAULT_RETRY_POLICY.getMaxErrorRetry(), 0, Property.NodeScope) + key -> Setting.intSetting(key, 3, 0, Property.NodeScope) ); /** Whether retries should be throttled (ie use backoff). */ static final Setting.AffixSetting USE_THROTTLE_RETRIES_SETTING = Setting.affixKeySetting( PREFIX, "use_throttle_retries", - key -> Setting.boolSetting(key, ClientConfiguration.DEFAULT_THROTTLE_RETRIES, Property.NodeScope) + key -> Setting.boolSetting(key, true, Property.NodeScope) ); /** Whether the s3 client should use path style access. */ @@ -187,7 +258,10 @@ final class S3ClientSettings { ); /** Credentials to authenticate with s3. */ - final S3BasicCredentials credentials; + final AwsCredentials credentials; + + /** Credentials to authenticate with s3 using IAM Roles for Service Accounts (IRSA). */ + final IrsaCredentials irsaCredentials; /** The s3 endpoint the client should talk to, or empty string to use the default. */ final String endpoint; @@ -201,6 +275,21 @@ final class S3ClientSettings { /** The read timeout for the s3 client. */ final int readTimeoutMillis; + /** The request timeout for the s3 client */ + final int requestTimeoutMillis; + + /** The connection timeout for the s3 client */ + final int connectionTimeoutMillis; + + /** The connection TTL for the s3 client */ + final int connectionTTLMillis; + + /** The max number of connections for the s3 client */ + final int maxConnections; + + /** The connnection acquisition timeout for the s3 async client */ + final int connectionAcquisitionTimeoutMillis; + /** The number of retries to use for the s3 client. */ final int maxRetries; @@ -220,10 +309,16 @@ final class S3ClientSettings { final String signerOverride; private S3ClientSettings( - S3BasicCredentials credentials, + AwsCredentials credentials, + IrsaCredentials irsaCredentials, String endpoint, Protocol protocol, int readTimeoutMillis, + int requestTimeoutMillis, + int connectionTimeoutMillis, + int connectionTTLMillis, + int maxConnections, + int connectionAcquisitionTimeoutMillis, int maxRetries, boolean throttleRetries, boolean pathStyleAccess, @@ -233,9 +328,15 @@ private S3ClientSettings( ProxySettings proxySettings ) { this.credentials = credentials; + this.irsaCredentials = irsaCredentials; this.endpoint = endpoint; this.protocol = protocol; this.readTimeoutMillis = readTimeoutMillis; + this.requestTimeoutMillis = requestTimeoutMillis; + this.connectionTimeoutMillis = connectionTimeoutMillis; + this.connectionTTLMillis = connectionTTLMillis; + this.maxConnections = maxConnections; + this.connectionAcquisitionTimeoutMillis = connectionAcquisitionTimeoutMillis; this.maxRetries = maxRetries; this.throttleRetries = throttleRetries; this.pathStyleAccess = pathStyleAccess; @@ -267,6 +368,24 @@ S3ClientSettings refine(Settings repositorySettings) { final int newReadTimeoutMillis = Math.toIntExact( getRepoSettingOrDefault(READ_TIMEOUT_SETTING, normalizedSettings, TimeValue.timeValueMillis(readTimeoutMillis)).millis() ); + final int newRequestTimeoutMillis = Math.toIntExact( + getRepoSettingOrDefault(REQUEST_TIMEOUT_SETTING, normalizedSettings, TimeValue.timeValueMillis(requestTimeoutMillis)).millis() + ); + final int newConnectionTimeoutMillis = Math.toIntExact( + getRepoSettingOrDefault(CONNECTION_TIMEOUT_SETTING, normalizedSettings, TimeValue.timeValueMillis(connectionTimeoutMillis)) + .millis() + ); + final int newConnectionTTLMillis = Math.toIntExact( + getRepoSettingOrDefault(CONNECTION_TTL_SETTING, normalizedSettings, TimeValue.timeValueMillis(connectionTTLMillis)).millis() + ); + final int newConnectionAcquisitionTimeoutMillis = Math.toIntExact( + getRepoSettingOrDefault( + CONNECTION_ACQUISITION_TIMEOUT, + normalizedSettings, + TimeValue.timeValueMillis(connectionAcquisitionTimeoutMillis) + ).millis() + ); + final int newMaxConnections = Math.toIntExact(getRepoSettingOrDefault(MAX_CONNECTIONS_SETTING, normalizedSettings, maxConnections)); final int newMaxRetries = getRepoSettingOrDefault(MAX_RETRIES_SETTING, normalizedSettings, maxRetries); final boolean newThrottleRetries = getRepoSettingOrDefault(USE_THROTTLE_RETRIES_SETTING, normalizedSettings, throttleRetries); final boolean newPathStyleAccess = getRepoSettingOrDefault(USE_PATH_STYLE_ACCESS, normalizedSettings, pathStyleAccess); @@ -275,7 +394,7 @@ S3ClientSettings refine(Settings repositorySettings) { normalizedSettings, disableChunkedEncoding ); - final S3BasicCredentials newCredentials; + final AwsCredentials newCredentials; if (checkDeprecatedCredentials(repositorySettings)) { newCredentials = loadDeprecatedCredentials(repositorySettings); } else { @@ -288,6 +407,11 @@ S3ClientSettings refine(Settings repositorySettings) { && Objects.equals(proxySettings.getHostName(), newProxyHost) && proxySettings.getPort() == newProxyPort && newReadTimeoutMillis == readTimeoutMillis + && newRequestTimeoutMillis == requestTimeoutMillis + && newConnectionTimeoutMillis == connectionTimeoutMillis + && newConnectionTTLMillis == connectionTTLMillis + && newMaxConnections == maxConnections + && newConnectionAcquisitionTimeoutMillis == connectionAcquisitionTimeoutMillis && maxRetries == newMaxRetries && newThrottleRetries == throttleRetries && Objects.equals(credentials, newCredentials) @@ -301,9 +425,15 @@ S3ClientSettings refine(Settings repositorySettings) { validateInetAddressFor(newProxyHost); return new S3ClientSettings( newCredentials, + irsaCredentials, newEndpoint, newProtocol, newReadTimeoutMillis, + newRequestTimeoutMillis, + newConnectionTimeoutMillis, + newConnectionTTLMillis, + newMaxConnections, + newConnectionAcquisitionTimeoutMillis, newMaxRetries, newThrottleRetries, newPathStyleAccess, @@ -319,16 +449,16 @@ S3ClientSettings refine(Settings repositorySettings) { * * Note this will always at least return a client named "default". */ - static Map load(Settings settings) { + static Map load(final Settings settings, final Path configPath) { final Set clientNames = settings.getGroups(PREFIX).keySet(); final Map clients = new HashMap<>(); for (final String clientName : clientNames) { - clients.put(clientName, getClientSettings(settings, clientName)); + clients.put(clientName, getClientSettings(settings, clientName, configPath)); } if (clients.containsKey("default") == false) { // this won't find any settings under the default client, // but it will pull all the fallback static settings - clients.put("default", getClientSettings(settings, "default")); + clients.put("default", getClientSettings(settings, "default", configPath)); } return Collections.unmodifiableMap(clients); } @@ -358,17 +488,17 @@ static boolean checkDeprecatedCredentials(Settings repositorySettings) { } // backcompat for reading keys out of repository settings (clusterState) - private static S3BasicCredentials loadDeprecatedCredentials(Settings repositorySettings) { + private static AwsCredentials loadDeprecatedCredentials(Settings repositorySettings) { assert checkDeprecatedCredentials(repositorySettings); try ( SecureString key = S3Repository.ACCESS_KEY_SETTING.get(repositorySettings); SecureString secret = S3Repository.SECRET_KEY_SETTING.get(repositorySettings) ) { - return new S3BasicCredentials(key.toString(), secret.toString()); + return AwsBasicCredentials.create(key.toString(), secret.toString()); } } - private static S3BasicCredentials loadCredentials(Settings settings, String clientName) { + private static AwsCredentials loadCredentials(Settings settings, String clientName) { try ( SecureString accessKey = getConfigValue(settings, clientName, ACCESS_KEY_SETTING); SecureString secretKey = getConfigValue(settings, clientName, SECRET_KEY_SETTING); @@ -377,9 +507,9 @@ private static S3BasicCredentials loadCredentials(Settings settings, String clie if (accessKey.length() != 0) { if (secretKey.length() != 0) { if (sessionToken.length() != 0) { - return new S3BasicSessionCredentials(accessKey.toString(), secretKey.toString(), sessionToken.toString()); + return AwsSessionCredentials.create(accessKey.toString(), secretKey.toString(), sessionToken.toString()); } else { - return new S3BasicCredentials(accessKey.toString(), secretKey.toString()); + return AwsBasicCredentials.create(accessKey.toString(), secretKey.toString()); } } else { throw new IllegalArgumentException("Missing secret key for s3 client [" + clientName + "]"); @@ -396,15 +526,44 @@ private static S3BasicCredentials loadCredentials(Settings settings, String clie } } + @SuppressForbidden(reason = "PathUtils#get") + private static IrsaCredentials loadIrsaCredentials(Settings settings, String clientName, Path configPath) { + String identityTokenFile = getConfigValue(settings, clientName, IDENTITY_TOKEN_FILE_SETTING); + if (identityTokenFile.length() != 0) { + final Path identityTokenFilePath = PathUtils.get(identityTokenFile); + // If the path is not absolute, resolve it relatively to config path + if (!identityTokenFilePath.isAbsolute()) { + identityTokenFile = PathUtils.get(new Path[] { configPath }, identityTokenFile).toString(); + } + } + + try ( + SecureString roleArn = getConfigValue(settings, clientName, ROLE_ARN_SETTING); + SecureString roleSessionName = getConfigValue(settings, clientName, ROLE_SESSION_NAME_SETTING) + ) { + if (identityTokenFile.length() != 0 || roleArn.length() != 0 || roleSessionName.length() != 0) { + return new IrsaCredentials(identityTokenFile.toString(), roleArn.toString(), roleSessionName.toString()); + } + + return null; + } + } + // pkg private for tests /** Parse settings for a single client. */ - static S3ClientSettings getClientSettings(final Settings settings, final String clientName) { + static S3ClientSettings getClientSettings(final Settings settings, final String clientName, final Path configPath) { final Protocol awsProtocol = getConfigValue(settings, clientName, PROTOCOL_SETTING); return new S3ClientSettings( S3ClientSettings.loadCredentials(settings, clientName), + S3ClientSettings.loadIrsaCredentials(settings, clientName, configPath), getConfigValue(settings, clientName, ENDPOINT_SETTING), awsProtocol, Math.toIntExact(getConfigValue(settings, clientName, READ_TIMEOUT_SETTING).millis()), + Math.toIntExact(getConfigValue(settings, clientName, REQUEST_TIMEOUT_SETTING).millis()), + Math.toIntExact(getConfigValue(settings, clientName, CONNECTION_TIMEOUT_SETTING).millis()), + Math.toIntExact(getConfigValue(settings, clientName, CONNECTION_TTL_SETTING).millis()), + Math.toIntExact(getConfigValue(settings, clientName, MAX_CONNECTIONS_SETTING)), + Math.toIntExact(getConfigValue(settings, clientName, CONNECTION_ACQUISITION_TIMEOUT).millis()), getConfigValue(settings, clientName, MAX_RETRIES_SETTING), getConfigValue(settings, clientName, USE_THROTTLE_RETRIES_SETTING), getConfigValue(settings, clientName, USE_PATH_STYLE_ACCESS), @@ -474,6 +633,11 @@ public boolean equals(final Object o) { } final S3ClientSettings that = (S3ClientSettings) o; return readTimeoutMillis == that.readTimeoutMillis + && requestTimeoutMillis == that.requestTimeoutMillis + && connectionTimeoutMillis == that.connectionTimeoutMillis + && connectionTTLMillis == that.connectionTTLMillis + && maxConnections == that.maxConnections + && connectionAcquisitionTimeoutMillis == that.connectionAcquisitionTimeoutMillis && maxRetries == that.maxRetries && throttleRetries == that.throttleRetries && Objects.equals(credentials, that.credentials) @@ -482,7 +646,8 @@ public boolean equals(final Object o) { && proxySettings.equals(that.proxySettings) && Objects.equals(disableChunkedEncoding, that.disableChunkedEncoding) && Objects.equals(region, that.region) - && Objects.equals(signerOverride, that.signerOverride); + && Objects.equals(signerOverride, that.signerOverride) + && Objects.equals(irsaCredentials, that.irsaCredentials); } @Override @@ -493,6 +658,11 @@ public int hashCode() { protocol, proxySettings, readTimeoutMillis, + requestTimeoutMillis, + connectionTimeoutMillis, + connectionTTLMillis, + maxConnections, + connectionAcquisitionTimeoutMillis, maxRetries, throttleRetries, disableChunkedEncoding, @@ -512,4 +682,51 @@ private static T getRepoSettingOrDefault(Setting.AffixSetting setting, Se } return defaultValue; } + + /** + * Class to store IAM Roles for Service Accounts (IRSA) credentials + * See please: https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html + */ + static class IrsaCredentials { + private final String identityTokenFile; + private final String roleArn; + private final String roleSessionName; + + IrsaCredentials(String identityTokenFile, String roleArn, String roleSessionName) { + this.identityTokenFile = Strings.isNullOrEmpty(identityTokenFile) ? null : identityTokenFile; + this.roleArn = Strings.isNullOrEmpty(roleArn) ? null : roleArn; + this.roleSessionName = Strings.isNullOrEmpty(roleSessionName) ? "s3-sdk-java-" + System.currentTimeMillis() : roleSessionName; + } + + public String getIdentityTokenFile() { + return identityTokenFile; + } + + public String getRoleArn() { + return roleArn; + } + + public String getRoleSessionName() { + return roleSessionName; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final IrsaCredentials that = (IrsaCredentials) o; + return Objects.equals(identityTokenFile, that.identityTokenFile) + && Objects.equals(roleArn, that.roleArn) + && Objects.equals(roleSessionName, that.roleSessionName); + } + + @Override + public int hashCode() { + return Objects.hash(identityTokenFile, roleArn, roleSessionName); + } + } } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Repository.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Repository.java index c8377949a6842..0e311c9419b24 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Repository.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Repository.java @@ -32,41 +32,51 @@ package org.opensearch.repositories.s3; +import software.amazon.awssdk.services.s3.model.ObjectCannedACL; +import software.amazon.awssdk.services.s3.model.StorageClass; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.BlobStore; +import org.opensearch.common.blobstore.BlobStoreException; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.settings.SecureSetting; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Setting; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.settings.SecureString; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.monitor.jvm.JvmInfo; import org.opensearch.repositories.RepositoryData; import org.opensearch.repositories.RepositoryException; import org.opensearch.repositories.ShardGenerations; import org.opensearch.repositories.blobstore.MeteredBlobStoreRepository; +import org.opensearch.repositories.s3.async.AsyncExecutorContainer; +import org.opensearch.repositories.s3.async.AsyncTransferManager; import org.opensearch.snapshots.SnapshotId; import org.opensearch.snapshots.SnapshotInfo; import org.opensearch.snapshots.SnapshotsService; import org.opensearch.threadpool.Scheduler; import org.opensearch.threadpool.ThreadPool; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -82,7 +92,6 @@ *

    {@code concurrent_streams}
    Number of concurrent read/write stream (per repository on each node). Defaults to 5.
    *
    {@code chunk_size}
    *
    Large file can be divided into chunks. This parameter specifies the chunk size. Defaults to not chucked.
    - *
    {@code compress}
    If set to true metadata files will be stored compressed. Defaults to false.
    * */ class S3Repository extends MeteredBlobStoreRepository { @@ -109,6 +118,11 @@ class S3Repository extends MeteredBlobStoreRepository { ByteSizeUnit.BYTES ); + private static final ByteSizeValue DEFAULT_MULTIPART_UPLOAD_MINIMUM_PART_SIZE = new ByteSizeValue( + ByteSizeUnit.MB.toBytes(16), + ByteSizeUnit.BYTES + ); + static final Setting BUCKET_SETTING = Setting.simpleString("bucket"); /** @@ -152,6 +166,26 @@ class S3Repository extends MeteredBlobStoreRepository { MAX_PART_SIZE_USING_MULTIPART ); + /** + * Minimum part size for parallel multipart uploads + */ + static final Setting PARALLEL_MULTIPART_UPLOAD_MINIMUM_PART_SIZE_SETTING = Setting.byteSizeSetting( + "parallel_multipart_upload.minimum_part_size", + DEFAULT_MULTIPART_UPLOAD_MINIMUM_PART_SIZE, + MIN_PART_SIZE_USING_MULTIPART, + MAX_PART_SIZE_USING_MULTIPART, + Setting.Property.NodeScope + ); + + /** + * This setting controls whether parallel multipart uploads will be used when calling S3 or not + */ + public static Setting PARALLEL_MULTIPART_UPLOAD_ENABLED_SETTING = Setting.boolSetting( + "parallel_multipart_upload.enabled", + true, + Setting.Property.NodeScope + ); + /** * Big files can be broken down into chunks during snapshotting if needed. Defaults to 1g. */ @@ -162,12 +196,6 @@ class S3Repository extends MeteredBlobStoreRepository { new ByteSizeValue(5, ByteSizeUnit.TB) ); - /** - * When set to true metadata files are stored in compressed format. This setting doesn’t affect index - * files that are already compressed by default. Defaults to false. - */ - static final Setting COMPRESS_SETTING = Setting.boolSetting("compress", false); - /** * Sets the S3 storage class type for the backup files. Values may be standard, reduced_redundancy, * standard_ia, onezone_ia and intelligent_tiering. Defaults to standard. @@ -207,21 +235,19 @@ class S3Repository extends MeteredBlobStoreRepository { private final S3Service service; - private final String bucket; - - private final ByteSizeValue bufferSize; + private volatile String bucket; - private final ByteSizeValue chunkSize; + private volatile ByteSizeValue bufferSize; - private final BlobPath basePath; + private volatile ByteSizeValue chunkSize; - private final boolean serverSideEncryption; + private volatile BlobPath basePath; - private final String storageClass; + private volatile boolean serverSideEncryption; - private final String cannedACL; + private volatile String storageClass; - private final RepositoryMetadata repositoryMetadata; + private volatile String cannedACL; /** * Time period to delay repository operations by after finalizing or deleting a snapshot. @@ -229,93 +255,74 @@ class S3Repository extends MeteredBlobStoreRepository { */ private final TimeValue coolDown; - /** - * Constructs an s3 backed repository - */ + private final AsyncTransferManager asyncUploadUtils; + private final S3AsyncService s3AsyncService; + private final boolean multipartUploadEnabled; + private final AsyncExecutorContainer priorityExecutorBuilder; + private final AsyncExecutorContainer normalExecutorBuilder; + private final Path pluginConfigPath; + + // Used by test classes S3Repository( final RepositoryMetadata metadata, final NamedXContentRegistry namedXContentRegistry, final S3Service service, final ClusterService clusterService, - final RecoverySettings recoverySettings + final RecoverySettings recoverySettings, + final AsyncTransferManager asyncUploadUtils, + final AsyncExecutorContainer priorityExecutorBuilder, + final AsyncExecutorContainer normalExecutorBuilder, + final S3AsyncService s3AsyncService, + final boolean multipartUploadEnabled ) { - super( + this( metadata, - COMPRESS_SETTING.get(metadata.settings()), namedXContentRegistry, + service, clusterService, recoverySettings, - buildLocation(metadata) + asyncUploadUtils, + priorityExecutorBuilder, + normalExecutorBuilder, + s3AsyncService, + multipartUploadEnabled, + Path.of("") ); - this.service = service; - - this.repositoryMetadata = metadata; - - // Parse and validate the user's S3 Storage Class setting - this.bucket = BUCKET_SETTING.get(metadata.settings()); - if (bucket == null) { - throw new RepositoryException(metadata.name(), "No bucket defined for s3 repository"); - } - - this.bufferSize = BUFFER_SIZE_SETTING.get(metadata.settings()); - this.chunkSize = CHUNK_SIZE_SETTING.get(metadata.settings()); - - // We make sure that chunkSize is bigger or equal than/to bufferSize - if (this.chunkSize.getBytes() < bufferSize.getBytes()) { - throw new RepositoryException( - metadata.name(), - CHUNK_SIZE_SETTING.getKey() - + " (" - + this.chunkSize - + ") can't be lower than " - + BUFFER_SIZE_SETTING.getKey() - + " (" - + bufferSize - + ")." - ); - } - - final String basePath = BASE_PATH_SETTING.get(metadata.settings()); - if (Strings.hasLength(basePath)) { - this.basePath = new BlobPath().add(basePath); - } else { - this.basePath = BlobPath.cleanPath(); - } - - this.serverSideEncryption = SERVER_SIDE_ENCRYPTION_SETTING.get(metadata.settings()); + } - this.storageClass = STORAGE_CLASS_SETTING.get(metadata.settings()); - this.cannedACL = CANNED_ACL_SETTING.get(metadata.settings()); + /** + * Constructs an s3 backed repository + */ + S3Repository( + final RepositoryMetadata metadata, + final NamedXContentRegistry namedXContentRegistry, + final S3Service service, + final ClusterService clusterService, + final RecoverySettings recoverySettings, + final AsyncTransferManager asyncUploadUtils, + final AsyncExecutorContainer priorityExecutorBuilder, + final AsyncExecutorContainer normalExecutorBuilder, + final S3AsyncService s3AsyncService, + final boolean multipartUploadEnabled, + Path pluginConfigPath + ) { + super(metadata, namedXContentRegistry, clusterService, recoverySettings, buildLocation(metadata)); + this.service = service; + this.s3AsyncService = s3AsyncService; + this.multipartUploadEnabled = multipartUploadEnabled; + this.pluginConfigPath = pluginConfigPath; + this.asyncUploadUtils = asyncUploadUtils; + this.priorityExecutorBuilder = priorityExecutorBuilder; + this.normalExecutorBuilder = normalExecutorBuilder; - if (S3ClientSettings.checkDeprecatedCredentials(metadata.settings())) { - // provided repository settings - deprecationLogger.deprecate( - "s3_repository_secret_settings", - "Using s3 access/secret key from repository settings. Instead " - + "store these in named clients and the opensearch keystore for secure settings." - ); - } + validateRepositoryMetadata(metadata); + readRepositoryMetadata(); coolDown = COOLDOWN_PERIOD.get(metadata.settings()); - - logger.debug( - "using bucket [{}], chunk_size [{}], server_side_encryption [{}], buffer_size [{}], cannedACL [{}], storageClass [{}]", - bucket, - chunkSize, - serverSideEncryption, - bufferSize, - cannedACL, - storageClass - ); } private static Map buildLocation(RepositoryMetadata metadata) { - return org.opensearch.common.collect.Map.of( - "base_path", - BASE_PATH_SETTING.get(metadata.settings()), - "bucket", - BUCKET_SETTING.get(metadata.settings()) - ); + return Map.of("base_path", BASE_PATH_SETTING.get(metadata.settings()), "bucket", BUCKET_SETTING.get(metadata.settings())); } /** @@ -410,10 +417,23 @@ private void logCooldownInfo() { @Override protected S3BlobStore createBlobStore() { - return new S3BlobStore(service, bucket, serverSideEncryption, bufferSize, cannedACL, storageClass, repositoryMetadata); + return new S3BlobStore( + service, + s3AsyncService, + multipartUploadEnabled, + bucket, + serverSideEncryption, + bufferSize, + cannedACL, + storageClass, + metadata, + asyncUploadUtils, + priorityExecutorBuilder, + normalExecutorBuilder + ); } - // only use for testing + // only use for testing (S3RepositoryTests) @Override protected BlobStore getBlobStore() { return super.getBlobStore(); @@ -424,11 +444,142 @@ public BlobPath basePath() { return basePath; } + @Override + public boolean isReloadable() { + return true; + } + + @Override + public void reload(RepositoryMetadata newRepositoryMetadata) { + if (isReloadable() == false) { + return; + } + + // Reload configs for S3Repository + super.reload(newRepositoryMetadata); + readRepositoryMetadata(); + + // Reload configs for S3RepositoryPlugin + final Map clientsSettings = S3ClientSettings.load(metadata.settings(), pluginConfigPath); + service.refreshAndClearCache(clientsSettings); + s3AsyncService.refreshAndClearCache(clientsSettings); + + // Reload configs for S3BlobStore + BlobStore blobStore = getBlobStore(); + blobStore.reload(metadata); + } + + /** + * Reloads the values derived from the Repository Metadata + */ + private void readRepositoryMetadata() { + this.bucket = BUCKET_SETTING.get(metadata.settings()); + this.bufferSize = BUFFER_SIZE_SETTING.get(metadata.settings()); + this.chunkSize = CHUNK_SIZE_SETTING.get(metadata.settings()); + final String basePath = BASE_PATH_SETTING.get(metadata.settings()); + if (Strings.hasLength(basePath)) { + this.basePath = new BlobPath().add(basePath); + } else { + this.basePath = BlobPath.cleanPath(); + } + + this.serverSideEncryption = SERVER_SIDE_ENCRYPTION_SETTING.get(metadata.settings()); + this.storageClass = STORAGE_CLASS_SETTING.get(metadata.settings()); + this.cannedACL = CANNED_ACL_SETTING.get(metadata.settings()); + if (S3ClientSettings.checkDeprecatedCredentials(metadata.settings())) { + // provided repository settings + deprecationLogger.deprecate( + "s3_repository_secret_settings", + "Using s3 access/secret key from repository settings. Instead " + + "store these in named clients and the opensearch keystore for secure settings." + ); + } + + logger.debug( + "using bucket [{}], chunk_size [{}], server_side_encryption [{}], buffer_size [{}], cannedACL [{}], storageClass [{}]", + bucket, + chunkSize, + serverSideEncryption, + bufferSize, + cannedACL, + storageClass + ); + } + + @Override + public void validateMetadata(RepositoryMetadata newRepositoryMetadata) { + super.validateMetadata(newRepositoryMetadata); + validateRepositoryMetadata(newRepositoryMetadata); + } + + private void validateRepositoryMetadata(RepositoryMetadata newRepositoryMetadata) { + Settings settings = newRepositoryMetadata.settings(); + if (BUCKET_SETTING.get(settings) == null) { + throw new RepositoryException(newRepositoryMetadata.name(), "No bucket defined for s3 repository"); + } + + // We make sure that chunkSize is bigger or equal than/to bufferSize + if (CHUNK_SIZE_SETTING.get(settings).getBytes() < BUFFER_SIZE_SETTING.get(settings).getBytes()) { + throw new RepositoryException( + newRepositoryMetadata.name(), + CHUNK_SIZE_SETTING.getKey() + + " (" + + CHUNK_SIZE_SETTING.get(settings) + + ") can't be lower than " + + BUFFER_SIZE_SETTING.getKey() + + " (" + + BUFFER_SIZE_SETTING.get(settings) + + ")." + ); + } + + validateStorageClass(STORAGE_CLASS_SETTING.get(settings)); + validateCannedACL(CANNED_ACL_SETTING.get(settings)); + } + + private static void validateStorageClass(String storageClassStringValue) { + if ((storageClassStringValue == null) || storageClassStringValue.equals("")) { + return; + } + + final StorageClass storageClass = StorageClass.fromValue(storageClassStringValue.toUpperCase(Locale.ENGLISH)); + if (storageClass.equals(StorageClass.GLACIER)) { + throw new BlobStoreException("Glacier storage class is not supported"); + } + + if (storageClass == StorageClass.UNKNOWN_TO_SDK_VERSION) { + throw new BlobStoreException("`" + storageClassStringValue + "` is not a valid S3 Storage Class."); + } + } + + private static void validateCannedACL(String cannedACLStringValue) { + if ((cannedACLStringValue == null) || cannedACLStringValue.equals("")) { + return; + } + + for (final ObjectCannedACL cur : ObjectCannedACL.values()) { + if (cur.toString().equalsIgnoreCase(cannedACLStringValue)) { + return; + } + } + + throw new BlobStoreException("cannedACL is not valid: [" + cannedACLStringValue + "]"); + } + @Override protected ByteSizeValue chunkSize() { return chunkSize; } + @Override + public List> getRestrictedSystemRepositorySettings() { + List> restrictedSettings = new ArrayList<>(); + restrictedSettings.addAll(super.getRestrictedSystemRepositorySettings()); + restrictedSettings.add(BUCKET_SETTING); + restrictedSettings.add(BASE_PATH_SETTING); + return restrictedSettings; + } + @Override protected void doClose() { final Scheduler.Cancellable cancellable = finalizationFuture.getAndSet(null); diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3RepositoryPlugin.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3RepositoryPlugin.java index 679243b28cfc7..a80ee0ca35fae 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3RepositoryPlugin.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3RepositoryPlugin.java @@ -32,61 +32,131 @@ package org.opensearch.repositories.s3; -import com.amazonaws.util.json.Jackson; -import org.opensearch.SpecialPermission; +import org.opensearch.client.Client; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.common.util.concurrent.OpenSearchExecutors; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; +import org.opensearch.env.NodeEnvironment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.ReloadablePlugin; import org.opensearch.plugins.RepositoryPlugin; +import org.opensearch.repositories.RepositoriesService; import org.opensearch.repositories.Repository; +import org.opensearch.repositories.s3.async.AsyncExecutorContainer; +import org.opensearch.repositories.s3.async.AsyncTransferEventLoopGroup; +import org.opensearch.repositories.s3.async.AsyncTransferManager; +import org.opensearch.script.ScriptService; +import org.opensearch.threadpool.ExecutorBuilder; +import org.opensearch.threadpool.FixedExecutorBuilder; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.watcher.ResourceWatcherService; import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Supplier; /** * A plugin to add a repository type that writes to and from the AWS S3. */ public class S3RepositoryPlugin extends Plugin implements RepositoryPlugin, ReloadablePlugin { - - static { - SpecialPermission.check(); - AccessController.doPrivileged((PrivilegedAction) () -> { - try { - // kick jackson to do some static caching of declared members info - Jackson.jsonNodeOf("{}"); - // ClientConfiguration clinit has some classloader problems - // TODO: fix that - Class.forName("com.amazonaws.ClientConfiguration"); - } catch (final ClassNotFoundException e) { - throw new RuntimeException(e); - } - return null; - }); - } + private static final String PRIORITY_FUTURE_COMPLETION = "priority_future_completion"; + private static final String PRIORITY_STREAM_READER = "priority_stream_reader"; + private static final String FUTURE_COMPLETION = "future_completion"; + private static final String STREAM_READER = "stream_reader"; protected final S3Service service; + private final S3AsyncService s3AsyncService; + + private final Path configPath; + + private AsyncExecutorContainer priorityExecutorBuilder; + private AsyncExecutorContainer normalExecutorBuilder; - public S3RepositoryPlugin(final Settings settings) { - this(settings, new S3Service()); + public S3RepositoryPlugin(final Settings settings, final Path configPath) { + this(settings, configPath, new S3Service(configPath), new S3AsyncService(configPath)); + } + + @Override + public List> getExecutorBuilders(Settings settings) { + List> executorBuilders = new ArrayList<>(); + executorBuilders.add( + new FixedExecutorBuilder(settings, PRIORITY_FUTURE_COMPLETION, priorityPoolCount(settings), 10_000, PRIORITY_FUTURE_COMPLETION) + ); + executorBuilders.add( + new FixedExecutorBuilder(settings, PRIORITY_STREAM_READER, priorityPoolCount(settings), 10_000, PRIORITY_STREAM_READER) + ); + executorBuilders.add(new FixedExecutorBuilder(settings, FUTURE_COMPLETION, normalPoolCount(settings), 10_000, FUTURE_COMPLETION)); + executorBuilders.add(new FixedExecutorBuilder(settings, STREAM_READER, normalPoolCount(settings), 10_000, STREAM_READER)); + return executorBuilders; } - S3RepositoryPlugin(final Settings settings, final S3Service service) { + S3RepositoryPlugin(final Settings settings, final Path configPath, final S3Service service, final S3AsyncService s3AsyncService) { this.service = Objects.requireNonNull(service, "S3 service must not be null"); + this.configPath = configPath; // eagerly load client settings so that secure settings are read - final Map clientsSettings = S3ClientSettings.load(settings); + Map clientsSettings = S3ClientSettings.load(settings, configPath); + this.s3AsyncService = Objects.requireNonNull(s3AsyncService, "S3AsyncService must not be null"); this.service.refreshAndClearCache(clientsSettings); + this.s3AsyncService.refreshAndClearCache(clientsSettings); + } + + private static int boundedBy(int value, int min, int max) { + return Math.min(max, Math.max(min, value)); + } + + private static int allocatedProcessors(Settings settings) { + return OpenSearchExecutors.allocatedProcessors(settings); + } + + private static int priorityPoolCount(Settings settings) { + return boundedBy((allocatedProcessors(settings) + 1) / 2, 2, 4); + } + + private static int normalPoolCount(Settings settings) { + return boundedBy((allocatedProcessors(settings) + 7) / 8, 1, 2); + } + + @Override + public Collection createComponents( + final Client client, + final ClusterService clusterService, + final ThreadPool threadPool, + final ResourceWatcherService resourceWatcherService, + final ScriptService scriptService, + final NamedXContentRegistry xContentRegistry, + final Environment environment, + final NodeEnvironment nodeEnvironment, + final NamedWriteableRegistry namedWriteableRegistry, + final IndexNameExpressionResolver expressionResolver, + final Supplier repositoriesServiceSupplier + ) { + int priorityEventLoopThreads = priorityPoolCount(clusterService.getSettings()); + int normalEventLoopThreads = normalPoolCount(clusterService.getSettings()); + this.priorityExecutorBuilder = new AsyncExecutorContainer( + threadPool.executor(PRIORITY_FUTURE_COMPLETION), + threadPool.executor(PRIORITY_STREAM_READER), + new AsyncTransferEventLoopGroup(priorityEventLoopThreads) + ); + this.normalExecutorBuilder = new AsyncExecutorContainer( + threadPool.executor(FUTURE_COMPLETION), + threadPool.executor(STREAM_READER), + new AsyncTransferEventLoopGroup(normalEventLoopThreads) + ); + return Collections.emptyList(); } // proxy method for testing @@ -96,7 +166,25 @@ protected S3Repository createRepository( final ClusterService clusterService, final RecoverySettings recoverySettings ) { - return new S3Repository(metadata, registry, service, clusterService, recoverySettings); + + AsyncTransferManager asyncUploadUtils = new AsyncTransferManager( + S3Repository.PARALLEL_MULTIPART_UPLOAD_MINIMUM_PART_SIZE_SETTING.get(clusterService.getSettings()).getBytes(), + normalExecutorBuilder.getStreamReader(), + priorityExecutorBuilder.getStreamReader() + ); + return new S3Repository( + metadata, + registry, + service, + clusterService, + recoverySettings, + asyncUploadUtils, + priorityExecutorBuilder, + normalExecutorBuilder, + s3AsyncService, + S3Repository.PARALLEL_MULTIPART_UPLOAD_ENABLED_SETTING.get(clusterService.getSettings()), + configPath + ); } @Override @@ -121,6 +209,7 @@ public List> getSettings() { S3ClientSettings.SESSION_TOKEN_SETTING, S3ClientSettings.ENDPOINT_SETTING, S3ClientSettings.PROTOCOL_SETTING, + S3ClientSettings.PROXY_TYPE_SETTING, S3ClientSettings.PROXY_HOST_SETTING, S3ClientSettings.PROXY_PORT_SETTING, S3ClientSettings.PROXY_USERNAME_SETTING, @@ -132,19 +221,26 @@ public List> getSettings() { S3Repository.ACCESS_KEY_SETTING, S3Repository.SECRET_KEY_SETTING, S3ClientSettings.SIGNER_OVERRIDE, - S3ClientSettings.REGION + S3ClientSettings.REGION, + S3ClientSettings.ROLE_ARN_SETTING, + S3ClientSettings.IDENTITY_TOKEN_FILE_SETTING, + S3ClientSettings.ROLE_SESSION_NAME_SETTING, + S3Repository.PARALLEL_MULTIPART_UPLOAD_MINIMUM_PART_SIZE_SETTING, + S3Repository.PARALLEL_MULTIPART_UPLOAD_ENABLED_SETTING ); } @Override public void reload(Settings settings) { // secure settings should be readable - final Map clientsSettings = S3ClientSettings.load(settings); + final Map clientsSettings = S3ClientSettings.load(settings, configPath); service.refreshAndClearCache(clientsSettings); + s3AsyncService.refreshAndClearCache(clientsSettings); } @Override public void close() throws IOException { service.close(); + s3AsyncService.close(); } } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3RetryingInputStream.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3RetryingInputStream.java index 388f5b8d74a2b..2c47dab386066 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3RetryingInputStream.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3RetryingInputStream.java @@ -31,23 +31,25 @@ package org.opensearch.repositories.s3; -import com.amazonaws.AmazonClientException; -import com.amazonaws.services.s3.model.AmazonS3Exception; -import com.amazonaws.services.s3.model.GetObjectRequest; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.S3Object; -import com.amazonaws.services.s3.model.S3ObjectInputStream; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.S3Exception; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.LegacyESVersion; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.repositories.s3.utils.HttpRangeUtils; import java.io.IOException; import java.io.InputStream; import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; /** * Wrapper around an S3 object that will retry the {@link GetObjectRequest} if the download fails part-way through, resuming from where @@ -69,7 +71,8 @@ class S3RetryingInputStream extends InputStream { private final int maxAttempts; private final List failures; - private S3ObjectInputStream currentStream; + private ResponseInputStream currentStream; + private final AtomicBoolean isStreamAborted = new AtomicBoolean(); private long currentStreamLastOffset; private int attempt = 1; private long currentOffset; @@ -99,8 +102,10 @@ class S3RetryingInputStream extends InputStream { private void openStream() throws IOException { try (AmazonS3Reference clientReference = blobStore.clientReference()) { - final GetObjectRequest getObjectRequest = new GetObjectRequest(blobStore.bucket(), blobKey); - getObjectRequest.setRequestMetricCollector(blobStore.getMetricCollector); + final GetObjectRequest.Builder getObjectRequest = GetObjectRequest.builder() + .bucket(blobStore.bucket()) + .key(blobKey) + .overrideConfiguration(o -> o.addMetricPublisher(blobStore.getStatsMetricPublisher().getObjectMetricPublisher)); if (currentOffset > 0 || start > 0 || end < Long.MAX_VALUE - 1) { assert start + currentOffset <= end : "requesting beyond end, start = " + start @@ -108,14 +113,20 @@ private void openStream() throws IOException { + currentOffset + " end=" + end; - getObjectRequest.setRange(Math.addExact(start, currentOffset), end); + getObjectRequest.range(HttpRangeUtils.toHttpRangeHeader(Math.addExact(start, currentOffset), end)); } - final S3Object s3Object = SocketAccess.doPrivileged(() -> clientReference.get().getObject(getObjectRequest)); - this.currentStreamLastOffset = Math.addExact(Math.addExact(start, currentOffset), getStreamLength(s3Object)); - this.currentStream = s3Object.getObjectContent(); - } catch (final AmazonClientException e) { - if (e instanceof AmazonS3Exception) { - if (404 == ((AmazonS3Exception) e).getStatusCode()) { + final ResponseInputStream getObjectResponseInputStream = SocketAccess.doPrivileged( + () -> clientReference.get().getObject(getObjectRequest.build()) + ); + this.currentStreamLastOffset = Math.addExact( + Math.addExact(start, currentOffset), + getObjectResponseInputStream.response().contentLength() + ); + this.currentStream = getObjectResponseInputStream; + this.isStreamAborted.set(false); + } catch (final SdkException e) { + if (e instanceof S3Exception) { + if (404 == ((S3Exception) e).statusCode()) { throw addSuppressedExceptions(new NoSuchFileException("Blob object [" + blobKey + "] not found: " + e.getMessage())); } } @@ -123,30 +134,6 @@ private void openStream() throws IOException { } } - private long getStreamLength(final S3Object object) { - final ObjectMetadata metadata = object.getObjectMetadata(); - try { - // Returns the content range of the object if response contains the Content-Range header. - final Long[] range = metadata.getContentRange(); - if (range != null) { - assert range[1] >= range[0] : range[1] + " vs " + range[0]; - assert range[0] == start + currentOffset : "Content-Range start value [" - + range[0] - + "] exceeds start [" - + start - + "] + current offset [" - + currentOffset - + ']'; - assert range[1] == end : "Content-Range end value [" + range[1] + "] exceeds end [" + end + ']'; - return range[1] - range[0] + 1L; - } - return metadata.getContentLength(); - } catch (Exception e) { - assert false : e; - return Long.MAX_VALUE - 1L; // assume a large stream so that the underlying stream is aborted on closing, unless eof is reached - } - } - @Override public int read() throws IOException { ensureOpen(); @@ -236,16 +223,17 @@ public void close() throws IOException { } /** - * Abort the {@link S3ObjectInputStream} if it wasn't read completely at the time this method is called, + * Abort the {@link ResponseInputStream} if it wasn't read completely at the time this method is called, * suppressing all thrown exceptions. */ - private void maybeAbort(S3ObjectInputStream stream) { - if (isEof()) { + private void maybeAbort(ResponseInputStream stream) { + if (isEof() || isAborted()) { return; } try { if (start + currentOffset < currentStreamLastOffset) { stream.abort(); + isStreamAborted.compareAndSet(false, true); } } catch (Exception e) { logger.warn("Failed to abort stream before closing", e); @@ -276,9 +264,6 @@ boolean isEof() { // package-private for tests boolean isAborted() { - if (currentStream == null || currentStream.getHttpRequest() == null) { - return false; - } - return currentStream.getHttpRequest().isAborted(); + return isStreamAborted.get(); } } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Service.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Service.java index 3ce19378ac05c..b1b3e19eac275 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Service.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/S3Service.java @@ -32,19 +32,31 @@ package org.opensearch.repositories.s3; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.http.IdleConnectionReaper; -import com.amazonaws.http.SystemPropertyTlsKeyManagersProvider; -import com.amazonaws.http.conn.ssl.SdkTLSSocketFactory; -import com.amazonaws.internal.SdkSSLContext; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.s3.internal.Constants; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.retry.RetryPolicy; +import software.amazon.awssdk.core.retry.backoff.BackoffStrategy; +import software.amazon.awssdk.http.SystemPropertyTlsKeyManagersProvider; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.http.apache.ProxyConfiguration; +import software.amazon.awssdk.http.apache.internal.conn.SdkTlsSocketFactory; +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3ClientBuilder; +import software.amazon.awssdk.services.sts.StsClient; +import software.amazon.awssdk.services.sts.StsClientBuilder; +import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; +import software.amazon.awssdk.services.sts.auth.StsWebIdentityTokenFileCredentialsProvider; +import software.amazon.awssdk.services.sts.model.AssumeRoleRequest; import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; @@ -52,11 +64,17 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.cluster.metadata.RepositoryMetadata; -import org.opensearch.common.Strings; +import org.opensearch.common.Nullable; +import org.opensearch.common.SuppressForbidden; import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; +import org.opensearch.repositories.s3.S3ClientSettings.IrsaCredentials; +import org.opensearch.repositories.s3.utils.AwsRequestSigner; +import org.opensearch.repositories.s3.utils.Protocol; import javax.net.ssl.SSLContext; + import java.io.Closeable; import java.io.IOException; import java.net.Authenticator; @@ -64,28 +82,43 @@ import java.net.PasswordAuthentication; import java.net.Proxy; import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.time.Duration; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import static java.util.Collections.emptyMap; class S3Service implements Closeable { private static final Logger logger = LogManager.getLogger(S3Service.class); - private volatile Map clientsCache = emptyMap(); + private static final String STS_ENDPOINT_OVERRIDE_SYSTEM_PROPERTY = "aws.stsEndpointOverride"; + + private static final String DEFAULT_S3_ENDPOINT = "s3.amazonaws.com"; + + private volatile Map clientsCache = new ConcurrentHashMap<>(); /** * Client settings calculated from static configuration and settings in the keystore. */ - private volatile Map staticClientSettings = MapBuilder.newMapBuilder() - .put("default", S3ClientSettings.getClientSettings(Settings.EMPTY, "default")) - .immutableMap(); + private volatile Map staticClientSettings; /** * Client settings derived from those in {@link #staticClientSettings} by combining them with settings * in the {@link RepositoryMetadata}. */ - private volatile Map derivedClientSettings = emptyMap(); + private volatile Map derivedClientSettings = new ConcurrentHashMap<>(); + + S3Service(final Path configPath) { + staticClientSettings = MapBuilder.newMapBuilder() + .put("default", S3ClientSettings.getClientSettings(Settings.EMPTY, "default", configPath)) + .immutableMap(); + } /** * Refreshes the settings for the AmazonS3 clients and clears the cache of @@ -163,19 +196,22 @@ S3ClientSettings settings(RepositoryMetadata repositoryMetadata) { } // proxy for testing - AmazonS3 buildClient(final S3ClientSettings clientSettings) { - final AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard(); - builder.withCredentials(buildCredentials(logger, clientSettings)); - builder.withClientConfiguration(buildConfiguration(clientSettings)); + AmazonS3WithCredentials buildClient(final S3ClientSettings clientSettings) { + setDefaultAwsProfilePath(); + final S3ClientBuilder builder = S3Client.builder(); - String endpoint = Strings.hasLength(clientSettings.endpoint) ? clientSettings.endpoint : Constants.S3_HOSTNAME; + final AwsCredentialsProvider credentials = buildCredentials(logger, clientSettings); + builder.credentialsProvider(credentials); + builder.httpClientBuilder(buildHttpClient(clientSettings)); + builder.overrideConfiguration(buildOverrideConfiguration(clientSettings)); + + String endpoint = Strings.hasLength(clientSettings.endpoint) ? clientSettings.endpoint : DEFAULT_S3_ENDPOINT; if ((endpoint.startsWith("http://") || endpoint.startsWith("https://")) == false) { // Manually add the schema to the endpoint to work around https://github.com/aws/aws-sdk-java/issues/2274 // TODO: Remove this once fixed in the AWS SDK endpoint = clientSettings.protocol.toString() + "://" + endpoint; } - final String region = Strings.hasLength(clientSettings.region) ? clientSettings.region : null; - logger.debug("using endpoint [{}] and region [{}]", endpoint, region); + logger.debug("using endpoint [{}] and region [{}]", endpoint, clientSettings.region); // If the endpoint configuration isn't set on the builder then the default behaviour is to try // and work out what region we are in and use an appropriate endpoint - see AwsClientBuilder#setRegion. @@ -185,25 +221,43 @@ AmazonS3 buildClient(final S3ClientSettings clientSettings) { // // We do this because directly constructing the client is deprecated (was already deprecated in 1.1.223 too) // so this change removes that usage of a deprecated API. - builder.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region)); + builder.endpointOverride(URI.create(endpoint)); + if (Strings.hasText(clientSettings.region)) { + builder.region(Region.of(clientSettings.region)); + } if (clientSettings.pathStyleAccess) { - builder.enablePathStyleAccess(); + builder.forcePathStyle(true); } if (clientSettings.disableChunkedEncoding) { - builder.disableChunkedEncoding(); + builder.serviceConfiguration(s -> s.chunkedEncodingEnabled(false)); } - return SocketAccess.doPrivileged(builder::build); + final S3Client client = SocketAccess.doPrivileged(builder::build); + return AmazonS3WithCredentials.create(client, credentials); } - // pkg private for tests - static ClientConfiguration buildConfiguration(S3ClientSettings clientSettings) { - final ClientConfiguration clientConfiguration = new ClientConfiguration(); - // the response metadata cache is only there for diagnostics purposes, - // but can force objects from every response to the old generation. - clientConfiguration.setResponseMetadataCacheSize(0); - clientConfiguration.setProtocol(clientSettings.protocol); - - if (clientSettings.proxySettings != ProxySettings.NO_PROXY_SETTINGS) { + // Aws v2 sdk tries to load a default profile from home path which is restricted. Hence, setting these to random + // valid paths. + @SuppressForbidden(reason = "Need to provide this override to v2 SDK so that path does not default to home path") + static void setDefaultAwsProfilePath() { + if (ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.getStringValue().isEmpty()) { + SocketAccess.doPrivileged( + () -> System.setProperty( + ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property(), + System.getProperty("opensearch.path.conf") + ) + ); + } + if (ProfileFileSystemSetting.AWS_CONFIG_FILE.getStringValue().isEmpty()) { + SocketAccess.doPrivileged( + () -> System.setProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), System.getProperty("opensearch.path.conf")) + ); + } + } + + static ApacheHttpClient.Builder buildHttpClient(S3ClientSettings clientSettings) { + ApacheHttpClient.Builder clientBuilder = ApacheHttpClient.builder(); + + if (!clientSettings.proxySettings.equals(ProxySettings.NO_PROXY_SETTINGS)) { if (clientSettings.proxySettings.getType() == ProxySettings.ProxyType.SOCKS) { SocketAccess.doPrivilegedVoid(() -> { if (clientSettings.proxySettings.isAuthenticated()) { @@ -217,86 +271,233 @@ protected PasswordAuthentication getPasswordAuthentication() { } }); } - clientConfiguration.getApacheHttpClientConfig() - .setSslSocketFactory(createSocksSslConnectionSocketFactory(clientSettings.proxySettings.getAddress())); + clientBuilder.socketFactory(createSocksSslConnectionSocketFactory(clientSettings.proxySettings.getAddress())); }); } else { - if (clientSettings.proxySettings.getType() != ProxySettings.ProxyType.DIRECT) { - clientConfiguration.setProxyProtocol(clientSettings.proxySettings.getType().toProtocol()); - } - clientConfiguration.setProxyHost(clientSettings.proxySettings.getHostName()); - clientConfiguration.setProxyPort(clientSettings.proxySettings.getPort()); - clientConfiguration.setProxyUsername(clientSettings.proxySettings.getUsername()); - clientConfiguration.setProxyPassword(clientSettings.proxySettings.getPassword()); + clientBuilder.proxyConfiguration(buildHttpProxyConfiguration(clientSettings)); } } - if (Strings.hasLength(clientSettings.signerOverride)) { - clientConfiguration.setSignerOverride(clientSettings.signerOverride); + clientBuilder.socketTimeout(Duration.ofMillis(clientSettings.readTimeoutMillis)); + + return clientBuilder; + } + + static ProxyConfiguration buildHttpProxyConfiguration(S3ClientSettings clientSettings) { + ProxyConfiguration.Builder proxyConfiguration = ProxyConfiguration.builder(); + if (clientSettings.proxySettings.getType() == ProxySettings.ProxyType.SOCKS) { + return proxyConfiguration.build(); } - clientConfiguration.setMaxErrorRetry(clientSettings.maxRetries); - clientConfiguration.setUseThrottleRetries(clientSettings.throttleRetries); - clientConfiguration.setSocketTimeout(clientSettings.readTimeoutMillis); + Protocol proxyProtocol = clientSettings.proxySettings.getType() == ProxySettings.ProxyType.DIRECT + ? Protocol.HTTP + : clientSettings.proxySettings.getType().toProtocol(); - return clientConfiguration; + try { + proxyConfiguration = proxyConfiguration.endpoint( + new URI( + proxyProtocol.toString(), + null, + clientSettings.proxySettings.getHost(), + clientSettings.proxySettings.getPort(), + null, + null, + null + ) + ); + } catch (URISyntaxException e) { + throw SdkException.create("Invalid proxy URL", e); + } + + proxyConfiguration = proxyConfiguration.username(clientSettings.proxySettings.getUsername()); + proxyConfiguration = proxyConfiguration.password(clientSettings.proxySettings.getPassword()); + + return proxyConfiguration.build(); + } + + static ClientOverrideConfiguration buildOverrideConfiguration(final S3ClientSettings clientSettings) { + ClientOverrideConfiguration.Builder clientOverrideConfiguration = ClientOverrideConfiguration.builder(); + if (Strings.hasLength(clientSettings.signerOverride)) { + clientOverrideConfiguration = clientOverrideConfiguration.putAdvancedOption( + SdkAdvancedClientOption.SIGNER, + AwsRequestSigner.fromSignerName(clientSettings.signerOverride).getSigner() + ); + } + RetryPolicy.Builder retryPolicy = SocketAccess.doPrivileged( + () -> RetryPolicy.builder().numRetries(clientSettings.maxRetries).retryCapacityCondition(null) + ); + if (!clientSettings.throttleRetries) { + retryPolicy.throttlingBackoffStrategy(BackoffStrategy.none()); + } + return clientOverrideConfiguration.retryPolicy(retryPolicy.build()).build(); } private static SSLConnectionSocketFactory createSocksSslConnectionSocketFactory(final InetSocketAddress address) { // This part was taken from AWS settings - final SSLContext sslCtx = SdkSSLContext.getPreferredSSLContext( - new SystemPropertyTlsKeyManagersProvider().getKeyManagers(), - new SecureRandom() - ); - return new SdkTLSSocketFactory(sslCtx, new DefaultHostnameVerifier()) { - @Override - public Socket createSocket(final HttpContext ctx) throws IOException { - return new Socket(new Proxy(Proxy.Type.SOCKS, address)); - } - }; + try { + final SSLContext sslCtx = SSLContext.getInstance("TLS"); + sslCtx.init(SystemPropertyTlsKeyManagersProvider.create().keyManagers(), null, new SecureRandom()); + return new SdkTlsSocketFactory(sslCtx, new DefaultHostnameVerifier()) { + @Override + public Socket createSocket(final HttpContext ctx) throws IOException { + return new Socket(new Proxy(Proxy.Type.SOCKS, address)); + } + }; + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw SdkException.create("Exception during SSL context creation for SOCKS proxy", e); + } } // pkg private for tests - static AWSCredentialsProvider buildCredentials(Logger logger, S3ClientSettings clientSettings) { - final S3BasicCredentials credentials = clientSettings.credentials; - if (credentials == null) { + static AwsCredentialsProvider buildCredentials(Logger logger, S3ClientSettings clientSettings) { + final AwsCredentials basicCredentials = clientSettings.credentials; + final IrsaCredentials irsaCredentials = buildFromEnviroment(clientSettings.irsaCredentials); + + // If IAM Roles for Service Accounts (IRSA) credentials are configured, start with them first + if (irsaCredentials != null) { + logger.debug("Using IRSA credentials"); + + StsClient stsClient = SocketAccess.doPrivileged(() -> { + StsClientBuilder builder = StsClient.builder(); + if (Strings.hasText(clientSettings.region)) { + builder.region(Region.of(clientSettings.region)); + } + + final String stsEndpoint = System.getProperty(STS_ENDPOINT_OVERRIDE_SYSTEM_PROPERTY); + if (stsEndpoint != null) { + builder = builder.endpointOverride(URI.create(stsEndpoint)); + } + + if (basicCredentials != null) { + builder = builder.credentialsProvider(StaticCredentialsProvider.create(basicCredentials)); + } else { + builder = builder.credentialsProvider(DefaultCredentialsProvider.create()); + } + + return builder.build(); + }); + + if (irsaCredentials.getIdentityTokenFile() == null) { + final StsAssumeRoleCredentialsProvider.Builder stsCredentialsProviderBuilder = StsAssumeRoleCredentialsProvider.builder() + .stsClient(stsClient) + .refreshRequest( + AssumeRoleRequest.builder() + .roleArn(irsaCredentials.getRoleArn()) + .roleSessionName(irsaCredentials.getRoleSessionName()) + .build() + ); + + final StsAssumeRoleCredentialsProvider stsCredentialsProvider = SocketAccess.doPrivileged( + stsCredentialsProviderBuilder::build + ); + + return new PrivilegedSTSAssumeRoleSessionCredentialsProvider<>(stsClient, stsCredentialsProvider); + } else { + final StsWebIdentityTokenFileCredentialsProvider.Builder stsCredentialsProviderBuilder = + StsWebIdentityTokenFileCredentialsProvider.builder() + .stsClient(stsClient) + .roleArn(irsaCredentials.getRoleArn()) + .roleSessionName(irsaCredentials.getRoleSessionName()) + .webIdentityTokenFile(Path.of(irsaCredentials.getIdentityTokenFile())); + + final StsWebIdentityTokenFileCredentialsProvider stsCredentialsProvider = SocketAccess.doPrivileged( + stsCredentialsProviderBuilder::build + ); + + return new PrivilegedSTSAssumeRoleSessionCredentialsProvider<>(stsClient, stsCredentialsProvider); + } + } else if (basicCredentials != null) { + logger.debug("Using basic key/secret credentials"); + return StaticCredentialsProvider.create(basicCredentials); + } else { logger.debug("Using instance profile credentials"); return new PrivilegedInstanceProfileCredentialsProvider(); - } else { - logger.debug("Using basic key/secret credentials"); - return new AWSStaticCredentialsProvider(credentials); } } + private static IrsaCredentials buildFromEnviroment(IrsaCredentials defaults) { + if (defaults == null) { + return null; + } + + String webIdentityTokenFile = defaults.getIdentityTokenFile(); + if (webIdentityTokenFile == null) { + webIdentityTokenFile = System.getenv(SdkSystemSetting.AWS_WEB_IDENTITY_TOKEN_FILE.environmentVariable()); + } + + String roleArn = defaults.getRoleArn(); + if (roleArn == null) { + roleArn = System.getenv(SdkSystemSetting.AWS_ROLE_ARN.environmentVariable()); + } + + String roleSessionName = defaults.getRoleSessionName(); + if (roleSessionName == null) { + roleSessionName = System.getenv(SdkSystemSetting.AWS_ROLE_SESSION_NAME.environmentVariable()); + } + + return new IrsaCredentials(webIdentityTokenFile, roleArn, roleSessionName); + } + private synchronized void releaseCachedClients() { // the clients will shutdown when they will not be used anymore for (final AmazonS3Reference clientReference : clientsCache.values()) { clientReference.decRef(); } + // clear previously cached clients, they will be build lazily clientsCache = emptyMap(); derivedClientSettings = emptyMap(); - // shutdown IdleConnectionReaper background thread - // it will be restarted on new client usage - IdleConnectionReaper.shutdown(); } - static class PrivilegedInstanceProfileCredentialsProvider implements AWSCredentialsProvider { - private final AWSCredentialsProvider credentials; + static class PrivilegedInstanceProfileCredentialsProvider implements AwsCredentialsProvider { + private final AwsCredentialsProvider credentials; private PrivilegedInstanceProfileCredentialsProvider() { + this.credentials = initializeProvider(); + } + + private AwsCredentialsProvider initializeProvider() { + if (SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI.getStringValue().isPresent() + || SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_FULL_URI.getStringValue().isPresent()) { + + return ContainerCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build(); + } // InstanceProfileCredentialsProvider as last item of chain - this.credentials = new EC2ContainerCredentialsProviderWrapper(); + return InstanceProfileCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build(); } @Override - public AWSCredentials getCredentials() { - return SocketAccess.doPrivileged(credentials::getCredentials); + public AwsCredentials resolveCredentials() { + return SocketAccess.doPrivileged(credentials::resolveCredentials); + } + } + + static class PrivilegedSTSAssumeRoleSessionCredentialsProvider

    + implements + AwsCredentialsProvider, + Closeable { + private final P credentials; + private final StsClient stsClient; + + private PrivilegedSTSAssumeRoleSessionCredentialsProvider(@Nullable final StsClient stsClient, final P credentials) { + this.stsClient = stsClient; + this.credentials = credentials; + } + + @Override + public void close() throws IOException { + SocketAccess.doPrivilegedIOException(() -> { + credentials.close(); + if (stsClient != null) { + stsClient.close(); + } + return null; + }); } @Override - public void refresh() { - SocketAccess.doPrivilegedVoid(credentials::refresh); + public AwsCredentials resolveCredentials() { + return SocketAccess.doPrivileged(credentials::resolveCredentials); } } diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/SocketAccess.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/SocketAccess.java index 0a6408764aeeb..4888764dbc720 100644 --- a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/SocketAccess.java +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/SocketAccess.java @@ -46,7 +46,7 @@ * {@link SocketPermission} 'connect' to establish connections. This class wraps the operations requiring access in * {@link AccessController#doPrivileged(PrivilegedAction)} blocks. */ -final class SocketAccess { +public final class SocketAccess { private SocketAccess() {} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/StatsMetricPublisher.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/StatsMetricPublisher.java new file mode 100644 index 0000000000000..cad0037f99249 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/StatsMetricPublisher.java @@ -0,0 +1,118 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import software.amazon.awssdk.http.HttpMetric; +import software.amazon.awssdk.metrics.MetricCollection; +import software.amazon.awssdk.metrics.MetricPublisher; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +public class StatsMetricPublisher { + + private final Stats stats = new Stats(); + + public MetricPublisher listObjectsMetricPublisher = new MetricPublisher() { + @Override + public void publish(MetricCollection metricCollection) { + stats.listCount.addAndGet( + metricCollection.children() + .stream() + .filter( + metricRecords -> metricRecords.name().equals("ApiCallAttempt") + && !metricRecords.metricValues(HttpMetric.HTTP_STATUS_CODE).isEmpty() + ) + .count() + ); + } + + @Override + public void close() {} + }; + + public MetricPublisher getObjectMetricPublisher = new MetricPublisher() { + @Override + public void publish(MetricCollection metricCollection) { + stats.getCount.addAndGet( + metricCollection.children() + .stream() + .filter( + metricRecords -> metricRecords.name().equals("ApiCallAttempt") + && !metricRecords.metricValues(HttpMetric.HTTP_STATUS_CODE).isEmpty() + ) + .count() + ); + } + + @Override + public void close() {} + }; + + public MetricPublisher putObjectMetricPublisher = new MetricPublisher() { + @Override + public void publish(MetricCollection metricCollection) { + stats.putCount.addAndGet( + metricCollection.children() + .stream() + .filter( + metricRecords -> metricRecords.name().equals("ApiCallAttempt") + && !metricRecords.metricValues(HttpMetric.HTTP_STATUS_CODE).isEmpty() + ) + .count() + ); + } + + @Override + public void close() {} + }; + + public MetricPublisher multipartUploadMetricCollector = new MetricPublisher() { + @Override + public void publish(MetricCollection metricCollection) { + stats.postCount.addAndGet( + metricCollection.children() + .stream() + .filter( + metricRecords -> metricRecords.name().equals("ApiCallAttempt") + && !metricRecords.metricValues(HttpMetric.HTTP_STATUS_CODE).isEmpty() + ) + .count() + ); + } + + @Override + public void close() {} + }; + + public Stats getStats() { + return stats; + } + + static class Stats { + + final AtomicLong listCount = new AtomicLong(); + + final AtomicLong getCount = new AtomicLong(); + + final AtomicLong putCount = new AtomicLong(); + + final AtomicLong postCount = new AtomicLong(); + + Map toMap() { + final Map results = new HashMap<>(); + results.put("GetObject", getCount.get()); + results.put("ListObjects", listCount.get()); + results.put("PutObject", putCount.get()); + results.put("PutMultipartObject", postCount.get()); + return results; + } + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncExecutorContainer.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncExecutorContainer.java new file mode 100644 index 0000000000000..1ae1a15ad4010 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncExecutorContainer.java @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.async; + +import java.util.concurrent.ExecutorService; + +/** + * An encapsulation for the {@link AsyncTransferEventLoopGroup}, and the stream reader and future completion executor services + */ +public class AsyncExecutorContainer { + + private final ExecutorService futureCompletionExecutor; + private final ExecutorService streamReader; + private final AsyncTransferEventLoopGroup asyncTransferEventLoopGroup; + + /** + * Construct a new AsyncExecutorBuilder object + * + * @param futureCompletionExecutor An {@link ExecutorService} to pass to {@link software.amazon.awssdk.services.s3.S3AsyncClient} for future completion + * @param streamReader An {@link ExecutorService} to read streams for upload + * @param asyncTransferEventLoopGroup A {@link AsyncTransferEventLoopGroup} which encapsulates the netty {@link io.netty.channel.EventLoopGroup} for async uploads + */ + public AsyncExecutorContainer( + ExecutorService futureCompletionExecutor, + ExecutorService streamReader, + AsyncTransferEventLoopGroup asyncTransferEventLoopGroup + ) { + this.asyncTransferEventLoopGroup = asyncTransferEventLoopGroup; + this.streamReader = streamReader; + this.futureCompletionExecutor = futureCompletionExecutor; + } + + public ExecutorService getFutureCompletionExecutor() { + return futureCompletionExecutor; + } + + public AsyncTransferEventLoopGroup getAsyncTransferEventLoopGroup() { + return asyncTransferEventLoopGroup; + } + + public ExecutorService getStreamReader() { + return streamReader; + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncPartsHandler.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncPartsHandler.java new file mode 100644 index 0000000000000..ad6939ce299d6 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncPartsHandler.java @@ -0,0 +1,184 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.async; + +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; +import software.amazon.awssdk.services.s3.model.CompletedPart; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.model.UploadPartResponse; +import software.amazon.awssdk.utils.CompletableFutureUtils; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.common.StreamContext; +import org.opensearch.common.blobstore.stream.write.WritePriority; +import org.opensearch.common.io.InputStreamContainer; +import org.opensearch.repositories.s3.SocketAccess; +import org.opensearch.repositories.s3.io.CheckedContainer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * Responsible for handling parts of the original multipart request + */ +public class AsyncPartsHandler { + + private static Logger log = LogManager.getLogger(AsyncPartsHandler.class); + + /** + * Uploads parts of the upload multipart request* + * @param s3AsyncClient S3 client to use for upload + * @param executorService Thread pool for regular upload + * @param priorityExecutorService Thread pool for priority uploads + * @param uploadRequest request for upload + * @param streamContext Stream context used in supplying individual file parts + * @param uploadId Upload Id against which multi-part is being performed + * @param completedParts Reference of completed parts + * @param inputStreamContainers Checksum containers + * @return list of completable futures + * @throws IOException thrown in case of an IO error + */ + public static List> uploadParts( + S3AsyncClient s3AsyncClient, + ExecutorService executorService, + ExecutorService priorityExecutorService, + UploadRequest uploadRequest, + StreamContext streamContext, + String uploadId, + AtomicReferenceArray completedParts, + AtomicReferenceArray inputStreamContainers + ) throws IOException { + List> futures = new ArrayList<>(); + for (int partIdx = 0; partIdx < streamContext.getNumberOfParts(); partIdx++) { + InputStreamContainer inputStreamContainer = streamContext.provideStream(partIdx); + inputStreamContainers.set(partIdx, new CheckedContainer(inputStreamContainer.getContentLength())); + UploadPartRequest.Builder uploadPartRequestBuilder = UploadPartRequest.builder() + .bucket(uploadRequest.getBucket()) + .partNumber(partIdx + 1) + .key(uploadRequest.getKey()) + .uploadId(uploadId) + .contentLength(inputStreamContainer.getContentLength()); + if (uploadRequest.doRemoteDataIntegrityCheck()) { + uploadPartRequestBuilder.checksumAlgorithm(ChecksumAlgorithm.CRC32); + } + uploadPart( + s3AsyncClient, + executorService, + priorityExecutorService, + completedParts, + inputStreamContainers, + futures, + uploadPartRequestBuilder.build(), + inputStreamContainer, + uploadRequest + ); + } + + return futures; + } + + /** + * Cleans up parts of the original multipart request* + * @param s3AsyncClient s3 client to use + * @param uploadRequest upload request + * @param uploadId upload id against which multi-part was carried out. + */ + public static void cleanUpParts(S3AsyncClient s3AsyncClient, UploadRequest uploadRequest, String uploadId) { + + AbortMultipartUploadRequest abortMultipartUploadRequest = AbortMultipartUploadRequest.builder() + .bucket(uploadRequest.getBucket()) + .key(uploadRequest.getKey()) + .uploadId(uploadId) + .build(); + SocketAccess.doPrivileged(() -> s3AsyncClient.abortMultipartUpload(abortMultipartUploadRequest).exceptionally(throwable -> { + log.warn( + () -> new ParameterizedMessage( + "Failed to abort previous multipart upload " + + "(id: {})" + + ". You may need to call " + + "S3AsyncClient#abortMultiPartUpload to " + + "free all storage consumed by" + + " all parts. ", + uploadId + ), + throwable + ); + return null; + })); + } + + private static void uploadPart( + S3AsyncClient s3AsyncClient, + ExecutorService executorService, + ExecutorService priorityExecutorService, + AtomicReferenceArray completedParts, + AtomicReferenceArray inputStreamContainers, + List> futures, + UploadPartRequest uploadPartRequest, + InputStreamContainer inputStreamContainer, + UploadRequest uploadRequest + ) { + Integer partNumber = uploadPartRequest.partNumber(); + + ExecutorService streamReadExecutor = uploadRequest.getWritePriority() == WritePriority.HIGH + ? priorityExecutorService + : executorService; + CompletableFuture uploadPartResponseFuture = SocketAccess.doPrivileged( + () -> s3AsyncClient.uploadPart( + uploadPartRequest, + AsyncRequestBody.fromInputStream( + inputStreamContainer.getInputStream(), + inputStreamContainer.getContentLength(), + streamReadExecutor + ) + ) + ); + + CompletableFuture convertFuture = uploadPartResponseFuture.thenApply( + uploadPartResponse -> convertUploadPartResponse( + completedParts, + inputStreamContainers, + uploadPartResponse, + partNumber, + uploadRequest.doRemoteDataIntegrityCheck() + ) + ); + futures.add(convertFuture); + + CompletableFutureUtils.forwardExceptionTo(convertFuture, uploadPartResponseFuture); + } + + private static CompletedPart convertUploadPartResponse( + AtomicReferenceArray completedParts, + AtomicReferenceArray inputStreamContainers, + UploadPartResponse partResponse, + int partNumber, + boolean isRemoteDataIntegrityCheckEnabled + ) { + CompletedPart.Builder completedPartBuilder = CompletedPart.builder().eTag(partResponse.eTag()).partNumber(partNumber); + if (isRemoteDataIntegrityCheckEnabled) { + completedPartBuilder.checksumCRC32(partResponse.checksumCRC32()); + CheckedContainer inputStreamCRC32Container = inputStreamContainers.get(partNumber - 1); + inputStreamCRC32Container.setChecksum(partResponse.checksumCRC32()); + inputStreamContainers.set(partNumber - 1, inputStreamCRC32Container); + } + CompletedPart completedPart = completedPartBuilder.build(); + completedParts.set(partNumber - 1, completedPart); + return completedPart; + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncTransferEventLoopGroup.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncTransferEventLoopGroup.java new file mode 100644 index 0000000000000..7a99989796444 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncTransferEventLoopGroup.java @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.async; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.common.util.concurrent.OpenSearchExecutors; +import org.opensearch.repositories.s3.SocketAccess; + +import java.io.Closeable; +import java.util.concurrent.TimeUnit; + +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.util.concurrent.Future; + +/** + * AsyncTransferEventLoopGroup is an encapsulation for netty {@link EventLoopGroup} + */ +public class AsyncTransferEventLoopGroup implements Closeable { + private static final String THREAD_PREFIX = "s3-async-transfer-worker"; + private final Logger logger = LogManager.getLogger(AsyncTransferEventLoopGroup.class); + + private final EventLoopGroup eventLoopGroup; + + /** + * Construct a new AsyncTransferEventLoopGroup + * + * @param eventLoopThreads The number of event loop threads for this event loop group + */ + public AsyncTransferEventLoopGroup(int eventLoopThreads) { + // Epoll event loop incurs less GC and provides better performance than Nio loop. Therefore, + // using epoll wherever available is preferred. + this.eventLoopGroup = SocketAccess.doPrivileged( + () -> Epoll.isAvailable() + ? new EpollEventLoopGroup(eventLoopThreads, OpenSearchExecutors.daemonThreadFactory(THREAD_PREFIX)) + : new NioEventLoopGroup(eventLoopThreads, OpenSearchExecutors.daemonThreadFactory(THREAD_PREFIX)) + ); + } + + public EventLoopGroup getEventLoopGroup() { + return eventLoopGroup; + } + + @Override + public void close() { + Future shutdownFuture = eventLoopGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS); + shutdownFuture.awaitUninterruptibly(); + if (!shutdownFuture.isSuccess()) { + logger.warn(new ParameterizedMessage("Error closing {} netty event loop group", THREAD_PREFIX), shutdownFuture.cause()); + } + } + +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncTransferManager.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncTransferManager.java new file mode 100644 index 0000000000000..8d45c2167a3d1 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/AsyncTransferManager.java @@ -0,0 +1,356 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.async; + +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.http.HttpStatusCode; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.CompletedMultipartUpload; +import software.amazon.awssdk.services.s3.model.CompletedPart; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.S3Exception; +import software.amazon.awssdk.utils.CompletableFutureUtils; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.ExceptionsHelper; +import org.opensearch.common.StreamContext; +import org.opensearch.common.blobstore.exception.CorruptFileException; +import org.opensearch.common.blobstore.stream.write.WritePriority; +import org.opensearch.common.io.InputStreamContainer; +import org.opensearch.common.util.ByteUtils; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.repositories.s3.SocketAccess; +import org.opensearch.repositories.s3.io.CheckedContainer; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.function.BiFunction; +import java.util.function.Supplier; +import java.util.stream.IntStream; + +import com.jcraft.jzlib.JZlib; + +/** + * A helper class that automatically uses multipart upload based on the size of the source object + */ +public final class AsyncTransferManager { + private static final Logger log = LogManager.getLogger(AsyncTransferManager.class); + private final ExecutorService executorService; + private final ExecutorService priorityExecutorService; + private final long minimumPartSize; + + /** + * The max number of parts on S3 side is 10,000 + */ + private static final long MAX_UPLOAD_PARTS = 10_000; + + /** + * Construct a new object of AsyncTransferManager + * + * @param minimumPartSize The minimum part size for parallel multipart uploads + * @param executorService The stream reader {@link ExecutorService} for normal priority uploads + * @param priorityExecutorService The stream read {@link ExecutorService} for high priority uploads + */ + public AsyncTransferManager(long minimumPartSize, ExecutorService executorService, ExecutorService priorityExecutorService) { + this.executorService = executorService; + this.priorityExecutorService = priorityExecutorService; + this.minimumPartSize = minimumPartSize; + } + + /** + * Upload an object to S3 using the async client + * + * @param s3AsyncClient S3 client to use for upload + * @param uploadRequest The {@link UploadRequest} object encapsulating all relevant details for upload + * @param streamContext The {@link StreamContext} to supply streams during upload + * @return A {@link CompletableFuture} to listen for upload completion + */ + public CompletableFuture uploadObject(S3AsyncClient s3AsyncClient, UploadRequest uploadRequest, StreamContext streamContext) { + + CompletableFuture returnFuture = new CompletableFuture<>(); + try { + if (streamContext.getNumberOfParts() == 1) { + log.debug(() -> "Starting the upload as a single upload part request"); + uploadInOneChunk(s3AsyncClient, uploadRequest, streamContext.provideStream(0), returnFuture); + } else { + log.debug(() -> "Starting the upload as multipart upload request"); + uploadInParts(s3AsyncClient, uploadRequest, streamContext, returnFuture); + } + } catch (Throwable throwable) { + returnFuture.completeExceptionally(throwable); + } + + return returnFuture; + } + + private void uploadInParts( + S3AsyncClient s3AsyncClient, + UploadRequest uploadRequest, + StreamContext streamContext, + CompletableFuture returnFuture + ) { + + CreateMultipartUploadRequest.Builder createMultipartUploadRequestBuilder = CreateMultipartUploadRequest.builder() + .bucket(uploadRequest.getBucket()) + .key(uploadRequest.getKey()); + if (uploadRequest.doRemoteDataIntegrityCheck()) { + createMultipartUploadRequestBuilder.checksumAlgorithm(ChecksumAlgorithm.CRC32); + } + CompletableFuture createMultipartUploadFuture = SocketAccess.doPrivileged( + () -> s3AsyncClient.createMultipartUpload(createMultipartUploadRequestBuilder.build()) + ); + + // Ensure cancellations are forwarded to the createMultipartUploadFuture future + CompletableFutureUtils.forwardExceptionTo(returnFuture, createMultipartUploadFuture); + + createMultipartUploadFuture.whenComplete((createMultipartUploadResponse, throwable) -> { + if (throwable != null) { + handleException(returnFuture, () -> "Failed to initiate multipart upload", throwable); + } else { + log.debug(() -> "Initiated new multipart upload, uploadId: " + createMultipartUploadResponse.uploadId()); + doUploadInParts(s3AsyncClient, uploadRequest, streamContext, returnFuture, createMultipartUploadResponse.uploadId()); + } + }); + } + + private void doUploadInParts( + S3AsyncClient s3AsyncClient, + UploadRequest uploadRequest, + StreamContext streamContext, + CompletableFuture returnFuture, + String uploadId + ) { + + // The list of completed parts must be sorted + AtomicReferenceArray completedParts = new AtomicReferenceArray<>(streamContext.getNumberOfParts()); + AtomicReferenceArray inputStreamContainers = new AtomicReferenceArray<>(streamContext.getNumberOfParts()); + + List> futures; + try { + futures = AsyncPartsHandler.uploadParts( + s3AsyncClient, + executorService, + priorityExecutorService, + uploadRequest, + streamContext, + uploadId, + completedParts, + inputStreamContainers + ); + } catch (Exception ex) { + try { + AsyncPartsHandler.cleanUpParts(s3AsyncClient, uploadRequest, uploadId); + } finally { + returnFuture.completeExceptionally(ex); + } + return; + } + + CompletableFutureUtils.allOfExceptionForwarded(futures.toArray(CompletableFuture[]::new)).thenApply(resp -> { + try { + uploadRequest.getUploadFinalizer().accept(true); + } catch (IOException e) { + throw new RuntimeException(e); + } + return resp; + }).thenApply(ignore -> { + if (uploadRequest.doRemoteDataIntegrityCheck()) { + mergeAndVerifyChecksum(inputStreamContainers, uploadRequest.getKey(), uploadRequest.getExpectedChecksum()); + } + return null; + }) + .thenCompose(ignore -> completeMultipartUpload(s3AsyncClient, uploadRequest, uploadId, completedParts)) + .handle(handleExceptionOrResponse(s3AsyncClient, uploadRequest, returnFuture, uploadId)) + .exceptionally(throwable -> { + handleException(returnFuture, () -> "Unexpected exception occurred", throwable); + return null; + }); + } + + private void mergeAndVerifyChecksum( + AtomicReferenceArray inputStreamContainers, + String fileName, + long expectedChecksum + ) { + long resultantChecksum = fromBase64String(inputStreamContainers.get(0).getChecksum()); + for (int index = 1; index < inputStreamContainers.length(); index++) { + long curChecksum = fromBase64String(inputStreamContainers.get(index).getChecksum()); + resultantChecksum = JZlib.crc32_combine(resultantChecksum, curChecksum, inputStreamContainers.get(index).getContentLength()); + } + + if (resultantChecksum != expectedChecksum) { + throw new RuntimeException(new CorruptFileException("File level checksums didn't match combined part checksums", fileName)); + } + } + + private BiFunction handleExceptionOrResponse( + S3AsyncClient s3AsyncClient, + UploadRequest uploadRequest, + CompletableFuture returnFuture, + String uploadId + ) { + + return (response, throwable) -> { + if (throwable != null) { + AsyncPartsHandler.cleanUpParts(s3AsyncClient, uploadRequest, uploadId); + handleException(returnFuture, () -> "Failed to send multipart upload requests.", throwable); + } else { + returnFuture.complete(null); + } + + return null; + }; + } + + private CompletableFuture completeMultipartUpload( + S3AsyncClient s3AsyncClient, + UploadRequest uploadRequest, + String uploadId, + AtomicReferenceArray completedParts + ) { + + log.debug(() -> new ParameterizedMessage("Sending completeMultipartUploadRequest, uploadId: {}", uploadId)); + CompletedPart[] parts = IntStream.range(0, completedParts.length()).mapToObj(completedParts::get).toArray(CompletedPart[]::new); + CompleteMultipartUploadRequest completeMultipartUploadRequest = CompleteMultipartUploadRequest.builder() + .bucket(uploadRequest.getBucket()) + .key(uploadRequest.getKey()) + .uploadId(uploadId) + .multipartUpload(CompletedMultipartUpload.builder().parts(parts).build()) + .build(); + + return SocketAccess.doPrivileged(() -> s3AsyncClient.completeMultipartUpload(completeMultipartUploadRequest)); + } + + private static String base64StringFromLong(Long val) { + return Base64.getEncoder().encodeToString(Arrays.copyOfRange(ByteUtils.toByteArrayBE(val), 4, 8)); + } + + private static long fromBase64String(String base64String) { + byte[] decodedBytes = Base64.getDecoder().decode(base64String); + if (decodedBytes.length != 4) { + throw new IllegalArgumentException("Invalid Base64 encoded CRC32 checksum"); + } + long result = 0; + for (int i = 0; i < 4; i++) { + result <<= 8; + result |= (decodedBytes[i] & 0xFF); + } + return result; + } + + private static void handleException(CompletableFuture returnFuture, Supplier message, Throwable throwable) { + Throwable cause = throwable instanceof CompletionException ? throwable.getCause() : throwable; + + if (cause instanceof Error) { + returnFuture.completeExceptionally(cause); + } else { + SdkClientException exception = SdkClientException.create(message.get(), cause); + returnFuture.completeExceptionally(exception); + } + } + + /** + * Calculates the optimal part size of each part request if the upload operation is carried out as multipart upload. + */ + public long calculateOptimalPartSize(long contentLengthOfSource) { + if (contentLengthOfSource < ByteSizeUnit.MB.toBytes(5)) { + return contentLengthOfSource; + } + double optimalPartSize = contentLengthOfSource / (double) MAX_UPLOAD_PARTS; + optimalPartSize = Math.ceil(optimalPartSize); + return (long) Math.max(optimalPartSize, minimumPartSize); + } + + private void uploadInOneChunk( + S3AsyncClient s3AsyncClient, + UploadRequest uploadRequest, + InputStreamContainer inputStreamContainer, + CompletableFuture returnFuture + ) { + PutObjectRequest.Builder putObjectRequestBuilder = PutObjectRequest.builder() + .bucket(uploadRequest.getBucket()) + .key(uploadRequest.getKey()) + .contentLength(uploadRequest.getContentLength()); + if (uploadRequest.doRemoteDataIntegrityCheck()) { + putObjectRequestBuilder.checksumAlgorithm(ChecksumAlgorithm.CRC32); + putObjectRequestBuilder.checksumCRC32(base64StringFromLong(uploadRequest.getExpectedChecksum())); + } + ExecutorService streamReadExecutor = uploadRequest.getWritePriority() == WritePriority.HIGH + ? priorityExecutorService + : executorService; + CompletableFuture putObjectFuture = SocketAccess.doPrivileged( + () -> s3AsyncClient.putObject( + putObjectRequestBuilder.build(), + AsyncRequestBody.fromInputStream( + inputStreamContainer.getInputStream(), + inputStreamContainer.getContentLength(), + streamReadExecutor + ) + ).handle((resp, throwable) -> { + if (throwable != null) { + Throwable unwrappedThrowable = ExceptionsHelper.unwrap(throwable, S3Exception.class); + if (unwrappedThrowable != null) { + S3Exception s3Exception = (S3Exception) unwrappedThrowable; + if (s3Exception.statusCode() == HttpStatusCode.BAD_REQUEST + && "BadDigest".equals(s3Exception.awsErrorDetails().errorCode())) { + throw new RuntimeException(new CorruptFileException(s3Exception, uploadRequest.getKey())); + } + } + returnFuture.completeExceptionally(throwable); + } else { + try { + uploadRequest.getUploadFinalizer().accept(true); + } catch (IOException e) { + throw new RuntimeException(e); + } + returnFuture.complete(null); + } + + return null; + }).handle((resp, throwable) -> { + if (throwable != null) { + deleteUploadedObject(s3AsyncClient, uploadRequest); + returnFuture.completeExceptionally(throwable); + } + + return null; + }) + ); + + CompletableFutureUtils.forwardExceptionTo(returnFuture, putObjectFuture); + CompletableFutureUtils.forwardResultTo(putObjectFuture, returnFuture); + } + + private void deleteUploadedObject(S3AsyncClient s3AsyncClient, UploadRequest uploadRequest) { + DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder() + .bucket(uploadRequest.getBucket()) + .key(uploadRequest.getKey()) + .build(); + + SocketAccess.doPrivileged(() -> s3AsyncClient.deleteObject(deleteObjectRequest)).exceptionally(throwable -> { + log.error(() -> new ParameterizedMessage("Failed to delete uploaded object of key {}", uploadRequest.getKey()), throwable); + return null; + }); + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/UploadRequest.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/UploadRequest.java new file mode 100644 index 0000000000000..3804c8417eb9f --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/async/UploadRequest.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.async; + +import org.opensearch.common.CheckedConsumer; +import org.opensearch.common.blobstore.stream.write.WritePriority; + +import java.io.IOException; + +/** + * A model encapsulating all details for an upload to S3 + */ +public class UploadRequest { + private final String bucket; + private final String key; + private final long contentLength; + private final WritePriority writePriority; + private final CheckedConsumer uploadFinalizer; + private final boolean doRemoteDataIntegrityCheck; + private final Long expectedChecksum; + + /** + * Construct a new UploadRequest object + * + * @param bucket The name of the S3 bucket + * @param key Key of the file needed to be uploaded + * @param contentLength Total content length of the file for upload + * @param writePriority The priority of this upload + * @param uploadFinalizer An upload finalizer to call once all parts are uploaded + * @param doRemoteDataIntegrityCheck A boolean to inform vendor plugins whether remote data integrity checks need to be done + * @param expectedChecksum Checksum of the file being uploaded for remote data integrity check + */ + public UploadRequest( + String bucket, + String key, + long contentLength, + WritePriority writePriority, + CheckedConsumer uploadFinalizer, + boolean doRemoteDataIntegrityCheck, + Long expectedChecksum + ) { + this.bucket = bucket; + this.key = key; + this.contentLength = contentLength; + this.writePriority = writePriority; + this.uploadFinalizer = uploadFinalizer; + this.doRemoteDataIntegrityCheck = doRemoteDataIntegrityCheck; + this.expectedChecksum = expectedChecksum; + } + + public String getBucket() { + return bucket; + } + + public String getKey() { + return key; + } + + public long getContentLength() { + return contentLength; + } + + public WritePriority getWritePriority() { + return writePriority; + } + + public CheckedConsumer getUploadFinalizer() { + return uploadFinalizer; + } + + public boolean doRemoteDataIntegrityCheck() { + return doRemoteDataIntegrityCheck; + } + + public Long getExpectedChecksum() { + return expectedChecksum; + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/io/CheckedContainer.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/io/CheckedContainer.java new file mode 100644 index 0000000000000..0596424093dca --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/io/CheckedContainer.java @@ -0,0 +1,31 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.io; + +public class CheckedContainer { + + private String checksum; + private long contentLength; + + public CheckedContainer(long contentLength) { + this.contentLength = contentLength; + } + + public void setChecksum(String checksum) { + this.checksum = checksum; + } + + public String getChecksum() { + return checksum; + } + + public long getContentLength() { + return contentLength; + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/AwsRequestSigner.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/AwsRequestSigner.java new file mode 100644 index 0000000000000..036629c22245d --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/AwsRequestSigner.java @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.utils; + +import software.amazon.awssdk.auth.signer.Aws4Signer; +import software.amazon.awssdk.auth.signer.Aws4UnsignedPayloadSigner; +import software.amazon.awssdk.auth.signer.AwsS3V4Signer; +import software.amazon.awssdk.core.signer.NoOpSigner; +import software.amazon.awssdk.core.signer.Signer; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public enum AwsRequestSigner { + + VERSION_FOUR_SIGNER("AWS4SignerType", Aws4Signer.create()), + VERSION_FOUR_UNSIGNED_PAYLOAD_SIGNER("AWS4UnsignedPayloadSignerType", Aws4UnsignedPayloadSigner.create()), + NO_OP_SIGNER("NoOpSignerType", new NoOpSigner()), + S3_V4_SIGNER("AWSS3V4SignerType", AwsS3V4Signer.create()); + + private final String name; + private final Signer signer; + + AwsRequestSigner(String name, Signer signer) { + this.name = name; + this.signer = signer; + } + + public String getName() { + return name; + } + + public Signer getSigner() { + return signer; + } + + public static AwsRequestSigner fromSignerName(String signerName) { + List matchingSigners = Arrays.stream(AwsRequestSigner.values()) + .filter(awsRequestSigner -> awsRequestSigner.getName().equals(signerName)) + .collect(Collectors.toList()); + if (matchingSigners.size() < 1) { + throw new IllegalArgumentException("No matching signers found for signerName: " + signerName); + } else if (matchingSigners.size() > 1) { + throw new IllegalArgumentException("More than 1 matching signers found for signerName: " + signerName); + } + return matchingSigners.get(0); + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/HttpRangeUtils.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/HttpRangeUtils.java new file mode 100644 index 0000000000000..2e2fc9b86a45b --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/HttpRangeUtils.java @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.utils; + +import software.amazon.awssdk.core.exception.SdkException; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class HttpRangeUtils { + private static final Pattern RANGE_PATTERN = Pattern.compile("^bytes\\s+(\\d+)-\\d+[/\\d*]+$"); + + /** + * Parses the content range header string value to calculate the start (offset) of the HTTP response. + * Tests against the RFC9110 specification of content range string. + * Sample values: "bytes 0-10/200", "bytes 0-10/*" + * Details here + * @param headerValue Header content range string value from the HTTP response + * @return Start (Offset) value of the HTTP response + */ + public static Long getStartOffsetFromRangeHeader(String headerValue) { + Matcher matcher = RANGE_PATTERN.matcher(headerValue); + if (!matcher.find()) { + throw SdkException.create("Regex match for Content-Range header {" + headerValue + "} failed", new RuntimeException()); + } + return Long.parseLong(matcher.group(1)); + } + + /** + * Provides a byte range string per RFC 9110 + * @param start start position (inclusive) + * @param end end position (inclusive) + * @return A 'bytes=start-end' string + */ + public static String toHttpRangeHeader(long start, long end) { + return "bytes=" + start + "-" + end; + } +} diff --git a/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/Protocol.java b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/Protocol.java new file mode 100644 index 0000000000000..0dbd9364f9c53 --- /dev/null +++ b/plugins/repository-s3/src/main/java/org/opensearch/repositories/s3/utils/Protocol.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.utils; + +/** + * Represents the communication protocol to use when sending requests to AWS. + *

    + * Communication over HTTPS is the default, and is more secure than HTTP, which + * is why AWS recommends using HTTPS. HTTPS connections can use more system + * resources because of the extra work to encrypt network traffic, so the option + * to use HTTP is available in case users need it. + */ +public enum Protocol { + + /** + * HTTP Protocol - Using the HTTP protocol is less secure than HTTPS, but + * can slightly reduce the system resources used when communicating with + * AWS. + */ + HTTP("http"), + + /** + * HTTPS Protocol - Using the HTTPS protocol is more secure than using the + * HTTP protocol, but may use slightly more system resources. AWS recommends + * using HTTPS for maximize security. + */ + HTTPS("https"); + + private final String protocol; + + private Protocol(String protocol) { + this.protocol = protocol; + } + + /* (non-Javadoc) + * @see java.lang.Enum#toString() + */ + @Override + public String toString() { + return protocol; + } +} diff --git a/plugins/repository-s3/src/main/plugin-metadata/plugin-security.policy b/plugins/repository-s3/src/main/plugin-metadata/plugin-security.policy index f6c154bb3b14d..106103d45e7eb 100644 --- a/plugins/repository-s3/src/main/plugin-metadata/plugin-security.policy +++ b/plugins/repository-s3/src/main/plugin-metadata/plugin-security.policy @@ -35,6 +35,7 @@ grant { // TODO: get these fixed in aws sdk permission java.lang.RuntimePermission "accessDeclaredMembers"; permission java.lang.RuntimePermission "getClassLoader"; + permission java.lang.RuntimePermission "setContextClassLoader"; // Needed because of problems in AmazonS3Client: // When no region is set on a AmazonS3Client instance, the // AWS SDK loads all known partitions from a JSON file and @@ -56,4 +57,10 @@ grant { // only for tests : org.opensearch.repositories.s3.S3RepositoryPlugin permission java.util.PropertyPermission "opensearch.allow_insecure_settings", "read,write"; + permission java.util.PropertyPermission "aws.sharedCredentialsFile", "read,write"; + permission java.util.PropertyPermission "aws.configFile", "read,write"; + permission java.util.PropertyPermission "opensearch.path.conf", "read,write"; + permission java.io.FilePermission "config", "read"; + + permission java.lang.RuntimePermission "accessDeclaredMembers"; }; diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AbstractS3RepositoryTestCase.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AbstractS3RepositoryTestCase.java new file mode 100644 index 0000000000000..aae86c4f93587 --- /dev/null +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AbstractS3RepositoryTestCase.java @@ -0,0 +1,63 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; + +import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.io.PathUtils; +import org.opensearch.test.OpenSearchTestCase; + +import java.nio.file.Path; + +public abstract class AbstractS3RepositoryTestCase extends OpenSearchTestCase { + @Override + public void setUp() throws Exception { + super.setUp(); + setUpAwsProfile(); + } + + @Override + public void tearDown() throws Exception { + resetAwsProfile(); + super.tearDown(); + } + + protected Path configPath() { + return PathUtils.get("config"); + } + + private String previousOpenSearchPathConf; + private String awsSharedCredentialsFile; + private String awsConfigFile; + + @SuppressForbidden(reason = "set predictable aws defaults") + private void setUpAwsProfile() throws Exception { + previousOpenSearchPathConf = SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); + awsSharedCredentialsFile = System.getProperty(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property()); + awsConfigFile = System.getProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property()); + SocketAccess.doPrivilegedVoid(S3Service::setDefaultAwsProfilePath); + } + + @SuppressForbidden(reason = "reset aws settings") + private void resetAwsProfile() throws Exception { + resetPropertyValue("opensearch.path.conf", previousOpenSearchPathConf); + resetPropertyValue(ProfileFileSystemSetting.AWS_SHARED_CREDENTIALS_FILE.property(), awsSharedCredentialsFile); + resetPropertyValue(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), awsConfigFile); + } + + @SuppressForbidden(reason = "reset aws settings") + private void resetPropertyValue(String key, String value) { + if (value != null) { + SocketAccess.doPrivileged(() -> System.setProperty(key, value)); + } else { + SocketAccess.doPrivileged(() -> System.clearProperty(key)); + } + } +} diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AmazonS3Wrapper.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AmazonS3Wrapper.java deleted file mode 100644 index 47f2e4fa14297..0000000000000 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AmazonS3Wrapper.java +++ /dev/null @@ -1,808 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.repositories.s3; - -import com.amazonaws.AmazonClientException; -import com.amazonaws.AmazonServiceException; -import com.amazonaws.AmazonWebServiceRequest; -import com.amazonaws.HttpMethod; -import com.amazonaws.regions.Region; -import com.amazonaws.services.s3.AbstractAmazonS3; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.S3ClientOptions; -import com.amazonaws.services.s3.S3ResponseMetadata; -import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; -import com.amazonaws.services.s3.model.AccessControlList; -import com.amazonaws.services.s3.model.Bucket; -import com.amazonaws.services.s3.model.BucketCrossOriginConfiguration; -import com.amazonaws.services.s3.model.BucketLifecycleConfiguration; -import com.amazonaws.services.s3.model.BucketLoggingConfiguration; -import com.amazonaws.services.s3.model.BucketNotificationConfiguration; -import com.amazonaws.services.s3.model.BucketPolicy; -import com.amazonaws.services.s3.model.BucketReplicationConfiguration; -import com.amazonaws.services.s3.model.BucketTaggingConfiguration; -import com.amazonaws.services.s3.model.BucketVersioningConfiguration; -import com.amazonaws.services.s3.model.BucketWebsiteConfiguration; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; -import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; -import com.amazonaws.services.s3.model.CopyObjectRequest; -import com.amazonaws.services.s3.model.CopyObjectResult; -import com.amazonaws.services.s3.model.CopyPartRequest; -import com.amazonaws.services.s3.model.CopyPartResult; -import com.amazonaws.services.s3.model.CreateBucketRequest; -import com.amazonaws.services.s3.model.DeleteBucketCrossOriginConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketLifecycleConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketPolicyRequest; -import com.amazonaws.services.s3.model.DeleteBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketRequest; -import com.amazonaws.services.s3.model.DeleteBucketTaggingConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.DeleteObjectsRequest; -import com.amazonaws.services.s3.model.DeleteObjectsResult; -import com.amazonaws.services.s3.model.DeleteVersionRequest; -import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; -import com.amazonaws.services.s3.model.GetBucketAclRequest; -import com.amazonaws.services.s3.model.GetBucketCrossOriginConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketLifecycleConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketLocationRequest; -import com.amazonaws.services.s3.model.GetBucketLoggingConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketNotificationConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketPolicyRequest; -import com.amazonaws.services.s3.model.GetBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketTaggingConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketVersioningConfigurationRequest; -import com.amazonaws.services.s3.model.GetBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.GetObjectAclRequest; -import com.amazonaws.services.s3.model.GetObjectMetadataRequest; -import com.amazonaws.services.s3.model.GetObjectRequest; -import com.amazonaws.services.s3.model.GetS3AccountOwnerRequest; -import com.amazonaws.services.s3.model.HeadBucketRequest; -import com.amazonaws.services.s3.model.HeadBucketResult; -import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; -import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; -import com.amazonaws.services.s3.model.ListBucketsRequest; -import com.amazonaws.services.s3.model.ListMultipartUploadsRequest; -import com.amazonaws.services.s3.model.ListNextBatchOfObjectsRequest; -import com.amazonaws.services.s3.model.ListNextBatchOfVersionsRequest; -import com.amazonaws.services.s3.model.ListObjectsRequest; -import com.amazonaws.services.s3.model.ListPartsRequest; -import com.amazonaws.services.s3.model.ListVersionsRequest; -import com.amazonaws.services.s3.model.MultipartUploadListing; -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.Owner; -import com.amazonaws.services.s3.model.PartListing; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.PutObjectResult; -import com.amazonaws.services.s3.model.RestoreObjectRequest; -import com.amazonaws.services.s3.model.S3Object; -import com.amazonaws.services.s3.model.SetBucketAclRequest; -import com.amazonaws.services.s3.model.SetBucketCrossOriginConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketLifecycleConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketLoggingConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketNotificationConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketPolicyRequest; -import com.amazonaws.services.s3.model.SetBucketReplicationConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketTaggingConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketVersioningConfigurationRequest; -import com.amazonaws.services.s3.model.SetBucketWebsiteConfigurationRequest; -import com.amazonaws.services.s3.model.SetObjectAclRequest; -import com.amazonaws.services.s3.model.StorageClass; -import com.amazonaws.services.s3.model.UploadPartRequest; -import com.amazonaws.services.s3.model.UploadPartResult; -import com.amazonaws.services.s3.model.VersionListing; -import org.opensearch.common.SuppressForbidden; - -import java.io.File; -import java.io.InputStream; -import java.net.URL; -import java.util.Date; -import java.util.List; - -@SuppressForbidden(reason = "implements AWS api that uses java.io.File!") -public class AmazonS3Wrapper extends AbstractAmazonS3 { - - protected AmazonS3 delegate; - - public AmazonS3Wrapper(AmazonS3 delegate) { - this.delegate = delegate; - } - - @Override - public void setEndpoint(String endpoint) { - delegate.setEndpoint(endpoint); - } - - @Override - public void setRegion(Region region) throws IllegalArgumentException { - delegate.setRegion(region); - } - - @Override - public void setS3ClientOptions(S3ClientOptions clientOptions) { - delegate.setS3ClientOptions(clientOptions); - } - - @Override - public void changeObjectStorageClass(String bucketName, String key, StorageClass newStorageClass) throws AmazonClientException, - AmazonServiceException { - delegate.changeObjectStorageClass(bucketName, key, newStorageClass); - } - - @Override - public void setObjectRedirectLocation(String bucketName, String key, String newRedirectLocation) throws AmazonClientException, - AmazonServiceException { - delegate.setObjectRedirectLocation(bucketName, key, newRedirectLocation); - } - - @Override - public ObjectListing listObjects(String bucketName) throws AmazonClientException, AmazonServiceException { - return delegate.listObjects(bucketName); - } - - @Override - public ObjectListing listObjects(String bucketName, String prefix) throws AmazonClientException, AmazonServiceException { - return delegate.listObjects(bucketName, prefix); - } - - @Override - public ObjectListing listObjects(ListObjectsRequest listObjectsRequest) throws AmazonClientException, AmazonServiceException { - return delegate.listObjects(listObjectsRequest); - } - - @Override - public ObjectListing listNextBatchOfObjects(ObjectListing previousObjectListing) throws AmazonClientException, AmazonServiceException { - return delegate.listNextBatchOfObjects(previousObjectListing); - } - - @Override - public VersionListing listVersions(String bucketName, String prefix) throws AmazonClientException, AmazonServiceException { - return delegate.listVersions(bucketName, prefix); - } - - @Override - public VersionListing listNextBatchOfVersions(VersionListing previousVersionListing) throws AmazonClientException, - AmazonServiceException { - return delegate.listNextBatchOfVersions(previousVersionListing); - } - - @Override - public VersionListing listVersions( - String bucketName, - String prefix, - String keyMarker, - String versionIdMarker, - String delimiter, - Integer maxResults - ) throws AmazonClientException, AmazonServiceException { - return delegate.listVersions(bucketName, prefix, keyMarker, versionIdMarker, delimiter, maxResults); - } - - @Override - public VersionListing listVersions(ListVersionsRequest listVersionsRequest) throws AmazonClientException, AmazonServiceException { - return delegate.listVersions(listVersionsRequest); - } - - @Override - public Owner getS3AccountOwner() throws AmazonClientException, AmazonServiceException { - return delegate.getS3AccountOwner(); - } - - @Override - public boolean doesBucketExist(String bucketName) throws AmazonClientException, AmazonServiceException { - return delegate.doesBucketExist(bucketName); - } - - @Override - public List listBuckets() throws AmazonClientException, AmazonServiceException { - return delegate.listBuckets(); - } - - @Override - public List listBuckets(ListBucketsRequest listBucketsRequest) throws AmazonClientException, AmazonServiceException { - return delegate.listBuckets(listBucketsRequest); - } - - @Override - public String getBucketLocation(String bucketName) throws AmazonClientException, AmazonServiceException { - return delegate.getBucketLocation(bucketName); - } - - @Override - public String getBucketLocation(GetBucketLocationRequest getBucketLocationRequest) throws AmazonClientException, - AmazonServiceException { - return delegate.getBucketLocation(getBucketLocationRequest); - } - - @Override - public Bucket createBucket(CreateBucketRequest createBucketRequest) throws AmazonClientException, AmazonServiceException { - return delegate.createBucket(createBucketRequest); - } - - @Override - public Bucket createBucket(String bucketName) throws AmazonClientException, AmazonServiceException { - return delegate.createBucket(bucketName); - } - - @Override - public Bucket createBucket(String bucketName, com.amazonaws.services.s3.model.Region region) throws AmazonClientException, - AmazonServiceException { - return delegate.createBucket(bucketName, region); - } - - @Override - public Bucket createBucket(String bucketName, String region) throws AmazonClientException, AmazonServiceException { - return delegate.createBucket(bucketName, region); - } - - @Override - public AccessControlList getObjectAcl(String bucketName, String key) throws AmazonClientException, AmazonServiceException { - return delegate.getObjectAcl(bucketName, key); - } - - @Override - public AccessControlList getObjectAcl(String bucketName, String key, String versionId) throws AmazonClientException, - AmazonServiceException { - return delegate.getObjectAcl(bucketName, key, versionId); - } - - @Override - public AccessControlList getObjectAcl(GetObjectAclRequest getObjectAclRequest) throws AmazonClientException, AmazonServiceException { - return delegate.getObjectAcl(getObjectAclRequest); - } - - @Override - public void setObjectAcl(String bucketName, String key, AccessControlList acl) throws AmazonClientException, AmazonServiceException { - delegate.setObjectAcl(bucketName, key, acl); - } - - @Override - public void setObjectAcl(String bucketName, String key, CannedAccessControlList acl) throws AmazonClientException, - AmazonServiceException { - delegate.setObjectAcl(bucketName, key, acl); - } - - @Override - public void setObjectAcl(String bucketName, String key, String versionId, AccessControlList acl) throws AmazonClientException, - AmazonServiceException { - delegate.setObjectAcl(bucketName, key, versionId, acl); - } - - @Override - public void setObjectAcl(String bucketName, String key, String versionId, CannedAccessControlList acl) throws AmazonClientException, - AmazonServiceException { - delegate.setObjectAcl(bucketName, key, versionId, acl); - } - - @Override - public void setObjectAcl(SetObjectAclRequest setObjectAclRequest) throws AmazonClientException, AmazonServiceException { - delegate.setObjectAcl(setObjectAclRequest); - } - - @Override - public AccessControlList getBucketAcl(String bucketName) throws AmazonClientException, AmazonServiceException { - return delegate.getBucketAcl(bucketName); - } - - @Override - public void setBucketAcl(SetBucketAclRequest setBucketAclRequest) throws AmazonClientException, AmazonServiceException { - delegate.setBucketAcl(setBucketAclRequest); - } - - @Override - public AccessControlList getBucketAcl(GetBucketAclRequest getBucketAclRequest) throws AmazonClientException, AmazonServiceException { - return delegate.getBucketAcl(getBucketAclRequest); - } - - @Override - public void setBucketAcl(String bucketName, AccessControlList acl) throws AmazonClientException, AmazonServiceException { - delegate.setBucketAcl(bucketName, acl); - } - - @Override - public void setBucketAcl(String bucketName, CannedAccessControlList acl) throws AmazonClientException, AmazonServiceException { - delegate.setBucketAcl(bucketName, acl); - } - - @Override - public ObjectMetadata getObjectMetadata(String bucketName, String key) throws AmazonClientException, AmazonServiceException { - return delegate.getObjectMetadata(bucketName, key); - } - - @Override - public ObjectMetadata getObjectMetadata(GetObjectMetadataRequest getObjectMetadataRequest) throws AmazonClientException, - AmazonServiceException { - return delegate.getObjectMetadata(getObjectMetadataRequest); - } - - @Override - public S3Object getObject(String bucketName, String key) throws AmazonClientException, AmazonServiceException { - return delegate.getObject(bucketName, key); - } - - @Override - public S3Object getObject(GetObjectRequest getObjectRequest) throws AmazonClientException, AmazonServiceException { - return delegate.getObject(getObjectRequest); - } - - @Override - public ObjectMetadata getObject(GetObjectRequest getObjectRequest, File destinationFile) throws AmazonClientException, - AmazonServiceException { - return delegate.getObject(getObjectRequest, destinationFile); - } - - @Override - public void deleteBucket(DeleteBucketRequest deleteBucketRequest) throws AmazonClientException, AmazonServiceException { - delegate.deleteBucket(deleteBucketRequest); - } - - @Override - public void deleteBucket(String bucketName) throws AmazonClientException, AmazonServiceException { - delegate.deleteBucket(bucketName); - } - - @Override - public void setBucketReplicationConfiguration(String bucketName, BucketReplicationConfiguration configuration) - throws AmazonServiceException, AmazonClientException { - delegate.setBucketReplicationConfiguration(bucketName, configuration); - } - - @Override - public void setBucketReplicationConfiguration(SetBucketReplicationConfigurationRequest setBucketReplicationConfigurationRequest) - throws AmazonServiceException, AmazonClientException { - delegate.setBucketReplicationConfiguration(setBucketReplicationConfigurationRequest); - } - - @Override - public BucketReplicationConfiguration getBucketReplicationConfiguration(String bucketName) throws AmazonServiceException, - AmazonClientException { - return delegate.getBucketReplicationConfiguration(bucketName); - } - - @Override - public void deleteBucketReplicationConfiguration(String bucketName) throws AmazonServiceException, AmazonClientException { - delegate.deleteBucketReplicationConfiguration(bucketName); - } - - @Override - public void deleteBucketReplicationConfiguration(DeleteBucketReplicationConfigurationRequest request) throws AmazonServiceException, - AmazonClientException { - delegate.deleteBucketReplicationConfiguration(request); - } - - @Override - public boolean doesObjectExist(String bucketName, String objectName) throws AmazonServiceException, AmazonClientException { - return delegate.doesObjectExist(bucketName, objectName); - } - - @Override - public PutObjectResult putObject(PutObjectRequest putObjectRequest) throws AmazonClientException, AmazonServiceException { - return delegate.putObject(putObjectRequest); - } - - @Override - public PutObjectResult putObject(String bucketName, String key, File file) throws AmazonClientException, AmazonServiceException { - return delegate.putObject(bucketName, key, file); - } - - @Override - public PutObjectResult putObject(String bucketName, String key, InputStream input, ObjectMetadata metadata) - throws AmazonClientException, AmazonServiceException { - return delegate.putObject(bucketName, key, input, metadata); - } - - @Override - public CopyObjectResult copyObject(String sourceBucketName, String sourceKey, String destinationBucketName, String destinationKey) - throws AmazonClientException, AmazonServiceException { - return delegate.copyObject(sourceBucketName, sourceKey, destinationBucketName, destinationKey); - } - - @Override - public CopyObjectResult copyObject(CopyObjectRequest copyObjectRequest) throws AmazonClientException, AmazonServiceException { - return delegate.copyObject(copyObjectRequest); - } - - @Override - public CopyPartResult copyPart(CopyPartRequest copyPartRequest) throws AmazonClientException, AmazonServiceException { - return delegate.copyPart(copyPartRequest); - } - - @Override - public void deleteObject(String bucketName, String key) throws AmazonClientException, AmazonServiceException { - delegate.deleteObject(bucketName, key); - } - - @Override - public void deleteObject(DeleteObjectRequest deleteObjectRequest) throws AmazonClientException, AmazonServiceException { - delegate.deleteObject(deleteObjectRequest); - } - - @Override - public DeleteObjectsResult deleteObjects(DeleteObjectsRequest deleteObjectsRequest) throws AmazonClientException, - AmazonServiceException { - return delegate.deleteObjects(deleteObjectsRequest); - } - - @Override - public void deleteVersion(String bucketName, String key, String versionId) throws AmazonClientException, AmazonServiceException { - delegate.deleteVersion(bucketName, key, versionId); - } - - @Override - public void deleteVersion(DeleteVersionRequest deleteVersionRequest) throws AmazonClientException, AmazonServiceException { - delegate.deleteVersion(deleteVersionRequest); - } - - @Override - public BucketLoggingConfiguration getBucketLoggingConfiguration(String bucketName) throws AmazonClientException, - AmazonServiceException { - return delegate.getBucketLoggingConfiguration(bucketName); - } - - @Override - public void setBucketLoggingConfiguration(SetBucketLoggingConfigurationRequest setBucketLoggingConfigurationRequest) - throws AmazonClientException, AmazonServiceException { - delegate.setBucketLoggingConfiguration(setBucketLoggingConfigurationRequest); - } - - @Override - public BucketVersioningConfiguration getBucketVersioningConfiguration(String bucketName) throws AmazonClientException, - AmazonServiceException { - return delegate.getBucketVersioningConfiguration(bucketName); - } - - @Override - public void setBucketVersioningConfiguration(SetBucketVersioningConfigurationRequest setBucketVersioningConfigurationRequest) - throws AmazonClientException, AmazonServiceException { - delegate.setBucketVersioningConfiguration(setBucketVersioningConfigurationRequest); - } - - @Override - public BucketLifecycleConfiguration getBucketLifecycleConfiguration(String bucketName) { - return delegate.getBucketLifecycleConfiguration(bucketName); - } - - @Override - public void setBucketLifecycleConfiguration(String bucketName, BucketLifecycleConfiguration bucketLifecycleConfiguration) { - delegate.setBucketLifecycleConfiguration(bucketName, bucketLifecycleConfiguration); - } - - @Override - public void setBucketLifecycleConfiguration(SetBucketLifecycleConfigurationRequest setBucketLifecycleConfigurationRequest) { - delegate.setBucketLifecycleConfiguration(setBucketLifecycleConfigurationRequest); - } - - @Override - public void deleteBucketLifecycleConfiguration(String bucketName) { - delegate.deleteBucketLifecycleConfiguration(bucketName); - } - - @Override - public void deleteBucketLifecycleConfiguration(DeleteBucketLifecycleConfigurationRequest deleteBucketLifecycleConfigurationRequest) { - delegate.deleteBucketLifecycleConfiguration(deleteBucketLifecycleConfigurationRequest); - } - - @Override - public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration(String bucketName) { - return delegate.getBucketCrossOriginConfiguration(bucketName); - } - - @Override - public void setBucketCrossOriginConfiguration(String bucketName, BucketCrossOriginConfiguration bucketCrossOriginConfiguration) { - delegate.setBucketCrossOriginConfiguration(bucketName, bucketCrossOriginConfiguration); - } - - @Override - public void setBucketCrossOriginConfiguration(SetBucketCrossOriginConfigurationRequest setBucketCrossOriginConfigurationRequest) { - delegate.setBucketCrossOriginConfiguration(setBucketCrossOriginConfigurationRequest); - } - - @Override - public void deleteBucketCrossOriginConfiguration(String bucketName) { - delegate.deleteBucketCrossOriginConfiguration(bucketName); - } - - @Override - public void deleteBucketCrossOriginConfiguration( - DeleteBucketCrossOriginConfigurationRequest deleteBucketCrossOriginConfigurationRequest - ) { - delegate.deleteBucketCrossOriginConfiguration(deleteBucketCrossOriginConfigurationRequest); - } - - @Override - public BucketTaggingConfiguration getBucketTaggingConfiguration(String bucketName) { - return delegate.getBucketTaggingConfiguration(bucketName); - } - - @Override - public void setBucketTaggingConfiguration(String bucketName, BucketTaggingConfiguration bucketTaggingConfiguration) { - delegate.setBucketTaggingConfiguration(bucketName, bucketTaggingConfiguration); - } - - @Override - public void setBucketTaggingConfiguration(SetBucketTaggingConfigurationRequest setBucketTaggingConfigurationRequest) { - delegate.setBucketTaggingConfiguration(setBucketTaggingConfigurationRequest); - } - - @Override - public void deleteBucketTaggingConfiguration(String bucketName) { - delegate.deleteBucketTaggingConfiguration(bucketName); - } - - @Override - public void deleteBucketTaggingConfiguration(DeleteBucketTaggingConfigurationRequest deleteBucketTaggingConfigurationRequest) { - delegate.deleteBucketTaggingConfiguration(deleteBucketTaggingConfigurationRequest); - } - - @Override - public BucketNotificationConfiguration getBucketNotificationConfiguration(String bucketName) throws AmazonClientException, - AmazonServiceException { - return delegate.getBucketNotificationConfiguration(bucketName); - } - - @Override - public void setBucketNotificationConfiguration(SetBucketNotificationConfigurationRequest setBucketNotificationConfigurationRequest) - throws AmazonClientException, AmazonServiceException { - delegate.setBucketNotificationConfiguration(setBucketNotificationConfigurationRequest); - } - - @Override - public void setBucketNotificationConfiguration(String bucketName, BucketNotificationConfiguration bucketNotificationConfiguration) - throws AmazonClientException, AmazonServiceException { - delegate.setBucketNotificationConfiguration(bucketName, bucketNotificationConfiguration); - } - - @Override - public BucketWebsiteConfiguration getBucketWebsiteConfiguration(String bucketName) throws AmazonClientException, - AmazonServiceException { - return delegate.getBucketWebsiteConfiguration(bucketName); - } - - @Override - public BucketWebsiteConfiguration getBucketWebsiteConfiguration( - GetBucketWebsiteConfigurationRequest getBucketWebsiteConfigurationRequest - ) throws AmazonClientException, AmazonServiceException { - return delegate.getBucketWebsiteConfiguration(getBucketWebsiteConfigurationRequest); - } - - @Override - public void setBucketWebsiteConfiguration(String bucketName, BucketWebsiteConfiguration configuration) throws AmazonClientException, - AmazonServiceException { - delegate.setBucketWebsiteConfiguration(bucketName, configuration); - } - - @Override - public void setBucketWebsiteConfiguration(SetBucketWebsiteConfigurationRequest setBucketWebsiteConfigurationRequest) - throws AmazonClientException, AmazonServiceException { - delegate.setBucketWebsiteConfiguration(setBucketWebsiteConfigurationRequest); - } - - @Override - public void deleteBucketWebsiteConfiguration(String bucketName) throws AmazonClientException, AmazonServiceException { - delegate.deleteBucketWebsiteConfiguration(bucketName); - } - - @Override - public void deleteBucketWebsiteConfiguration(DeleteBucketWebsiteConfigurationRequest deleteBucketWebsiteConfigurationRequest) - throws AmazonClientException, AmazonServiceException { - delegate.deleteBucketWebsiteConfiguration(deleteBucketWebsiteConfigurationRequest); - } - - @Override - public BucketPolicy getBucketPolicy(String bucketName) throws AmazonClientException, AmazonServiceException { - return delegate.getBucketPolicy(bucketName); - } - - @Override - public BucketPolicy getBucketPolicy(GetBucketPolicyRequest getBucketPolicyRequest) throws AmazonClientException, - AmazonServiceException { - return delegate.getBucketPolicy(getBucketPolicyRequest); - } - - @Override - public void setBucketPolicy(String bucketName, String policyText) throws AmazonClientException, AmazonServiceException { - delegate.setBucketPolicy(bucketName, policyText); - } - - @Override - public void setBucketPolicy(SetBucketPolicyRequest setBucketPolicyRequest) throws AmazonClientException, AmazonServiceException { - delegate.setBucketPolicy(setBucketPolicyRequest); - } - - @Override - public void deleteBucketPolicy(String bucketName) throws AmazonClientException, AmazonServiceException { - delegate.deleteBucketPolicy(bucketName); - } - - @Override - public void deleteBucketPolicy(DeleteBucketPolicyRequest deleteBucketPolicyRequest) throws AmazonClientException, - AmazonServiceException { - delegate.deleteBucketPolicy(deleteBucketPolicyRequest); - } - - @Override - public URL generatePresignedUrl(String bucketName, String key, Date expiration) throws AmazonClientException { - return delegate.generatePresignedUrl(bucketName, key, expiration); - } - - @Override - public URL generatePresignedUrl(String bucketName, String key, Date expiration, HttpMethod method) throws AmazonClientException { - return delegate.generatePresignedUrl(bucketName, key, expiration, method); - } - - @Override - public URL generatePresignedUrl(GeneratePresignedUrlRequest generatePresignedUrlRequest) throws AmazonClientException { - return delegate.generatePresignedUrl(generatePresignedUrlRequest); - } - - @Override - public InitiateMultipartUploadResult initiateMultipartUpload(InitiateMultipartUploadRequest request) throws AmazonClientException, - AmazonServiceException { - return delegate.initiateMultipartUpload(request); - } - - @Override - public UploadPartResult uploadPart(UploadPartRequest request) throws AmazonClientException, AmazonServiceException { - return delegate.uploadPart(request); - } - - @Override - public PartListing listParts(ListPartsRequest request) throws AmazonClientException, AmazonServiceException { - return delegate.listParts(request); - } - - @Override - public void abortMultipartUpload(AbortMultipartUploadRequest request) throws AmazonClientException, AmazonServiceException { - delegate.abortMultipartUpload(request); - } - - @Override - public CompleteMultipartUploadResult completeMultipartUpload(CompleteMultipartUploadRequest request) throws AmazonClientException, - AmazonServiceException { - return delegate.completeMultipartUpload(request); - } - - @Override - public MultipartUploadListing listMultipartUploads(ListMultipartUploadsRequest request) throws AmazonClientException, - AmazonServiceException { - return delegate.listMultipartUploads(request); - } - - @Override - public S3ResponseMetadata getCachedResponseMetadata(AmazonWebServiceRequest request) { - return delegate.getCachedResponseMetadata(request); - } - - @Override - public void restoreObject(RestoreObjectRequest copyGlacierObjectRequest) throws AmazonServiceException { - delegate.restoreObject(copyGlacierObjectRequest); - } - - @Override - public void restoreObject(String bucketName, String key, int expirationInDays) throws AmazonServiceException { - delegate.restoreObject(bucketName, key, expirationInDays); - } - - @Override - public void enableRequesterPays(String bucketName) throws AmazonServiceException, AmazonClientException { - delegate.enableRequesterPays(bucketName); - } - - @Override - public void disableRequesterPays(String bucketName) throws AmazonServiceException, AmazonClientException { - delegate.disableRequesterPays(bucketName); - } - - @Override - public boolean isRequesterPaysEnabled(String bucketName) throws AmazonServiceException, AmazonClientException { - return delegate.isRequesterPaysEnabled(bucketName); - } - - @Override - public ObjectListing listNextBatchOfObjects(ListNextBatchOfObjectsRequest listNextBatchOfObjectsRequest) throws AmazonClientException, - AmazonServiceException { - return delegate.listNextBatchOfObjects(listNextBatchOfObjectsRequest); - } - - @Override - public VersionListing listNextBatchOfVersions(ListNextBatchOfVersionsRequest listNextBatchOfVersionsRequest) - throws AmazonClientException, AmazonServiceException { - return delegate.listNextBatchOfVersions(listNextBatchOfVersionsRequest); - } - - @Override - public Owner getS3AccountOwner(GetS3AccountOwnerRequest getS3AccountOwnerRequest) throws AmazonClientException, AmazonServiceException { - return delegate.getS3AccountOwner(getS3AccountOwnerRequest); - } - - @Override - public BucketLoggingConfiguration getBucketLoggingConfiguration( - GetBucketLoggingConfigurationRequest getBucketLoggingConfigurationRequest - ) throws AmazonClientException, AmazonServiceException { - return delegate.getBucketLoggingConfiguration(getBucketLoggingConfigurationRequest); - } - - @Override - public BucketVersioningConfiguration getBucketVersioningConfiguration( - GetBucketVersioningConfigurationRequest getBucketVersioningConfigurationRequest - ) throws AmazonClientException, AmazonServiceException { - return delegate.getBucketVersioningConfiguration(getBucketVersioningConfigurationRequest); - } - - @Override - public BucketLifecycleConfiguration getBucketLifecycleConfiguration( - GetBucketLifecycleConfigurationRequest getBucketLifecycleConfigurationRequest - ) { - return delegate.getBucketLifecycleConfiguration(getBucketLifecycleConfigurationRequest); - } - - @Override - public BucketCrossOriginConfiguration getBucketCrossOriginConfiguration( - GetBucketCrossOriginConfigurationRequest getBucketCrossOriginConfigurationRequest - ) { - return delegate.getBucketCrossOriginConfiguration(getBucketCrossOriginConfigurationRequest); - } - - @Override - public BucketTaggingConfiguration getBucketTaggingConfiguration( - GetBucketTaggingConfigurationRequest getBucketTaggingConfigurationRequest - ) { - return delegate.getBucketTaggingConfiguration(getBucketTaggingConfigurationRequest); - } - - @Override - public BucketNotificationConfiguration getBucketNotificationConfiguration( - GetBucketNotificationConfigurationRequest getBucketNotificationConfigurationRequest - ) throws AmazonClientException, AmazonServiceException { - return delegate.getBucketNotificationConfiguration(getBucketNotificationConfigurationRequest); - } - - @Override - public BucketReplicationConfiguration getBucketReplicationConfiguration( - GetBucketReplicationConfigurationRequest getBucketReplicationConfigurationRequest - ) throws AmazonServiceException, AmazonClientException { - return delegate.getBucketReplicationConfiguration(getBucketReplicationConfigurationRequest); - } - - @Override - public HeadBucketResult headBucket(HeadBucketRequest headBucketRequest) throws AmazonClientException, AmazonServiceException { - return delegate.headBucket(headBucketRequest); - } - - @Override - public void shutdown() { - delegate.shutdown(); - } -} diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AwsS3ServiceImplTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AwsS3ServiceImplTests.java index 38d9ebf337731..b80b857644f2a 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AwsS3ServiceImplTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/AwsS3ServiceImplTests.java @@ -32,34 +32,56 @@ package org.opensearch.repositories.s3; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.Protocol; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.retry.backoff.BackoffStrategy; +import software.amazon.awssdk.http.apache.ProxyConfiguration; + +import org.opensearch.common.SuppressForbidden; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.repositories.s3.utils.Protocol; +import org.junit.Before; +import java.io.Closeable; import java.io.IOException; +import java.util.HashMap; import java.util.Locale; import java.util.Map; +import static org.opensearch.repositories.s3.S3ClientSettings.PROTOCOL_SETTING; +import static org.opensearch.repositories.s3.S3ClientSettings.PROXY_TYPE_SETTING; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; -import static org.opensearch.repositories.s3.S3ClientSettings.PROTOCOL_SETTING; -import static org.opensearch.repositories.s3.S3ClientSettings.PROXY_TYPE_SETTING; -public class AwsS3ServiceImplTests extends OpenSearchTestCase { +public class AwsS3ServiceImplTests extends AbstractS3RepositoryTestCase { - public void testAWSCredentialsDefaultToInstanceProviders() { + private static final String HOST = "127.0.0.10"; + private static final int PORT = 8080; + + private Settings.Builder settingsBuilder; + + @Override + @Before + @SuppressForbidden(reason = "Need to set system property here for AWS SDK v2") + public void setUp() throws Exception { + settingsBuilder = Settings.builder() + .put("s3.client.default.proxy.type", "http") + .put("s3.client.default.proxy.host", HOST) + .put("s3.client.default.proxy.port", PORT); + super.setUp(); + } + + public void testAwsCredentialsDefaultToInstanceProviders() { final String inexistentClientName = randomAlphaOfLength(8).toLowerCase(Locale.ROOT); - final S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(Settings.EMPTY, inexistentClientName); - final AWSCredentialsProvider credentialsProvider = S3Service.buildCredentials(logger, clientSettings); + final S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(Settings.EMPTY, inexistentClientName, configPath()); + final AwsCredentialsProvider credentialsProvider = S3Service.buildCredentials(logger, clientSettings); assertThat(credentialsProvider, instanceOf(S3Service.PrivilegedInstanceProfileCredentialsProvider.class)); } - public void testAWSCredentialsFromKeystore() { + public void testAwsCredentialsFromKeystore() { final MockSecureSettings secureSettings = new MockSecureSettings(); final String clientNamePrefix = "some_client_name_"; final int clientsCount = randomIntBetween(0, 4); @@ -69,20 +91,115 @@ public void testAWSCredentialsFromKeystore() { secureSettings.setString("s3.client." + clientName + ".secret_key", clientName + "_aws_secret_key"); } final Settings settings = Settings.builder().setSecureSettings(secureSettings).build(); - final Map allClientsSettings = S3ClientSettings.load(settings); + final Map allClientsSettings = S3ClientSettings.load(settings, configPath()); + // no less, no more + assertThat(allClientsSettings.size(), is(clientsCount + 1)); // including default + for (int i = 0; i < clientsCount; i++) { + final String clientName = clientNamePrefix + i; + final S3ClientSettings someClientSettings = allClientsSettings.get(clientName); + final AwsCredentialsProvider credentialsProvider = S3Service.buildCredentials(logger, someClientSettings); + assertThat(credentialsProvider, instanceOf(StaticCredentialsProvider.class)); + assertThat(credentialsProvider.resolveCredentials().accessKeyId(), is(clientName + "_aws_access_key")); + assertThat(credentialsProvider.resolveCredentials().secretAccessKey(), is(clientName + "_aws_secret_key")); + } + // test default exists and is an Instance provider + final S3ClientSettings defaultClientSettings = allClientsSettings.get("default"); + final AwsCredentialsProvider defaultCredentialsProvider = S3Service.buildCredentials(logger, defaultClientSettings); + assertThat(defaultCredentialsProvider, instanceOf(S3Service.PrivilegedInstanceProfileCredentialsProvider.class)); + } + + public void testCredentialsAndIrsaWithIdentityTokenFileCredentialsFromKeystore() throws IOException { + final Map plainSettings = new HashMap<>(); + final MockSecureSettings secureSettings = new MockSecureSettings(); + final String clientNamePrefix = "some_client_name_"; + final int clientsCount = randomIntBetween(0, 4); + for (int i = 0; i < clientsCount; i++) { + final String clientName = clientNamePrefix + i; + secureSettings.setString("s3.client." + clientName + ".role_arn", clientName + "_role_arn"); + + // Use static AWS credentials for tests + secureSettings.setString("s3.client." + clientName + ".access_key", clientName + "_aws_access_key"); + secureSettings.setString("s3.client." + clientName + ".secret_key", clientName + "_aws_secret_key"); + + // Use explicit region setting + plainSettings.put("s3.client." + clientName + ".region", "us-east1"); + plainSettings.put("s3.client." + clientName + ".identity_token_file", clientName + "_identity_token_file"); + } + final Settings settings = Settings.builder().loadFromMap(plainSettings).setSecureSettings(secureSettings).build(); + final Map allClientsSettings = S3ClientSettings.load(settings, configPath()); + // no less, no more + assertThat(allClientsSettings.size(), is(clientsCount + 1)); // including default + for (int i = 0; i < clientsCount; i++) { + final String clientName = clientNamePrefix + i; + final S3ClientSettings someClientSettings = allClientsSettings.get(clientName); + final AwsCredentialsProvider credentialsProvider = S3Service.buildCredentials(logger, someClientSettings); + assertThat(credentialsProvider, instanceOf(S3Service.PrivilegedSTSAssumeRoleSessionCredentialsProvider.class)); + ((Closeable) credentialsProvider).close(); + } + // test default exists and is an Instance provider + final S3ClientSettings defaultClientSettings = allClientsSettings.get("default"); + final AwsCredentialsProvider defaultCredentialsProvider = S3Service.buildCredentials(logger, defaultClientSettings); + assertThat(defaultCredentialsProvider, instanceOf(S3Service.PrivilegedInstanceProfileCredentialsProvider.class)); + } + + public void testCredentialsAndIrsaCredentialsFromKeystore() throws IOException { + final Map plainSettings = new HashMap<>(); + final MockSecureSettings secureSettings = new MockSecureSettings(); + final String clientNamePrefix = "some_client_name_"; + final int clientsCount = randomIntBetween(0, 4); + for (int i = 0; i < clientsCount; i++) { + final String clientName = clientNamePrefix + i; + secureSettings.setString("s3.client." + clientName + ".role_arn", clientName + "_role_arn"); + secureSettings.setString("s3.client." + clientName + ".role_session_name", clientName + "_role_session_name"); + + // Use static AWS credentials for tests + secureSettings.setString("s3.client." + clientName + ".access_key", clientName + "_aws_access_key"); + secureSettings.setString("s3.client." + clientName + ".secret_key", clientName + "_aws_secret_key"); + + // Use explicit region setting + plainSettings.put("s3.client." + clientName + ".region", "us-east1"); + } + final Settings settings = Settings.builder().loadFromMap(plainSettings).setSecureSettings(secureSettings).build(); + final Map allClientsSettings = S3ClientSettings.load(settings, configPath()); // no less, no more assertThat(allClientsSettings.size(), is(clientsCount + 1)); // including default for (int i = 0; i < clientsCount; i++) { final String clientName = clientNamePrefix + i; final S3ClientSettings someClientSettings = allClientsSettings.get(clientName); - final AWSCredentialsProvider credentialsProvider = S3Service.buildCredentials(logger, someClientSettings); - assertThat(credentialsProvider, instanceOf(AWSStaticCredentialsProvider.class)); - assertThat(credentialsProvider.getCredentials().getAWSAccessKeyId(), is(clientName + "_aws_access_key")); - assertThat(credentialsProvider.getCredentials().getAWSSecretKey(), is(clientName + "_aws_secret_key")); + final AwsCredentialsProvider credentialsProvider = S3Service.buildCredentials(logger, someClientSettings); + assertThat(credentialsProvider, instanceOf(S3Service.PrivilegedSTSAssumeRoleSessionCredentialsProvider.class)); + ((Closeable) credentialsProvider).close(); } // test default exists and is an Instance provider final S3ClientSettings defaultClientSettings = allClientsSettings.get("default"); - final AWSCredentialsProvider defaultCredentialsProvider = S3Service.buildCredentials(logger, defaultClientSettings); + final AwsCredentialsProvider defaultCredentialsProvider = S3Service.buildCredentials(logger, defaultClientSettings); + assertThat(defaultCredentialsProvider, instanceOf(S3Service.PrivilegedInstanceProfileCredentialsProvider.class)); + } + + public void testIrsaCredentialsFromKeystore() throws IOException { + final Map plainSettings = new HashMap<>(); + final MockSecureSettings secureSettings = new MockSecureSettings(); + final String clientNamePrefix = "some_client_name_"; + final int clientsCount = randomIntBetween(0, 4); + for (int i = 0; i < clientsCount; i++) { + final String clientName = clientNamePrefix + i; + secureSettings.setString("s3.client." + clientName + ".role_arn", clientName + "_role_arn"); + secureSettings.setString("s3.client." + clientName + ".role_session_name", clientName + "_role_session_name"); + } + final Settings settings = Settings.builder().loadFromMap(plainSettings).setSecureSettings(secureSettings).build(); + final Map allClientsSettings = S3ClientSettings.load(settings, configPath()); + // no less, no more + assertThat(allClientsSettings.size(), is(clientsCount + 1)); // including default + for (int i = 0; i < clientsCount; i++) { + final String clientName = clientNamePrefix + i; + final S3ClientSettings someClientSettings = allClientsSettings.get(clientName); + final AwsCredentialsProvider credentialsProvider = S3Service.buildCredentials(logger, someClientSettings); + assertThat(credentialsProvider, instanceOf(S3Service.PrivilegedSTSAssumeRoleSessionCredentialsProvider.class)); + ((Closeable) credentialsProvider).close(); + } + // test default exists and is an Instance provider + final S3ClientSettings defaultClientSettings = allClientsSettings.get("default"); + final AwsCredentialsProvider defaultCredentialsProvider = S3Service.buildCredentials(logger, defaultClientSettings); assertThat(defaultCredentialsProvider, instanceOf(S3Service.PrivilegedInstanceProfileCredentialsProvider.class)); } @@ -93,14 +210,14 @@ public void testSetDefaultCredential() { secureSettings.setString("s3.client.default.access_key", awsAccessKey); secureSettings.setString("s3.client.default.secret_key", awsSecretKey); final Settings settings = Settings.builder().setSecureSettings(secureSettings).build(); - final Map allClientsSettings = S3ClientSettings.load(settings); + final Map allClientsSettings = S3ClientSettings.load(settings, configPath()); assertThat(allClientsSettings.size(), is(1)); // test default exists and is an Instance provider final S3ClientSettings defaultClientSettings = allClientsSettings.get("default"); - final AWSCredentialsProvider defaultCredentialsProvider = S3Service.buildCredentials(logger, defaultClientSettings); - assertThat(defaultCredentialsProvider, instanceOf(AWSStaticCredentialsProvider.class)); - assertThat(defaultCredentialsProvider.getCredentials().getAWSAccessKeyId(), is(awsAccessKey)); - assertThat(defaultCredentialsProvider.getCredentials().getAWSSecretKey(), is(awsSecretKey)); + final AwsCredentialsProvider defaultCredentialsProvider = S3Service.buildCredentials(logger, defaultClientSettings); + assertThat(defaultCredentialsProvider, instanceOf(StaticCredentialsProvider.class)); + assertThat(defaultCredentialsProvider.resolveCredentials().accessKeyId(), is(awsAccessKey)); + assertThat(defaultCredentialsProvider.resolveCredentials().secretAccessKey(), is(awsSecretKey)); } public void testCredentialsIncomplete() { @@ -113,7 +230,7 @@ public void testCredentialsIncomplete() { secureSettings.setString("s3.client." + clientName + ".secret_key", "aws_secret_key"); } final Settings settings = Settings.builder().setSecureSettings(secureSettings).build(); - final Exception e = expectThrows(IllegalArgumentException.class, () -> S3ClientSettings.load(settings)); + final Exception e = expectThrows(IllegalArgumentException.class, () -> S3ClientSettings.load(settings, configPath())); if (missingOrMissing) { assertThat(e.getMessage(), containsString("Missing secret key for s3 client [" + clientName + "]")); } else { @@ -122,16 +239,8 @@ public void testCredentialsIncomplete() { } public void testAWSDefaultConfiguration() { - launchAWSConfigurationTest( - Settings.EMPTY, - Protocol.HTTPS, - null, - -1, - null, - null, - 3, - ClientConfiguration.DEFAULT_THROTTLE_RETRIES, - ClientConfiguration.DEFAULT_SOCKET_TIMEOUT + SocketAccess.doPrivilegedVoid( + () -> launchAWSConfigurationTest(Settings.EMPTY, Protocol.HTTPS, null, -1, null, null, 3, true, 50_000) ); } @@ -142,20 +251,22 @@ public void testAWSConfigurationWithAwsSettings() { final Settings settings = Settings.builder() .setSecureSettings(secureSettings) .put("s3.client.default.protocol", "http") - .put("s3.client.default.proxy.host", "127.0.0.10") - .put("s3.client.default.proxy.port", 8080) + .put("s3.client.default.proxy.host", HOST) + .put("s3.client.default.proxy.port", PORT) .put("s3.client.default.read_timeout", "10s") .build(); - launchAWSConfigurationTest( - settings, - Protocol.HTTP, - "127.0.0.10", - 8080, - "aws_proxy_username", - "aws_proxy_password", - 3, - ClientConfiguration.DEFAULT_THROTTLE_RETRIES, - 10000 + SocketAccess.doPrivilegedVoid( + () -> launchAWSConfigurationTest( + settings, + Protocol.HTTP, + HOST, + PORT, + "aws_proxy_username", + "aws_proxy_password", + 3, + true, + 10000 + ) ); assertWarnings( "Using of " @@ -174,20 +285,22 @@ public void testProxyTypeOverrideProtocolSettings() { .setSecureSettings(secureSettings) .put("s3.client.default.protocol", "http") .put("s3.client.default.proxy.type", "https") - .put("s3.client.default.proxy.host", "127.0.0.10") - .put("s3.client.default.proxy.port", 8080) + .put("s3.client.default.proxy.host", HOST) + .put("s3.client.default.proxy.port", PORT) .put("s3.client.default.read_timeout", "10s") .build(); - launchAWSConfigurationTest( - settings, - Protocol.HTTP, - "127.0.0.10", - 8080, - "aws_proxy_username", - "aws_proxy_password", - 3, - ClientConfiguration.DEFAULT_THROTTLE_RETRIES, - 10000 + SocketAccess.doPrivilegedVoid( + () -> launchAWSConfigurationTest( + settings, + Protocol.HTTP, + HOST, + PORT, + "aws_proxy_username", + "aws_proxy_password", + 3, + true, + 10000 + ) ); } @@ -198,31 +311,29 @@ public void testSocksProxyConfiguration() throws IOException { final Settings settings = Settings.builder() .setSecureSettings(secureSettings) .put("s3.client.default.proxy.type", "socks") - .put("s3.client.default.proxy.host", "127.0.0.10") - .put("s3.client.default.proxy.port", 8080) + .put("s3.client.default.proxy.host", HOST) + .put("s3.client.default.proxy.port", PORT) .put("s3.client.default.read_timeout", "10s") .build(); - final S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(settings, "default"); - final ClientConfiguration configuration = S3Service.buildConfiguration(clientSettings); + final S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(settings, "default", configPath()); + final ProxyConfiguration configuration = S3Service.buildHttpProxyConfiguration(clientSettings); - assertEquals(Protocol.HTTPS, configuration.getProtocol()); - assertEquals(Protocol.HTTP, configuration.getProxyProtocol()); // default value in SDK - assertEquals(-1, configuration.getProxyPort()); - assertNull(configuration.getProxyUsername()); - assertNull(configuration.getProxyPassword()); + assertEquals(0, configuration.port()); + assertNull(configuration.username()); + assertNull(configuration.password()); } public void testRepositoryMaxRetries() { - final Settings settings = Settings.builder().put("s3.client.default.max_retries", 5).build(); - launchAWSConfigurationTest(settings, Protocol.HTTPS, null, -1, null, null, 5, ClientConfiguration.DEFAULT_THROTTLE_RETRIES, 50000); + final Settings settings = settingsBuilder.put("s3.client.default.max_retries", 5).build(); + SocketAccess.doPrivilegedVoid(() -> launchAWSConfigurationTest(settings, Protocol.HTTPS, HOST, PORT, "", "", 5, true, 50000)); } public void testRepositoryThrottleRetries() { final boolean throttling = randomBoolean(); - final Settings settings = Settings.builder().put("s3.client.default.use_throttle_retries", throttling).build(); - launchAWSConfigurationTest(settings, Protocol.HTTPS, null, -1, null, null, 3, throttling, 50000); + final Settings settings = settingsBuilder.put("s3.client.default.use_throttle_retries", throttling).build(); + SocketAccess.doPrivilegedVoid(() -> launchAWSConfigurationTest(settings, Protocol.HTTPS, HOST, PORT, "", "", 3, throttling, 50000)); } private void launchAWSConfigurationTest( @@ -237,18 +348,28 @@ private void launchAWSConfigurationTest( int expectedReadTimeout ) { - final S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(settings, "default"); - final ClientConfiguration configuration = S3Service.buildConfiguration(clientSettings); - - assertThat(configuration.getResponseMetadataCacheSize(), is(0)); - assertThat(configuration.getProtocol(), is(expectedProtocol)); - assertThat(configuration.getProxyHost(), is(expectedProxyHost)); - assertThat(configuration.getProxyPort(), is(expectedProxyPort)); - assertThat(configuration.getProxyUsername(), is(expectedProxyUsername)); - assertThat(configuration.getProxyPassword(), is(expectedProxyPassword)); - assertThat(configuration.getMaxErrorRetry(), is(expectedMaxRetries)); - assertThat(configuration.useThrottledRetries(), is(expectedUseThrottleRetries)); - assertThat(configuration.getSocketTimeout(), is(expectedReadTimeout)); + final S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(settings, "default", configPath()); + if (clientSettings.proxySettings != ProxySettings.NO_PROXY_SETTINGS) { + final ProxyConfiguration proxyConfiguration = S3Service.buildHttpProxyConfiguration(clientSettings); + assertThat(proxyConfiguration.host(), is(expectedProxyHost)); + assertThat(proxyConfiguration.port(), is(expectedProxyPort)); + assertThat(proxyConfiguration.username(), is(expectedProxyUsername)); + assertThat(proxyConfiguration.password(), is(expectedProxyPassword)); + } + + final ClientOverrideConfiguration clientOverrideConfiguration = S3Service.buildOverrideConfiguration(clientSettings); + + assertTrue(clientOverrideConfiguration.retryPolicy().isPresent()); + assertThat(clientOverrideConfiguration.retryPolicy().get().numRetries(), is(expectedMaxRetries)); + if (expectedUseThrottleRetries) { + assertThat( + clientOverrideConfiguration.retryPolicy().get().throttlingBackoffStrategy(), + is(BackoffStrategy.defaultThrottlingStrategy()) + ); + } else { + assertThat(clientOverrideConfiguration.retryPolicy().get().throttlingBackoffStrategy(), is(BackoffStrategy.none())); + } + // assertThat(proxyConfiguration.getSocketTimeout(), is(expectedReadTimeout)); } public void testEndpointSetting() { @@ -258,8 +379,7 @@ public void testEndpointSetting() { private void assertEndpoint(Settings repositorySettings, Settings settings, String expectedEndpoint) { final String configName = S3Repository.CLIENT_NAME.get(repositorySettings); - final S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(settings, configName); + final S3ClientSettings clientSettings = S3ClientSettings.getClientSettings(settings, configName, configPath()); assertThat(clientSettings.endpoint, is(expectedEndpoint)); } - } diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/ConfigPathSupport.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/ConfigPathSupport.java new file mode 100644 index 0000000000000..890dd245c67fd --- /dev/null +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/ConfigPathSupport.java @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import org.opensearch.common.io.PathUtils; + +import java.nio.file.Path; + +/** + * The trait that adds the config path to the test cases + */ +interface ConfigPathSupport { + default Path configPath() { + return PathUtils.get("config"); + } +} diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/RepositoryCredentialsTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/RepositoryCredentialsTests.java index 9c359d67db88b..a4bfe11383b4f 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/RepositoryCredentialsTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/RepositoryCredentialsTests.java @@ -32,12 +32,13 @@ package org.opensearch.repositories.s3; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.services.s3.AmazonS3; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.services.s3.DelegatingS3Client; +import software.amazon.awssdk.services.s3.S3Client; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; @@ -45,7 +46,7 @@ import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.PluginsService; @@ -57,6 +58,7 @@ import org.opensearch.test.OpenSearchSingleNodeTestCase; import org.opensearch.test.rest.FakeRestRequest; +import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collection; @@ -74,7 +76,7 @@ import static org.hamcrest.Matchers.notNullValue; @SuppressForbidden(reason = "test requires to set a System property to allow insecure settings when running in IDE") -public class RepositoryCredentialsTests extends OpenSearchSingleNodeTestCase { +public class RepositoryCredentialsTests extends OpenSearchSingleNodeTestCase implements ConfigPathSupport { static { AccessController.doPrivileged((PrivilegedAction) () -> { @@ -106,6 +108,7 @@ protected Settings nodeSettings() { } public void testRepositoryCredentialsOverrideSecureCredentials() { + SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); final String repositoryName = "repo-creds-override"; final Settings.Builder repositorySettings = Settings.builder() // repository settings for credentials override node secure settings @@ -123,12 +126,14 @@ public void testRepositoryCredentialsOverrideSecureCredentials() { assertThat(repositories.repository(repositoryName), instanceOf(S3Repository.class)); final S3Repository repository = (S3Repository) repositories.repository(repositoryName); - final AmazonS3 client = repository.createBlobStore().clientReference().get(); - assertThat(client, instanceOf(ProxyS3RepositoryPlugin.ClientAndCredentials.class)); + try (final AmazonS3Reference clientReference = repository.createBlobStore().clientReference()) { + S3Client client = clientReference.get(); + assertThat(client, instanceOf(ProxyS3RepositoryPlugin.ClientAndCredentials.class)); - final AWSCredentials credentials = ((ProxyS3RepositoryPlugin.ClientAndCredentials) client).credentials.getCredentials(); - assertThat(credentials.getAWSAccessKeyId(), is("insecure_aws_key")); - assertThat(credentials.getAWSSecretKey(), is("insecure_aws_secret")); + final AwsCredentials credentials = ((ProxyS3RepositoryPlugin.ClientAndCredentials) client).credentials.resolveCredentials(); + assertThat(credentials.accessKeyId(), is("insecure_aws_key")); + assertThat(credentials.secretAccessKey(), is("insecure_aws_secret")); + } assertWarnings( "[secret_key] setting was deprecated in OpenSearch and will be removed in a future release!" @@ -141,6 +146,7 @@ public void testRepositoryCredentialsOverrideSecureCredentials() { } public void testReinitSecureCredentials() { + SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); final String clientName = randomFrom("default", "other"); final Settings.Builder repositorySettings = Settings.builder(); @@ -162,19 +168,19 @@ public void testReinitSecureCredentials() { final S3Repository repository = (S3Repository) repositories.repository(repositoryName); try (AmazonS3Reference clientReference = ((S3BlobStore) repository.blobStore()).clientReference()) { - final AmazonS3 client = clientReference.get(); + final S3Client client = clientReference.get(); assertThat(client, instanceOf(ProxyS3RepositoryPlugin.ClientAndCredentials.class)); - final AWSCredentials credentials = ((ProxyS3RepositoryPlugin.ClientAndCredentials) client).credentials.getCredentials(); + final AwsCredentials credentials = ((ProxyS3RepositoryPlugin.ClientAndCredentials) client).credentials.resolveCredentials(); if (hasInsecureSettings) { - assertThat(credentials.getAWSAccessKeyId(), is("insecure_aws_key")); - assertThat(credentials.getAWSSecretKey(), is("insecure_aws_secret")); + assertThat(credentials.accessKeyId(), is("insecure_aws_key")); + assertThat(credentials.secretAccessKey(), is("insecure_aws_secret")); } else if ("other".equals(clientName)) { - assertThat(credentials.getAWSAccessKeyId(), is("secure_other_key")); - assertThat(credentials.getAWSSecretKey(), is("secure_other_secret")); + assertThat(credentials.accessKeyId(), is("secure_other_key")); + assertThat(credentials.secretAccessKey(), is("secure_other_secret")); } else { - assertThat(credentials.getAWSAccessKeyId(), is("secure_default_key")); - assertThat(credentials.getAWSSecretKey(), is("secure_default_secret")); + assertThat(credentials.accessKeyId(), is("secure_default_key")); + assertThat(credentials.secretAccessKey(), is("secure_default_secret")); } // new settings @@ -189,29 +195,29 @@ public void testReinitSecureCredentials() { // check the not-yet-closed client reference still has the same credentials if (hasInsecureSettings) { - assertThat(credentials.getAWSAccessKeyId(), is("insecure_aws_key")); - assertThat(credentials.getAWSSecretKey(), is("insecure_aws_secret")); + assertThat(credentials.accessKeyId(), is("insecure_aws_key")); + assertThat(credentials.secretAccessKey(), is("insecure_aws_secret")); } else if ("other".equals(clientName)) { - assertThat(credentials.getAWSAccessKeyId(), is("secure_other_key")); - assertThat(credentials.getAWSSecretKey(), is("secure_other_secret")); + assertThat(credentials.accessKeyId(), is("secure_other_key")); + assertThat(credentials.secretAccessKey(), is("secure_other_secret")); } else { - assertThat(credentials.getAWSAccessKeyId(), is("secure_default_key")); - assertThat(credentials.getAWSSecretKey(), is("secure_default_secret")); + assertThat(credentials.accessKeyId(), is("secure_default_key")); + assertThat(credentials.secretAccessKey(), is("secure_default_secret")); } } // check credentials have been updated try (AmazonS3Reference clientReference = ((S3BlobStore) repository.blobStore()).clientReference()) { - final AmazonS3 client = clientReference.get(); + final S3Client client = clientReference.get(); assertThat(client, instanceOf(ProxyS3RepositoryPlugin.ClientAndCredentials.class)); - final AWSCredentials newCredentials = ((ProxyS3RepositoryPlugin.ClientAndCredentials) client).credentials.getCredentials(); + final AwsCredentials newCredentials = ((ProxyS3RepositoryPlugin.ClientAndCredentials) client).credentials.resolveCredentials(); if (hasInsecureSettings) { - assertThat(newCredentials.getAWSAccessKeyId(), is("insecure_aws_key")); - assertThat(newCredentials.getAWSSecretKey(), is("insecure_aws_secret")); + assertThat(newCredentials.accessKeyId(), is("insecure_aws_key")); + assertThat(newCredentials.secretAccessKey(), is("insecure_aws_secret")); } else { - assertThat(newCredentials.getAWSAccessKeyId(), is("new_secret_aws_key")); - assertThat(newCredentials.getAWSSecretKey(), is("new_secret_aws_secret")); + assertThat(newCredentials.accessKeyId(), is("new_secret_aws_key")); + assertThat(newCredentials.secretAccessKey(), is("new_secret_aws_secret")); } } @@ -228,6 +234,7 @@ public void testReinitSecureCredentials() { } public void testInsecureRepositoryCredentials() throws Exception { + SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); final String repositoryName = "repo-insecure-creds"; createRepository( repositoryName, @@ -284,8 +291,8 @@ private void createRepository(final String name, final Settings repositorySettin */ public static final class ProxyS3RepositoryPlugin extends S3RepositoryPlugin { - public ProxyS3RepositoryPlugin(Settings settings) { - super(settings, new ProxyS3Service()); + public ProxyS3RepositoryPlugin(Settings settings, Path configPath) { + super(settings, configPath, new ProxyS3Service(configPath), new S3AsyncService(configPath)); } @Override @@ -295,7 +302,7 @@ protected S3Repository createRepository( ClusterService clusterService, RecoverySettings recoverySettings ) { - return new S3Repository(metadata, registry, service, clusterService, recoverySettings) { + return new S3Repository(metadata, registry, service, clusterService, recoverySettings, null, null, null, null, false) { @Override protected void assertSnapshotOrGenericThread() { // eliminate thread name check as we create repo manually on test/main threads @@ -303,10 +310,10 @@ protected void assertSnapshotOrGenericThread() { }; } - public static final class ClientAndCredentials extends AmazonS3Wrapper { - final AWSCredentialsProvider credentials; + public static final class ClientAndCredentials extends DelegatingS3Client { + final AwsCredentialsProvider credentials; - ClientAndCredentials(AmazonS3 delegate, AWSCredentialsProvider credentials) { + ClientAndCredentials(S3Client delegate, AwsCredentialsProvider credentials) { super(delegate); this.credentials = credentials; } @@ -316,10 +323,15 @@ public static final class ProxyS3Service extends S3Service { private static final Logger logger = LogManager.getLogger(ProxyS3Service.class); + ProxyS3Service(final Path configPath) { + super(configPath); + } + @Override - AmazonS3 buildClient(final S3ClientSettings clientSettings) { - final AmazonS3 client = super.buildClient(clientSettings); - return new ClientAndCredentials(client, buildCredentials(logger, clientSettings)); + AmazonS3WithCredentials buildClient(final S3ClientSettings clientSettings) { + final AmazonS3WithCredentials client = SocketAccess.doPrivileged(() -> super.buildClient(clientSettings)); + final AwsCredentialsProvider credentials = buildCredentials(logger, clientSettings); + return AmazonS3WithCredentials.create(new ClientAndCredentials(client.client(), credentials), credentials); } } diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3AsyncServiceTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3AsyncServiceTests.java new file mode 100644 index 0000000000000..e9fe557ab751a --- /dev/null +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3AsyncServiceTests.java @@ -0,0 +1,95 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import org.opensearch.cli.SuppressForbidden; +import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.common.settings.MockSecureSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.repositories.s3.async.AsyncExecutorContainer; +import org.opensearch.repositories.s3.async.AsyncTransferEventLoopGroup; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; + +import java.util.Map; +import java.util.concurrent.Executors; + +public class S3AsyncServiceTests extends OpenSearchTestCase implements ConfigPathSupport { + + @Override + @Before + @SuppressForbidden(reason = "Need to set opensearch.path.conf for async client") + public void setUp() throws Exception { + SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); + super.setUp(); + } + + public void testCachedClientsAreReleased() { + final S3AsyncService s3AsyncService = new S3AsyncService(configPath()); + final Settings settings = Settings.builder().put("endpoint", "http://first").put("region", "us-east-2").build(); + final RepositoryMetadata metadata1 = new RepositoryMetadata("first", "s3", settings); + final RepositoryMetadata metadata2 = new RepositoryMetadata("second", "s3", settings); + final AsyncExecutorContainer asyncExecutorContainer = new AsyncExecutorContainer( + Executors.newSingleThreadExecutor(), + Executors.newSingleThreadExecutor(), + new AsyncTransferEventLoopGroup(1) + ); + final S3ClientSettings clientSettings = s3AsyncService.settings(metadata2); + final S3ClientSettings otherClientSettings = s3AsyncService.settings(metadata2); + assertSame(clientSettings, otherClientSettings); + final AmazonAsyncS3Reference reference = SocketAccess.doPrivileged( + () -> s3AsyncService.client(metadata1, asyncExecutorContainer, asyncExecutorContainer) + ); + reference.close(); + s3AsyncService.close(); + final AmazonAsyncS3Reference referenceReloaded = SocketAccess.doPrivileged( + () -> s3AsyncService.client(metadata1, asyncExecutorContainer, asyncExecutorContainer) + ); + assertNotSame(referenceReloaded, reference); + referenceReloaded.close(); + s3AsyncService.close(); + final S3ClientSettings clientSettingsReloaded = s3AsyncService.settings(metadata1); + assertNotSame(clientSettings, clientSettingsReloaded); + } + + public void testCachedClientsWithCredentialsAreReleased() { + final MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("s3.client.default.role_arn", "role"); + final Map defaults = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).put("s3.client.default.identity_token_file", "file").build(), + configPath() + ); + final S3AsyncService s3AsyncService = new S3AsyncService(configPath()); + s3AsyncService.refreshAndClearCache(defaults); + final Settings settings = Settings.builder().put("endpoint", "http://first").put("region", "us-east-2").build(); + final RepositoryMetadata metadata1 = new RepositoryMetadata("first", "s3", settings); + final RepositoryMetadata metadata2 = new RepositoryMetadata("second", "s3", settings); + final AsyncExecutorContainer asyncExecutorContainer = new AsyncExecutorContainer( + Executors.newSingleThreadExecutor(), + Executors.newSingleThreadExecutor(), + new AsyncTransferEventLoopGroup(1) + ); + final S3ClientSettings clientSettings = s3AsyncService.settings(metadata2); + final S3ClientSettings otherClientSettings = s3AsyncService.settings(metadata2); + assertSame(clientSettings, otherClientSettings); + final AmazonAsyncS3Reference reference = SocketAccess.doPrivileged( + () -> s3AsyncService.client(metadata1, asyncExecutorContainer, asyncExecutorContainer) + ); + reference.close(); + s3AsyncService.close(); + final AmazonAsyncS3Reference referenceReloaded = SocketAccess.doPrivileged( + () -> s3AsyncService.client(metadata1, asyncExecutorContainer, asyncExecutorContainer) + ); + assertNotSame(referenceReloaded, reference); + referenceReloaded.close(); + s3AsyncService.close(); + final S3ClientSettings clientSettingsReloaded = s3AsyncService.settings(metadata1); + assertNotSame(clientSettings, clientSettingsReloaded); + } +} diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerMockClientTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerMockClientTests.java new file mode 100644 index 0000000000000..8c8524212e08e --- /dev/null +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerMockClientTests.java @@ -0,0 +1,544 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3; + +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.model.UploadPartResponse; + +import org.apache.lucene.store.IndexInput; +import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.common.CheckedTriFunction; +import org.opensearch.common.StreamContext; +import org.opensearch.common.blobstore.BlobPath; +import org.opensearch.common.blobstore.stream.write.StreamContextSupplier; +import org.opensearch.common.blobstore.stream.write.WriteContext; +import org.opensearch.common.blobstore.stream.write.WritePriority; +import org.opensearch.common.blobstore.transfer.stream.OffsetRangeIndexInputStream; +import org.opensearch.common.io.InputStreamContainer; +import org.opensearch.common.lucene.store.ByteArrayIndexInput; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.repositories.s3.async.AsyncExecutorContainer; +import org.opensearch.repositories.s3.async.AsyncTransferEventLoopGroup; +import org.opensearch.repositories.s3.async.AsyncTransferManager; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import org.mockito.invocation.InvocationOnMock; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +public class S3BlobContainerMockClientTests extends OpenSearchTestCase implements ConfigPathSupport { + + private MockS3AsyncService asyncService; + private ExecutorService futureCompletionService; + private ExecutorService streamReaderService; + private AsyncTransferEventLoopGroup transferNIOGroup; + private S3BlobContainer blobContainer; + + static class MockS3AsyncService extends S3AsyncService { + + private final S3AsyncClient asyncClient = mock(S3AsyncClient.class); + private final int maxDelayInFutureCompletionMillis; + + private boolean failPutObjectRequest; + private boolean failCreateMultipartUploadRequest; + private boolean failUploadPartRequest; + private boolean failCompleteMultipartUploadRequest; + + private String multipartUploadId; + + public MockS3AsyncService(Path configPath, int maxDelayInFutureCompletionMillis) { + super(configPath); + this.maxDelayInFutureCompletionMillis = maxDelayInFutureCompletionMillis; + } + + public void initializeMocks( + boolean failPutObjectRequest, + boolean failCreateMultipartUploadRequest, + boolean failUploadPartRequest, + boolean failCompleteMultipartUploadRequest + ) { + setupFailureBooleans( + failPutObjectRequest, + failCreateMultipartUploadRequest, + failUploadPartRequest, + failCompleteMultipartUploadRequest + ); + doAnswer(this::doOnPutObject).when(asyncClient).putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class)); + doAnswer(this::doOnDeleteObject).when(asyncClient).deleteObject(any(DeleteObjectRequest.class)); + doAnswer(this::doOnCreateMultipartUpload).when(asyncClient).createMultipartUpload(any(CreateMultipartUploadRequest.class)); + doAnswer(this::doOnPartUpload).when(asyncClient).uploadPart(any(UploadPartRequest.class), any(AsyncRequestBody.class)); + doAnswer(this::doOnCompleteMultipartUpload).when(asyncClient) + .completeMultipartUpload(any(CompleteMultipartUploadRequest.class)); + doAnswer(this::doOnAbortMultipartUpload).when(asyncClient).abortMultipartUpload(any(AbortMultipartUploadRequest.class)); + } + + private void setupFailureBooleans( + boolean failPutObjectRequest, + boolean failCreateMultipartUploadRequest, + boolean failUploadPartRequest, + boolean failCompleteMultipartUploadRequest + ) { + this.failPutObjectRequest = failPutObjectRequest; + this.failCreateMultipartUploadRequest = failCreateMultipartUploadRequest; + this.failUploadPartRequest = failUploadPartRequest; + this.failCompleteMultipartUploadRequest = failCompleteMultipartUploadRequest; + } + + private CompletableFuture doOnPutObject(InvocationOnMock invocationOnMock) { + CompletableFuture completableFuture = new CompletableFuture<>(); + new Thread(() -> { + try { + Thread.sleep(randomInt(maxDelayInFutureCompletionMillis)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (failPutObjectRequest) { + completableFuture.completeExceptionally(new IOException()); + } else { + completableFuture.complete(PutObjectResponse.builder().build()); + } + }).start(); + + return completableFuture; + } + + private CompletableFuture doOnDeleteObject(InvocationOnMock invocationOnMock) { + CompletableFuture completableFuture = new CompletableFuture<>(); + new Thread(() -> { + try { + Thread.sleep(randomInt(maxDelayInFutureCompletionMillis)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (failPutObjectRequest) { + completableFuture.completeExceptionally(new IOException()); + } else { + completableFuture.complete(DeleteObjectResponse.builder().build()); + } + }).start(); + + return completableFuture; + } + + private CompletableFuture doOnCreateMultipartUpload(InvocationOnMock invocationOnMock) { + multipartUploadId = randomAlphaOfLength(5); + CompletableFuture completableFuture = new CompletableFuture<>(); + new Thread(() -> { + try { + Thread.sleep(randomInt(maxDelayInFutureCompletionMillis)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (failCreateMultipartUploadRequest) { + completableFuture.completeExceptionally(new IOException()); + } else { + completableFuture.complete(CreateMultipartUploadResponse.builder().uploadId(multipartUploadId).build()); + } + }).start(); + + return completableFuture; + } + + private CompletableFuture doOnPartUpload(InvocationOnMock invocationOnMock) { + UploadPartRequest uploadPartRequest = invocationOnMock.getArgument(0); + assertEquals(multipartUploadId, uploadPartRequest.uploadId()); + CompletableFuture completableFuture = new CompletableFuture<>(); + new Thread(() -> { + try { + Thread.sleep(randomInt(maxDelayInFutureCompletionMillis)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (failUploadPartRequest) { + completableFuture.completeExceptionally(new IOException()); + } else { + completableFuture.complete(UploadPartResponse.builder().eTag("eTag").build()); + } + }).start(); + + return completableFuture; + } + + private CompletableFuture doOnCompleteMultipartUpload(InvocationOnMock invocationOnMock) { + CompleteMultipartUploadRequest completeMultipartUploadRequest = invocationOnMock.getArgument(0); + assertEquals(multipartUploadId, completeMultipartUploadRequest.uploadId()); + CompletableFuture completableFuture = new CompletableFuture<>(); + new Thread(() -> { + try { + Thread.sleep(randomInt(maxDelayInFutureCompletionMillis)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (failCompleteMultipartUploadRequest) { + completableFuture.completeExceptionally(new IOException()); + } else { + completableFuture.complete(CompleteMultipartUploadResponse.builder().build()); + } + }).start(); + + return completableFuture; + } + + private CompletableFuture doOnAbortMultipartUpload(InvocationOnMock invocationOnMock) { + AbortMultipartUploadRequest abortMultipartUploadRequest = invocationOnMock.getArgument(0); + assertEquals(multipartUploadId, abortMultipartUploadRequest.uploadId()); + CompletableFuture completableFuture = new CompletableFuture<>(); + new Thread(() -> { + try { + Thread.sleep(randomInt(maxDelayInFutureCompletionMillis)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + completableFuture.complete(AbortMultipartUploadResponse.builder().build()); + + }).start(); + + return completableFuture; + } + + public void verifyMultipartUploadCallCount(int numberOfParts, boolean finalizeUploadFailure) { + verify(asyncClient, times(1)).createMultipartUpload(any(CreateMultipartUploadRequest.class)); + verify(asyncClient, times(!failCreateMultipartUploadRequest ? numberOfParts : 0)).uploadPart( + any(UploadPartRequest.class), + any(AsyncRequestBody.class) + ); + verify(asyncClient, times(!failCreateMultipartUploadRequest && !failUploadPartRequest && !finalizeUploadFailure ? 1 : 0)) + .completeMultipartUpload(any(CompleteMultipartUploadRequest.class)); + verify( + asyncClient, + times( + (!failCreateMultipartUploadRequest && (failUploadPartRequest || failCompleteMultipartUploadRequest)) + || finalizeUploadFailure ? 1 : 0 + ) + ).abortMultipartUpload(any(AbortMultipartUploadRequest.class)); + } + + public void verifySingleChunkUploadCallCount(boolean finalizeUploadFailure) { + verify(asyncClient, times(1)).putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class)); + verify(asyncClient, times(finalizeUploadFailure ? 1 : 0)).deleteObject(any(DeleteObjectRequest.class)); + } + + @Override + public AmazonAsyncS3Reference client( + RepositoryMetadata repositoryMetadata, + AsyncExecutorContainer priorityExecutorBuilder, + AsyncExecutorContainer normalExecutorBuilder + ) { + return new AmazonAsyncS3Reference(AmazonAsyncS3WithCredentials.create(asyncClient, asyncClient, null)); + } + } + + /** + * An IndexInput implementation that serves only zeroes + */ + static class ZeroIndexInput extends IndexInput { + + private final AtomicBoolean closed = new AtomicBoolean(false); + private final AtomicLong reads = new AtomicLong(0); + private final long length; + + /** + * @param resourceDescription resourceDescription should be a non-null, opaque string describing this resource; it's returned + * from {@link #toString}. + */ + public ZeroIndexInput(String resourceDescription, final long length) { + super(resourceDescription); + this.length = length; + } + + @Override + public void close() throws IOException { + closed.set(true); + } + + @Override + public long getFilePointer() { + return reads.get(); + } + + @Override + public void seek(long pos) throws IOException { + reads.set(pos); + } + + @Override + public long length() { + return length; + } + + @Override + public IndexInput slice(String sliceDescription, long offset, long length) throws IOException { + return new ZeroIndexInput(sliceDescription, length); + } + + @Override + public byte readByte() throws IOException { + ensureOpen(); + return (byte) ((reads.incrementAndGet() <= length) ? 0 : -1); + } + + @Override + public void readBytes(byte[] b, int offset, int len) throws IOException { + ensureOpen(); + final long available = available(); + final int toCopy = Math.min(len, (int) available); + Arrays.fill(b, offset, offset + toCopy, (byte) 0); + reads.addAndGet(toCopy); + } + + private long available() { + return Math.max(length - reads.get(), 0); + } + + private void ensureOpen() throws IOException { + if (closed.get()) { + throw new IOException("Stream closed"); + } + } + } + + @Override + @Before + public void setUp() throws Exception { + asyncService = new MockS3AsyncService(configPath(), 1000); + futureCompletionService = Executors.newSingleThreadExecutor(); + streamReaderService = Executors.newSingleThreadExecutor(); + transferNIOGroup = new AsyncTransferEventLoopGroup(1); + blobContainer = createBlobContainer(); + super.setUp(); + } + + @Override + @After + public void tearDown() throws Exception { + IOUtils.close(asyncService); + super.tearDown(); + } + + private S3BlobContainer createBlobContainer() { + return new S3BlobContainer(BlobPath.cleanPath(), createBlobStore()); + } + + private S3BlobStore createBlobStore() { + final String clientName = randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + + final RepositoryMetadata repositoryMetadata = new RepositoryMetadata( + "repository", + S3Repository.TYPE, + Settings.builder().put(S3Repository.CLIENT_NAME.getKey(), clientName).build() + ); + + AsyncExecutorContainer asyncExecutorContainer = new AsyncExecutorContainer( + futureCompletionService, + streamReaderService, + transferNIOGroup + ); + + return new S3BlobStore( + null, + asyncService, + true, + "bucket", + S3Repository.SERVER_SIDE_ENCRYPTION_SETTING.getDefault(Settings.EMPTY), + S3Repository.BUFFER_SIZE_SETTING.getDefault(Settings.EMPTY), + S3Repository.CANNED_ACL_SETTING.getDefault(Settings.EMPTY), + S3Repository.STORAGE_CLASS_SETTING.getDefault(Settings.EMPTY), + repositoryMetadata, + new AsyncTransferManager( + S3Repository.PARALLEL_MULTIPART_UPLOAD_MINIMUM_PART_SIZE_SETTING.getDefault(Settings.EMPTY).getBytes(), + asyncExecutorContainer.getStreamReader(), + asyncExecutorContainer.getStreamReader() + ), + asyncExecutorContainer, + asyncExecutorContainer + ); + } + + public void testWriteBlobByStreamsNoFailure() throws IOException, ExecutionException, InterruptedException { + asyncService.initializeMocks(false, false, false, false); + testWriteBlobByStreamsLargeBlob(false, false); + } + + public void testWriteBlobByStreamsFinalizeUploadFailure() throws IOException, ExecutionException, InterruptedException { + asyncService.initializeMocks(false, false, false, false); + testWriteBlobByStreamsLargeBlob(false, true); + } + + public void testWriteBlobByStreamsCreateMultipartRequestFailure() throws IOException, ExecutionException, InterruptedException { + asyncService.initializeMocks(false, true, false, false); + testWriteBlobByStreamsLargeBlob(true, false); + } + + public void testWriteBlobByStreamsUploadPartRequestFailure() throws IOException, ExecutionException, InterruptedException { + asyncService.initializeMocks(false, false, true, false); + testWriteBlobByStreamsLargeBlob(true, false); + } + + public void testWriteBlobByStreamsCompleteMultipartRequestFailure() throws IOException, ExecutionException, InterruptedException { + asyncService.initializeMocks(false, false, false, true); + testWriteBlobByStreamsLargeBlob(true, false); + } + + public void testWriteBlobByStreamsSingleChunkUploadNoFailure() throws IOException, ExecutionException, InterruptedException { + asyncService.initializeMocks(false, false, false, false); + testWriteBlobByStreams(false, false); + } + + public void testWriteBlobByStreamsSingleChunkUploadPutObjectFailure() throws IOException, ExecutionException, InterruptedException { + asyncService.initializeMocks(true, false, false, false); + testWriteBlobByStreams(true, false); + } + + public void testWriteBlobByStreamsSingleChunkUploadFinalizeUploadFailure() throws IOException, ExecutionException, + InterruptedException { + asyncService.initializeMocks(false, false, false, false); + testWriteBlobByStreams(false, true); + } + + private void testWriteBlobByStreams(boolean expectException, boolean throwExceptionOnFinalizeUpload) throws IOException, + ExecutionException, InterruptedException { + final byte[] bytes = randomByteArrayOfLength(100); + List openInputStreams = new ArrayList<>(); + CountDownLatch countDownLatch = new CountDownLatch(1); + AtomicReference exceptionRef = new AtomicReference<>(); + ActionListener completionListener = ActionListener.wrap(resp -> { countDownLatch.countDown(); }, ex -> { + exceptionRef.set(ex); + countDownLatch.countDown(); + }); + blobContainer.asyncBlobUpload(new WriteContext("write_blob_by_streams_max_retries", new StreamContextSupplier() { + @Override + public StreamContext supplyStreamContext(long partSize) { + return new StreamContext(new CheckedTriFunction() { + @Override + public InputStreamContainer apply(Integer partNo, Long size, Long position) throws IOException { + InputStream inputStream = new OffsetRangeIndexInputStream(new ByteArrayIndexInput("desc", bytes), size, position); + openInputStreams.add(inputStream); + return new InputStreamContainer(inputStream, size, position); + } + }, partSize, calculateLastPartSize(bytes.length, partSize), calculateNumberOfParts(bytes.length, partSize)); + } + }, bytes.length, false, WritePriority.NORMAL, uploadSuccess -> { + assertTrue(uploadSuccess); + if (throwExceptionOnFinalizeUpload) { + throw new RuntimeException(); + } + }, false, null), completionListener); + + assertTrue(countDownLatch.await(5000, TimeUnit.SECONDS)); + // wait for completableFuture to finish + if (expectException || throwExceptionOnFinalizeUpload) { + assertNotNull(exceptionRef.get()); + } + + asyncService.verifySingleChunkUploadCallCount(throwExceptionOnFinalizeUpload); + + openInputStreams.forEach(inputStream -> { + try { + inputStream.close(); + } catch (IOException e) { + fail("Failure while closing open input streams"); + } + }); + } + + private void testWriteBlobByStreamsLargeBlob(boolean expectException, boolean throwExceptionOnFinalizeUpload) throws IOException, + ExecutionException, InterruptedException { + final ByteSizeValue partSize = S3Repository.PARALLEL_MULTIPART_UPLOAD_MINIMUM_PART_SIZE_SETTING.getDefault(Settings.EMPTY); + + int numberOfParts = randomIntBetween(2, 5); + final long lastPartSize = randomLongBetween(10, 512); + final long blobSize = ((numberOfParts - 1) * partSize.getBytes()) + lastPartSize; + CountDownLatch countDownLatch = new CountDownLatch(1); + AtomicReference exceptionRef = new AtomicReference<>(); + ActionListener completionListener = ActionListener.wrap(resp -> { countDownLatch.countDown(); }, ex -> { + exceptionRef.set(ex); + countDownLatch.countDown(); + }); + List openInputStreams = new ArrayList<>(); + blobContainer.asyncBlobUpload(new WriteContext("write_large_blob", new StreamContextSupplier() { + @Override + public StreamContext supplyStreamContext(long partSize) { + return new StreamContext(new CheckedTriFunction() { + @Override + public InputStreamContainer apply(Integer partNo, Long size, Long position) throws IOException { + InputStream inputStream = new OffsetRangeIndexInputStream(new ZeroIndexInput("desc", blobSize), size, position); + openInputStreams.add(inputStream); + return new InputStreamContainer(inputStream, size, position); + } + }, partSize, calculateLastPartSize(blobSize, partSize), calculateNumberOfParts(blobSize, partSize)); + } + }, blobSize, false, WritePriority.HIGH, uploadSuccess -> { + assertTrue(uploadSuccess); + if (throwExceptionOnFinalizeUpload) { + throw new RuntimeException(); + } + }, false, null), completionListener); + + assertTrue(countDownLatch.await(5000, TimeUnit.SECONDS)); + if (expectException || throwExceptionOnFinalizeUpload) { + assertNotNull(exceptionRef.get()); + } + + asyncService.verifyMultipartUploadCallCount(numberOfParts, throwExceptionOnFinalizeUpload); + + openInputStreams.forEach(inputStream -> { + try { + inputStream.close(); + } catch (IOException ex) { + logger.error("Error closing input stream"); + } + }); + } + + private long calculateLastPartSize(long totalSize, long partSize) { + return totalSize % partSize == 0 ? partSize : totalSize % partSize; + } + + private int calculateNumberOfParts(long contentLength, long partSize) { + return (int) ((contentLength % partSize) == 0 ? contentLength / partSize : (contentLength / partSize) + 1); + } +} diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerRetriesTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerRetriesTests.java index 4f4ec0afcf9f4..ecad68474b601 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerRetriesTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobContainerRetriesTests.java @@ -31,29 +31,45 @@ package org.opensearch.repositories.s3; -import com.amazonaws.SdkClientException; -import com.amazonaws.services.s3.internal.MD5DigestCalculatingInputStream; -import com.amazonaws.util.Base16; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.io.SdkDigestInputStream; +import software.amazon.awssdk.utils.internal.Base16; + import org.apache.http.HttpStatus; import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.common.CheckedTriFunction; import org.opensearch.common.Nullable; +import org.opensearch.common.StreamContext; import org.opensearch.common.SuppressForbidden; +import org.opensearch.common.blobstore.AsyncMultiStreamBlobContainer; import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.common.blobstore.stream.write.StreamContextSupplier; +import org.opensearch.common.blobstore.stream.write.WriteContext; +import org.opensearch.common.blobstore.stream.write.WritePriority; +import org.opensearch.common.blobstore.transfer.stream.OffsetRangeIndexInputStream; +import org.opensearch.common.hash.MessageDigests; +import org.opensearch.common.io.InputStreamContainer; import org.opensearch.common.io.Streams; import org.opensearch.common.lucene.store.ByteArrayIndexInput; import org.opensearch.common.lucene.store.InputStreamIndexInput; import org.opensearch.common.network.InetAddresses; import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.CountDown; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.repositories.blobstore.AbstractBlobContainerRetriesTestCase; +import org.opensearch.repositories.blobstore.ZeroInputStream; +import org.opensearch.repositories.s3.async.AsyncExecutorContainer; +import org.opensearch.repositories.s3.async.AsyncTransferEventLoopGroup; +import org.opensearch.repositories.s3.async.AsyncTransferManager; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import java.io.ByteArrayInputStream; @@ -63,14 +79,22 @@ import java.net.InetSocketAddress; import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import static org.opensearch.repositories.s3.S3ClientSettings.DISABLE_CHUNKED_ENCODING; import static org.opensearch.repositories.s3.S3ClientSettings.ENDPOINT_SETTING; import static org.opensearch.repositories.s3.S3ClientSettings.MAX_RETRIES_SETTING; import static org.opensearch.repositories.s3.S3ClientSettings.READ_TIMEOUT_SETTING; +import static org.opensearch.repositories.s3.S3ClientSettings.REGION; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -81,19 +105,43 @@ * This class tests how a {@link S3BlobContainer} and its underlying AWS S3 client are retrying requests when reading or writing blobs. */ @SuppressForbidden(reason = "use a http server") -public class S3BlobContainerRetriesTests extends AbstractBlobContainerRetriesTestCase { +public class S3BlobContainerRetriesTests extends AbstractBlobContainerRetriesTestCase implements ConfigPathSupport { private S3Service service; + private String previousOpenSearchPathConf; + private S3AsyncService asyncService; + private ExecutorService futureCompletionService; + private ExecutorService streamReaderService; + private AsyncTransferEventLoopGroup transferNIOGroup; @Before public void setUp() throws Exception { - service = new S3Service(); + previousOpenSearchPathConf = SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); + service = new S3Service(configPath()); + asyncService = new S3AsyncService(configPath()); + + futureCompletionService = Executors.newSingleThreadExecutor(); + streamReaderService = Executors.newSingleThreadExecutor(); + transferNIOGroup = new AsyncTransferEventLoopGroup(1); + + // needed by S3AsyncService + SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", configPath().toString())); super.setUp(); } @After public void tearDown() throws Exception { - IOUtils.close(service); + IOUtils.close(service, asyncService); + + streamReaderService.shutdown(); + futureCompletionService.shutdown(); + IOUtils.close(transferNIOGroup); + + if (previousOpenSearchPathConf != null) { + SocketAccess.doPrivileged(() -> System.setProperty("opensearch.path.conf", previousOpenSearchPathConf)); + } else { + SocketAccess.doPrivileged(() -> System.clearProperty("opensearch.path.conf")); + } super.tearDown(); } @@ -113,7 +161,7 @@ protected Class unresponsiveExceptionType() { } @Override - protected BlobContainer createBlobContainer( + protected AsyncMultiStreamBlobContainer createBlobContainer( final @Nullable Integer maxRetries, final @Nullable TimeValue readTimeout, final @Nullable Boolean disableChunkedEncoding, @@ -125,6 +173,7 @@ protected BlobContainer createBlobContainer( final InetSocketAddress address = httpServer.getAddress(); final String endpoint = "http://" + InetAddresses.toUriString(address.getAddress()) + ":" + address.getPort(); clientSettings.put(ENDPOINT_SETTING.getConcreteSettingForNamespace(clientName).getKey(), endpoint); + clientSettings.put(REGION.getConcreteSettingForNamespace(clientName).getKey(), "region"); if (maxRetries != null) { clientSettings.put(MAX_RETRIES_SETTING.getConcreteSettingForNamespace(clientName).getKey(), maxRetries); @@ -140,7 +189,8 @@ protected BlobContainer createBlobContainer( secureSettings.setString(S3ClientSettings.ACCESS_KEY_SETTING.getConcreteSettingForNamespace(clientName).getKey(), "access"); secureSettings.setString(S3ClientSettings.SECRET_KEY_SETTING.getConcreteSettingForNamespace(clientName).getKey(), "secret"); clientSettings.setSecureSettings(secureSettings); - service.refreshAndClearCache(S3ClientSettings.load(clientSettings.build())); + service.refreshAndClearCache(S3ClientSettings.load(clientSettings.build(), configPath())); + asyncService.refreshAndClearCache(S3ClientSettings.load(clientSettings.build(), configPath())); final RepositoryMetadata repositoryMetadata = new RepositoryMetadata( "repository", @@ -148,16 +198,31 @@ protected BlobContainer createBlobContainer( Settings.builder().put(S3Repository.CLIENT_NAME.getKey(), clientName).build() ); + AsyncExecutorContainer asyncExecutorContainer = new AsyncExecutorContainer( + futureCompletionService, + streamReaderService, + transferNIOGroup + ); + return new S3BlobContainer( BlobPath.cleanPath(), new S3BlobStore( service, + asyncService, + true, "bucket", S3Repository.SERVER_SIDE_ENCRYPTION_SETTING.getDefault(Settings.EMPTY), bufferSize == null ? S3Repository.BUFFER_SIZE_SETTING.getDefault(Settings.EMPTY) : bufferSize, S3Repository.CANNED_ACL_SETTING.getDefault(Settings.EMPTY), S3Repository.STORAGE_CLASS_SETTING.getDefault(Settings.EMPTY), - repositoryMetadata + repositoryMetadata, + new AsyncTransferManager( + S3Repository.PARALLEL_MULTIPART_UPLOAD_MINIMUM_PART_SIZE_SETTING.getDefault(Settings.EMPTY).getBytes(), + asyncExecutorContainer.getStreamReader(), + asyncExecutorContainer.getStreamReader() + ), + asyncExecutorContainer, + asyncExecutorContainer ) ) { @Override @@ -211,12 +276,93 @@ public void testWriteBlobWithRetries() throws Exception { }); final BlobContainer blobContainer = createBlobContainer(maxRetries, null, true, null); - try (InputStream stream = new InputStreamIndexInput(new ByteArrayIndexInput("desc", bytes), bytes.length)) { + try (InputStream stream = new ByteArrayInputStream(bytes)) { blobContainer.writeBlob("write_blob_max_retries", stream, bytes.length, false); } assertThat(countDown.isCountedDown(), is(true)); } + public void testWriteBlobByStreamsWithRetries() throws Exception { + final int maxRetries = randomInt(5); + final CountDown countDown = new CountDown(maxRetries + 1); + + final byte[] bytes = randomBlobContent(); + httpServer.createContext("/bucket/write_blob_by_streams_max_retries", exchange -> { + if ("PUT".equals(exchange.getRequestMethod()) && exchange.getRequestURI().getQuery() == null) { + if (countDown.countDown()) { + final BytesReference body = Streams.readFully(exchange.getRequestBody()); + if (Objects.deepEquals(bytes, BytesReference.toBytes(body))) { + exchange.sendResponseHeaders(HttpStatus.SC_OK, -1); + } else { + exchange.sendResponseHeaders(HttpStatus.SC_BAD_REQUEST, -1); + } + exchange.close(); + return; + } + + if (randomBoolean()) { + if (randomBoolean()) { + Streams.readFully(exchange.getRequestBody(), new byte[randomIntBetween(1, Math.max(1, bytes.length - 1))]); + } else { + Streams.readFully(exchange.getRequestBody()); + exchange.sendResponseHeaders( + randomFrom( + HttpStatus.SC_INTERNAL_SERVER_ERROR, + HttpStatus.SC_BAD_GATEWAY, + HttpStatus.SC_SERVICE_UNAVAILABLE, + HttpStatus.SC_GATEWAY_TIMEOUT + ), + -1 + ); + } + } + exchange.close(); + } + }); + + final AsyncMultiStreamBlobContainer blobContainer = createBlobContainer(maxRetries, null, true, null); + List openInputStreams = new ArrayList<>(); + CountDownLatch countDownLatch = new CountDownLatch(1); + AtomicReference exceptionRef = new AtomicReference<>(); + ActionListener completionListener = ActionListener.wrap(resp -> { countDownLatch.countDown(); }, ex -> { + exceptionRef.set(ex); + countDownLatch.countDown(); + }); + blobContainer.asyncBlobUpload(new WriteContext("write_blob_by_streams_max_retries", new StreamContextSupplier() { + @Override + public StreamContext supplyStreamContext(long partSize) { + return new StreamContext(new CheckedTriFunction() { + @Override + public InputStreamContainer apply(Integer partNo, Long size, Long position) throws IOException { + InputStream inputStream = new OffsetRangeIndexInputStream(new ByteArrayIndexInput("desc", bytes), size, position); + openInputStreams.add(inputStream); + return new InputStreamContainer(inputStream, size, position); + } + }, partSize, calculateLastPartSize(bytes.length, partSize), calculateNumberOfParts(bytes.length, partSize)); + } + }, bytes.length, false, WritePriority.NORMAL, Assert::assertTrue, false, null), completionListener); + + assertTrue(countDownLatch.await(5000, TimeUnit.SECONDS)); + + assertThat(countDown.isCountedDown(), is(true)); + + openInputStreams.forEach(inputStream -> { + try { + inputStream.close(); + } catch (IOException e) { + fail("Failure while closing open input streams"); + } + }); + } + + private long calculateLastPartSize(long totalSize, long partSize) { + return totalSize % partSize == 0 ? partSize : totalSize % partSize; + } + + private int calculateNumberOfParts(long contentLength, long partSize) { + return (int) ((contentLength % partSize) == 0 ? contentLength / partSize : (contentLength / partSize) + 1); + } + public void testWriteBlobWithReadTimeouts() { final byte[] bytes = randomByteArrayOfLength(randomIntBetween(10, 128)); final TimeValue readTimeout = TimeValue.timeValueMillis(randomIntBetween(100, 500)); @@ -287,13 +433,13 @@ public void testWriteLargeBlob() throws Exception { && exchange.getRequestURI().getQuery().contains("uploadId=TEST") && exchange.getRequestURI().getQuery().contains("partNumber=")) { // upload part request - MD5DigestCalculatingInputStream md5 = new MD5DigestCalculatingInputStream(exchange.getRequestBody()); - BytesReference bytes = Streams.readFully(md5); + SdkDigestInputStream digestInputStream = new SdkDigestInputStream(exchange.getRequestBody(), MessageDigests.md5()); + BytesReference bytes = Streams.readFully(digestInputStream); assertThat((long) bytes.length(), anyOf(equalTo(lastPartSize), equalTo(bufferSize.getBytes()))); assertThat(contentLength, anyOf(equalTo(lastPartSize), equalTo(bufferSize.getBytes()))); if (countDownUploads.decrementAndGet() % 2 == 0) { - exchange.getResponseHeaders().add("ETag", Base16.encodeAsString(md5.getMd5Digest())); + exchange.getResponseHeaders().add("ETag", Base16.encodeAsString(digestInputStream.getMessageDigest().digest())); exchange.sendResponseHeaders(HttpStatus.SC_OK, -1); exchange.close(); return; diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobStoreContainerTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobStoreContainerTests.java index 1c7dc05b89bf5..2e54705e9cd78 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobStoreContainerTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3BlobStoreContainerTests.java @@ -32,45 +32,89 @@ package org.opensearch.repositories.s3; -import com.amazonaws.AmazonClientException; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.AbortMultipartUploadRequest; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.CompleteMultipartUploadRequest; -import com.amazonaws.services.s3.model.CompleteMultipartUploadResult; -import com.amazonaws.services.s3.model.InitiateMultipartUploadRequest; -import com.amazonaws.services.s3.model.InitiateMultipartUploadResult; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.PartETag; -import com.amazonaws.services.s3.model.PutObjectRequest; -import com.amazonaws.services.s3.model.PutObjectResult; -import com.amazonaws.services.s3.model.StorageClass; -import com.amazonaws.services.s3.model.UploadPartRequest; -import com.amazonaws.services.s3.model.UploadPartResult; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.Checksum; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.CompletedPart; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse; +import software.amazon.awssdk.services.s3.model.GetObjectAttributesParts; +import software.amazon.awssdk.services.s3.model.GetObjectAttributesRequest; +import software.amazon.awssdk.services.s3.model.GetObjectAttributesResponse; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.HeadObjectRequest; +import software.amazon.awssdk.services.s3.model.HeadObjectResponse; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.NoSuchKeyException; +import software.amazon.awssdk.services.s3.model.ObjectCannedACL; +import software.amazon.awssdk.services.s3.model.ObjectIdentifier; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.services.s3.model.S3Error; +import software.amazon.awssdk.services.s3.model.S3Object; +import software.amazon.awssdk.services.s3.model.ServerSideEncryption; +import software.amazon.awssdk.services.s3.model.StorageClass; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.model.UploadPartResponse; +import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Iterable; + +import org.opensearch.action.LatchedActionListener; +import org.opensearch.common.blobstore.BlobContainer; +import org.opensearch.common.blobstore.BlobMetadata; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.BlobStoreException; +import org.opensearch.common.blobstore.DeleteResult; +import org.opensearch.common.blobstore.stream.read.ReadContext; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.unit.ByteSizeUnit; +import org.opensearch.common.io.InputStreamContainer; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.unit.ByteSizeUnit; import org.opensearch.test.OpenSearchTestCase; -import org.mockito.ArgumentCaptor; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; + import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.doAnswer; public class S3BlobStoreContainerTests extends OpenSearchTestCase { @@ -100,6 +144,281 @@ public void testExecuteSingleUploadBlobSizeLargerThanBufferSize() { assertEquals("Upload request size [2097152] can't be larger than buffer size", e.getMessage()); } + public void testBlobExists() { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + final String blobName = randomAlphaOfLengthBetween(1, 10); + + final BlobPath blobPath = new BlobPath(); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.bucket()).thenReturn(bucketName); + + final S3Client client = mock(S3Client.class); + when(client.headObject(any(HeadObjectRequest.class))).thenReturn(HeadObjectResponse.builder().build()); + final AmazonS3Reference clientReference = new AmazonS3Reference(client); + when(blobStore.clientReference()).thenReturn(clientReference); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + assertTrue(blobContainer.blobExists(blobName)); + verify(client, times(1)).headObject(any(HeadObjectRequest.class)); + } + + public void testBlobExistsNoSuchKeyException() { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + final String blobName = randomAlphaOfLengthBetween(1, 10); + + final BlobPath blobPath = new BlobPath(); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.bucket()).thenReturn(bucketName); + + final S3Client client = mock(S3Client.class); + when(client.headObject(any(HeadObjectRequest.class))).thenThrow(NoSuchKeyException.builder().build()); + final AmazonS3Reference clientReference = new AmazonS3Reference(client); + when(blobStore.clientReference()).thenReturn(clientReference); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + assertFalse(blobContainer.blobExists(blobName)); + verify(client, times(1)).headObject(any(HeadObjectRequest.class)); + } + + public void testBlobExistsRequestFailure() { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + final String blobName = randomAlphaOfLengthBetween(1, 10); + + final BlobPath blobPath = new BlobPath(); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.bucket()).thenReturn(bucketName); + + final S3Client client = mock(S3Client.class); + final AmazonS3Reference clientReference = new AmazonS3Reference(client); + when(blobStore.clientReference()).thenReturn(clientReference); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + when(client.headObject(any(HeadObjectRequest.class))).thenThrow(new RuntimeException()); + + assertThrows(BlobStoreException.class, () -> blobContainer.blobExists(blobName)); + verify(client, times(1)).headObject(any(HeadObjectRequest.class)); + } + + private static class MockListObjectsV2ResponseIterator implements Iterator { + + private final int totalPageCount; + private final int s3ObjectsPerPage; + private final long s3ObjectSize; + + private final AtomicInteger currInvocationCount = new AtomicInteger(); + private final List keysListed; + private final boolean throwExceptionOnNextInvocation; + + public MockListObjectsV2ResponseIterator(int totalPageCount, int s3ObjectsPerPage, long s3ObjectSize) { + this(totalPageCount, s3ObjectsPerPage, s3ObjectSize, ""); + } + + public MockListObjectsV2ResponseIterator(int totalPageCount, int s3ObjectsPerPage, long s3ObjectSize, String blobPath) { + this(totalPageCount, s3ObjectsPerPage, s3ObjectSize, blobPath, false); + } + + public MockListObjectsV2ResponseIterator( + int totalPageCount, + int s3ObjectsPerPage, + long s3ObjectSize, + String blobPath, + boolean throwExceptionOnNextInvocation + ) { + this.totalPageCount = totalPageCount; + this.s3ObjectsPerPage = s3ObjectsPerPage; + this.s3ObjectSize = s3ObjectSize; + this.throwExceptionOnNextInvocation = throwExceptionOnNextInvocation; + keysListed = new ArrayList<>(); + for (int i = 0; i < totalPageCount * s3ObjectsPerPage; i++) { + keysListed.add(blobPath + UUID.randomUUID().toString()); + } + // S3 lists keys in lexicographic order + keysListed.sort(String::compareTo); + } + + @Override + public boolean hasNext() { + return currInvocationCount.get() < totalPageCount; + } + + @Override + public ListObjectsV2Response next() { + if (throwExceptionOnNextInvocation) { + throw SdkException.builder().build(); + } + if (currInvocationCount.getAndIncrement() < totalPageCount) { + List s3Objects = new ArrayList<>(); + for (int i = 0; i < s3ObjectsPerPage; i++) { + String s3ObjectKey = keysListed.get((currInvocationCount.get() - 1) * s3ObjectsPerPage + i); + s3Objects.add(S3Object.builder().key(s3ObjectKey).size(s3ObjectSize).build()); + } + return ListObjectsV2Response.builder().contents(s3Objects).build(); + } + throw new NoSuchElementException(); + } + + public List getKeysListed() { + return keysListed; + } + + public int numberOfPagesFetched() { + return currInvocationCount.get(); + } + } + + public void testDelete() throws IOException { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + + final BlobPath blobPath = new BlobPath(); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + + final S3Client client = mock(S3Client.class); + doAnswer(invocation -> new AmazonS3Reference(client)).when(blobStore).clientReference(); + + ListObjectsV2Iterable listObjectsV2Iterable = mock(ListObjectsV2Iterable.class); + final int totalPageCount = 3; + final long s3ObjectSize = ByteSizeUnit.MB.toBytes(5); + final int s3ObjectsPerPage = 5; + MockListObjectsV2ResponseIterator listObjectsV2ResponseIterator = new MockListObjectsV2ResponseIterator( + totalPageCount, + s3ObjectsPerPage, + s3ObjectSize + ); + when(listObjectsV2Iterable.iterator()).thenReturn(listObjectsV2ResponseIterator); + when(client.listObjectsV2Paginator(any(ListObjectsV2Request.class))).thenReturn(listObjectsV2Iterable); + + final List keysDeleted = new ArrayList<>(); + doAnswer(invocation -> { + DeleteObjectsRequest deleteObjectsRequest = invocation.getArgument(0); + keysDeleted.addAll(deleteObjectsRequest.delete().objects().stream().map(ObjectIdentifier::key).collect(Collectors.toList())); + return DeleteObjectsResponse.builder().build(); + }).when(client).deleteObjects(any(DeleteObjectsRequest.class)); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + DeleteResult deleteResult = blobContainer.delete(); + assertEquals(s3ObjectSize * s3ObjectsPerPage * totalPageCount, deleteResult.bytesDeleted()); + assertEquals(s3ObjectsPerPage * totalPageCount, deleteResult.blobsDeleted()); + // keysDeleted will have blobPath also + assertEquals(listObjectsV2ResponseIterator.getKeysListed().size(), keysDeleted.size() - 1); + assertTrue(keysDeleted.contains(blobPath.buildAsString())); + keysDeleted.remove(blobPath.buildAsString()); + assertEquals(new HashSet<>(listObjectsV2ResponseIterator.getKeysListed()), new HashSet<>(keysDeleted)); + } + + public void testDeleteItemLevelErrorsDuringDelete() { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + + final BlobPath blobPath = new BlobPath(); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + + final S3Client client = mock(S3Client.class); + doAnswer(invocation -> new AmazonS3Reference(client)).when(blobStore).clientReference(); + + ListObjectsV2Iterable listObjectsV2Iterable = mock(ListObjectsV2Iterable.class); + final int totalPageCount = 3; + final long s3ObjectSize = ByteSizeUnit.MB.toBytes(5); + final int s3ObjectsPerPage = 5; + MockListObjectsV2ResponseIterator listObjectsV2ResponseIterator = new MockListObjectsV2ResponseIterator( + totalPageCount, + s3ObjectsPerPage, + s3ObjectSize + ); + when(listObjectsV2Iterable.iterator()).thenReturn(listObjectsV2ResponseIterator); + when(client.listObjectsV2Paginator(any(ListObjectsV2Request.class))).thenReturn(listObjectsV2Iterable); + + final List keysFailedDeletion = new ArrayList<>(); + doAnswer(invocation -> { + DeleteObjectsRequest deleteObjectsRequest = invocation.getArgument(0); + int i = 0; + for (ObjectIdentifier objectIdentifier : deleteObjectsRequest.delete().objects()) { + if (i % 2 == 0) { + keysFailedDeletion.add(objectIdentifier.key()); + } + i++; + } + return DeleteObjectsResponse.builder() + .errors(keysFailedDeletion.stream().map(key -> S3Error.builder().key(key).build()).collect(Collectors.toList())) + .build(); + }).when(client).deleteObjects(any(DeleteObjectsRequest.class)); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + assertThrows(AssertionError.class, blobContainer::delete); + } + + public void testDeleteSdkExceptionDuringListOperation() { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + + final BlobPath blobPath = new BlobPath(); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + + final S3Client client = mock(S3Client.class); + doAnswer(invocation -> new AmazonS3Reference(client)).when(blobStore).clientReference(); + + ListObjectsV2Iterable listObjectsV2Iterable = mock(ListObjectsV2Iterable.class); + final int totalPageCount = 3; + final long s3ObjectSize = ByteSizeUnit.MB.toBytes(5); + final int s3ObjectsPerPage = 5; + MockListObjectsV2ResponseIterator listObjectsV2ResponseIterator = new MockListObjectsV2ResponseIterator( + totalPageCount, + s3ObjectsPerPage, + s3ObjectSize + ); + when(listObjectsV2Iterable.iterator()).thenReturn(listObjectsV2ResponseIterator); + when(client.listObjectsV2Paginator(any(ListObjectsV2Request.class))).thenReturn(listObjectsV2Iterable); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + assertThrows(IOException.class, blobContainer::delete); + } + + public void testDeleteSdkExceptionDuringDeleteOperation() { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + + final BlobPath blobPath = new BlobPath(); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + + final S3Client client = mock(S3Client.class); + doAnswer(invocation -> new AmazonS3Reference(client)).when(blobStore).clientReference(); + + ListObjectsV2Iterable listObjectsV2Iterable = mock(ListObjectsV2Iterable.class); + final int totalPageCount = 3; + final long s3ObjectSize = ByteSizeUnit.MB.toBytes(5); + final int s3ObjectsPerPage = 5; + MockListObjectsV2ResponseIterator listObjectsV2ResponseIterator = new MockListObjectsV2ResponseIterator( + totalPageCount, + s3ObjectsPerPage, + s3ObjectSize + ); + when(listObjectsV2Iterable.iterator()).thenReturn(listObjectsV2ResponseIterator); + when(client.listObjectsV2Paginator(any(ListObjectsV2Request.class))).thenReturn(listObjectsV2Iterable); + + when(client.deleteObjects(any(DeleteObjectsRequest.class))).thenThrow(SdkException.builder().build()); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + assertThrows(IOException.class, blobContainer::delete); + } + public void testExecuteSingleUpload() throws IOException { final String bucketName = randomAlphaOfLengthBetween(1, 10); final String blobName = randomAlphaOfLengthBetween(1, 10); @@ -115,6 +434,7 @@ public void testExecuteSingleUpload() throws IOException { final S3BlobStore blobStore = mock(S3BlobStore.class); when(blobStore.bucket()).thenReturn(bucketName); when(blobStore.bufferSizeInBytes()).thenReturn((long) bufferSize); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); @@ -124,30 +444,37 @@ public void testExecuteSingleUpload() throws IOException { final StorageClass storageClass = randomFrom(StorageClass.values()); when(blobStore.getStorageClass()).thenReturn(storageClass); - final CannedAccessControlList cannedAccessControlList = randomBoolean() ? randomFrom(CannedAccessControlList.values()) : null; + final ObjectCannedACL cannedAccessControlList = randomBoolean() ? randomFrom(ObjectCannedACL.values()) : null; if (cannedAccessControlList != null) { when(blobStore.getCannedACL()).thenReturn(cannedAccessControlList); } - final AmazonS3 client = mock(AmazonS3.class); + final S3Client client = mock(S3Client.class); final AmazonS3Reference clientReference = new AmazonS3Reference(client); when(blobStore.clientReference()).thenReturn(clientReference); - final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(PutObjectRequest.class); - when(client.putObject(argumentCaptor.capture())).thenReturn(new PutObjectResult()); + final ArgumentCaptor putObjectRequestArgumentCaptor = ArgumentCaptor.forClass(PutObjectRequest.class); + final ArgumentCaptor requestBodyArgumentCaptor = ArgumentCaptor.forClass(RequestBody.class); + when(client.putObject(putObjectRequestArgumentCaptor.capture(), requestBodyArgumentCaptor.capture())).thenReturn( + PutObjectResponse.builder().build() + ); final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[blobSize]); blobContainer.executeSingleUpload(blobStore, blobName, inputStream, blobSize); - final PutObjectRequest request = argumentCaptor.getValue(); - assertEquals(bucketName, request.getBucketName()); - assertEquals(blobPath.buildAsString() + blobName, request.getKey()); - assertEquals(inputStream, request.getInputStream()); - assertEquals(blobSize, request.getMetadata().getContentLength()); - assertEquals(storageClass.toString(), request.getStorageClass()); - assertEquals(cannedAccessControlList, request.getCannedAcl()); + final PutObjectRequest request = putObjectRequestArgumentCaptor.getValue(); + final RequestBody requestBody = requestBodyArgumentCaptor.getValue(); + assertEquals(bucketName, request.bucket()); + assertEquals(blobPath.buildAsString() + blobName, request.key()); + byte[] expectedBytes = inputStream.readAllBytes(); + try (InputStream is = requestBody.contentStreamProvider().newStream()) { + assertArrayEquals(expectedBytes, is.readAllBytes()); + } + assertEquals(blobSize, request.contentLength().longValue()); + assertEquals(storageClass, request.storageClass()); + assertEquals(cannedAccessControlList, request.acl()); if (serverSideEncryption) { - assertEquals(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION, request.getMetadata().getSSEAlgorithm()); + assertEquals(ServerSideEncryption.AES256, request.serverSideEncryption()); } } @@ -189,6 +516,7 @@ public void testExecuteMultipartUpload() throws IOException { final S3BlobStore blobStore = mock(S3BlobStore.class); when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); when(blobStore.bufferSizeInBytes()).thenReturn(bufferSize); final boolean serverSideEncryption = randomBoolean(); @@ -197,21 +525,25 @@ public void testExecuteMultipartUpload() throws IOException { final StorageClass storageClass = randomFrom(StorageClass.values()); when(blobStore.getStorageClass()).thenReturn(storageClass); - final CannedAccessControlList cannedAccessControlList = randomBoolean() ? randomFrom(CannedAccessControlList.values()) : null; + final ObjectCannedACL cannedAccessControlList = randomBoolean() ? randomFrom(ObjectCannedACL.values()) : null; if (cannedAccessControlList != null) { when(blobStore.getCannedACL()).thenReturn(cannedAccessControlList); } - final AmazonS3 client = mock(AmazonS3.class); + final S3Client client = mock(S3Client.class); final AmazonS3Reference clientReference = new AmazonS3Reference(client); when(blobStore.clientReference()).thenReturn(clientReference); - final ArgumentCaptor initArgCaptor = ArgumentCaptor.forClass(InitiateMultipartUploadRequest.class); - final InitiateMultipartUploadResult initResult = new InitiateMultipartUploadResult(); - initResult.setUploadId(randomAlphaOfLength(10)); - when(client.initiateMultipartUpload(initArgCaptor.capture())).thenReturn(initResult); + final ArgumentCaptor createMultipartUploadRequestArgumentCaptor = ArgumentCaptor.forClass( + CreateMultipartUploadRequest.class + ); + final CreateMultipartUploadResponse createMultipartUploadResponse = CreateMultipartUploadResponse.builder() + .uploadId(randomAlphaOfLength(10)) + .build(); + when(client.createMultipartUpload(createMultipartUploadRequestArgumentCaptor.capture())).thenReturn(createMultipartUploadResponse); - final ArgumentCaptor uploadArgCaptor = ArgumentCaptor.forClass(UploadPartRequest.class); + final ArgumentCaptor uploadPartRequestArgumentCaptor = ArgumentCaptor.forClass(UploadPartRequest.class); + final ArgumentCaptor requestBodyArgumentCaptor = ArgumentCaptor.forClass(RequestBody.class); final List expectedEtags = new ArrayList<>(); final long partSize = Math.min(bufferSize, blobSize); @@ -221,59 +553,65 @@ public void testExecuteMultipartUpload() throws IOException { totalBytes += partSize; } while (totalBytes < blobSize); - when(client.uploadPart(uploadArgCaptor.capture())).thenAnswer(invocationOnMock -> { - final UploadPartRequest request = (UploadPartRequest) invocationOnMock.getArguments()[0]; - final UploadPartResult response = new UploadPartResult(); - response.setPartNumber(request.getPartNumber()); - response.setETag(expectedEtags.get(request.getPartNumber() - 1)); - return response; - }); + when(client.uploadPart(uploadPartRequestArgumentCaptor.capture(), requestBodyArgumentCaptor.capture())).thenAnswer( + invocationOnMock -> { + final UploadPartRequest request = (UploadPartRequest) invocationOnMock.getArguments()[0]; + final UploadPartResponse response = UploadPartResponse.builder().eTag(expectedEtags.get(request.partNumber() - 1)).build(); + return response; + } + ); - final ArgumentCaptor compArgCaptor = ArgumentCaptor.forClass(CompleteMultipartUploadRequest.class); - when(client.completeMultipartUpload(compArgCaptor.capture())).thenReturn(new CompleteMultipartUploadResult()); + final ArgumentCaptor completeMultipartUploadRequestArgumentCaptor = ArgumentCaptor.forClass( + CompleteMultipartUploadRequest.class + ); + when(client.completeMultipartUpload(completeMultipartUploadRequestArgumentCaptor.capture())).thenReturn( + CompleteMultipartUploadResponse.builder().build() + ); final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[0]); final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); blobContainer.executeMultipartUpload(blobStore, blobName, inputStream, blobSize); - final InitiateMultipartUploadRequest initRequest = initArgCaptor.getValue(); - assertEquals(bucketName, initRequest.getBucketName()); - assertEquals(blobPath.buildAsString() + blobName, initRequest.getKey()); - assertEquals(storageClass, initRequest.getStorageClass()); - assertEquals(cannedAccessControlList, initRequest.getCannedACL()); + final CreateMultipartUploadRequest initRequest = createMultipartUploadRequestArgumentCaptor.getValue(); + assertEquals(bucketName, initRequest.bucket()); + assertEquals(blobPath.buildAsString() + blobName, initRequest.key()); + assertEquals(storageClass, initRequest.storageClass()); + assertEquals(cannedAccessControlList, initRequest.acl()); if (serverSideEncryption) { - assertEquals(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION, initRequest.getObjectMetadata().getSSEAlgorithm()); + assertEquals(ServerSideEncryption.AES256, initRequest.serverSideEncryption()); } final Tuple numberOfParts = S3BlobContainer.numberOfMultiparts(blobSize, bufferSize); - final List uploadRequests = uploadArgCaptor.getAllValues(); + final List uploadRequests = uploadPartRequestArgumentCaptor.getAllValues(); + final List requestBodies = requestBodyArgumentCaptor.getAllValues(); assertEquals(numberOfParts.v1().intValue(), uploadRequests.size()); for (int i = 0; i < uploadRequests.size(); i++) { - final UploadPartRequest uploadRequest = uploadRequests.get(i); - - assertEquals(bucketName, uploadRequest.getBucketName()); - assertEquals(blobPath.buildAsString() + blobName, uploadRequest.getKey()); - assertEquals(initResult.getUploadId(), uploadRequest.getUploadId()); - assertEquals(i + 1, uploadRequest.getPartNumber()); - assertEquals(inputStream, uploadRequest.getInputStream()); - - if (i == (uploadRequests.size() - 1)) { - assertTrue(uploadRequest.isLastPart()); - assertEquals(numberOfParts.v2().longValue(), uploadRequest.getPartSize()); - } else { - assertFalse(uploadRequest.isLastPart()); - assertEquals(bufferSize, uploadRequest.getPartSize()); + final UploadPartRequest uploadPartRequest = uploadRequests.get(i); + final RequestBody requestBody = requestBodies.get(i); + + assertEquals(bucketName, uploadPartRequest.bucket()); + assertEquals(blobPath.buildAsString() + blobName, uploadPartRequest.key()); + assertEquals(createMultipartUploadResponse.uploadId(), uploadPartRequest.uploadId()); + assertEquals(i + 1, uploadPartRequest.partNumber().intValue()); + byte[] expectedBytes = inputStream.readAllBytes(); + try (InputStream is = requestBody.contentStreamProvider().newStream()) { + byte[] actualBytes = is.readAllBytes(); + assertArrayEquals(expectedBytes, actualBytes); } } - final CompleteMultipartUploadRequest compRequest = compArgCaptor.getValue(); - assertEquals(bucketName, compRequest.getBucketName()); - assertEquals(blobPath.buildAsString() + blobName, compRequest.getKey()); - assertEquals(initResult.getUploadId(), compRequest.getUploadId()); + final CompleteMultipartUploadRequest completeMultipartUploadRequest = completeMultipartUploadRequestArgumentCaptor.getValue(); + assertEquals(bucketName, completeMultipartUploadRequest.bucket()); + assertEquals(blobPath.buildAsString() + blobName, completeMultipartUploadRequest.key()); + assertEquals(createMultipartUploadResponse.uploadId(), completeMultipartUploadRequest.uploadId()); - final List actualETags = compRequest.getPartETags().stream().map(PartETag::getETag).collect(Collectors.toList()); + final List actualETags = completeMultipartUploadRequest.multipartUpload() + .parts() + .stream() + .map(CompletedPart::eTag) + .collect(Collectors.toList()); assertEquals(expectedEtags, actualETags); } @@ -289,8 +627,9 @@ public void testExecuteMultipartUploadAborted() { when(blobStore.bucket()).thenReturn(bucketName); when(blobStore.bufferSizeInBytes()).thenReturn(bufferSize); when(blobStore.getStorageClass()).thenReturn(randomFrom(StorageClass.values())); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); - final AmazonS3 client = mock(AmazonS3.class); + final S3Client client = mock(S3Client.class); final AmazonS3Reference clientReference = new AmazonS3Reference(client); doAnswer(invocation -> { clientReference.incRef(); @@ -300,34 +639,34 @@ public void testExecuteMultipartUploadAborted() { final String uploadId = randomAlphaOfLength(25); final int stage = randomInt(2); - final List exceptions = Arrays.asList( - new AmazonClientException("Expected initialization request to fail"), - new AmazonClientException("Expected upload part request to fail"), - new AmazonClientException("Expected completion request to fail") + final List exceptions = Arrays.asList( + SdkException.create("Expected initialization request to fail", new RuntimeException()), + SdkException.create("Expected upload part request to fail", new RuntimeException()), + SdkException.create("Expected completion request to fail", new RuntimeException()) ); if (stage == 0) { // Fail the initialization request - when(client.initiateMultipartUpload(any(InitiateMultipartUploadRequest.class))).thenThrow(exceptions.get(stage)); + when(client.createMultipartUpload(any(CreateMultipartUploadRequest.class))).thenThrow(exceptions.get(stage)); } else if (stage == 1) { - final InitiateMultipartUploadResult initResult = new InitiateMultipartUploadResult(); - initResult.setUploadId(uploadId); - when(client.initiateMultipartUpload(any(InitiateMultipartUploadRequest.class))).thenReturn(initResult); + final CreateMultipartUploadResponse createMultipartUploadResponse = CreateMultipartUploadResponse.builder() + .uploadId(uploadId) + .build(); + when(client.createMultipartUpload(any(CreateMultipartUploadRequest.class))).thenReturn(createMultipartUploadResponse); // Fail the upload part request - when(client.uploadPart(any(UploadPartRequest.class))).thenThrow(exceptions.get(stage)); + when(client.uploadPart(any(UploadPartRequest.class), any(RequestBody.class))).thenThrow(exceptions.get(stage)); } else { - final InitiateMultipartUploadResult initResult = new InitiateMultipartUploadResult(); - initResult.setUploadId(uploadId); - when(client.initiateMultipartUpload(any(InitiateMultipartUploadRequest.class))).thenReturn(initResult); + final CreateMultipartUploadResponse createMultipartUploadResponse = CreateMultipartUploadResponse.builder() + .uploadId(uploadId) + .build(); + when(client.createMultipartUpload(any(CreateMultipartUploadRequest.class))).thenReturn(createMultipartUploadResponse); - when(client.uploadPart(any(UploadPartRequest.class))).thenAnswer(invocationOnMock -> { + when(client.uploadPart(any(UploadPartRequest.class), any(RequestBody.class))).thenAnswer(invocationOnMock -> { final UploadPartRequest request = (UploadPartRequest) invocationOnMock.getArguments()[0]; - final UploadPartResult response = new UploadPartResult(); - response.setPartNumber(request.getPartNumber()); - response.setETag(randomAlphaOfLength(20)); + final UploadPartResponse response = UploadPartResponse.builder().eTag(randomAlphaOfLength(20)).build(); return response; }); @@ -336,7 +675,7 @@ public void testExecuteMultipartUploadAborted() { } final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(AbortMultipartUploadRequest.class); - doNothing().when(client).abortMultipartUpload(argumentCaptor.capture()); + when(client.abortMultipartUpload(argumentCaptor.capture())).thenReturn(AbortMultipartUploadResponse.builder().build()); final IOException e = expectThrows(IOException.class, () -> { final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); @@ -344,32 +683,32 @@ public void testExecuteMultipartUploadAborted() { }); assertEquals("Unable to upload object [" + blobName + "] using multipart upload", e.getMessage()); - assertThat(e.getCause(), instanceOf(AmazonClientException.class)); + assertThat(e.getCause(), instanceOf(SdkException.class)); assertEquals(exceptions.get(stage).getMessage(), e.getCause().getMessage()); if (stage == 0) { - verify(client, times(1)).initiateMultipartUpload(any(InitiateMultipartUploadRequest.class)); - verify(client, times(0)).uploadPart(any(UploadPartRequest.class)); + verify(client, times(1)).createMultipartUpload(any(CreateMultipartUploadRequest.class)); + verify(client, times(0)).uploadPart(any(UploadPartRequest.class), any(RequestBody.class)); verify(client, times(0)).completeMultipartUpload(any(CompleteMultipartUploadRequest.class)); verify(client, times(0)).abortMultipartUpload(any(AbortMultipartUploadRequest.class)); } else { - verify(client, times(1)).initiateMultipartUpload(any(InitiateMultipartUploadRequest.class)); + verify(client, times(1)).createMultipartUpload(any(CreateMultipartUploadRequest.class)); if (stage == 1) { - verify(client, times(1)).uploadPart(any(UploadPartRequest.class)); + verify(client, times(1)).uploadPart(any(UploadPartRequest.class), any(RequestBody.class)); verify(client, times(0)).completeMultipartUpload(any(CompleteMultipartUploadRequest.class)); } else { - verify(client, times(6)).uploadPart(any(UploadPartRequest.class)); + verify(client, times(6)).uploadPart(any(UploadPartRequest.class), any(RequestBody.class)); verify(client, times(1)).completeMultipartUpload(any(CompleteMultipartUploadRequest.class)); } verify(client, times(1)).abortMultipartUpload(any(AbortMultipartUploadRequest.class)); final AbortMultipartUploadRequest abortRequest = argumentCaptor.getValue(); - assertEquals(bucketName, abortRequest.getBucketName()); - assertEquals(blobName, abortRequest.getKey()); - assertEquals(uploadId, abortRequest.getUploadId()); + assertEquals(bucketName, abortRequest.bucket()); + assertEquals(blobName, abortRequest.key()); + assertEquals(uploadId, abortRequest.uploadId()); } } @@ -407,23 +746,22 @@ public void testInitCannedACL() { "public-read", "public-read-write", "authenticated-read", - "log-delivery-write", "bucket-owner-read", "bucket-owner-full-control" }; // empty acl - assertThat(S3BlobStore.initCannedACL(null), equalTo(CannedAccessControlList.Private)); - assertThat(S3BlobStore.initCannedACL(""), equalTo(CannedAccessControlList.Private)); + assertThat(S3BlobStore.initCannedACL(null), equalTo(ObjectCannedACL.PRIVATE)); + assertThat(S3BlobStore.initCannedACL(""), equalTo(ObjectCannedACL.PRIVATE)); // it should init cannedACL correctly for (String aclString : aclList) { - CannedAccessControlList acl = S3BlobStore.initCannedACL(aclString); + ObjectCannedACL acl = S3BlobStore.initCannedACL(aclString); assertThat(acl.toString(), equalTo(aclString)); } // it should accept all aws cannedACLs - for (CannedAccessControlList awsList : CannedAccessControlList.values()) { - CannedAccessControlList acl = S3BlobStore.initCannedACL(awsList.toString()); + for (ObjectCannedACL awsList : ObjectCannedACL.values()) { + ObjectCannedACL acl = S3BlobStore.initCannedACL(awsList.toString()); assertThat(acl, equalTo(awsList)); } } @@ -435,23 +773,23 @@ public void testInvalidCannedACL() { public void testInitStorageClass() { // it should default to `standard` - assertThat(S3BlobStore.initStorageClass(null), equalTo(StorageClass.Standard)); - assertThat(S3BlobStore.initStorageClass(""), equalTo(StorageClass.Standard)); + assertThat(S3BlobStore.initStorageClass(null), equalTo(StorageClass.STANDARD)); + assertThat(S3BlobStore.initStorageClass(""), equalTo(StorageClass.STANDARD)); // it should accept [standard, standard_ia, onezone_ia, reduced_redundancy, intelligent_tiering] - assertThat(S3BlobStore.initStorageClass("standard"), equalTo(StorageClass.Standard)); - assertThat(S3BlobStore.initStorageClass("standard_ia"), equalTo(StorageClass.StandardInfrequentAccess)); - assertThat(S3BlobStore.initStorageClass("onezone_ia"), equalTo(StorageClass.OneZoneInfrequentAccess)); - assertThat(S3BlobStore.initStorageClass("reduced_redundancy"), equalTo(StorageClass.ReducedRedundancy)); - assertThat(S3BlobStore.initStorageClass("intelligent_tiering"), equalTo(StorageClass.IntelligentTiering)); + assertThat(S3BlobStore.initStorageClass("standard"), equalTo(StorageClass.STANDARD)); + assertThat(S3BlobStore.initStorageClass("standard_ia"), equalTo(StorageClass.STANDARD_IA)); + assertThat(S3BlobStore.initStorageClass("onezone_ia"), equalTo(StorageClass.ONEZONE_IA)); + assertThat(S3BlobStore.initStorageClass("reduced_redundancy"), equalTo(StorageClass.REDUCED_REDUNDANCY)); + assertThat(S3BlobStore.initStorageClass("intelligent_tiering"), equalTo(StorageClass.INTELLIGENT_TIERING)); } public void testCaseInsensitiveStorageClass() { - assertThat(S3BlobStore.initStorageClass("sTandaRd"), equalTo(StorageClass.Standard)); - assertThat(S3BlobStore.initStorageClass("sTandaRd_Ia"), equalTo(StorageClass.StandardInfrequentAccess)); - assertThat(S3BlobStore.initStorageClass("oNeZoNe_iA"), equalTo(StorageClass.OneZoneInfrequentAccess)); - assertThat(S3BlobStore.initStorageClass("reduCED_redundancy"), equalTo(StorageClass.ReducedRedundancy)); - assertThat(S3BlobStore.initStorageClass("intelLigeNt_tieriNG"), equalTo(StorageClass.IntelligentTiering)); + assertThat(S3BlobStore.initStorageClass("sTandaRd"), equalTo(StorageClass.STANDARD)); + assertThat(S3BlobStore.initStorageClass("sTandaRd_Ia"), equalTo(StorageClass.STANDARD_IA)); + assertThat(S3BlobStore.initStorageClass("oNeZoNe_iA"), equalTo(StorageClass.ONEZONE_IA)); + assertThat(S3BlobStore.initStorageClass("reduCED_redundancy"), equalTo(StorageClass.REDUCED_REDUNDANCY)); + assertThat(S3BlobStore.initStorageClass("intelLigeNt_tieriNG"), equalTo(StorageClass.INTELLIGENT_TIERING)); } public void testInvalidStorageClass() { @@ -470,4 +808,481 @@ private static void assertNumberOfMultiparts(final int expectedParts, final long assertEquals("Expected number of parts [" + expectedParts + "] but got [" + result.v1() + "]", expectedParts, (long) result.v1()); assertEquals("Expected remaining [" + expectedRemaining + "] but got [" + result.v2() + "]", expectedRemaining, (long) result.v2()); } + + public void testListBlobsByPrefix() throws IOException { + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + + final S3Client client = mock(S3Client.class); + final AmazonS3Reference clientReference = new AmazonS3Reference(client); + when(blobStore.clientReference()).thenReturn(clientReference); + + BlobPath blobPath = mock(BlobPath.class); + when(blobPath.buildAsString()).thenReturn("/dummy/path"); + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + final ListObjectsV2Iterable listObjectsV2Iterable = mock(ListObjectsV2Iterable.class); + when(client.listObjectsV2Paginator(any(ListObjectsV2Request.class))).thenReturn(listObjectsV2Iterable); + + MockListObjectsV2ResponseIterator iterator = new MockListObjectsV2ResponseIterator(2, 5, 100); + when(listObjectsV2Iterable.iterator()).thenReturn(iterator); + + Map listOfBlobs = blobContainer.listBlobsByPrefix(null); + assertEquals(10, listOfBlobs.size()); + + Set keys = iterator.keysListed.stream() + .map(s -> s.substring(blobPath.buildAsString().length())) + .collect(Collectors.toSet()); + assertEquals(keys, listOfBlobs.keySet()); + } + + private void testListBlobsByPrefixInLexicographicOrder( + int limit, + int expectedNumberofPagesFetched, + BlobContainer.BlobNameSortOrder blobNameSortOrder + ) throws IOException { + final S3BlobStore blobStore = mock(S3BlobStore.class); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + + final S3Client client = mock(S3Client.class); + final AmazonS3Reference clientReference = new AmazonS3Reference(client); + when(blobStore.clientReference()).thenReturn(clientReference); + + BlobPath blobPath = mock(BlobPath.class); + when(blobPath.buildAsString()).thenReturn("/dummy/path"); + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + final ListObjectsV2Iterable listObjectsV2Iterable = mock(ListObjectsV2Iterable.class); + when(client.listObjectsV2Paginator(any(ListObjectsV2Request.class))).thenReturn(listObjectsV2Iterable); + + final MockListObjectsV2ResponseIterator iterator = new MockListObjectsV2ResponseIterator(2, 5, 100, blobPath.buildAsString()); + when(listObjectsV2Iterable.iterator()).thenReturn(iterator); + + if (limit >= 0) { + blobContainer.listBlobsByPrefixInSortedOrder(null, limit, blobNameSortOrder, new ActionListener<>() { + @Override + public void onResponse(List blobMetadata) { + int actualLimit = Math.max(0, Math.min(limit, 10)); + assertEquals(actualLimit, blobMetadata.size()); + + List keys = iterator.keysListed.stream() + .map(s -> s.substring(blobPath.buildAsString().length())) + .collect(Collectors.toList()); + Comparator keysComparator = String::compareTo; + if (blobNameSortOrder != BlobContainer.BlobNameSortOrder.LEXICOGRAPHIC) { + keysComparator = Collections.reverseOrder(String::compareTo); + } + keys.sort(keysComparator); + List sortedKeys = keys.subList(0, actualLimit); + assertEquals(sortedKeys, blobMetadata.stream().map(BlobMetadata::name).collect(Collectors.toList())); + assertEquals(expectedNumberofPagesFetched, iterator.numberOfPagesFetched()); + } + + @Override + public void onFailure(Exception e) { + fail("blobContainer.listBlobsByPrefixInLexicographicOrder failed with exception: " + e.getMessage()); + } + }); + } else { + assertThrows( + IllegalArgumentException.class, + () -> blobContainer.listBlobsByPrefixInSortedOrder(null, limit, blobNameSortOrder, new ActionListener<>() { + @Override + public void onResponse(List blobMetadata) {} + + @Override + public void onFailure(Exception e) {} + }) + ); + } + } + + public void testListBlobsByPrefixInLexicographicOrderWithNegativeLimit() throws IOException { + testListBlobsByPrefixInLexicographicOrder(-5, 0, BlobContainer.BlobNameSortOrder.LEXICOGRAPHIC); + } + + public void testListBlobsByPrefixInLexicographicOrderWithZeroLimit() throws IOException { + testListBlobsByPrefixInLexicographicOrder(0, 1, BlobContainer.BlobNameSortOrder.LEXICOGRAPHIC); + } + + public void testListBlobsByPrefixInLexicographicOrderWithLimitLessThanPageSize() throws IOException { + testListBlobsByPrefixInLexicographicOrder(2, 1, BlobContainer.BlobNameSortOrder.LEXICOGRAPHIC); + } + + public void testListBlobsByPrefixInLexicographicOrderWithLimitGreaterThanPageSize() throws IOException { + testListBlobsByPrefixInLexicographicOrder(8, 2, BlobContainer.BlobNameSortOrder.LEXICOGRAPHIC); + } + + public void testListBlobsByPrefixInLexicographicOrderWithLimitGreaterThanNumberOfRecords() throws IOException { + testListBlobsByPrefixInLexicographicOrder(12, 2, BlobContainer.BlobNameSortOrder.LEXICOGRAPHIC); + } + + public void testReadBlobAsyncMultiPart() throws Exception { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + final String blobName = randomAlphaOfLengthBetween(1, 10); + final String checksum = randomAlphaOfLength(10); + + final long objectSize = 100L; + final int objectPartCount = 10; + final int partSize = 10; + + final S3AsyncClient s3AsyncClient = mock(S3AsyncClient.class); + final AmazonAsyncS3Reference amazonAsyncS3Reference = new AmazonAsyncS3Reference( + AmazonAsyncS3WithCredentials.create(s3AsyncClient, s3AsyncClient, null) + ); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + final BlobPath blobPath = new BlobPath(); + + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + when(blobStore.serverSideEncryption()).thenReturn(false); + when(blobStore.asyncClientReference()).thenReturn(amazonAsyncS3Reference); + + CompletableFuture getObjectAttributesResponseCompletableFuture = new CompletableFuture<>(); + getObjectAttributesResponseCompletableFuture.complete( + GetObjectAttributesResponse.builder() + .checksum(Checksum.builder().checksumCRC32(checksum).build()) + .objectSize(objectSize) + .objectParts(GetObjectAttributesParts.builder().totalPartsCount(objectPartCount).build()) + .build() + ); + when(s3AsyncClient.getObjectAttributes(any(GetObjectAttributesRequest.class))).thenReturn( + getObjectAttributesResponseCompletableFuture + ); + + mockObjectPartResponse(s3AsyncClient, bucketName, blobName, objectPartCount, partSize, objectSize); + + CountDownLatch countDownLatch = new CountDownLatch(1); + CountingCompletionListener readContextActionListener = new CountingCompletionListener<>(); + LatchedActionListener listener = new LatchedActionListener<>(readContextActionListener, countDownLatch); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + blobContainer.readBlobAsync(blobName, listener); + countDownLatch.await(); + + assertEquals(1, readContextActionListener.getResponseCount()); + assertEquals(0, readContextActionListener.getFailureCount()); + ReadContext readContext = readContextActionListener.getResponse(); + assertEquals(objectPartCount, readContext.getNumberOfParts()); + assertEquals(checksum, readContext.getBlobChecksum()); + assertEquals(objectSize, readContext.getBlobSize()); + + for (int partNumber = 1; partNumber < objectPartCount; partNumber++) { + InputStreamContainer inputStreamContainer = readContext.getPartStreams().get(partNumber).get().join(); + final int offset = partNumber * partSize; + assertEquals(partSize, inputStreamContainer.getContentLength()); + assertEquals(offset, inputStreamContainer.getOffset()); + assertEquals(partSize, inputStreamContainer.getInputStream().readAllBytes().length); + } + } + + public void testReadBlobAsyncSinglePart() throws Exception { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + final String blobName = randomAlphaOfLengthBetween(1, 10); + final String checksum = randomAlphaOfLength(10); + + final int objectSize = 100; + + final S3AsyncClient s3AsyncClient = mock(S3AsyncClient.class); + final AmazonAsyncS3Reference amazonAsyncS3Reference = new AmazonAsyncS3Reference( + AmazonAsyncS3WithCredentials.create(s3AsyncClient, s3AsyncClient, null) + ); + final S3BlobStore blobStore = mock(S3BlobStore.class); + final BlobPath blobPath = new BlobPath(); + + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + when(blobStore.serverSideEncryption()).thenReturn(false); + when(blobStore.asyncClientReference()).thenReturn(amazonAsyncS3Reference); + + CompletableFuture getObjectAttributesResponseCompletableFuture = new CompletableFuture<>(); + getObjectAttributesResponseCompletableFuture.complete( + GetObjectAttributesResponse.builder() + .checksum(Checksum.builder().checksumCRC32(checksum).build()) + .objectSize((long) objectSize) + .build() + ); + when(s3AsyncClient.getObjectAttributes(any(GetObjectAttributesRequest.class))).thenReturn( + getObjectAttributesResponseCompletableFuture + ); + + mockObjectResponse(s3AsyncClient, bucketName, blobName, objectSize); + + CountDownLatch countDownLatch = new CountDownLatch(1); + CountingCompletionListener readContextActionListener = new CountingCompletionListener<>(); + LatchedActionListener listener = new LatchedActionListener<>(readContextActionListener, countDownLatch); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + blobContainer.readBlobAsync(blobName, listener); + countDownLatch.await(); + + assertEquals(1, readContextActionListener.getResponseCount()); + assertEquals(0, readContextActionListener.getFailureCount()); + ReadContext readContext = readContextActionListener.getResponse(); + assertEquals(1, readContext.getNumberOfParts()); + assertEquals(checksum, readContext.getBlobChecksum()); + assertEquals(objectSize, readContext.getBlobSize()); + + InputStreamContainer inputStreamContainer = readContext.getPartStreams().stream().findFirst().get().get().join(); + assertEquals(objectSize, inputStreamContainer.getContentLength()); + assertEquals(0, inputStreamContainer.getOffset()); + assertEquals(objectSize, inputStreamContainer.getInputStream().readAllBytes().length); + + } + + public void testReadBlobAsyncFailure() throws Exception { + final String bucketName = randomAlphaOfLengthBetween(1, 10); + final String blobName = randomAlphaOfLengthBetween(1, 10); + final String checksum = randomAlphaOfLength(10); + + final long objectSize = 100L; + final int objectPartCount = 10; + + final S3AsyncClient s3AsyncClient = mock(S3AsyncClient.class); + final AmazonAsyncS3Reference amazonAsyncS3Reference = new AmazonAsyncS3Reference( + AmazonAsyncS3WithCredentials.create(s3AsyncClient, s3AsyncClient, null) + ); + + final S3BlobStore blobStore = mock(S3BlobStore.class); + final BlobPath blobPath = new BlobPath(); + + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + when(blobStore.serverSideEncryption()).thenReturn(false); + when(blobStore.asyncClientReference()).thenReturn(amazonAsyncS3Reference); + + CompletableFuture getObjectAttributesResponseCompletableFuture = new CompletableFuture<>(); + getObjectAttributesResponseCompletableFuture.complete( + GetObjectAttributesResponse.builder() + .checksum(Checksum.builder().checksumCRC32(checksum).build()) + .objectSize(objectSize) + .objectParts(GetObjectAttributesParts.builder().totalPartsCount(objectPartCount).build()) + .build() + ); + when(s3AsyncClient.getObjectAttributes(any(GetObjectAttributesRequest.class))).thenThrow(new RuntimeException()); + + CountDownLatch countDownLatch = new CountDownLatch(1); + CountingCompletionListener readContextActionListener = new CountingCompletionListener<>(); + LatchedActionListener listener = new LatchedActionListener<>(readContextActionListener, countDownLatch); + + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + blobContainer.readBlobAsync(blobName, listener); + countDownLatch.await(); + + assertEquals(0, readContextActionListener.getResponseCount()); + assertEquals(1, readContextActionListener.getFailureCount()); + } + + public void testGetBlobMetadata() throws Exception { + final String checksum = randomAlphaOfLengthBetween(1, 10); + final long objectSize = 100L; + final int objectPartCount = 10; + final String blobName = randomAlphaOfLengthBetween(1, 10); + final String bucketName = randomAlphaOfLengthBetween(1, 10); + + final S3AsyncClient s3AsyncClient = mock(S3AsyncClient.class); + final S3BlobStore blobStore = mock(S3BlobStore.class); + final BlobPath blobPath = new BlobPath(); + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + when(blobStore.serverSideEncryption()).thenReturn(false); + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + CompletableFuture getObjectAttributesResponseCompletableFuture = new CompletableFuture<>(); + getObjectAttributesResponseCompletableFuture.complete( + GetObjectAttributesResponse.builder() + .checksum(Checksum.builder().checksumCRC32(checksum).build()) + .objectSize(objectSize) + .objectParts(GetObjectAttributesParts.builder().totalPartsCount(objectPartCount).build()) + .build() + ); + when(s3AsyncClient.getObjectAttributes(any(GetObjectAttributesRequest.class))).thenReturn( + getObjectAttributesResponseCompletableFuture + ); + + CompletableFuture responseFuture = blobContainer.getBlobMetadata(s3AsyncClient, bucketName, blobName); + GetObjectAttributesResponse objectAttributesResponse = responseFuture.get(); + + assertEquals(checksum, objectAttributesResponse.checksum().checksumCRC32()); + assertEquals(Long.valueOf(objectSize), objectAttributesResponse.objectSize()); + assertEquals(Integer.valueOf(objectPartCount), objectAttributesResponse.objectParts().totalPartsCount()); + } + + public void testGetBlobPartInputStream() throws Exception { + final String blobName = randomAlphaOfLengthBetween(1, 10); + final String bucketName = randomAlphaOfLengthBetween(1, 10); + final long contentLength = 10L; + final String contentRange = "bytes 10-20/100"; + final InputStream inputStream = ResponseInputStream.nullInputStream(); + + final S3AsyncClient s3AsyncClient = mock(S3AsyncClient.class); + final S3BlobStore blobStore = mock(S3BlobStore.class); + final BlobPath blobPath = new BlobPath(); + when(blobStore.bucket()).thenReturn(bucketName); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); + when(blobStore.serverSideEncryption()).thenReturn(false); + final S3BlobContainer blobContainer = new S3BlobContainer(blobPath, blobStore); + + GetObjectResponse getObjectResponse = GetObjectResponse.builder().contentLength(contentLength).contentRange(contentRange).build(); + + CompletableFuture> getObjectPartResponse = new CompletableFuture<>(); + ResponseInputStream responseInputStream = new ResponseInputStream<>(getObjectResponse, inputStream); + getObjectPartResponse.complete(responseInputStream); + + when( + s3AsyncClient.getObject( + any(GetObjectRequest.class), + ArgumentMatchers.>>any() + ) + ).thenReturn(getObjectPartResponse); + + // Header based offset in case of a multi part object request + InputStreamContainer inputStreamContainer = blobContainer.getBlobPartInputStreamContainer(s3AsyncClient, bucketName, blobName, 0) + .get(); + + assertEquals(10, inputStreamContainer.getOffset()); + assertEquals(contentLength, inputStreamContainer.getContentLength()); + assertEquals(inputStream.available(), inputStreamContainer.getInputStream().available()); + + // 0 offset in case of a single part object request + inputStreamContainer = blobContainer.getBlobPartInputStreamContainer(s3AsyncClient, bucketName, blobName, null).get(); + + assertEquals(0, inputStreamContainer.getOffset()); + assertEquals(contentLength, inputStreamContainer.getContentLength()); + assertEquals(inputStream.available(), inputStreamContainer.getInputStream().available()); + } + + public void testTransformResponseToInputStreamContainer() throws Exception { + final String contentRange = "bytes 0-10/100"; + final long contentLength = 10L; + final InputStream inputStream = ResponseInputStream.nullInputStream(); + + GetObjectResponse getObjectResponse = GetObjectResponse.builder().contentLength(contentLength).build(); + + // Exception when content range absent for multipart object + ResponseInputStream responseInputStreamNoRange = new ResponseInputStream<>(getObjectResponse, inputStream); + assertThrows(SdkException.class, () -> S3BlobContainer.transformResponseToInputStreamContainer(responseInputStreamNoRange, true)); + + // No exception when content range absent for single part object + ResponseInputStream responseInputStreamNoRangeSinglePart = new ResponseInputStream<>( + getObjectResponse, + inputStream + ); + InputStreamContainer inputStreamContainer = S3BlobContainer.transformResponseToInputStreamContainer( + responseInputStreamNoRangeSinglePart, + false + ); + assertEquals(contentLength, inputStreamContainer.getContentLength()); + assertEquals(0, inputStreamContainer.getOffset()); + + // Exception when length is absent + getObjectResponse = GetObjectResponse.builder().contentRange(contentRange).build(); + ResponseInputStream responseInputStreamNoContentLength = new ResponseInputStream<>( + getObjectResponse, + inputStream + ); + assertThrows( + SdkException.class, + () -> S3BlobContainer.transformResponseToInputStreamContainer(responseInputStreamNoContentLength, true) + ); + + // No exception when range and length both are present + getObjectResponse = GetObjectResponse.builder().contentRange(contentRange).contentLength(contentLength).build(); + ResponseInputStream responseInputStream = new ResponseInputStream<>(getObjectResponse, inputStream); + inputStreamContainer = S3BlobContainer.transformResponseToInputStreamContainer(responseInputStream, true); + assertEquals(contentLength, inputStreamContainer.getContentLength()); + assertEquals(0, inputStreamContainer.getOffset()); + assertEquals(inputStream.available(), inputStreamContainer.getInputStream().available()); + } + + private void mockObjectResponse(S3AsyncClient s3AsyncClient, String bucketName, String blobName, int objectSize) { + + final InputStream inputStream = new ByteArrayInputStream(randomByteArrayOfLength(objectSize)); + + GetObjectResponse getObjectResponse = GetObjectResponse.builder().contentLength((long) objectSize).build(); + + CompletableFuture> getObjectPartResponse = new CompletableFuture<>(); + ResponseInputStream responseInputStream = new ResponseInputStream<>(getObjectResponse, inputStream); + getObjectPartResponse.complete(responseInputStream); + + GetObjectRequest getObjectRequest = GetObjectRequest.builder().bucket(bucketName).key(blobName).build(); + + when( + s3AsyncClient.getObject( + eq(getObjectRequest), + ArgumentMatchers.>>any() + ) + ).thenReturn(getObjectPartResponse); + + } + + private void mockObjectPartResponse( + S3AsyncClient s3AsyncClient, + String bucketName, + String blobName, + int totalNumberOfParts, + int partSize, + long objectSize + ) { + for (int partNumber = 1; partNumber <= totalNumberOfParts; partNumber++) { + final int start = (partNumber - 1) * partSize; + final int end = partNumber * partSize; + final String contentRange = "bytes " + start + "-" + end + "/" + objectSize; + final InputStream inputStream = new ByteArrayInputStream(randomByteArrayOfLength(partSize)); + + GetObjectResponse getObjectResponse = GetObjectResponse.builder() + .contentLength((long) partSize) + .contentRange(contentRange) + .build(); + + CompletableFuture> getObjectPartResponse = new CompletableFuture<>(); + ResponseInputStream responseInputStream = new ResponseInputStream<>(getObjectResponse, inputStream); + getObjectPartResponse.complete(responseInputStream); + + GetObjectRequest getObjectRequest = GetObjectRequest.builder().bucket(bucketName).key(blobName).partNumber(partNumber).build(); + + when( + s3AsyncClient.getObject( + eq(getObjectRequest), + ArgumentMatchers.>>any() + ) + ).thenReturn(getObjectPartResponse); + } + } + + private static class CountingCompletionListener implements ActionListener { + private int responseCount; + private int failureCount; + private T response; + private Exception exception; + + @Override + public void onResponse(T response) { + this.response = response; + responseCount++; + } + + @Override + public void onFailure(Exception e) { + exception = e; + failureCount++; + } + + public int getResponseCount() { + return responseCount; + } + + public int getFailureCount() { + return failureCount; + } + + public T getResponse() { + return response; + } + + public Exception getException() { + return exception; + } + } } diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3ClientSettingsTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3ClientSettingsTests.java index 462ed5377ff9a..61c9c998b1dec 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3ClientSettingsTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3ClientSettingsTests.java @@ -32,27 +32,36 @@ package org.opensearch.repositories.s3; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.Protocol; -import com.amazonaws.services.s3.AmazonS3Client; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.core.signer.Signer; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsException; -import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.repositories.s3.utils.AwsRequestSigner; +import org.opensearch.repositories.s3.utils.Protocol; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.Locale; import java.util.Map; +import java.util.Optional; +import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.sameInstance; -public class S3ClientSettingsTests extends OpenSearchTestCase { +public class S3ClientSettingsTests extends AbstractS3RepositoryTestCase { public void testThereIsADefaultClientByDefault() { - final Map settings = S3ClientSettings.load(Settings.EMPTY); + final Map settings = S3ClientSettings.load(Settings.EMPTY, configPath()); assertThat(settings.keySet(), contains("default")); final S3ClientSettings defaultSettings = settings.get("default"); @@ -60,14 +69,19 @@ public void testThereIsADefaultClientByDefault() { assertThat(defaultSettings.endpoint, is(emptyString())); assertThat(defaultSettings.protocol, is(Protocol.HTTPS)); assertThat(defaultSettings.proxySettings, is(ProxySettings.NO_PROXY_SETTINGS)); - assertThat(defaultSettings.readTimeoutMillis, is(ClientConfiguration.DEFAULT_SOCKET_TIMEOUT)); - assertThat(defaultSettings.maxRetries, is(ClientConfiguration.DEFAULT_RETRY_POLICY.getMaxErrorRetry())); - assertThat(defaultSettings.throttleRetries, is(ClientConfiguration.DEFAULT_THROTTLE_RETRIES)); + assertThat(defaultSettings.readTimeoutMillis, is(50 * 1000)); + assertThat(defaultSettings.requestTimeoutMillis, is(120 * 1000)); + assertThat(defaultSettings.connectionTimeoutMillis, is(10 * 1000)); + assertThat(defaultSettings.connectionTTLMillis, is(5 * 1000)); + assertThat(defaultSettings.maxConnections, is(100)); + assertThat(defaultSettings.maxRetries, is(3)); + assertThat(defaultSettings.throttleRetries, is(true)); } public void testDefaultClientSettingsCanBeSet() { final Map settings = S3ClientSettings.load( - Settings.builder().put("s3.client.default.max_retries", 10).build() + Settings.builder().put("s3.client.default.max_retries", 10).build(), + configPath() ); assertThat(settings.keySet(), contains("default")); @@ -77,12 +91,13 @@ public void testDefaultClientSettingsCanBeSet() { public void testNondefaultClientCreatedBySettingItsSettings() { final Map settings = S3ClientSettings.load( - Settings.builder().put("s3.client.another_client.max_retries", 10).build() + Settings.builder().put("s3.client.another_client.max_retries", 10).build(), + configPath() ); assertThat(settings.keySet(), contains("default", "another_client")); final S3ClientSettings defaultSettings = settings.get("default"); - assertThat(defaultSettings.maxRetries, is(ClientConfiguration.DEFAULT_RETRY_POLICY.getMaxErrorRetry())); + assertThat(defaultSettings.maxRetries, is(3)); final S3ClientSettings anotherClientSettings = settings.get("another_client"); assertThat(anotherClientSettings.maxRetries, is(10)); @@ -93,7 +108,7 @@ public void testRejectionOfLoneAccessKey() { secureSettings.setString("s3.client.default.access_key", "aws_key"); final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build()) + () -> S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build(), configPath()) ); assertThat(e.getMessage(), is("Missing secret key for s3 client [default]")); } @@ -103,7 +118,7 @@ public void testRejectionOfLoneSecretKey() { secureSettings.setString("s3.client.default.secret_key", "aws_key"); final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build()) + () -> S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build(), configPath()) ); assertThat(e.getMessage(), is("Missing access key for s3 client [default]")); } @@ -113,20 +128,92 @@ public void testRejectionOfLoneSessionToken() { secureSettings.setString("s3.client.default.session_token", "aws_key"); final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build()) + () -> S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build(), configPath()) ); assertThat(e.getMessage(), is("Missing access key and secret key for s3 client [default]")); } + public void testIrsaCredentialsTypeWithIdentityTokenFile() { + final Map settings = S3ClientSettings.load( + Settings.builder().put("s3.client.default.identity_token_file", "file").build(), + configPath() + ); + final S3ClientSettings defaultSettings = settings.get("default"); + final S3ClientSettings.IrsaCredentials credentials = defaultSettings.irsaCredentials; + assertThat(credentials.getIdentityTokenFile(), is("config/file")); + assertThat(credentials.getRoleArn(), is(nullValue())); + assertThat(credentials.getRoleSessionName(), startsWith("s3-sdk-java-")); + } + + public void testIrsaCredentialsTypeRoleArn() { + final MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("s3.client.default.role_arn", "role"); + final Map settings = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).build(), + configPath() + ); + final S3ClientSettings defaultSettings = settings.get("default"); + final S3ClientSettings.IrsaCredentials credentials = defaultSettings.irsaCredentials; + assertThat(credentials.getRoleArn(), is("role")); + assertThat(credentials.getRoleSessionName(), startsWith("s3-sdk-java-")); + } + + public void testIrsaCredentialsTypeWithRoleArnAndRoleSessionName() { + final MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("s3.client.default.role_arn", "role"); + secureSettings.setString("s3.client.default.role_session_name", "session"); + final Map settings = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).build(), + configPath() + ); + final S3ClientSettings defaultSettings = settings.get("default"); + final S3ClientSettings.IrsaCredentials credentials = defaultSettings.irsaCredentials; + assertThat(credentials.getRoleArn(), is("role")); + assertThat(credentials.getRoleSessionName(), is("session")); + } + + public void testIrsaCredentialsTypeWithRoleArnAndRoleSessionNameAndIdentityTokeFileRelative() { + final MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("s3.client.default.role_arn", "role"); + secureSettings.setString("s3.client.default.role_session_name", "session"); + final Map settings = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).put("s3.client.default.identity_token_file", "file").build(), + configPath() + ); + final S3ClientSettings defaultSettings = settings.get("default"); + final S3ClientSettings.IrsaCredentials credentials = defaultSettings.irsaCredentials; + assertThat(credentials.getIdentityTokenFile(), is("config/file")); + assertThat(credentials.getRoleArn(), is("role")); + assertThat(credentials.getRoleSessionName(), is("session")); + } + + public void testIrsaCredentialsTypeWithRoleArnAndRoleSessionNameAndIdentityTokeFileAbsolute() { + final MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("s3.client.default.role_arn", "role"); + secureSettings.setString("s3.client.default.role_session_name", "session"); + final Map settings = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).put("s3.client.default.identity_token_file", "/file").build(), + configPath() + ); + final S3ClientSettings defaultSettings = settings.get("default"); + final S3ClientSettings.IrsaCredentials credentials = defaultSettings.irsaCredentials; + assertThat(credentials.getIdentityTokenFile(), is("/file")); + assertThat(credentials.getRoleArn(), is("role")); + assertThat(credentials.getRoleSessionName(), is("session")); + } + public void testCredentialsTypeWithAccessKeyAndSecretKey() { final MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString("s3.client.default.access_key", "access_key"); secureSettings.setString("s3.client.default.secret_key", "secret_key"); - final Map settings = S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build()); + final Map settings = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).build(), + configPath() + ); final S3ClientSettings defaultSettings = settings.get("default"); - S3BasicCredentials credentials = defaultSettings.credentials; - assertThat(credentials.getAWSAccessKeyId(), is("access_key")); - assertThat(credentials.getAWSSecretKey(), is("secret_key")); + AwsCredentials credentials = defaultSettings.credentials; + assertThat(credentials.accessKeyId(), is("access_key")); + assertThat(credentials.secretAccessKey(), is("secret_key")); } public void testCredentialsTypeWithAccessKeyAndSecretKeyAndSessionToken() { @@ -134,12 +221,15 @@ public void testCredentialsTypeWithAccessKeyAndSecretKeyAndSessionToken() { secureSettings.setString("s3.client.default.access_key", "access_key"); secureSettings.setString("s3.client.default.secret_key", "secret_key"); secureSettings.setString("s3.client.default.session_token", "session_token"); - final Map settings = S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build()); + final Map settings = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).build(), + configPath() + ); final S3ClientSettings defaultSettings = settings.get("default"); - S3BasicSessionCredentials credentials = (S3BasicSessionCredentials) defaultSettings.credentials; - assertThat(credentials.getAWSAccessKeyId(), is("access_key")); - assertThat(credentials.getAWSSecretKey(), is("secret_key")); - assertThat(credentials.getSessionToken(), is("session_token")); + AwsSessionCredentials credentials = (AwsSessionCredentials) defaultSettings.credentials; + assertThat(credentials.accessKeyId(), is("access_key")); + assertThat(credentials.secretAccessKey(), is("secret_key")); + assertThat(credentials.sessionToken(), is("session_token")); } public void testRefineWithRepoSettings() { @@ -147,8 +237,10 @@ public void testRefineWithRepoSettings() { secureSettings.setString("s3.client.default.access_key", "access_key"); secureSettings.setString("s3.client.default.secret_key", "secret_key"); secureSettings.setString("s3.client.default.session_token", "session_token"); - final S3ClientSettings baseSettings = S3ClientSettings.load(Settings.builder().setSecureSettings(secureSettings).build()) - .get("default"); + final S3ClientSettings baseSettings = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).build(), + configPath() + ).get("default"); { final S3ClientSettings refinedSettings = baseSettings.refine(Settings.EMPTY); @@ -159,25 +251,26 @@ public void testRefineWithRepoSettings() { final String endpoint = "some.host"; final S3ClientSettings refinedSettings = baseSettings.refine(Settings.builder().put("endpoint", endpoint).build()); assertThat(refinedSettings.endpoint, is(endpoint)); - S3BasicSessionCredentials credentials = (S3BasicSessionCredentials) refinedSettings.credentials; - assertThat(credentials.getAWSAccessKeyId(), is("access_key")); - assertThat(credentials.getAWSSecretKey(), is("secret_key")); - assertThat(credentials.getSessionToken(), is("session_token")); + AwsSessionCredentials credentials = (AwsSessionCredentials) refinedSettings.credentials; + assertThat(credentials.accessKeyId(), is("access_key")); + assertThat(credentials.secretAccessKey(), is("secret_key")); + assertThat(credentials.sessionToken(), is("session_token")); } { final S3ClientSettings refinedSettings = baseSettings.refine(Settings.builder().put("path_style_access", true).build()); assertThat(refinedSettings.pathStyleAccess, is(true)); - S3BasicSessionCredentials credentials = (S3BasicSessionCredentials) refinedSettings.credentials; - assertThat(credentials.getAWSAccessKeyId(), is("access_key")); - assertThat(credentials.getAWSSecretKey(), is("secret_key")); - assertThat(credentials.getSessionToken(), is("session_token")); + AwsSessionCredentials credentials = (AwsSessionCredentials) refinedSettings.credentials; + assertThat(credentials.accessKeyId(), is("access_key")); + assertThat(credentials.secretAccessKey(), is("secret_key")); + assertThat(credentials.sessionToken(), is("session_token")); } } public void testPathStyleAccessCanBeSet() { final Map settings = S3ClientSettings.load( - Settings.builder().put("s3.client.other.path_style_access", true).build() + Settings.builder().put("s3.client.other.path_style_access", true).build(), + configPath() ); assertThat(settings.get("default").pathStyleAccess, is(false)); assertThat(settings.get("other").pathStyleAccess, is(true)); @@ -185,36 +278,51 @@ public void testPathStyleAccessCanBeSet() { public void testUseChunkedEncodingCanBeSet() { final Map settings = S3ClientSettings.load( - Settings.builder().put("s3.client.other.disable_chunked_encoding", true).build() + Settings.builder().put("s3.client.other.disable_chunked_encoding", true).build(), + configPath() ); assertThat(settings.get("default").disableChunkedEncoding, is(false)); assertThat(settings.get("other").disableChunkedEncoding, is(true)); } public void testRegionCanBeSet() { - final String region = randomAlphaOfLength(5); + final String region = randomFrom(Region.regions().stream().map(Region::toString).toArray(String[]::new)); final Map settings = S3ClientSettings.load( - Settings.builder().put("s3.client.other.region", region).build() + Settings.builder().put("s3.client.other.region", region).build(), + configPath() ); assertThat(settings.get("default").region, is("")); assertThat(settings.get("other").region, is(region)); - try (S3Service s3Service = new S3Service()) { - AmazonS3Client other = (AmazonS3Client) s3Service.buildClient(settings.get("other")); - assertThat(other.getSignerRegionOverride(), is(region)); + try ( + S3Service s3Service = new S3Service(configPath()); + S3Client other = SocketAccess.doPrivileged(() -> s3Service.buildClient(settings.get("other")).client()); + ) { + assertThat(other.serviceClientConfiguration().region(), is(Region.of(region))); } } public void testSignerOverrideCanBeSet() { - final String signerOverride = randomAlphaOfLength(5); + S3Service.setDefaultAwsProfilePath(); + final String signerOverride = randomFrom(AwsRequestSigner.values()).getName(); final Map settings = S3ClientSettings.load( - Settings.builder().put("s3.client.other.signer_override", signerOverride).build() + Settings.builder().put("s3.client.other.signer_override", signerOverride).build(), + configPath() ); assertThat(settings.get("default").region, is("")); assertThat(settings.get("other").signerOverride, is(signerOverride)); - ClientConfiguration defaultConfiguration = S3Service.buildConfiguration(settings.get("default")); - assertThat(defaultConfiguration.getSignerOverride(), nullValue()); - ClientConfiguration configuration = S3Service.buildConfiguration(settings.get("other")); - assertThat(configuration.getSignerOverride(), is(signerOverride)); + + ClientOverrideConfiguration defaultConfiguration = SocketAccess.doPrivileged( + () -> S3Service.buildOverrideConfiguration(settings.get("default")) + ); + Optional defaultSigner = defaultConfiguration.advancedOption(SdkAdvancedClientOption.SIGNER); + assertFalse(defaultSigner.isPresent()); + + ClientOverrideConfiguration configuration = SocketAccess.doPrivileged( + () -> S3Service.buildOverrideConfiguration(settings.get("other")) + ); + Optional otherSigner = configuration.advancedOption(SdkAdvancedClientOption.SIGNER); + assertTrue(otherSigner.isPresent()); + assertThat(otherSigner.get(), sameInstance(AwsRequestSigner.fromSignerName(signerOverride).getSigner())); } public void testSetProxySettings() throws Exception { @@ -234,7 +342,7 @@ public void testSetProxySettings() throws Exception { .setSecureSettings(secureSettings) .build(); - final S3ClientSettings s3ClientSettings = S3ClientSettings.load(settings).get("default"); + final S3ClientSettings s3ClientSettings = S3ClientSettings.load(settings, configPath()).get("default"); assertEquals(ProxySettings.ProxyType.valueOf(proxyType.toUpperCase(Locale.ROOT)), s3ClientSettings.proxySettings.getType()); assertEquals(new InetSocketAddress(InetAddress.getByName("127.0.0.10"), port), s3ClientSettings.proxySettings.getAddress()); @@ -248,7 +356,7 @@ public void testProxyWrongHost() { .put("s3.client.default.proxy.host", "thisisnotavalidhostorwehavebeensuperunlucky") .put("s3.client.default.proxy.port", 8080) .build(); - final SettingsException e = expectThrows(SettingsException.class, () -> S3ClientSettings.load(settings)); + final SettingsException e = expectThrows(SettingsException.class, () -> S3ClientSettings.load(settings, configPath())); assertEquals("S3 proxy host is unknown.", e.getMessage()); } @@ -258,7 +366,7 @@ public void testProxyTypeNotSet() { .put("s3.client.default.proxy.port", 8080) .build(); - SettingsException e = expectThrows(SettingsException.class, () -> S3ClientSettings.load(hostPortSettings)); + SettingsException e = expectThrows(SettingsException.class, () -> S3ClientSettings.load(hostPortSettings, configPath())); assertEquals("S3 proxy port or host or username or password have been set but proxy type is not defined.", e.getMessage()); final MockSecureSettings secureSettings = new MockSecureSettings(); @@ -266,7 +374,7 @@ public void testProxyTypeNotSet() { secureSettings.setString("s3.client.default.proxy.password", "bbbb"); final Settings usernamePasswordSettings = Settings.builder().setSecureSettings(secureSettings).build(); - e = expectThrows(SettingsException.class, () -> S3ClientSettings.load(usernamePasswordSettings)); + e = expectThrows(SettingsException.class, () -> S3ClientSettings.load(usernamePasswordSettings, configPath())); assertEquals("S3 proxy port or host or username or password have been set but proxy type is not defined.", e.getMessage()); } @@ -275,7 +383,7 @@ public void testProxyHostNotSet() { .put("s3.client.default.proxy.port", 8080) .put("s3.client.default.proxy.type", randomFrom("socks", "http", "https")) .build(); - final SettingsException e = expectThrows(SettingsException.class, () -> S3ClientSettings.load(settings)); + final SettingsException e = expectThrows(SettingsException.class, () -> S3ClientSettings.load(settings, configPath())); assertEquals("S3 proxy type has been set but proxy host or port is not defined.", e.getMessage()); } @@ -286,7 +394,6 @@ public void testSocksDoesNotSupportForHttpProtocol() { .put("s3.client.default.protocol", "http") .put("s3.client.default.proxy.type", "socks") .build(); - expectThrows(SettingsException.class, () -> S3ClientSettings.load(settings)); + expectThrows(SettingsException.class, () -> S3ClientSettings.load(settings, configPath())); } - } diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3RepositoryTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3RepositoryTests.java index da1f5d71b4da5..e65ca69a5047b 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3RepositoryTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3RepositoryTests.java @@ -32,19 +32,24 @@ package org.opensearch.repositories.s3; -import com.amazonaws.services.s3.AbstractAmazonS3; +import software.amazon.awssdk.services.s3.S3Client; + import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.repositories.RepositoryException; +import org.opensearch.repositories.blobstore.BlobStoreRepository; import org.opensearch.repositories.blobstore.BlobStoreTestUtil; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.Matchers; +import java.nio.file.Path; +import java.util.List; import java.util.Map; import static org.hamcrest.Matchers.containsString; @@ -52,20 +57,16 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; -public class S3RepositoryTests extends OpenSearchTestCase { - - private static class DummyS3Client extends AbstractAmazonS3 { +public class S3RepositoryTests extends OpenSearchTestCase implements ConfigPathSupport { - @Override - public void shutdown() { - // TODO check is closed + private static class DummyS3Service extends S3Service { + DummyS3Service(final Path configPath) { + super(configPath); } - } - private static class DummyS3Service extends S3Service { @Override public AmazonS3Reference client(RepositoryMetadata repositoryMetadata) { - return new AmazonS3Reference(new DummyS3Client()); + return new AmazonS3Reference(S3Client.create()); } @Override @@ -124,7 +125,8 @@ public void testBasePathSetting() { } public void testDefaultBufferSize() { - final RepositoryMetadata metadata = new RepositoryMetadata("dummy-repo", "mock", Settings.EMPTY); + Settings settings = Settings.builder().build(); + final RepositoryMetadata metadata = new RepositoryMetadata("dummy-repo", "mock", settings); try (S3Repository s3repo = createS3Repo(metadata)) { assertThat(s3repo.getBlobStore(), is(nullValue())); s3repo.start(); @@ -135,13 +137,38 @@ public void testDefaultBufferSize() { } } + public void testIsReloadable() { + final RepositoryMetadata metadata = new RepositoryMetadata("dummy-repo", "mock", Settings.EMPTY); + try (S3Repository s3repo = createS3Repo(metadata)) { + assertTrue(s3repo.isReloadable()); + } + } + + public void testRestrictedSettingsDefault() { + final RepositoryMetadata metadata = new RepositoryMetadata("dummy-repo", "mock", Settings.EMPTY); + try (S3Repository s3repo = createS3Repo(metadata)) { + List> restrictedSettings = s3repo.getRestrictedSystemRepositorySettings(); + assertThat(restrictedSettings.size(), is(5)); + assertTrue(restrictedSettings.contains(BlobStoreRepository.SYSTEM_REPOSITORY_SETTING)); + assertTrue(restrictedSettings.contains(BlobStoreRepository.READONLY_SETTING)); + assertTrue(restrictedSettings.contains(BlobStoreRepository.REMOTE_STORE_INDEX_SHALLOW_COPY)); + assertTrue(restrictedSettings.contains(S3Repository.BUCKET_SETTING)); + assertTrue(restrictedSettings.contains(S3Repository.BASE_PATH_SETTING)); + } + } + private S3Repository createS3Repo(RepositoryMetadata metadata) { return new S3Repository( metadata, NamedXContentRegistry.EMPTY, - new DummyS3Service(), + new DummyS3Service(configPath()), BlobStoreTestUtil.mockClusterService(), - new RecoverySettings(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)) + new RecoverySettings(Settings.EMPTY, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)), + null, + null, + null, + null, + false ) { @Override protected void assertSnapshotOrGenericThread() { diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3RetryingInputStreamTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3RetryingInputStreamTests.java index 0f40a7b3392e8..b38d5119b4108 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3RetryingInputStreamTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3RetryingInputStreamTests.java @@ -32,12 +32,11 @@ package org.opensearch.repositories.s3; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.GetObjectRequest; -import com.amazonaws.services.s3.model.S3Object; -import com.amazonaws.services.s3.model.S3ObjectInputStream; -import org.apache.http.client.methods.HttpGet; -import org.opensearch.common.Nullable; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; + import org.opensearch.common.io.Streams; import org.opensearch.test.OpenSearchTestCase; @@ -55,7 +54,7 @@ public class S3RetryingInputStreamTests extends OpenSearchTestCase { public void testInputStreamFullyConsumed() throws IOException { final byte[] expectedBytes = randomByteArrayOfLength(randomIntBetween(1, 512)); - final S3RetryingInputStream stream = createInputStream(expectedBytes, null, null); + final S3RetryingInputStream stream = createInputStream(expectedBytes, 0L, (long) (Integer.MAX_VALUE - 1)); Streams.consumeFully(stream); assertThat(stream.isEof(), is(true)); @@ -66,7 +65,7 @@ public void testInputStreamIsAborted() throws IOException { final byte[] expectedBytes = randomByteArrayOfLength(randomIntBetween(10, 512)); final byte[] actualBytes = new byte[randomIntBetween(1, Math.max(1, expectedBytes.length - 1))]; - final S3RetryingInputStream stream = createInputStream(expectedBytes, null, null); + final S3RetryingInputStream stream = createInputStream(expectedBytes, 0L, (long) (Integer.MAX_VALUE - 1)); stream.read(actualBytes); stream.close(); @@ -77,8 +76,8 @@ public void testInputStreamIsAborted() throws IOException { public void testRangeInputStreamFullyConsumed() throws IOException { final byte[] bytes = randomByteArrayOfLength(randomIntBetween(1, 512)); - final int position = randomIntBetween(0, bytes.length - 1); - final int length = randomIntBetween(1, bytes.length - position); + final long position = randomLongBetween(0, bytes.length - 1); + final long length = randomLongBetween(1, bytes.length - position); final S3RetryingInputStream stream = createInputStream(bytes, position, length); Streams.consumeFully(stream); @@ -91,36 +90,33 @@ public void testRangeInputStreamIsAborted() throws IOException { final byte[] expectedBytes = randomByteArrayOfLength(randomIntBetween(10, 512)); final byte[] actualBytes = new byte[randomIntBetween(1, Math.max(1, expectedBytes.length - 1))]; - final int length = randomIntBetween(actualBytes.length + 1, expectedBytes.length); - final int position = randomIntBetween(0, Math.max(1, expectedBytes.length - length)); + final long length = randomLongBetween(actualBytes.length + 1, expectedBytes.length); + final long position = randomLongBetween(0L, Math.max(1, expectedBytes.length - length)); final S3RetryingInputStream stream = createInputStream(expectedBytes, position, length); stream.read(actualBytes); stream.close(); - assertArrayEquals(Arrays.copyOfRange(expectedBytes, position, position + actualBytes.length), actualBytes); + assertArrayEquals(Arrays.copyOfRange(expectedBytes, (int) position, (int) (position + actualBytes.length)), actualBytes); assertThat(stream.isEof(), is(false)); assertThat(stream.isAborted(), is(true)); } - private S3RetryingInputStream createInputStream(final byte[] data, @Nullable final Integer position, @Nullable final Integer length) - throws IOException { - final S3Object s3Object = new S3Object(); - final AmazonS3 client = mock(AmazonS3.class); - when(client.getObject(any(GetObjectRequest.class))).thenReturn(s3Object); + private S3RetryingInputStream createInputStream(final byte[] data, final Long start, final Long length) throws IOException { + final long end = Math.addExact(start, length - 1); + final S3Client client = mock(S3Client.class); + when(client.getObject(any(GetObjectRequest.class))).thenReturn( + new ResponseInputStream<>( + GetObjectResponse.builder().contentLength(length).build(), + new ByteArrayInputStream(data, Math.toIntExact(start), Math.toIntExact(length)) + ) + ); final AmazonS3Reference clientReference = mock(AmazonS3Reference.class); when(clientReference.get()).thenReturn(client); final S3BlobStore blobStore = mock(S3BlobStore.class); when(blobStore.clientReference()).thenReturn(clientReference); + when(blobStore.getStatsMetricPublisher()).thenReturn(new StatsMetricPublisher()); - if (position != null && length != null) { - s3Object.getObjectMetadata().setContentLength(length); - s3Object.setObjectContent(new S3ObjectInputStream(new ByteArrayInputStream(data, position, length), new HttpGet())); - return new S3RetryingInputStream(blobStore, "_blob", position, Math.addExact(position, length - 1)); - } else { - s3Object.getObjectMetadata().setContentLength(data.length); - s3Object.setObjectContent(new S3ObjectInputStream(new ByteArrayInputStream(data), new HttpGet())); - return new S3RetryingInputStream(blobStore, "_blob"); - } + return new S3RetryingInputStream(blobStore, "_blob", start, end); } } diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3ServiceTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3ServiceTests.java index cb0e76e272b4e..400905eec8b1c 100644 --- a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3ServiceTests.java +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/S3ServiceTests.java @@ -32,24 +32,50 @@ package org.opensearch.repositories.s3; import org.opensearch.cluster.metadata.RepositoryMetadata; - +import org.opensearch.common.settings.MockSecureSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.test.OpenSearchTestCase; -public class S3ServiceTests extends OpenSearchTestCase { +import java.util.Map; +public class S3ServiceTests extends AbstractS3RepositoryTestCase { public void testCachedClientsAreReleased() { - final S3Service s3Service = new S3Service(); - final Settings settings = Settings.builder().put("endpoint", "http://first").build(); + final S3Service s3Service = new S3Service(configPath()); + final Settings settings = Settings.builder().put("endpoint", "http://first").put("region", "region").build(); + final RepositoryMetadata metadata1 = new RepositoryMetadata("first", "s3", settings); + final RepositoryMetadata metadata2 = new RepositoryMetadata("second", "s3", settings); + final S3ClientSettings clientSettings = s3Service.settings(metadata2); + final S3ClientSettings otherClientSettings = s3Service.settings(metadata2); + assertSame(clientSettings, otherClientSettings); + final AmazonS3Reference reference = SocketAccess.doPrivileged(() -> s3Service.client(metadata1)); + reference.close(); + s3Service.close(); + final AmazonS3Reference referenceReloaded = SocketAccess.doPrivileged(() -> s3Service.client(metadata1)); + assertNotSame(referenceReloaded, reference); + referenceReloaded.close(); + s3Service.close(); + final S3ClientSettings clientSettingsReloaded = s3Service.settings(metadata1); + assertNotSame(clientSettings, clientSettingsReloaded); + } + + public void testCachedClientsWithCredentialsAreReleased() { + final MockSecureSettings secureSettings = new MockSecureSettings(); + secureSettings.setString("s3.client.default.role_arn", "role"); + final Map defaults = S3ClientSettings.load( + Settings.builder().setSecureSettings(secureSettings).put("s3.client.default.identity_token_file", "file").build(), + configPath() + ); + final S3Service s3Service = new S3Service(configPath()); + s3Service.refreshAndClearCache(defaults); + final Settings settings = Settings.builder().put("endpoint", "http://first").put("region", "us-east-2").build(); final RepositoryMetadata metadata1 = new RepositoryMetadata("first", "s3", settings); final RepositoryMetadata metadata2 = new RepositoryMetadata("second", "s3", settings); final S3ClientSettings clientSettings = s3Service.settings(metadata2); final S3ClientSettings otherClientSettings = s3Service.settings(metadata2); assertSame(clientSettings, otherClientSettings); - final AmazonS3Reference reference = s3Service.client(metadata1); + final AmazonS3Reference reference = SocketAccess.doPrivileged(() -> s3Service.client(metadata1)); reference.close(); s3Service.close(); - final AmazonS3Reference referenceReloaded = s3Service.client(metadata1); + final AmazonS3Reference referenceReloaded = SocketAccess.doPrivileged(() -> s3Service.client(metadata1)); assertNotSame(referenceReloaded, reference); referenceReloaded.close(); s3Service.close(); diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/async/AsyncTransferManagerTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/async/AsyncTransferManagerTests.java new file mode 100644 index 0000000000000..9c07b929052bc --- /dev/null +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/async/AsyncTransferManagerTests.java @@ -0,0 +1,239 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.async; + +import software.amazon.awssdk.awscore.exception.AwsErrorDetails; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.http.HttpStatusCode; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.services.s3.model.S3Exception; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.model.UploadPartResponse; + +import org.opensearch.ExceptionsHelper; +import org.opensearch.common.StreamContext; +import org.opensearch.common.blobstore.exception.CorruptFileException; +import org.opensearch.common.blobstore.stream.write.WritePriority; +import org.opensearch.common.io.InputStreamContainer; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.repositories.blobstore.ZeroInputStream; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class AsyncTransferManagerTests extends OpenSearchTestCase { + + private AsyncTransferManager asyncTransferManager; + private S3AsyncClient s3AsyncClient; + + @Override + @Before + public void setUp() throws Exception { + s3AsyncClient = mock(S3AsyncClient.class); + asyncTransferManager = new AsyncTransferManager( + ByteSizeUnit.MB.toBytes(5), + Executors.newSingleThreadExecutor(), + Executors.newSingleThreadExecutor() + ); + super.setUp(); + } + + public void testOneChunkUpload() { + CompletableFuture putObjectResponseCompletableFuture = new CompletableFuture<>(); + putObjectResponseCompletableFuture.complete(PutObjectResponse.builder().build()); + when(s3AsyncClient.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class))).thenReturn( + putObjectResponseCompletableFuture + ); + + CompletableFuture resultFuture = asyncTransferManager.uploadObject( + s3AsyncClient, + new UploadRequest("bucket", "key", ByteSizeUnit.MB.toBytes(1), WritePriority.HIGH, uploadSuccess -> { + // do nothing + }, false, null), + new StreamContext( + (partIdx, partSize, position) -> new InputStreamContainer(new ZeroInputStream(partSize), partSize, position), + ByteSizeUnit.MB.toBytes(1), + ByteSizeUnit.MB.toBytes(1), + 1 + ) + ); + + try { + resultFuture.get(); + } catch (ExecutionException | InterruptedException e) { + fail("did not expect resultFuture to fail"); + } + + verify(s3AsyncClient, times(1)).putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class)); + } + + public void testOneChunkUploadCorruption() { + CompletableFuture putObjectResponseCompletableFuture = new CompletableFuture<>(); + putObjectResponseCompletableFuture.completeExceptionally( + S3Exception.builder() + .statusCode(HttpStatusCode.BAD_REQUEST) + .awsErrorDetails(AwsErrorDetails.builder().errorCode("BadDigest").build()) + .build() + ); + when(s3AsyncClient.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class))).thenReturn( + putObjectResponseCompletableFuture + ); + + CompletableFuture deleteObjectResponseCompletableFuture = new CompletableFuture<>(); + deleteObjectResponseCompletableFuture.complete(DeleteObjectResponse.builder().build()); + when(s3AsyncClient.deleteObject(any(DeleteObjectRequest.class))).thenReturn(deleteObjectResponseCompletableFuture); + + CompletableFuture resultFuture = asyncTransferManager.uploadObject( + s3AsyncClient, + new UploadRequest("bucket", "key", ByteSizeUnit.MB.toBytes(1), WritePriority.HIGH, uploadSuccess -> { + // do nothing + }, false, null), + new StreamContext( + (partIdx, partSize, position) -> new InputStreamContainer(new ZeroInputStream(partSize), partSize, position), + ByteSizeUnit.MB.toBytes(1), + ByteSizeUnit.MB.toBytes(1), + 1 + ) + ); + + try { + resultFuture.get(); + fail("did not expect resultFuture to pass"); + } catch (ExecutionException | InterruptedException e) { + Throwable throwable = ExceptionsHelper.unwrap(e, CorruptFileException.class); + assertNotNull(throwable); + assertTrue(throwable instanceof CorruptFileException); + } + + verify(s3AsyncClient, times(1)).putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class)); + verify(s3AsyncClient, times(1)).deleteObject(any(DeleteObjectRequest.class)); + } + + public void testMultipartUpload() { + CompletableFuture createMultipartUploadRequestCompletableFuture = new CompletableFuture<>(); + createMultipartUploadRequestCompletableFuture.complete(CreateMultipartUploadResponse.builder().uploadId("uploadId").build()); + when(s3AsyncClient.createMultipartUpload(any(CreateMultipartUploadRequest.class))).thenReturn( + createMultipartUploadRequestCompletableFuture + ); + + CompletableFuture uploadPartResponseCompletableFuture = new CompletableFuture<>(); + uploadPartResponseCompletableFuture.complete(UploadPartResponse.builder().checksumCRC32("pzjqHA==").build()); + when(s3AsyncClient.uploadPart(any(UploadPartRequest.class), any(AsyncRequestBody.class))).thenReturn( + uploadPartResponseCompletableFuture + ); + + CompletableFuture completeMultipartUploadResponseCompletableFuture = new CompletableFuture<>(); + completeMultipartUploadResponseCompletableFuture.complete(CompleteMultipartUploadResponse.builder().build()); + when(s3AsyncClient.completeMultipartUpload(any(CompleteMultipartUploadRequest.class))).thenReturn( + completeMultipartUploadResponseCompletableFuture + ); + + CompletableFuture abortMultipartUploadResponseCompletableFuture = new CompletableFuture<>(); + abortMultipartUploadResponseCompletableFuture.complete(AbortMultipartUploadResponse.builder().build()); + when(s3AsyncClient.abortMultipartUpload(any(AbortMultipartUploadRequest.class))).thenReturn( + abortMultipartUploadResponseCompletableFuture + ); + + CompletableFuture resultFuture = asyncTransferManager.uploadObject( + s3AsyncClient, + new UploadRequest("bucket", "key", ByteSizeUnit.MB.toBytes(5), WritePriority.HIGH, uploadSuccess -> { + // do nothing + }, true, 3376132981L), + new StreamContext( + (partIdx, partSize, position) -> new InputStreamContainer(new ZeroInputStream(partSize), partSize, position), + ByteSizeUnit.MB.toBytes(1), + ByteSizeUnit.MB.toBytes(1), + 5 + ) + ); + + try { + resultFuture.get(); + } catch (ExecutionException | InterruptedException e) { + fail("did not expect resultFuture to fail"); + } + + verify(s3AsyncClient, times(1)).createMultipartUpload(any(CreateMultipartUploadRequest.class)); + verify(s3AsyncClient, times(5)).uploadPart(any(UploadPartRequest.class), any(AsyncRequestBody.class)); + verify(s3AsyncClient, times(1)).completeMultipartUpload(any(CompleteMultipartUploadRequest.class)); + verify(s3AsyncClient, times(0)).abortMultipartUpload(any(AbortMultipartUploadRequest.class)); + } + + public void testMultipartUploadCorruption() { + CompletableFuture createMultipartUploadRequestCompletableFuture = new CompletableFuture<>(); + createMultipartUploadRequestCompletableFuture.complete(CreateMultipartUploadResponse.builder().uploadId("uploadId").build()); + when(s3AsyncClient.createMultipartUpload(any(CreateMultipartUploadRequest.class))).thenReturn( + createMultipartUploadRequestCompletableFuture + ); + + CompletableFuture uploadPartResponseCompletableFuture = new CompletableFuture<>(); + uploadPartResponseCompletableFuture.complete(UploadPartResponse.builder().checksumCRC32("pzjqHA==").build()); + when(s3AsyncClient.uploadPart(any(UploadPartRequest.class), any(AsyncRequestBody.class))).thenReturn( + uploadPartResponseCompletableFuture + ); + + CompletableFuture completeMultipartUploadResponseCompletableFuture = new CompletableFuture<>(); + completeMultipartUploadResponseCompletableFuture.complete(CompleteMultipartUploadResponse.builder().build()); + when(s3AsyncClient.completeMultipartUpload(any(CompleteMultipartUploadRequest.class))).thenReturn( + completeMultipartUploadResponseCompletableFuture + ); + + CompletableFuture abortMultipartUploadResponseCompletableFuture = new CompletableFuture<>(); + abortMultipartUploadResponseCompletableFuture.complete(AbortMultipartUploadResponse.builder().build()); + when(s3AsyncClient.abortMultipartUpload(any(AbortMultipartUploadRequest.class))).thenReturn( + abortMultipartUploadResponseCompletableFuture + ); + + CompletableFuture resultFuture = asyncTransferManager.uploadObject( + s3AsyncClient, + new UploadRequest("bucket", "key", ByteSizeUnit.MB.toBytes(5), WritePriority.HIGH, uploadSuccess -> { + // do nothing + }, true, 0L), + new StreamContext( + (partIdx, partSize, position) -> new InputStreamContainer(new ZeroInputStream(partSize), partSize, position), + ByteSizeUnit.MB.toBytes(1), + ByteSizeUnit.MB.toBytes(1), + 5 + ) + ); + + try { + resultFuture.get(); + fail("did not expect resultFuture to pass"); + } catch (ExecutionException | InterruptedException e) { + Throwable throwable = ExceptionsHelper.unwrap(e, CorruptFileException.class); + assertNotNull(throwable); + assertTrue(throwable instanceof CorruptFileException); + } + + verify(s3AsyncClient, times(1)).createMultipartUpload(any(CreateMultipartUploadRequest.class)); + verify(s3AsyncClient, times(5)).uploadPart(any(UploadPartRequest.class), any(AsyncRequestBody.class)); + verify(s3AsyncClient, times(0)).completeMultipartUpload(any(CompleteMultipartUploadRequest.class)); + verify(s3AsyncClient, times(1)).abortMultipartUpload(any(AbortMultipartUploadRequest.class)); + } +} diff --git a/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/utils/HttpRangeUtilsTests.java b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/utils/HttpRangeUtilsTests.java new file mode 100644 index 0000000000000..9a4267c5266e5 --- /dev/null +++ b/plugins/repository-s3/src/test/java/org/opensearch/repositories/s3/utils/HttpRangeUtilsTests.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.repositories.s3.utils; + +import software.amazon.awssdk.core.exception.SdkException; + +import org.opensearch.test.OpenSearchTestCase; + +public final class HttpRangeUtilsTests extends OpenSearchTestCase { + + public void testFromHttpRangeHeader() { + String headerValue = "bytes 0-10/200"; + Long offset = HttpRangeUtils.getStartOffsetFromRangeHeader(headerValue); + assertEquals(0L, offset.longValue()); + + headerValue = "bytes 0-10/*"; + offset = HttpRangeUtils.getStartOffsetFromRangeHeader(headerValue); + assertEquals(0L, offset.longValue()); + + final String invalidHeaderValue = "bytes */*"; + assertThrows(SdkException.class, () -> HttpRangeUtils.getStartOffsetFromRangeHeader(invalidHeaderValue)); + } +} diff --git a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/10_basic.yml b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/10_basic.yml index cde14321805f5..b35366d904263 100644 --- a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/10_basic.yml +++ b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: repository-s3 } } + - contains: { nodes.$cluster_manager.plugins: { name: repository-s3 } } diff --git a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/20_repository_permanent_credentials.yml b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/20_repository_permanent_credentials.yml index a0c2d2e593a47..5590122e26b86 100644 --- a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/20_repository_permanent_credentials.yml +++ b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/20_repository_permanent_credentials.yml @@ -11,6 +11,7 @@ setup: type: s3 settings: bucket: ${permanent_bucket} + region: ${permanent_region} client: integration_test_permanent base_path: "${permanent_base_path}" canned_acl: private @@ -42,6 +43,7 @@ setup: type: s3 settings: bucket: ${permanent_bucket} + region: ${permanent_region} client: integration_test_permanent base_path: "${permanent_base_path}" endpoint: 127.0.0.1:5 @@ -57,6 +59,7 @@ setup: type: s3 settings: bucket: ${permanent_bucket} + region: ${permanent_region} client: integration_test_permanent base_path: "${permanent_base_path}" endpoint: 127.0.0.1:5 @@ -275,6 +278,7 @@ setup: type: s3 settings: bucket: zHHkfSqlbnBsbpSgvCYtxrEfFLqghXtyPvvvKPNBnRCicNHQLE + region: ${permanent_region} client: integration_test_permanent --- @@ -288,6 +292,7 @@ setup: type: s3 settings: bucket: repository_permanent + region: ${permanent_region} client: unknown --- @@ -302,6 +307,7 @@ setup: settings: readonly: true bucket: zHHkfSqlbnBsbpSgvCYtxrEfFLqghXtyPvvvKPNBnRCicNHQLE + region: ${permanent_region} client: integration_test_permanent --- @@ -316,6 +322,7 @@ setup: settings: readonly: true bucket: repository_permanent + region: ${permanent_region} client: unknown --- diff --git a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/30_repository_temporary_credentials.yml b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/30_repository_temporary_credentials.yml index d26a07eec85dc..741e96110ea96 100644 --- a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/30_repository_temporary_credentials.yml +++ b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/30_repository_temporary_credentials.yml @@ -11,6 +11,7 @@ setup: type: s3 settings: bucket: ${temporary_bucket} + region: ${temporary_region} client: integration_test_temporary base_path: "${temporary_base_path}" canned_acl: private @@ -26,6 +27,7 @@ setup: repository: repository_temporary - match: { repository_temporary.settings.bucket : ${temporary_bucket} } + - match: { repository_temporary.settings.region : ${temporary_region} } - match: { repository_temporary.settings.client : "integration_test_temporary" } - match: { repository_temporary.settings.base_path : "${temporary_base_path}" } - match: { repository_temporary.settings.canned_acl : "private" } @@ -185,6 +187,7 @@ setup: type: s3 settings: bucket: zHHkfSqlbnBsbpSgvCYtxrEfFLqghXtyPvvvKPNBnRCicNHQLE + region: ${temporary_region} client: integration_test_temporary --- @@ -198,6 +201,7 @@ setup: type: s3 settings: bucket: repository_temporary + region: ${temporary_region} client: unknown --- @@ -212,6 +216,7 @@ setup: settings: readonly: true bucket: zHHkfSqlbnBsbpSgvCYtxrEfFLqghXtyPvvvKPNBnRCicNHQLE + region: ${temporary_region} client: integration_test_temporary --- @@ -226,6 +231,7 @@ setup: settings: readonly: true bucket: repository_temporary + region: ${temporary_region} client: unknown --- diff --git a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/40_repository_ec2_credentials.yml b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/40_repository_ec2_credentials.yml index 6d3b174b99863..8d4349845a1f6 100644 --- a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/40_repository_ec2_credentials.yml +++ b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/40_repository_ec2_credentials.yml @@ -11,6 +11,7 @@ setup: type: s3 settings: bucket: ${ec2_bucket} + region: region client: integration_test_ec2 base_path: "${ec2_base_path}" canned_acl: private @@ -30,6 +31,7 @@ setup: - match: { repository_ec2.settings.base_path : "${ec2_base_path}" } - match: { repository_ec2.settings.canned_acl : "private" } - match: { repository_ec2.settings.storage_class : "standard" } + - is_false: repository_ec2.settings.region - is_false: repository_ec2.settings.access_key - is_false: repository_ec2.settings.secret_key - is_false: repository_ec2.settings.session_token diff --git a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/50_repository_ecs_credentials.yml b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/50_repository_ecs_credentials.yml index 79d1e3d9c3bbe..8650af2d29852 100644 --- a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/50_repository_ecs_credentials.yml +++ b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/50_repository_ecs_credentials.yml @@ -11,6 +11,7 @@ setup: type: s3 settings: bucket: ${ecs_bucket} + region: region client: integration_test_ecs base_path: "${ecs_base_path}" canned_acl: private @@ -30,6 +31,7 @@ setup: - match: { repository_ecs.settings.base_path : "${ecs_base_path}" } - match: { repository_ecs.settings.canned_acl : "private" } - match: { repository_ecs.settings.storage_class : "standard" } + - is_false: repository_ecs.settings.region - is_false: repository_ecs.settings.access_key - is_false: repository_ecs.settings.secret_key - is_false: repository_ecs.settings.session_token diff --git a/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/60_repository_eks_credentials.yml b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/60_repository_eks_credentials.yml new file mode 100644 index 0000000000000..e01d3e87eb35f --- /dev/null +++ b/plugins/repository-s3/src/yamlRestTest/resources/rest-api-spec/test/repository_s3/60_repository_eks_credentials.yml @@ -0,0 +1,269 @@ +# Integration tests for repository-s3 + +--- +setup: + + # Register repository with eks credentials + - do: + snapshot.create_repository: + repository: repository_eks + body: + type: s3 + settings: + bucket: ${eks_bucket} + region: region + client: integration_test_eks + base_path: "${eks_base_path}" + canned_acl: private + storage_class: standard + disable_chunked_encoding: ${disable_chunked_encoding} + +--- +"Snapshot and Restore with repository-s3 using eks credentials": + + # Get repository + - do: + snapshot.get_repository: + repository: repository_eks + + - match: { repository_eks.settings.bucket : ${eks_bucket} } + - match: { repository_eks.settings.client : "integration_test_eks" } + - match: { repository_eks.settings.base_path : "${eks_base_path}" } + - match: { repository_eks.settings.canned_acl : "private" } + - match: { repository_eks.settings.storage_class : "standard" } + - is_false: repository_eks.settings.access_key + - is_false: repository_eks.settings.secret_key + - is_false: repository_eks.settings.session_token + - is_false: repository_eks.settings.role_arn + - is_false: repository_eks.settings.role_session_name + - is_false: repository_eks.settings.identity_token_file + + # Index documents + - do: + bulk: + refresh: true + body: + - index: + _index: docs + _id: 1 + - snapshot: one + - index: + _index: docs + _id: 2 + - snapshot: one + - index: + _index: docs + _id: 3 + - snapshot: one + + - do: + count: + index: docs + + - match: {count: 3} + + # Create a first snapshot + - do: + snapshot.create: + repository: repository_eks + snapshot: snapshot-one + wait_for_completion: true + + - match: { snapshot.snapshot: snapshot-one } + - match: { snapshot.state : SUCCESS } + - match: { snapshot.include_global_state: true } + - match: { snapshot.shards.failed : 0 } + + - do: + snapshot.status: + repository: repository_eks + snapshot: snapshot-one + + - is_true: snapshots + - match: { snapshots.0.snapshot: snapshot-one } + - match: { snapshots.0.state : SUCCESS } + + # Index more documents + - do: + bulk: + refresh: true + body: + - index: + _index: docs + _id: 4 + - snapshot: two + - index: + _index: docs + _id: 5 + - snapshot: two + - index: + _index: docs + _id: 6 + - snapshot: two + - index: + _index: docs + _id: 7 + - snapshot: two + + - do: + count: + index: docs + + - match: {count: 7} + + # Create a second snapshot + - do: + snapshot.create: + repository: repository_eks + snapshot: snapshot-two + wait_for_completion: true + + - match: { snapshot.snapshot: snapshot-two } + - match: { snapshot.state : SUCCESS } + - match: { snapshot.shards.failed : 0 } + + - do: + snapshot.get: + repository: repository_eks + snapshot: snapshot-one,snapshot-two + + - is_true: snapshots + - match: { snapshots.0.state : SUCCESS } + - match: { snapshots.1.state : SUCCESS } + + # Delete the index + - do: + indices.delete: + index: docs + + # Restore the second snapshot + - do: + snapshot.restore: + repository: repository_eks + snapshot: snapshot-two + wait_for_completion: true + + - do: + count: + index: docs + + - match: {count: 7} + + # Delete the index again + - do: + indices.delete: + index: docs + + # Restore the first snapshot + - do: + snapshot.restore: + repository: repository_eks + snapshot: snapshot-one + wait_for_completion: true + + - do: + count: + index: docs + + - match: {count: 3} + + # Remove the snapshots + - do: + snapshot.delete: + repository: repository_eks + snapshot: snapshot-two + + - do: + snapshot.delete: + repository: repository_eks + snapshot: snapshot-one + +--- +"Register a repository with a non existing bucket": + + - do: + catch: /repository_verification_exception/ + snapshot.create_repository: + repository: repository_eks + body: + type: s3 + settings: + bucket: zHHkfSqlbnBsbpSgvCYtxrEfFLqghXtyPvvvKPNBnRCicNHQLE + client: integration_test_eks + +--- +"Register a repository with a non existing client": + + - do: + catch: /illegal_argument_exception/ + snapshot.create_repository: + repository: repository_eks + body: + type: s3 + settings: + bucket: repository_eks + client: unknown + +--- +"Register a read-only repository with a non existing bucket": + +- do: + catch: /repository_verification_exception/ + snapshot.create_repository: + repository: repository_eks + body: + type: s3 + settings: + readonly: true + bucket: zHHkfSqlbnBsbpSgvCYtxrEfFLqghXtyPvvvKPNBnRCicNHQLE + client: integration_test_eks + +--- +"Register a read-only repository with a non existing client": + +- do: + catch: /illegal_argument_exception/ + snapshot.create_repository: + repository: repository_eks + body: + type: s3 + settings: + readonly: true + bucket: repository_eks + client: unknown + +--- +"Get a non existing snapshot": + + - do: + catch: /snapshot_missing_exception/ + snapshot.get: + repository: repository_eks + snapshot: missing + +--- +"Delete a non existing snapshot": + + - do: + catch: /snapshot_missing_exception/ + snapshot.delete: + repository: repository_eks + snapshot: missing + +--- +"Restore a non existing snapshot": + + - do: + catch: /snapshot_restore_exception/ + snapshot.restore: + repository: repository_eks + snapshot: missing + wait_for_completion: true + +--- +teardown: + + # Remove our repository + - do: + snapshot.delete_repository: + repository: repository_eks diff --git a/plugins/store-smb/src/test/java/org/opensearch/index/store/SmbMMapDirectoryTests.java b/plugins/store-smb/src/test/java/org/opensearch/index/store/SmbMMapDirectoryTests.java index 547f2f36a7643..2cac58262c75a 100644 --- a/plugins/store-smb/src/test/java/org/opensearch/index/store/SmbMMapDirectoryTests.java +++ b/plugins/store-smb/src/test/java/org/opensearch/index/store/SmbMMapDirectoryTests.java @@ -32,11 +32,12 @@ package org.opensearch.index.store; -import java.io.IOException; -import java.nio.file.Path; import org.apache.lucene.store.Directory; import org.apache.lucene.store.MMapDirectory; +import java.io.IOException; +import java.nio.file.Path; + public class SmbMMapDirectoryTests extends OpenSearchBaseDirectoryTestCase { @Override diff --git a/plugins/store-smb/src/yamlRestTest/resources/rest-api-spec/test/store_smb/10_basic.yml b/plugins/store-smb/src/yamlRestTest/resources/rest-api-spec/test/store_smb/10_basic.yml index 8956b3a8c116f..d358c8980d245 100644 --- a/plugins/store-smb/src/yamlRestTest/resources/rest-api-spec/test/store_smb/10_basic.yml +++ b/plugins/store-smb/src/yamlRestTest/resources/rest-api-spec/test/store_smb/10_basic.yml @@ -7,10 +7,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - contains: { nodes.$master.plugins: { name: store-smb } } + - contains: { nodes.$cluster_manager.plugins: { name: store-smb } } diff --git a/plugins/telemetry-otel/build.gradle b/plugins/telemetry-otel/build.gradle new file mode 100644 index 0000000000000..04fff20947b4f --- /dev/null +++ b/plugins/telemetry-otel/build.gradle @@ -0,0 +1,113 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +import org.apache.tools.ant.taskdefs.condition.Os +import org.opensearch.gradle.Architecture +import org.opensearch.gradle.OS +import org.opensearch.gradle.info.BuildParams + +apply plugin: 'opensearch.internal-cluster-test' + +opensearchplugin { + description 'Opentelemetry based telemetry implementation.' + classname 'org.opensearch.telemetry.OTelTelemetryPlugin' + hasClientJar = true +} + +dependencies { + api project(":libs:opensearch-telemetry") + api "io.opentelemetry:opentelemetry-api:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-context:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-sdk:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-sdk-common:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-sdk-trace:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-sdk-metrics:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-exporter-logging:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-semconv:${versions.opentelemetry}-alpha" + api "io.opentelemetry:opentelemetry-sdk-logs:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-exporter-otlp:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-exporter-common:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-exporter-otlp-common:${versions.opentelemetry}" + runtimeOnly "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}" + runtimeOnly "com.squareup.okhttp3:okhttp:4.11.0" + runtimeOnly "com.squareup.okio:okio-jvm:3.5.0" + runtimeOnly "io.opentelemetry:opentelemetry-exporter-sender-okhttp:${versions.opentelemetry}" + api "io.opentelemetry:opentelemetry-extension-incubator:${versions.opentelemetry}-alpha" + testImplementation "io.opentelemetry:opentelemetry-sdk-testing:${versions.opentelemetry}" +} + + +thirdPartyAudit { + ignoreViolations( + 'io.opentelemetry.internal.shaded.jctools.queues.MpscArrayQueueConsumerIndexField', + 'io.opentelemetry.internal.shaded.jctools.queues.MpscArrayQueueProducerIndexField', + 'io.opentelemetry.internal.shaded.jctools.queues.MpscArrayQueueProducerLimitField', + 'io.opentelemetry.internal.shaded.jctools.util.UnsafeAccess', + 'io.opentelemetry.internal.shaded.jctools.util.UnsafeRefArrayAccess' + ) + + ignoreMissingClasses( + 'android.net.http.X509TrustManagerExtensions', + 'android.net.ssl.SSLSockets', + 'android.os.Build$VERSION', + 'android.security.NetworkSecurityPolicy', + 'android.util.Log', + 'com.google.common.io.ByteStreams', + 'com.google.common.util.concurrent.ListenableFuture', + 'io.grpc.CallOptions', + 'io.grpc.Channel', + 'io.grpc.Drainable', + 'io.grpc.KnownLength', + 'io.grpc.ManagedChannel', + 'io.grpc.MethodDescriptor', + 'io.grpc.MethodDescriptor$Builder', + 'io.grpc.MethodDescriptor$Marshaller', + 'io.grpc.MethodDescriptor$MethodType', + 'io.grpc.stub.AbstractFutureStub', + 'io.grpc.stub.AbstractStub', + 'io.grpc.stub.ClientCalls', + 'org.bouncycastle.jsse.BCSSLParameters', + 'org.bouncycastle.jsse.BCSSLSocket', + 'org.conscrypt.Conscrypt', + 'org.conscrypt.Conscrypt$Version', + 'org.conscrypt.ConscryptHostnameVerifier', + 'org.openjsse.javax.net.ssl.SSLParameters', + 'org.openjsse.javax.net.ssl.SSLSocket', + 'io.opentelemetry.api.events.EventEmitter', + 'io.opentelemetry.api.events.EventEmitterBuilder', + 'io.opentelemetry.api.events.EventEmitterProvider', + 'io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties', + 'io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider', + 'io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider', + 'io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider', + 'kotlin.io.path.PathsKt', + 'io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider' + ) +} + +tasks.named("bundlePlugin").configure { + from('config/telemetry-otel') { + into 'config' + } +} + +tasks.register("writeTestJavaPolicy") { + doLast { + final File tmp = file("${buildDir}/tmp") + if (tmp.exists() == false && tmp.mkdirs() == false) { + throw new GradleException("failed to create temporary directory [${tmp}]") + } + final File javaPolicy = file("${tmp}/java.policy") + javaPolicy.write( + [ + "grant {", + " permission java.io.FilePermission \"config\", \"read\";", + "};" + ].join("\n")) + } +} diff --git a/plugins/telemetry-otel/config/telemetry-otel/log4j2.properties b/plugins/telemetry-otel/config/telemetry-otel/log4j2.properties new file mode 100644 index 0000000000000..8dec1119eec66 --- /dev/null +++ b/plugins/telemetry-otel/config/telemetry-otel/log4j2.properties @@ -0,0 +1,47 @@ +# +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# + + +appender.tracing.type = RollingFile +appender.tracing.name = tracing +appender.tracing.fileName = ${sys:opensearch.logs.base_path}${sys:file.separator}${sys:opensearch.logs.cluster_name}_otel_traces.log +appender.tracing.filePermissions = rw-r----- +appender.tracing.layout.type = PatternLayout +appender.tracing.layout.pattern = %m%n +appender.tracing.filePattern = ${sys:opensearch.logs.base_path}${sys:file.separator}${sys:opensearch.logs.cluster_name}_otel_traces-%i.log.gz +appender.tracing.policies.type = Policies +appender.tracing.policies.size.type = SizeBasedTriggeringPolicy +appender.tracing.policies.size.size = 1GB +appender.tracing.strategy.type = DefaultRolloverStrategy +appender.tracing.strategy.max = 4 + + +logger.exporter.name = io.opentelemetry.exporter.logging.LoggingSpanExporter +logger.exporter.level = INFO +logger.exporter.appenderRef.tracing.ref = tracing +logger.exporter.additivity = false + + +appender.metrics.type = RollingFile +appender.metrics.name = metrics +appender.metrics.fileName = ${sys:opensearch.logs.base_path}${sys:file.separator}${sys:opensearch.logs.cluster_name}_otel_metrics.log +appender.metrics.filePermissions = rw-r----- +appender.metrics.layout.type = PatternLayout +appender.metrics.layout.pattern = %m%n +appender.metrics.filePattern = ${sys:opensearch.logs.base_path}${sys:file.separator}${sys:opensearch.logs.cluster_name}_otel_metrics-%i.log.gz +appender.metrics.policies.type = Policies +appender.metrics.policies.size.type = SizeBasedTriggeringPolicy +appender.metrics.policies.size.size = 1GB +appender.metrics.strategy.type = DefaultRolloverStrategy +appender.metrics.strategy.max = 4 + + +logger.metrics_exporter.name = io.opentelemetry.exporter.logging.LoggingMetricExporter +logger.metrics_exporter.level = INFO +logger.metrics_exporter.appenderRef.tracing.ref = metrics +logger.metrics_exporter.additivity = false diff --git a/plugins/telemetry-otel/licenses/kotlin-stdlib-1.7.10.jar.sha1 b/plugins/telemetry-otel/licenses/kotlin-stdlib-1.7.10.jar.sha1 new file mode 100644 index 0000000000000..4d119fbf4df70 --- /dev/null +++ b/plugins/telemetry-otel/licenses/kotlin-stdlib-1.7.10.jar.sha1 @@ -0,0 +1 @@ +d2abf9e77736acc4450dc4a3f707fa2c10f5099d \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/kotlin-stdlib-LICENSE.txt b/plugins/telemetry-otel/licenses/kotlin-stdlib-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/kotlin-stdlib-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/kotlin-stdlib-NOTICE.txt b/plugins/telemetry-otel/licenses/kotlin-stdlib-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/okhttp-4.11.0.jar.sha1 b/plugins/telemetry-otel/licenses/okhttp-4.11.0.jar.sha1 new file mode 100644 index 0000000000000..1fc0db6615cb5 --- /dev/null +++ b/plugins/telemetry-otel/licenses/okhttp-4.11.0.jar.sha1 @@ -0,0 +1 @@ +436932d695b2c43f2c86b8111c596179cd133d56 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/okhttp-LICENSE.txt b/plugins/telemetry-otel/licenses/okhttp-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/okhttp-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/okhttp-NOTICE.txt b/plugins/telemetry-otel/licenses/okhttp-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/okio-jvm-3.5.0.jar.sha1 b/plugins/telemetry-otel/licenses/okio-jvm-3.5.0.jar.sha1 new file mode 100644 index 0000000000000..7b19d32d872fa --- /dev/null +++ b/plugins/telemetry-otel/licenses/okio-jvm-3.5.0.jar.sha1 @@ -0,0 +1 @@ +d6a0bc7343210eff7dd5cfdd6eb9b5f0036638ce \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/okio-jvm-LICENSE.txt b/plugins/telemetry-otel/licenses/okio-jvm-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/okio-jvm-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/okio-jvm-NOTICE.txt b/plugins/telemetry-otel/licenses/okio-jvm-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-api-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-api-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..b0ce00e191830 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-api-1.30.1.jar.sha1 @@ -0,0 +1 @@ +a32dfbd7f01de6711fd0e970f8d4b4c0405056d6 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-api-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-api-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-api-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-api-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-api-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-context-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-context-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..84cb60a2f7acb --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-context-1.30.1.jar.sha1 @@ -0,0 +1 @@ +58f665ff01ce6b964cdf0b8cb5cd1c196dfe94ce \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-context-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-context-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-context-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-context-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-context-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-common-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-exporter-common-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..eccb15f7b7c8e --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-common-1.30.1.jar.sha1 @@ -0,0 +1 @@ +f299d336dba1039478497f37b273dfa764c6faef \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-common-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-common-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-common-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-common-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-common-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-logging-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-exporter-logging-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..40537a399ab14 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-logging-1.30.1.jar.sha1 @@ -0,0 +1 @@ +58f1a09e89955e6145babf8bcdf80c95174eb817 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-logging-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-logging-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-logging-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-logging-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-logging-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..e88b7514ee54d --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-1.30.1.jar.sha1 @@ -0,0 +1 @@ +15692246539571c41180aff2b55abe527b939a7b \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-common-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-common-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..86937743208c6 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-common-1.30.1.jar.sha1 @@ -0,0 +1 @@ +947cf43a6411c4a323e14594431040a476ad43e8 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-common-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-common-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-common-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-common-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-otlp-common-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-sender-okhttp-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-exporter-sender-okhttp-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..068926277253c --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-sender-okhttp-1.30.1.jar.sha1 @@ -0,0 +1 @@ +9f3a14515500e4df260ce7b10a668237a95ac791 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-sender-okhttp-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-sender-okhttp-LICENSE.txt new file mode 100644 index 0000000000000..6b0b1270ff0ca --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-exporter-sender-okhttp-LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/plugins/telemetry-otel/licenses/opentelemetry-exporter-sender-okhttp-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-exporter-sender-okhttp-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-extension-incubator-1.30.1-alpha.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-extension-incubator-1.30.1-alpha.jar.sha1 new file mode 100644 index 0000000000000..bde43937e82e4 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-extension-incubator-1.30.1-alpha.jar.sha1 @@ -0,0 +1 @@ +bfcea9bd71f97dd4e8a4f92c15ba5659fb07ff05 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-extension-incubator-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-extension-incubator-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-extension-incubator-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-extension-incubator-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-extension-incubator-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-sdk-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..d425ed61cc4cd --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-1.30.1.jar.sha1 @@ -0,0 +1 @@ +4d15a9ea26e8e6ea93287a9f4ee02d91e5a74392 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-common-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-sdk-common-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..6b32d98b0f7c7 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-common-1.30.1.jar.sha1 @@ -0,0 +1 @@ +8e437ba87004bb63069d04fb06beae65b98dd13a \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-common-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-common-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-common-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-common-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-common-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-logs-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-sdk-logs-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..13ef6de11e82d --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-logs-1.30.1.jar.sha1 @@ -0,0 +1 @@ +5985d0950746ad12b49cc42c063f26ddfbcaaacb \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-logs-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-logs-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-logs-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-logs-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-logs-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-metrics-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-sdk-metrics-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..fc5aad9c9011e --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-metrics-1.30.1.jar.sha1 @@ -0,0 +1 @@ +b12825541c5dae52a0fb35045c1b36df3ca8f632 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-metrics-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-metrics-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-metrics-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-metrics-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-metrics-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-trace-1.30.1.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-sdk-trace-1.30.1.jar.sha1 new file mode 100644 index 0000000000000..ac522b765da05 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-trace-1.30.1.jar.sha1 @@ -0,0 +1 @@ +4c5531fbc44178a7bcfeb7021ae80e70a7c43458 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-trace-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-trace-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-sdk-trace-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-sdk-trace-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-sdk-trace-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/licenses/opentelemetry-semconv-1.30.1-alpha.jar.sha1 b/plugins/telemetry-otel/licenses/opentelemetry-semconv-1.30.1-alpha.jar.sha1 new file mode 100644 index 0000000000000..089a2484dd1d5 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-semconv-1.30.1-alpha.jar.sha1 @@ -0,0 +1 @@ +8e8f7a97a4896a81846553275b9d61885be7ef50 \ No newline at end of file diff --git a/plugins/telemetry-otel/licenses/opentelemetry-semconv-LICENSE.txt b/plugins/telemetry-otel/licenses/opentelemetry-semconv-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/plugins/telemetry-otel/licenses/opentelemetry-semconv-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/plugins/telemetry-otel/licenses/opentelemetry-semconv-NOTICE.txt b/plugins/telemetry-otel/licenses/opentelemetry-semconv-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/InMemorySingletonSpanExporter.java b/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/InMemorySingletonSpanExporter.java new file mode 100644 index 0000000000000..6dd451ea37465 --- /dev/null +++ b/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/InMemorySingletonSpanExporter.java @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.test.telemetry.tracing.MockSpanData; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +public class InMemorySingletonSpanExporter implements SpanExporter { + + public static final InMemorySingletonSpanExporter INSTANCE = new InMemorySingletonSpanExporter(InMemorySpanExporter.create()); + + private static InMemorySpanExporter delegate; + + public static InMemorySingletonSpanExporter create() { + return INSTANCE; + } + + private InMemorySingletonSpanExporter(InMemorySpanExporter delegate) { + InMemorySingletonSpanExporter.delegate = delegate; + } + + @Override + public CompletableResultCode export(Collection spans) { + return delegate.export(spans); + } + + @Override + public CompletableResultCode flush() { + return delegate.flush(); + } + + @Override + public CompletableResultCode shutdown() { + return delegate.shutdown(); + } + + public List getFinishedSpanItems() { + return convertSpanDataListToMockSpanDataList(delegate.getFinishedSpanItems()); + } + + private List convertSpanDataListToMockSpanDataList(List spanDataList) { + List mockSpanDataList = spanDataList.stream() + .map( + spanData -> new MockSpanData( + spanData.getSpanId(), + spanData.getParentSpanId(), + spanData.getTraceId(), + spanData.getStartEpochNanos(), + spanData.getEndEpochNanos(), + spanData.hasEnded(), + spanData.getName(), + getAttributes(spanData) + ) + ) + .collect(Collectors.toList()); + return mockSpanDataList; + } + + private Map getAttributes(SpanData spanData) { + if (spanData.getAttributes() != null) { + return spanData.getAttributes() + .asMap() + .entrySet() + .stream() + .collect(Collectors.toMap(e -> e.getKey().getKey(), e -> e.getValue())); + } else { + return Collections.emptyMap(); + } + } + + /** + * Clears the state. + */ + public void reset() { + delegate.reset(); + } +} diff --git a/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/IntegrationTestOTelTelemetryPlugin.java b/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/IntegrationTestOTelTelemetryPlugin.java new file mode 100644 index 0000000000000..ed4d13f3abb7d --- /dev/null +++ b/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/IntegrationTestOTelTelemetryPlugin.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.OTelTelemetryPlugin; +import org.opensearch.telemetry.Telemetry; +import org.opensearch.telemetry.TelemetrySettings; + +import java.util.Optional; + +import io.opentelemetry.api.GlobalOpenTelemetry; + +/** + * Telemetry plugin used for Integration tests. +*/ +public class IntegrationTestOTelTelemetryPlugin extends OTelTelemetryPlugin { + /** + * Creates IntegrationTestOTelTelemetryPlugin + * @param settings cluster settings + */ + public IntegrationTestOTelTelemetryPlugin(Settings settings) { + super(settings); + } + + /** + * This method overrides getTelemetry() method in OTel plugin class, so we create only one instance of global OpenTelemetry + * resetForTest() will set OpenTelemetry to null again. + * @param telemetrySettings telemetry settings + */ + public Optional getTelemetry(TelemetrySettings telemetrySettings) { + GlobalOpenTelemetry.resetForTest(); + return super.getTelemetry(telemetrySettings); + } +} diff --git a/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/TelemetryTracerDisabledSanityIT.java b/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/TelemetryTracerDisabledSanityIT.java new file mode 100644 index 0000000000000..949a58f6cab41 --- /dev/null +++ b/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/TelemetryTracerDisabledSanityIT.java @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.client.Client; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.plugins.Plugin; +import org.opensearch.telemetry.OTelTelemetrySettings; +import org.opensearch.telemetry.TelemetrySettings; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; + +import static org.opensearch.index.query.QueryBuilders.queryStringQuery; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, supportsDedicatedMasters = false, minNumDataNodes = 2) +public class TelemetryTracerDisabledSanityIT extends OpenSearchIntegTestCase { + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put( + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), + "org.opensearch.telemetry.tracing.InMemorySingletonSpanExporter" + ) + .put(OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING.getKey(), TimeValue.timeValueSeconds(1)) + .build(); + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(IntegrationTestOTelTelemetryPlugin.class); + } + + @Override + protected boolean addMockTelemetryPlugin() { + return false; + } + + public void testSanityCheckWhenTracingDisabled() throws Exception { + Client client = client(); + // DISABLE TRACING + client.admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(TelemetrySettings.TRACER_ENABLED_SETTING.getKey(), false)) + .get(); + + // Create Index and ingest data + String indexName = "test-index-11"; + Settings basicSettings = Settings.builder().put("number_of_shards", 3).put("number_of_replicas", 1).build(); + createIndex(indexName, basicSettings); + indexRandom(true, client.prepareIndex(indexName).setId("1").setSource("field1", "t`")); + + ensureGreen(); + refresh(); + InMemorySingletonSpanExporter exporter = InMemorySingletonSpanExporter.INSTANCE; + exporter.reset(); + + // Make the search call; + client.prepareSearch().setQuery(queryStringQuery("fox")).get(); + + // Sleep for about 3s to wait for traces are published (the delay is 1s) + Thread.sleep(3000); + + assertTrue(exporter.getFinishedSpanItems().isEmpty()); + } + +} diff --git a/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/TelemetryTracerEnabledSanityIT.java b/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/TelemetryTracerEnabledSanityIT.java new file mode 100644 index 0000000000000..8a49a0abf5512 --- /dev/null +++ b/plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/tracing/TelemetryTracerEnabledSanityIT.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.client.Client; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.plugins.Plugin; +import org.opensearch.telemetry.OTelTelemetrySettings; +import org.opensearch.telemetry.TelemetrySettings; +import org.opensearch.telemetry.tracing.attributes.Attributes; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.telemetry.tracing.TelemetryValidators; +import org.opensearch.test.telemetry.tracing.validators.AllSpansAreEndedProperly; +import org.opensearch.test.telemetry.tracing.validators.AllSpansHaveUniqueId; +import org.opensearch.test.telemetry.tracing.validators.NumberOfTraceIDsEqualToRequests; +import org.opensearch.test.telemetry.tracing.validators.TotalRootSpansEqualToRequests; + +import java.util.Arrays; +import java.util.Collection; + +import static org.opensearch.index.query.QueryBuilders.queryStringQuery; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, minNumDataNodes = 2) +public class TelemetryTracerEnabledSanityIT extends OpenSearchIntegTestCase { + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put( + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), + "org.opensearch.telemetry.tracing.InMemorySingletonSpanExporter" + ) + .put(OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING.getKey(), TimeValue.timeValueSeconds(1)) + .put(TelemetrySettings.TRACER_SAMPLER_PROBABILITY.getKey(), 1.0d) + .build(); + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(IntegrationTestOTelTelemetryPlugin.class); + } + + @Override + protected boolean addMockTelemetryPlugin() { + return false; + } + + public void testSanityChecksWhenTracingEnabled() throws Exception { + Client client = internalCluster().clusterManagerClient(); + // ENABLE TRACING + updateTelemetrySetting(client, true); + + // Create Index and ingest data + String indexName = "test-index-11"; + Settings basicSettings = Settings.builder().put("number_of_shards", 3).put("number_of_replicas", 0).build(); + createIndex(indexName, basicSettings); + indexRandom(true, client.prepareIndex(indexName).setId("1").setSource("field1", "the fox jumps in the well")); + indexRandom(true, client.prepareIndex(indexName).setId("1").setSource("field2", "another fox did the same.")); + + ensureGreen(); + refresh(); + + // Make the search calls; adding the searchType and PreFilterShardSize to make the query path predictable across all the runs. + client.prepareSearch().setSearchType("query_then_fetch").setPreFilterShardSize(3).setQuery(queryStringQuery("fox")).get(); + client.prepareSearch().setSearchType("query_then_fetch").setPreFilterShardSize(3).setQuery(queryStringQuery("jumps")).get(); + + ensureGreen(); + refresh(); + + // Sleep for about 3s to wait for traces are published, delay is (the delay is 1s). + Thread.sleep(3000); + + TelemetryValidators validators = new TelemetryValidators( + Arrays.asList( + new AllSpansAreEndedProperly(), + new AllSpansHaveUniqueId(), + new NumberOfTraceIDsEqualToRequests(Attributes.create().addAttribute("action", "indices:data/read/search[phase/query]")), + new TotalRootSpansEqualToRequests() + ) + ); + + InMemorySingletonSpanExporter exporter = InMemorySingletonSpanExporter.INSTANCE; + if (!exporter.getFinishedSpanItems().isEmpty()) { + validators.validate(exporter.getFinishedSpanItems(), 6); + } + } + + private static void updateTelemetrySetting(Client client, boolean value) { + client.admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(TelemetrySettings.TRACER_ENABLED_SETTING.getKey(), value)) + .get(); + } + +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelAttributesConverter.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelAttributesConverter.java new file mode 100644 index 0000000000000..98d265e92ba3c --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelAttributesConverter.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry; + +import org.opensearch.telemetry.metrics.tags.Tags; + +import java.util.Locale; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.AttributesBuilder; + +/** + * Converts {@link org.opensearch.telemetry.tracing.attributes.Attributes} to OTel {@link Attributes} + */ +public final class OTelAttributesConverter { + + /** + * Constructor. + */ + private OTelAttributesConverter() {} + + /** + * Attribute converter. + * @param attributes attributes + * @return otel attributes. + */ + public static Attributes convert(org.opensearch.telemetry.tracing.attributes.Attributes attributes) { + AttributesBuilder attributesBuilder = Attributes.builder(); + if (attributes != null) { + attributes.getAttributesMap().forEach((x, y) -> addSpanAttribute(x, y, attributesBuilder)); + } + return attributesBuilder.build(); + } + + private static void addSpanAttribute(String key, Object value, AttributesBuilder attributesBuilder) { + if (value instanceof Boolean) { + attributesBuilder.put(key, (Boolean) value); + } else if (value instanceof Long) { + attributesBuilder.put(key, (Long) value); + } else if (value instanceof Double) { + attributesBuilder.put(key, (Double) value); + } else if (value instanceof String) { + attributesBuilder.put(key, (String) value); + } else { + throw new IllegalArgumentException(String.format(Locale.ROOT, "Span attribute value %s type not supported", value)); + } + } + + /** + * Attribute converter. + * @param tags attributes + * @return otel attributes. + */ + public static Attributes convert(Tags tags) { + AttributesBuilder attributesBuilder = Attributes.builder(); + if (tags != null) { + tags.getTagsMap().forEach((x, y) -> addSpanAttribute(x, y, attributesBuilder)); + } + return attributesBuilder.build(); + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetryPlugin.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetryPlugin.java new file mode 100644 index 0000000000000..b57876c9310f3 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetryPlugin.java @@ -0,0 +1,77 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry; + +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; +import org.opensearch.plugins.Plugin; +import org.opensearch.plugins.TelemetryPlugin; +import org.opensearch.telemetry.metrics.OTelMetricsTelemetry; +import org.opensearch.telemetry.tracing.OTelResourceProvider; +import org.opensearch.telemetry.tracing.OTelTelemetry; +import org.opensearch.telemetry.tracing.OTelTracingTelemetry; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import io.opentelemetry.sdk.OpenTelemetrySdk; + +/** + * Telemetry plugin based on Otel + */ +public class OTelTelemetryPlugin extends Plugin implements TelemetryPlugin { + + /** + * Instrumentation scope name. + */ + public static final String INSTRUMENTATION_SCOPE_NAME = "org.opensearch.telemetry"; + + static final String OTEL_TRACER_NAME = "otel"; + + private final Settings settings; + + /** + * Creates Otel plugin + * @param settings cluster settings + */ + public OTelTelemetryPlugin(Settings settings) { + this.settings = settings; + } + + @Override + public List> getSettings() { + return Arrays.asList( + OTelTelemetrySettings.TRACER_EXPORTER_BATCH_SIZE_SETTING, + OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING, + OTelTelemetrySettings.TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING, + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING, + OTelTelemetrySettings.OTEL_METRICS_EXPORTER_CLASS_SETTING + ); + } + + @Override + public Optional getTelemetry(TelemetrySettings telemetrySettings) { + return Optional.of(telemetry(telemetrySettings)); + } + + @Override + public String getName() { + return OTEL_TRACER_NAME; + } + + private Telemetry telemetry(TelemetrySettings telemetrySettings) { + final OpenTelemetrySdk openTelemetry = OTelResourceProvider.get(telemetrySettings, settings); + return new OTelTelemetry( + new OTelTracingTelemetry<>(openTelemetry, openTelemetry.getSdkTracerProvider()), + new OTelMetricsTelemetry<>(openTelemetry.getSdkMeterProvider()) + ); + } + +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetrySettings.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetrySettings.java new file mode 100644 index 0000000000000..8e23f724b4570 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/OTelTelemetrySettings.java @@ -0,0 +1,113 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry; + +import org.opensearch.SpecialPermission; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.telemetry.metrics.exporter.OTelMetricsExporterFactory; +import org.opensearch.telemetry.tracing.exporter.OTelSpanExporterFactory; + +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +/** + * OTel specific telemetry settings. + */ +public final class OTelTelemetrySettings { + + /** + * Base Constructor. + */ + private OTelTelemetrySettings() {} + + /** + * span exporter batch size + */ + public static final Setting TRACER_EXPORTER_BATCH_SIZE_SETTING = Setting.intSetting( + "telemetry.otel.tracer.exporter.batch_size", + 512, + 1, + Setting.Property.NodeScope, + Setting.Property.Final + ); + /** + * span exporter max queue size + */ + public static final Setting TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING = Setting.intSetting( + "telemetry.otel.tracer.exporter.max_queue_size", + 2048, + 1, + Setting.Property.NodeScope, + Setting.Property.Final + ); + /** + * span exporter delay in seconds + */ + public static final Setting TRACER_EXPORTER_DELAY_SETTING = Setting.timeSetting( + "telemetry.otel.tracer.exporter.delay", + TimeValue.timeValueSeconds(2), + Setting.Property.NodeScope, + Setting.Property.Final + ); + + /** + * Span Exporter type setting. + */ + @SuppressWarnings("unchecked") + public static final Setting> OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING = new Setting<>( + "telemetry.otel.tracer.span.exporter.class", + LoggingSpanExporter.class.getName(), + className -> { + // Check we ourselves are not being called by unprivileged code. + SpecialPermission.check(); + + try { + return AccessController.doPrivileged((PrivilegedExceptionAction>) () -> { + final ClassLoader loader = OTelSpanExporterFactory.class.getClassLoader(); + return (Class) loader.loadClass(className); + }); + } catch (PrivilegedActionException ex) { + throw new IllegalStateException("Unable to load span exporter class:" + className, ex.getCause()); + } + }, + Setting.Property.NodeScope, + Setting.Property.Final + ); + + /** + * Metrics Exporter type setting. + */ + @SuppressWarnings("unchecked") + public static final Setting> OTEL_METRICS_EXPORTER_CLASS_SETTING = new Setting<>( + "telemetry.otel.metrics.exporter.class", + LoggingMetricExporter.class.getName(), + className -> { + // Check we ourselves are not being called by unprivileged code. + SpecialPermission.check(); + + try { + return AccessController.doPrivileged((PrivilegedExceptionAction>) () -> { + final ClassLoader loader = OTelMetricsExporterFactory.class.getClassLoader(); + return (Class) loader.loadClass(className); + }); + } catch (PrivilegedActionException ex) { + throw new IllegalStateException("Unable to load span exporter class:" + className, ex.getCause()); + } + }, + Setting.Property.NodeScope, + Setting.Property.Final + ); +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelCounter.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelCounter.java new file mode 100644 index 0000000000000..b72f63e027243 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelCounter.java @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import org.opensearch.telemetry.OTelAttributesConverter; +import org.opensearch.telemetry.metrics.tags.Tags; + +import io.opentelemetry.api.metrics.DoubleCounter; + +/** + * OTel Counter + */ +class OTelCounter implements Counter { + + private final DoubleCounter otelDoubleCounter; + + /** + * Constructor + * @param otelDoubleCounter delegate counter. + */ + public OTelCounter(DoubleCounter otelDoubleCounter) { + this.otelDoubleCounter = otelDoubleCounter; + } + + @Override + public void add(double value) { + otelDoubleCounter.add(value); + } + + @Override + public void add(double value, Tags tags) { + otelDoubleCounter.add(value, OTelAttributesConverter.convert(tags)); + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelMetricsTelemetry.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelMetricsTelemetry.java new file mode 100644 index 0000000000000..8598e5976d20d --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelMetricsTelemetry.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import org.opensearch.telemetry.OTelTelemetryPlugin; + +import java.io.Closeable; +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import io.opentelemetry.api.metrics.DoubleCounter; +import io.opentelemetry.api.metrics.DoubleUpDownCounter; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterProvider; + +/** + * OTel implementation for {@link MetricsTelemetry} + */ +public class OTelMetricsTelemetry implements MetricsTelemetry { + private final Meter otelMeter; + private final T meterProvider; + + /** + * Creates OTel based {@link MetricsTelemetry}. + * @param meterProvider {@link MeterProvider} instance + */ + public OTelMetricsTelemetry(T meterProvider) { + this.meterProvider = meterProvider; + this.otelMeter = meterProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME); + } + + @Override + public Counter createCounter(String name, String description, String unit) { + DoubleCounter doubleCounter = AccessController.doPrivileged( + (PrivilegedAction) () -> otelMeter.counterBuilder(name) + .setUnit(unit) + .setDescription(description) + .ofDoubles() + .build() + ); + return new OTelCounter(doubleCounter); + } + + @Override + public Counter createUpDownCounter(String name, String description, String unit) { + DoubleUpDownCounter doubleUpDownCounter = AccessController.doPrivileged( + (PrivilegedAction) () -> otelMeter.upDownCounterBuilder(name) + .setUnit(unit) + .setDescription(description) + .ofDoubles() + .build() + ); + return new OTelUpDownCounter(doubleUpDownCounter); + } + + @Override + public void close() throws IOException { + meterProvider.close(); + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelUpDownCounter.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelUpDownCounter.java new file mode 100644 index 0000000000000..2f40881996f7e --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelUpDownCounter.java @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import org.opensearch.telemetry.OTelAttributesConverter; +import org.opensearch.telemetry.metrics.tags.Tags; + +import io.opentelemetry.api.metrics.DoubleUpDownCounter; + +/** + * OTel Counter + */ +public class OTelUpDownCounter implements Counter { + + private final DoubleUpDownCounter doubleUpDownCounter; + + /** + * Constructor + * @param doubleUpDownCounter delegate counter. + */ + public OTelUpDownCounter(DoubleUpDownCounter doubleUpDownCounter) { + this.doubleUpDownCounter = doubleUpDownCounter; + } + + @Override + public void add(double value) { + doubleUpDownCounter.add(value); + } + + @Override + public void add(double value, Tags tags) { + doubleUpDownCounter.add(value, OTelAttributesConverter.convert(tags)); + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/exporter/OTelMetricsExporterFactory.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/exporter/OTelMetricsExporterFactory.java new file mode 100644 index 0000000000000..ef5a31e4003ca --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/exporter/OTelMetricsExporterFactory.java @@ -0,0 +1,90 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics.exporter; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.SpecialPermission; +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.OTelTelemetrySettings; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +import io.opentelemetry.sdk.metrics.export.MetricExporter; + +/** + * Factory class to create the {@link MetricExporter} instance. + */ +public class OTelMetricsExporterFactory { + + private static final Logger logger = LogManager.getLogger(OTelMetricsExporterFactory.class); + + /** + * Base constructor. + */ + private OTelMetricsExporterFactory() { + + } + + /** + * Creates the {@link MetricExporter} instances based on the OTEL_METRIC_EXPORTER_CLASS_SETTING value. + * As of now, it expects the MetricExporter implementations to have a create factory method to instantiate the + * MetricExporter. + * @param settings settings. + * @return MetricExporter instance. + */ + public static MetricExporter create(Settings settings) { + Class MetricExporterProviderClass = OTelTelemetrySettings.OTEL_METRICS_EXPORTER_CLASS_SETTING.get(settings); + MetricExporter metricExporter = instantiateExporter(MetricExporterProviderClass); + logger.info("Successfully instantiated the Metrics MetricExporter class {}", MetricExporterProviderClass); + return metricExporter; + } + + private static MetricExporter instantiateExporter(Class exporterProviderClass) { + try { + // Check we ourselves are not being called by unprivileged code. + SpecialPermission.check(); + return AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + String methodName = "create"; + String getDefaultMethod = "getDefault"; + for (Method m : exporterProviderClass.getMethods()) { + if (m.getName().equals(getDefaultMethod)) { + methodName = getDefaultMethod; + break; + } + } + try { + return (MetricExporter) MethodHandles.publicLookup() + .findStatic(exporterProviderClass, methodName, MethodType.methodType(exporterProviderClass)) + .asType(MethodType.methodType(MetricExporter.class)) + .invokeExact(); + } catch (Throwable e) { + if (e.getCause() instanceof NoSuchMethodException) { + throw new IllegalStateException("No create factory method exist in [" + exporterProviderClass.getName() + "]"); + } else { + throw new IllegalStateException( + "MetricExporter instantiation failed for class [" + exporterProviderClass.getName() + "]", + e.getCause() + ); + } + } + }); + } catch (PrivilegedActionException ex) { + throw new IllegalStateException( + "MetricExporter instantiation failed for class [" + exporterProviderClass.getName() + "]", + ex.getCause() + ); + } + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/exporter/package-info.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/exporter/package-info.java new file mode 100644 index 0000000000000..b48ec3e2336c4 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/exporter/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * This package contains classes needed for tracing requests. + */ +package org.opensearch.telemetry.metrics.exporter; diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/package-info.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/package-info.java new file mode 100644 index 0000000000000..803c159eb201a --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * This package contains classes needed for tracing requests. + */ +package org.opensearch.telemetry.metrics; diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/package-info.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/package-info.java new file mode 100644 index 0000000000000..4545f0ef5990e --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * This package contains classes needed for telemetry. + */ +package org.opensearch.telemetry; diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelPropagatedSpan.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelPropagatedSpan.java new file mode 100644 index 0000000000000..5aa1069e60367 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelPropagatedSpan.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +/** + * Propagated span through context propagation + */ +public class OTelPropagatedSpan extends OTelSpan { + + /** + * Creates OTelPropagatedSpan + * @param span otel propagated span + */ + public OTelPropagatedSpan(io.opentelemetry.api.trace.Span span) { + super(null, span, null); + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelResourceProvider.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelResourceProvider.java new file mode 100644 index 0000000000000..a6a1f12aab8a9 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelResourceProvider.java @@ -0,0 +1,118 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.TelemetrySettings; +import org.opensearch.telemetry.metrics.exporter.OTelMetricsExporterFactory; +import org.opensearch.telemetry.tracing.exporter.OTelSpanExporterFactory; +import org.opensearch.telemetry.tracing.sampler.ProbabilisticSampler; +import org.opensearch.telemetry.tracing.sampler.RequestSampler; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.concurrent.TimeUnit; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; + +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_BATCH_SIZE_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING; + +/** + * This class encapsulates all OpenTelemetry related resources + */ +public final class OTelResourceProvider { + private OTelResourceProvider() {} + + /** + * Creates OpenTelemetry instance with default configuration + * @param telemetrySettings telemetry settings + * @param settings cluster settings + * @return OpenTelemetrySdk instance + */ + public static OpenTelemetrySdk get(TelemetrySettings telemetrySettings, Settings settings) { + return AccessController.doPrivileged( + (PrivilegedAction) () -> get( + settings, + OTelSpanExporterFactory.create(settings), + ContextPropagators.create(W3CTraceContextPropagator.getInstance()), + Sampler.parentBased(new RequestSampler(new ProbabilisticSampler(telemetrySettings))) + ) + ); + } + + /** + * Creates OpenTelemetry instance with provided configuration + * @param settings cluster settings + * @param spanExporter span exporter instance + * @param contextPropagators context propagator instance + * @param sampler sampler instance + * @return OpenTelemetrySdk instance + */ + public static OpenTelemetrySdk get( + Settings settings, + SpanExporter spanExporter, + ContextPropagators contextPropagators, + Sampler sampler + ) { + Resource resource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "OpenSearch")); + SdkTracerProvider sdkTracerProvider = createSdkTracerProvider(settings, spanExporter, sampler, resource); + SdkMeterProvider sdkMeterProvider = createSdkMetricProvider(settings, resource); + return OpenTelemetrySdk.builder() + .setTracerProvider(sdkTracerProvider) + .setMeterProvider(sdkMeterProvider) + .setPropagators(contextPropagators) + .buildAndRegisterGlobal(); + } + + private static SdkMeterProvider createSdkMetricProvider(Settings settings, Resource resource) { + return SdkMeterProvider.builder() + .setResource(resource) + .registerMetricReader( + PeriodicMetricReader.builder(OTelMetricsExporterFactory.create(settings)) + .setInterval(TelemetrySettings.METRICS_PUBLISH_INTERVAL_SETTING.get(settings).getSeconds(), TimeUnit.SECONDS) + .build() + ) + .build(); + } + + private static SdkTracerProvider createSdkTracerProvider( + Settings settings, + SpanExporter spanExporter, + Sampler sampler, + Resource resource + ) { + return SdkTracerProvider.builder() + .addSpanProcessor(spanProcessor(settings, spanExporter)) + .setResource(resource) + .setSampler(sampler) + .build(); + } + + private static BatchSpanProcessor spanProcessor(Settings settings, SpanExporter spanExporter) { + return BatchSpanProcessor.builder(spanExporter) + .setScheduleDelay(TRACER_EXPORTER_DELAY_SETTING.get(settings).getSeconds(), TimeUnit.SECONDS) + .setMaxExportBatchSize(TRACER_EXPORTER_BATCH_SIZE_SETTING.get(settings)) + .setMaxQueueSize(TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING.get(settings)) + .build(); + } + +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpan.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpan.java new file mode 100644 index 0000000000000..fc917968579e1 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpan.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; + +/** + * Default implementation of {@link Span} using Otel span. It keeps a reference of OpenTelemetry Span and handles span + * lifecycle management by delegating calls to it. + */ +class OTelSpan extends AbstractSpan { + + private final Span delegateSpan; + + /** + * Constructor + * @param spanName span name + * @param span the delegate span + * @param parentSpan the parent span + */ + public OTelSpan(String spanName, Span span, org.opensearch.telemetry.tracing.Span parentSpan) { + super(spanName, parentSpan); + this.delegateSpan = span; + } + + @Override + public void endSpan() { + delegateSpan.end(); + } + + @Override + public void addAttribute(String key, String value) { + delegateSpan.setAttribute(key, value); + } + + @Override + public void addAttribute(String key, Long value) { + delegateSpan.setAttribute(key, value); + } + + @Override + public void addAttribute(String key, Double value) { + delegateSpan.setAttribute(key, value); + } + + @Override + public void addAttribute(String key, Boolean value) { + delegateSpan.setAttribute(key, value); + } + + @Override + public void setError(Exception exception) { + if (exception != null) { + delegateSpan.setStatus(StatusCode.ERROR, exception.getMessage()); + } + } + + @Override + public void addEvent(String event) { + delegateSpan.addEvent(event); + } + + @Override + public String getTraceId() { + return delegateSpan.getSpanContext().getTraceId(); + } + + @Override + public String getSpanId() { + return delegateSpan.getSpanContext().getSpanId(); + } + + io.opentelemetry.api.trace.Span getDelegateSpan() { + return delegateSpan; + } + +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpanKindConverter.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpanKindConverter.java new file mode 100644 index 0000000000000..4edb837082126 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpanKindConverter.java @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import io.opentelemetry.api.trace.SpanKind; + +/** + * Converts {@link org.opensearch.telemetry.tracing.SpanKind} to OTel {@link SpanKind} + */ +final class OTelSpanKindConverter { + + /** + * Constructor. + */ + private OTelSpanKindConverter() {} + + /** + * SpanKind converter. + * @param spanKind span kind. + * @return otel attributes. + */ + static SpanKind convert(org.opensearch.telemetry.tracing.SpanKind spanKind) { + if (spanKind == null) { + return SpanKind.INTERNAL; + } else { + switch (spanKind) { + case CLIENT: + return SpanKind.CLIENT; + case SERVER: + return SpanKind.SERVER; + case INTERNAL: + default: + return SpanKind.INTERNAL; + } + } + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTelemetry.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTelemetry.java new file mode 100644 index 0000000000000..282fabd43346b --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTelemetry.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.telemetry.Telemetry; +import org.opensearch.telemetry.metrics.MetricsTelemetry; + +/** + * Otel implementation of Telemetry + */ +public class OTelTelemetry implements Telemetry { + + private final TracingTelemetry tracingTelemetry; + private final MetricsTelemetry metricsTelemetry; + + /** + * Creates Telemetry instance + * @param tracingTelemetry tracing telemetry + * @param metricsTelemetry metrics telemetry + */ + public OTelTelemetry(TracingTelemetry tracingTelemetry, MetricsTelemetry metricsTelemetry) { + this.tracingTelemetry = tracingTelemetry; + this.metricsTelemetry = metricsTelemetry; + } + + @Override + public TracingTelemetry getTracingTelemetry() { + return tracingTelemetry; + } + + @Override + public MetricsTelemetry getMetricsTelemetry() { + return metricsTelemetry; + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingContextPropagator.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingContextPropagator.java new file mode 100644 index 0000000000000..f8fe885ee450c --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingContextPropagator.java @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.core.common.Strings; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.context.propagation.TextMapSetter; + +/** + * Otel implementation of TracingContextPropagator + */ +public class OTelTracingContextPropagator implements TracingContextPropagator { + + private final OpenTelemetry openTelemetry; + + /** + * Creates OTelTracingContextPropagator instance + * @param openTelemetry Otel OpenTelemetry instance + */ + public OTelTracingContextPropagator(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + @Override + public Optional extract(Map props) { + Context context = openTelemetry.getPropagators().getTextMapPropagator().extract(Context.current(), props, TEXT_MAP_GETTER); + return Optional.ofNullable(getPropagatedSpan(context)); + } + + private static OTelPropagatedSpan getPropagatedSpan(Context context) { + if (context != null) { + io.opentelemetry.api.trace.Span span = io.opentelemetry.api.trace.Span.fromContext(context); + return new OTelPropagatedSpan(span); + } + return null; + } + + @Override + public Optional extractFromHeaders(Map> headers) { + Context context = openTelemetry.getPropagators().getTextMapPropagator().extract(Context.current(), headers, HEADER_TEXT_MAP_GETTER); + return Optional.ofNullable(getPropagatedSpan(context)); + } + + @Override + public void inject(Span currentSpan, BiConsumer setter) { + openTelemetry.getPropagators().getTextMapPropagator().inject(context((OTelSpan) currentSpan), setter, TEXT_MAP_SETTER); + + } + + private static Context context(OTelSpan oTelSpan) { + return Context.current().with(io.opentelemetry.api.trace.Span.wrap(oTelSpan.getDelegateSpan().getSpanContext())); + } + + private static final TextMapSetter> TEXT_MAP_SETTER = (carrier, key, value) -> { + if (carrier != null) { + carrier.accept(key, value); + } + }; + + private static final TextMapGetter> TEXT_MAP_GETTER = new TextMapGetter<>() { + @Override + public Iterable keys(Map headers) { + return headers.keySet(); + } + + @Override + public String get(Map headers, String key) { + if (headers != null && headers.containsKey(key)) { + return headers.get(key); + } + return null; + } + }; + + private static final TextMapGetter>> HEADER_TEXT_MAP_GETTER = new TextMapGetter<>() { + @Override + public Iterable keys(Map> headers) { + if (headers != null) { + return headers.keySet(); + } else { + return Collections.emptySet(); + } + } + + @Override + public String get(Map> headers, String key) { + if (headers != null && headers.containsKey(key)) { + return Strings.collectionToCommaDelimitedString(headers.get(key)); + } + return null; + } + }; + +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingTelemetry.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingTelemetry.java new file mode 100644 index 0000000000000..f88afe623fd56 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingTelemetry.java @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.telemetry.OTelAttributesConverter; +import org.opensearch.telemetry.OTelTelemetryPlugin; + +import java.io.Closeable; +import java.io.IOException; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.context.Context; + +/** + * OTel based Telemetry provider + */ +public class OTelTracingTelemetry implements TracingTelemetry { + private final OpenTelemetry openTelemetry; + private final T tracerProvider; + private final io.opentelemetry.api.trace.Tracer otelTracer; + + /** + * Creates OTel based {@link TracingTelemetry} + * @param openTelemetry OpenTelemetry instance + * @param tracerProvider {@link TracerProvider} instance. + */ + public OTelTracingTelemetry(OpenTelemetry openTelemetry, T tracerProvider) { + this.openTelemetry = openTelemetry; + this.tracerProvider = tracerProvider; + this.otelTracer = tracerProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME); + } + + @Override + public void close() throws IOException { + tracerProvider.close(); + } + + @Override + public Span createSpan(SpanCreationContext spanCreationContext, Span parentSpan) { + return createOtelSpan(spanCreationContext, parentSpan); + } + + @Override + public TracingContextPropagator getContextPropagator() { + return new OTelTracingContextPropagator(openTelemetry); + } + + private Span createOtelSpan(SpanCreationContext spanCreationContext, Span parentSpan) { + io.opentelemetry.api.trace.Span otelSpan = otelSpan( + spanCreationContext.getSpanName(), + parentSpan, + OTelAttributesConverter.convert(spanCreationContext.getAttributes()), + OTelSpanKindConverter.convert(spanCreationContext.getSpanKind()) + ); + Span newSpan = new OTelSpan(spanCreationContext.getSpanName(), otelSpan, parentSpan); + return newSpan; + } + + io.opentelemetry.api.trace.Span otelSpan( + String spanName, + Span parentOTelSpan, + io.opentelemetry.api.common.Attributes attributes, + io.opentelemetry.api.trace.SpanKind spanKind + ) { + return parentOTelSpan == null || !(parentOTelSpan instanceof OTelSpan) + ? otelTracer.spanBuilder(spanName).setAllAttributes(attributes).startSpan() + : otelTracer.spanBuilder(spanName) + .setParent(Context.current().with(((OTelSpan) parentOTelSpan).getDelegateSpan())) + .setAllAttributes(attributes) + .setSpanKind(spanKind) + .startSpan(); + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactory.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactory.java new file mode 100644 index 0000000000000..da7ce5c47d9ca --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactory.java @@ -0,0 +1,90 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.exporter; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.SpecialPermission; +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.OTelTelemetrySettings; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +import io.opentelemetry.sdk.trace.export.SpanExporter; + +/** + * Factory class to create the {@link SpanExporter} instance. + */ +public class OTelSpanExporterFactory { + + private static final Logger logger = LogManager.getLogger(OTelSpanExporterFactory.class); + + /** + * Base constructor. + */ + private OTelSpanExporterFactory() { + + } + + /** + * Creates the {@link SpanExporter} instances based on the OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING value. + * As of now, it expects the SpanExporter implementations to have a create factory method to instantiate the + * SpanExporter. + * @param settings settings. + * @return SpanExporter instance. + */ + public static SpanExporter create(Settings settings) { + Class spanExporterProviderClass = OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.get(settings); + SpanExporter spanExporter = instantiateSpanExporter(spanExporterProviderClass); + logger.info("Successfully instantiated the SpanExporter class {}", spanExporterProviderClass); + return spanExporter; + } + + private static SpanExporter instantiateSpanExporter(Class spanExporterProviderClass) { + try { + // Check we ourselves are not being called by unprivileged code. + SpecialPermission.check(); + return AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + String methodName = "create"; + String getDefaultMethod = "getDefault"; + for (Method m : spanExporterProviderClass.getMethods()) { + if (m.getName().equals(getDefaultMethod)) { + methodName = getDefaultMethod; + break; + } + } + try { + return (SpanExporter) MethodHandles.publicLookup() + .findStatic(spanExporterProviderClass, methodName, MethodType.methodType(spanExporterProviderClass)) + .asType(MethodType.methodType(SpanExporter.class)) + .invokeExact(); + } catch (Throwable e) { + if (e.getCause() instanceof NoSuchMethodException) { + throw new IllegalStateException("No create factory method exist in [" + spanExporterProviderClass.getName() + "]"); + } else { + throw new IllegalStateException( + "SpanExporter instantiation failed for class [" + spanExporterProviderClass.getName() + "]", + e.getCause() + ); + } + } + }); + } catch (PrivilegedActionException ex) { + throw new IllegalStateException( + "SpanExporter instantiation failed for class [" + spanExporterProviderClass.getName() + "]", + ex.getCause() + ); + } + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/package-info.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/package-info.java new file mode 100644 index 0000000000000..a5fffadabd75f --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/exporter/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * This package contains classes needed for telemetry. + */ +package org.opensearch.telemetry.tracing.exporter; diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/package-info.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/package-info.java new file mode 100644 index 0000000000000..4ac1e4c212c81 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * This package contains classes needed for tracing requests. + */ +package org.opensearch.telemetry.tracing; diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/ProbabilisticSampler.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/ProbabilisticSampler.java new file mode 100644 index 0000000000000..774070aa39df6 --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/ProbabilisticSampler.java @@ -0,0 +1,82 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.sampler; + +import org.opensearch.telemetry.TelemetrySettings; + +import java.util.List; +import java.util.Objects; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; + +/** + * ProbabilisticSampler implements a head-based sampling strategy based on provided settings. + */ +public class ProbabilisticSampler implements Sampler { + private Sampler defaultSampler; + private final TelemetrySettings telemetrySettings; + private double samplingRatio; + + /** + * Constructor + * + * @param telemetrySettings Telemetry settings. + */ + public ProbabilisticSampler(TelemetrySettings telemetrySettings) { + this.telemetrySettings = Objects.requireNonNull(telemetrySettings); + this.samplingRatio = telemetrySettings.getSamplingProbability(); + this.defaultSampler = Sampler.traceIdRatioBased(samplingRatio); + } + + Sampler getSampler() { + double newSamplingRatio = telemetrySettings.getSamplingProbability(); + if (isSamplingRatioChanged(newSamplingRatio)) { + synchronized (this) { + this.samplingRatio = newSamplingRatio; + defaultSampler = Sampler.traceIdRatioBased(samplingRatio); + } + } + return defaultSampler; + } + + private boolean isSamplingRatioChanged(double newSamplingRatio) { + return Double.compare(this.samplingRatio, newSamplingRatio) != 0; + } + + double getSamplingRatio() { + return samplingRatio; + } + + @Override + public SamplingResult shouldSample( + Context parentContext, + String traceId, + String name, + SpanKind spanKind, + Attributes attributes, + List parentLinks + ) { + return getSampler().shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); + } + + @Override + public String getDescription() { + return "Probabilistic Sampler"; + } + + @Override + public String toString() { + return getDescription(); + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/RequestSampler.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/RequestSampler.java new file mode 100644 index 0000000000000..9ea681370a3ec --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/RequestSampler.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.sampler; + +import java.util.List; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; + +/** + * HeadBased sampler + */ +public class RequestSampler implements Sampler { + private final Sampler defaultSampler; + + // TODO: Pick value of TRACE from PR #9415. + private static final String TRACE = "trace"; + + /** + * Creates Head based sampler + * @param defaultSampler defaultSampler + */ + public RequestSampler(Sampler defaultSampler) { + this.defaultSampler = defaultSampler; + } + + @Override + public SamplingResult shouldSample( + Context parentContext, + String traceId, + String name, + SpanKind spanKind, + Attributes attributes, + List parentLinks + ) { + + final String trace = attributes.get(AttributeKey.stringKey(TRACE)); + + if (trace != null) { + return (Boolean.parseBoolean(trace) == true) ? SamplingResult.recordAndSample() : SamplingResult.drop(); + } else { + return defaultSampler.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); + } + + } + + @Override + public String getDescription() { + return "Request Sampler"; + } + + @Override + public String toString() { + return getDescription(); + } +} diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/package-info.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/package-info.java new file mode 100644 index 0000000000000..6534b33f6177c --- /dev/null +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/sampler/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * This package contains classes needed for sampler. + */ +package org.opensearch.telemetry.tracing.sampler; diff --git a/plugins/telemetry-otel/src/main/plugin-metadata/plugin-security.policy b/plugins/telemetry-otel/src/main/plugin-metadata/plugin-security.policy new file mode 100644 index 0000000000000..9d529ed5a2a56 --- /dev/null +++ b/plugins/telemetry-otel/src/main/plugin-metadata/plugin-security.policy @@ -0,0 +1,17 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +grant { + permission java.lang.RuntimePermission "getClassLoader"; + permission java.lang.RuntimePermission "accessDeclaredMembers"; + permission java.net.NetPermission "getProxySelector"; + permission java.net.SocketPermission "*", "connect,resolve"; + permission java.util.PropertyPermission "*", "read,write"; +}; + + diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/OTelTelemetryPluginTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/OTelTelemetryPluginTests.java new file mode 100644 index 0000000000000..2fcf89947e537 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/OTelTelemetryPluginTests.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry; + +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.telemetry.metrics.MetricsTelemetry; +import org.opensearch.telemetry.metrics.OTelMetricsTelemetry; +import org.opensearch.telemetry.tracing.OTelTracingTelemetry; +import org.opensearch.telemetry.tracing.TracingTelemetry; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static org.opensearch.telemetry.OTelTelemetryPlugin.OTEL_TRACER_NAME; +import static org.opensearch.telemetry.OTelTelemetrySettings.OTEL_METRICS_EXPORTER_CLASS_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_BATCH_SIZE_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING; +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING; +import static org.opensearch.telemetry.TelemetrySettings.TRACER_ENABLED_SETTING; +import static org.opensearch.telemetry.TelemetrySettings.TRACER_SAMPLER_PROBABILITY; + +public class OTelTelemetryPluginTests extends OpenSearchTestCase { + + private OTelTelemetryPlugin oTelTelemetryPlugin; + private Optional telemetry; + private TracingTelemetry tracingTelemetry; + + private MetricsTelemetry metricsTelemetry; + + @Before + public void setup() { + // TRACER_EXPORTER_DELAY_SETTING should always be less than 10 seconds because + // io.opentelemetry.sdk.OpenTelemetrySdk.close waits only for 10 seconds for shutdown to complete. + Settings settings = Settings.builder().put(TRACER_EXPORTER_DELAY_SETTING.getKey(), "1s").build(); + oTelTelemetryPlugin = new OTelTelemetryPlugin(settings); + telemetry = oTelTelemetryPlugin.getTelemetry( + new TelemetrySettings(Settings.EMPTY, new ClusterSettings(settings, Set.of(TRACER_ENABLED_SETTING, TRACER_SAMPLER_PROBABILITY))) + ); + tracingTelemetry = telemetry.get().getTracingTelemetry(); + metricsTelemetry = telemetry.get().getMetricsTelemetry(); + } + + public void testGetTelemetry() { + Set> allTracerSettings = new HashSet<>(); + ClusterSettings.FEATURE_FLAGGED_CLUSTER_SETTINGS.get(List.of(FeatureFlags.TELEMETRY)).stream().forEach((allTracerSettings::add)); + assertEquals(OTEL_TRACER_NAME, oTelTelemetryPlugin.getName()); + assertTrue(tracingTelemetry instanceof OTelTracingTelemetry); + assertTrue(metricsTelemetry instanceof OTelMetricsTelemetry); + assertEquals( + Arrays.asList( + TRACER_EXPORTER_BATCH_SIZE_SETTING, + TRACER_EXPORTER_DELAY_SETTING, + TRACER_EXPORTER_MAX_QUEUE_SIZE_SETTING, + OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING, + OTEL_METRICS_EXPORTER_CLASS_SETTING + ), + oTelTelemetryPlugin.getSettings() + ); + + } + + @After + public void cleanup() throws IOException { + tracingTelemetry.close(); + metricsTelemetry.close(); + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/OTelMetricsTelemetryTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/OTelMetricsTelemetryTests.java new file mode 100644 index 0000000000000..233c93e6b9a36 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/OTelMetricsTelemetryTests.java @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics; + +import org.opensearch.telemetry.OTelAttributesConverter; +import org.opensearch.telemetry.OTelTelemetryPlugin; +import org.opensearch.telemetry.metrics.tags.Tags; +import org.opensearch.test.OpenSearchTestCase; + +import io.opentelemetry.api.metrics.DoubleCounter; +import io.opentelemetry.api.metrics.DoubleCounterBuilder; +import io.opentelemetry.api.metrics.DoubleUpDownCounter; +import io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder; +import io.opentelemetry.api.metrics.LongCounterBuilder; +import io.opentelemetry.api.metrics.LongUpDownCounterBuilder; +import io.opentelemetry.api.metrics.Meter; +import io.opentelemetry.api.metrics.MeterProvider; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OTelMetricsTelemetryTests extends OpenSearchTestCase { + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testCounter() { + String counterName = "test-counter"; + String description = "test"; + String unit = "1"; + Meter mockMeter = mock(Meter.class); + DoubleCounter mockOTelDoubleCounter = mock(DoubleCounter.class); + LongCounterBuilder mockOTelLongCounterBuilder = mock(LongCounterBuilder.class); + DoubleCounterBuilder mockOTelDoubleCounterBuilder = mock(DoubleCounterBuilder.class); + MeterProvider meterProvider = mock(MeterProvider.class); + when(meterProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME)).thenReturn(mockMeter); + MetricsTelemetry metricsTelemetry = new OTelMetricsTelemetry(meterProvider); + when(mockMeter.counterBuilder(counterName)).thenReturn(mockOTelLongCounterBuilder); + when(mockOTelLongCounterBuilder.setDescription(description)).thenReturn(mockOTelLongCounterBuilder); + when(mockOTelLongCounterBuilder.setUnit(unit)).thenReturn(mockOTelLongCounterBuilder); + when(mockOTelLongCounterBuilder.ofDoubles()).thenReturn(mockOTelDoubleCounterBuilder); + when(mockOTelDoubleCounterBuilder.build()).thenReturn(mockOTelDoubleCounter); + + Counter counter = metricsTelemetry.createCounter(counterName, description, unit); + counter.add(1.0); + verify(mockOTelDoubleCounter).add(1.0); + Tags tags = Tags.create().addTag("test", "test"); + counter.add(2.0, tags); + verify(mockOTelDoubleCounter).add(2.0, OTelAttributesConverter.convert(tags)); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testCounterNegativeValue() { + String counterName = "test-counter"; + String description = "test"; + String unit = "1"; + Meter mockMeter = mock(Meter.class); + DoubleCounter mockOTelDoubleCounter = mock(DoubleCounter.class); + LongCounterBuilder mockOTelLongCounterBuilder = mock(LongCounterBuilder.class); + DoubleCounterBuilder mockOTelDoubleCounterBuilder = mock(DoubleCounterBuilder.class); + + MeterProvider meterProvider = mock(MeterProvider.class); + when(meterProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME)).thenReturn(mockMeter); + MetricsTelemetry metricsTelemetry = new OTelMetricsTelemetry(meterProvider); + when(mockMeter.counterBuilder(counterName)).thenReturn(mockOTelLongCounterBuilder); + when(mockOTelLongCounterBuilder.setDescription(description)).thenReturn(mockOTelLongCounterBuilder); + when(mockOTelLongCounterBuilder.setUnit(unit)).thenReturn(mockOTelLongCounterBuilder); + when(mockOTelLongCounterBuilder.ofDoubles()).thenReturn(mockOTelDoubleCounterBuilder); + when(mockOTelDoubleCounterBuilder.build()).thenReturn(mockOTelDoubleCounter); + + Counter counter = metricsTelemetry.createCounter(counterName, description, unit); + counter.add(-1.0); + verify(mockOTelDoubleCounter).add(-1.0); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testUpDownCounter() { + String counterName = "test-counter"; + String description = "test"; + String unit = "1"; + Meter mockMeter = mock(Meter.class); + DoubleUpDownCounter mockOTelUpDownDoubleCounter = mock(DoubleUpDownCounter.class); + LongUpDownCounterBuilder mockOTelLongUpDownCounterBuilder = mock(LongUpDownCounterBuilder.class); + DoubleUpDownCounterBuilder mockOTelDoubleUpDownCounterBuilder = mock(DoubleUpDownCounterBuilder.class); + + MeterProvider meterProvider = mock(MeterProvider.class); + when(meterProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME)).thenReturn(mockMeter); + MetricsTelemetry metricsTelemetry = new OTelMetricsTelemetry(meterProvider); + when(mockMeter.upDownCounterBuilder(counterName)).thenReturn(mockOTelLongUpDownCounterBuilder); + when(mockOTelLongUpDownCounterBuilder.setDescription(description)).thenReturn(mockOTelLongUpDownCounterBuilder); + when(mockOTelLongUpDownCounterBuilder.setUnit(unit)).thenReturn(mockOTelLongUpDownCounterBuilder); + when(mockOTelLongUpDownCounterBuilder.ofDoubles()).thenReturn(mockOTelDoubleUpDownCounterBuilder); + when(mockOTelDoubleUpDownCounterBuilder.build()).thenReturn(mockOTelUpDownDoubleCounter); + + Counter counter = metricsTelemetry.createUpDownCounter(counterName, description, unit); + counter.add(1.0); + verify(mockOTelUpDownDoubleCounter).add(1.0); + Tags tags = Tags.create().addTag("test", "test"); + counter.add(-2.0, tags); + verify(mockOTelUpDownDoubleCounter).add((-2.0), OTelAttributesConverter.convert(tags)); + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/exporter/DummyMetricExporter.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/exporter/DummyMetricExporter.java new file mode 100644 index 0000000000000..65c52911dbef9 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/exporter/DummyMetricExporter.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics.exporter; + +import java.util.Collection; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricExporter; + +public class DummyMetricExporter implements MetricExporter { + @Override + public CompletableResultCode export(Collection metrics) { + return null; + } + + @Override + public CompletableResultCode flush() { + return null; + } + + @Override + public CompletableResultCode shutdown() { + return null; + } + + @Override + public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) { + return null; + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/exporter/OTelMetricsExporterFactoryTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/exporter/OTelMetricsExporterFactoryTests.java new file mode 100644 index 0000000000000..e68da030bfb52 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/exporter/OTelMetricsExporterFactoryTests.java @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.metrics.exporter; + +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.OTelTelemetrySettings; +import org.opensearch.test.OpenSearchTestCase; + +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; +import io.opentelemetry.sdk.metrics.export.MetricExporter; + +public class OTelMetricsExporterFactoryTests extends OpenSearchTestCase { + + public void testMetricsExporterDefault() { + Settings settings = Settings.builder().build(); + MetricExporter metricExporter = OTelMetricsExporterFactory.create(settings); + assertTrue(metricExporter instanceof LoggingMetricExporter); + } + + public void testMetricsExporterLogging() { + Settings settings = Settings.builder() + .put( + OTelTelemetrySettings.OTEL_METRICS_EXPORTER_CLASS_SETTING.getKey(), + "io.opentelemetry.exporter.logging.LoggingMetricExporter" + ) + .build(); + MetricExporter metricExporter = OTelMetricsExporterFactory.create(settings); + assertTrue(metricExporter instanceof LoggingMetricExporter); + } + + public void testMetricExporterInvalid() { + Settings settings = Settings.builder().put(OTelTelemetrySettings.OTEL_METRICS_EXPORTER_CLASS_SETTING.getKey(), "abc").build(); + assertThrows(IllegalArgumentException.class, () -> OTelMetricsExporterFactory.create(settings)); + } + + public void testMetricExporterNoCreateFactoryMethod() { + Settings settings = Settings.builder() + .put( + OTelTelemetrySettings.OTEL_METRICS_EXPORTER_CLASS_SETTING.getKey(), + "org.opensearch.telemetry.metrics.exporter.DummyMetricExporter" + ) + .build(); + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> OTelMetricsExporterFactory.create(settings)); + assertEquals( + "MetricExporter instantiation failed for class [org.opensearch.telemetry.metrics.exporter.DummyMetricExporter]", + exception.getMessage() + ); + } + + public void testMetricExporterNonMetricExporterClass() { + Settings settings = Settings.builder() + .put(OTelTelemetrySettings.OTEL_METRICS_EXPORTER_CLASS_SETTING.getKey(), "java.lang.String") + .build(); + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> OTelMetricsExporterFactory.create(settings)); + assertEquals("MetricExporter instantiation failed for class [java.lang.String]", exception.getMessage()); + assertTrue(exception.getCause() instanceof NoSuchMethodError); + + } + + public void testMetricExporterGetDefaultMethod() { + Settings settings = Settings.builder() + .put( + OTelTelemetrySettings.OTEL_METRICS_EXPORTER_CLASS_SETTING.getKey(), + "io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter" + ) + .build(); + + assertTrue(OTelMetricsExporterFactory.create(settings) instanceof OtlpGrpcMetricExporter); + } + +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelAttributesConverterTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelAttributesConverterTests.java new file mode 100644 index 0000000000000..ee67384d01759 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelAttributesConverterTests.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.telemetry.OTelAttributesConverter; +import org.opensearch.telemetry.metrics.tags.Tags; +import org.opensearch.telemetry.tracing.attributes.Attributes; +import org.opensearch.test.OpenSearchTestCase; + +import java.util.Map; + +import io.opentelemetry.api.common.AttributeType; +import io.opentelemetry.api.internal.InternalAttributeKeyImpl; + +public class OTelAttributesConverterTests extends OpenSearchTestCase { + + public void testConverterNullAttributes() { + io.opentelemetry.api.common.Attributes otelAttributes = OTelAttributesConverter.convert((Attributes) null); + assertEquals(0, otelAttributes.size()); + } + + public void testConverterEmptyAttributes() { + Attributes attributes = Attributes.EMPTY; + io.opentelemetry.api.common.Attributes otelAttributes = OTelAttributesConverter.convert(attributes); + assertEquals(0, otelAttributes.size()); + } + + public void testConverterSingleAttributes() { + Attributes attributes = Attributes.create().addAttribute("key1", "value"); + io.opentelemetry.api.common.Attributes otelAttributes = OTelAttributesConverter.convert(attributes); + assertEquals(1, otelAttributes.size()); + assertEquals("value", otelAttributes.get(InternalAttributeKeyImpl.create("key1", AttributeType.STRING))); + } + + public void testConverterMultipleAttributes() { + Attributes attributes = Attributes.create() + .addAttribute("key1", 1l) + .addAttribute("key2", 1.0) + .addAttribute("key3", true) + .addAttribute("key4", "value4"); + Map attributeMap = attributes.getAttributesMap(); + io.opentelemetry.api.common.Attributes otelAttributes = OTelAttributesConverter.convert(attributes); + assertEquals(4, otelAttributes.size()); + otelAttributes.asMap().forEach((x, y) -> assertEquals(attributeMap.get(x.getKey()), y)); + } + + public void testConverterMultipleTags() { + Tags tags = Tags.create().addTag("key1", 1l).addTag("key2", 1.0).addTag("key3", true).addTag("key4", "value4"); + Map tagsMap = tags.getTagsMap(); + io.opentelemetry.api.common.Attributes otelAttributes = OTelAttributesConverter.convert(tags); + assertEquals(4, otelAttributes.size()); + otelAttributes.asMap().forEach((x, y) -> assertEquals(tagsMap.get(x.getKey()), y)); + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelSpanKindConverterTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelSpanKindConverterTests.java new file mode 100644 index 0000000000000..d07e32d00a92a --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelSpanKindConverterTests.java @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.test.OpenSearchTestCase; + +import io.opentelemetry.api.trace.SpanKind; + +public class OTelSpanKindConverterTests extends OpenSearchTestCase { + + public void testSpanKindNullConverterNull() { + assertEquals(SpanKind.INTERNAL, OTelSpanKindConverter.convert(null)); + } + + public void testSpanKindConverter() { + assertEquals(SpanKind.INTERNAL, OTelSpanKindConverter.convert(org.opensearch.telemetry.tracing.SpanKind.INTERNAL)); + assertEquals(SpanKind.CLIENT, OTelSpanKindConverter.convert(org.opensearch.telemetry.tracing.SpanKind.CLIENT)); + assertEquals(SpanKind.SERVER, OTelSpanKindConverter.convert(org.opensearch.telemetry.tracing.SpanKind.SERVER)); + } + +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelSpanTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelSpanTests.java new file mode 100644 index 0000000000000..fc92ab36908e1 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelSpanTests.java @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.test.OpenSearchTestCase; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OTelSpanTests extends OpenSearchTestCase { + + private static final String TRACE_ID = "4aa59968f31dcbff7807741afa9d7d62"; + private static final String SPAN_ID = "bea205cd25756b5e"; + + public void testEndSpanTest() { + Span mockSpan = getMockSpan(); + OTelSpan oTelSpan = new OTelSpan("spanName", mockSpan, null); + oTelSpan.endSpan(); + verify(mockSpan).end(); + } + + public void testAddAttributeString() { + Span mockSpan = getMockSpan(); + OTelSpan oTelSpan = new OTelSpan("spanName", mockSpan, null); + oTelSpan.addAttribute("key", "value"); + + verify(mockSpan).setAttribute("key", "value"); + } + + public void testAddAttributeLong() { + Span mockSpan = getMockSpan(); + OTelSpan oTelSpan = new OTelSpan("spanName", mockSpan, null); + oTelSpan.addAttribute("key", 1L); + + verify(mockSpan).setAttribute("key", 1L); + } + + public void testAddAttributeDouble() { + Span mockSpan = getMockSpan(); + OTelSpan oTelSpan = new OTelSpan("spanName", mockSpan, null); + oTelSpan.addAttribute("key", 1.0); + + verify(mockSpan).setAttribute("key", 1.0); + } + + public void testAddAttributeBoolean() { + Span mockSpan = getMockSpan(); + OTelSpan oTelSpan = new OTelSpan("spanName", mockSpan, null); + oTelSpan.addAttribute("key", true); + + verify(mockSpan).setAttribute("key", true); + } + + public void testAddEvent() { + Span mockSpan = getMockSpan(); + OTelSpan oTelSpan = new OTelSpan("spanName", mockSpan, null); + oTelSpan.addEvent("eventName"); + + verify(mockSpan).addEvent("eventName"); + } + + public void testGetTraceId() { + Span mockSpan = getMockSpan(); + OTelSpan oTelSpan = new OTelSpan("spanName", mockSpan, null); + + assertEquals(TRACE_ID, oTelSpan.getTraceId()); + } + + public void testGetSpanId() { + Span mockSpan = getMockSpan(); + OTelSpan oTelSpan = new OTelSpan("spanName", mockSpan, null); + + assertEquals(SPAN_ID, oTelSpan.getSpanId()); + } + + private Span getMockSpan() { + Span mockSpan = mock(Span.class); + when(mockSpan.getSpanContext()).thenReturn(SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault())); + return mockSpan; + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelTracingContextPropagatorTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelTracingContextPropagatorTests.java new file mode 100644 index 0000000000000..16a3ec9493d5d --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelTracingContextPropagatorTests.java @@ -0,0 +1,89 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.test.OpenSearchTestCase; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.ContextPropagators; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class OTelTracingContextPropagatorTests extends OpenSearchTestCase { + + private static final String TRACE_ID = "4aa59968f31dcbff7807741afa9d7d62"; + private static final String SPAN_ID = "bea205cd25756b5e"; + + public void testAddTracerContextToHeader() { + Span mockSpan = mock(Span.class); + when(mockSpan.getSpanContext()).thenReturn(SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getDefault(), TraceState.getDefault())); + OTelSpan span = new OTelSpan("spanName", mockSpan, null); + Map requestHeaders = new HashMap<>(); + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + when(mockOpenTelemetry.getPropagators()).thenReturn(ContextPropagators.create(W3CTraceContextPropagator.getInstance())); + TracingContextPropagator tracingContextPropagator = new OTelTracingContextPropagator(mockOpenTelemetry); + + tracingContextPropagator.inject(span, (key, value) -> requestHeaders.put(key, value)); + assertEquals("00-" + TRACE_ID + "-" + SPAN_ID + "-00", requestHeaders.get("traceparent")); + } + + public void testExtractTracerContextFromHeader() { + Map requestHeaders = new HashMap<>(); + requestHeaders.put("traceparent", "00-" + TRACE_ID + "-" + SPAN_ID + "-00"); + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + when(mockOpenTelemetry.getPropagators()).thenReturn(ContextPropagators.create(W3CTraceContextPropagator.getInstance())); + TracingContextPropagator tracingContextPropagator = new OTelTracingContextPropagator(mockOpenTelemetry); + org.opensearch.telemetry.tracing.Span span = tracingContextPropagator.extract(requestHeaders).orElse(null); + assertEquals(TRACE_ID, span.getTraceId()); + assertEquals(SPAN_ID, span.getSpanId()); + } + + public void testExtractTracerContextFromHttpHeader() { + Map> requestHeaders = new HashMap<>(); + requestHeaders.put("traceparent", Arrays.asList("00-" + TRACE_ID + "-" + SPAN_ID + "-00")); + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + when(mockOpenTelemetry.getPropagators()).thenReturn(ContextPropagators.create(W3CTraceContextPropagator.getInstance())); + TracingContextPropagator tracingContextPropagator = new OTelTracingContextPropagator(mockOpenTelemetry); + org.opensearch.telemetry.tracing.Span span = tracingContextPropagator.extractFromHeaders(requestHeaders).get(); + assertEquals(TRACE_ID, span.getTraceId()); + assertEquals(SPAN_ID, span.getSpanId()); + } + + public void testExtractTracerContextFromHttpHeaderNull() { + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + when(mockOpenTelemetry.getPropagators()).thenReturn(ContextPropagators.create(W3CTraceContextPropagator.getInstance())); + TracingContextPropagator tracingContextPropagator = new OTelTracingContextPropagator(mockOpenTelemetry); + org.opensearch.telemetry.tracing.Span span = tracingContextPropagator.extractFromHeaders(null).get(); + org.opensearch.telemetry.tracing.Span propagatedSpan = new OTelPropagatedSpan(Span.fromContext(Context.root())); + assertEquals(propagatedSpan.getTraceId(), span.getTraceId()); + assertEquals(propagatedSpan.getSpanId(), span.getSpanId()); + } + + public void testExtractTracerContextFromHttpHeaderEmpty() { + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + when(mockOpenTelemetry.getPropagators()).thenReturn(ContextPropagators.create(W3CTraceContextPropagator.getInstance())); + TracingContextPropagator tracingContextPropagator = new OTelTracingContextPropagator(mockOpenTelemetry); + org.opensearch.telemetry.tracing.Span span = tracingContextPropagator.extractFromHeaders(new HashMap<>()).get(); + org.opensearch.telemetry.tracing.Span propagatedSpan = new OTelPropagatedSpan(Span.fromContext(Context.root())); + assertEquals(propagatedSpan.getTraceId(), span.getTraceId()); + assertEquals(propagatedSpan.getSpanId(), span.getSpanId()); + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelTracingTelemetryTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelTracingTelemetryTests.java new file mode 100644 index 0000000000000..1a508ed252493 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/OTelTracingTelemetryTests.java @@ -0,0 +1,133 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.telemetry.OTelTelemetryPlugin; +import org.opensearch.telemetry.tracing.attributes.Attributes; +import org.opensearch.test.OpenSearchTestCase; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.trace.TracerProvider; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class OTelTracingTelemetryTests extends OpenSearchTestCase { + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testCreateSpanWithoutParent() { + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + Tracer mockTracer = mock(Tracer.class); + TracerProvider mockTracerProvider = mock(TracerProvider.class); + when(mockTracerProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME)).thenReturn(mockTracer); + SpanBuilder mockSpanBuilder = mock(SpanBuilder.class); + when(mockTracer.spanBuilder("span_name")).thenReturn(mockSpanBuilder); + when(mockSpanBuilder.setAllAttributes(any(io.opentelemetry.api.common.Attributes.class))).thenReturn(mockSpanBuilder); + when(mockSpanBuilder.startSpan()).thenReturn(mock(io.opentelemetry.api.trace.Span.class)); + when(mockSpanBuilder.setSpanKind(any(io.opentelemetry.api.trace.SpanKind.class))).thenReturn(mockSpanBuilder); + Attributes attributes = Attributes.create().addAttribute("name", "value"); + TracingTelemetry tracingTelemetry = new OTelTracingTelemetry(mockOpenTelemetry, mockTracerProvider); + Span span = tracingTelemetry.createSpan(SpanCreationContext.internal().name("span_name").attributes(attributes), null); + verify(mockSpanBuilder, never()).setParent(any()); + verify(mockSpanBuilder).setAllAttributes(createAttribute(attributes)); + assertNull(span.getParentSpan()); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testCreateSpanWithParent() { + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + Tracer mockTracer = mock(Tracer.class); + TracerProvider mockTracerProvider = mock(TracerProvider.class); + when(mockTracerProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME)).thenReturn(mockTracer); + SpanBuilder mockSpanBuilder = mock(SpanBuilder.class); + when(mockTracer.spanBuilder("span_name")).thenReturn(mockSpanBuilder); + when(mockSpanBuilder.setParent(any())).thenReturn(mockSpanBuilder); + when(mockSpanBuilder.setAllAttributes(any(io.opentelemetry.api.common.Attributes.class))).thenReturn(mockSpanBuilder); + when(mockSpanBuilder.startSpan()).thenReturn(mock(io.opentelemetry.api.trace.Span.class)); + when(mockSpanBuilder.setSpanKind(any(io.opentelemetry.api.trace.SpanKind.class))).thenReturn(mockSpanBuilder); + + Span parentSpan = new OTelSpan("parent_span", mock(io.opentelemetry.api.trace.Span.class), null); + + TracingTelemetry tracingTelemetry = new OTelTracingTelemetry(mockOpenTelemetry, mockTracerProvider); + Attributes attributes = Attributes.create().addAttribute("name", 1l); + Span span = tracingTelemetry.createSpan(SpanCreationContext.internal().name("span_name").attributes(attributes), parentSpan); + + verify(mockSpanBuilder).setParent(any()); + verify(mockSpanBuilder).setAllAttributes(createAttributeLong(attributes)); + assertNotNull(span.getParentSpan()); + + assertEquals("parent_span", span.getParentSpan().getSpanName()); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testCreateSpanWithParentWithMultipleAttributes() { + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + Tracer mockTracer = mock(Tracer.class); + TracerProvider mockTracerProvider = mock(TracerProvider.class); + when(mockTracerProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME)).thenReturn(mockTracer); + SpanBuilder mockSpanBuilder = mock(SpanBuilder.class); + when(mockTracer.spanBuilder("span_name")).thenReturn(mockSpanBuilder); + when(mockSpanBuilder.setParent(any())).thenReturn(mockSpanBuilder); + when(mockSpanBuilder.setAllAttributes(any(io.opentelemetry.api.common.Attributes.class))).thenReturn(mockSpanBuilder); + when(mockSpanBuilder.startSpan()).thenReturn(mock(io.opentelemetry.api.trace.Span.class)); + when(mockSpanBuilder.setSpanKind(any(io.opentelemetry.api.trace.SpanKind.class))).thenReturn(mockSpanBuilder); + + Span parentSpan = new OTelSpan("parent_span", mock(io.opentelemetry.api.trace.Span.class), null); + + TracingTelemetry tracingTelemetry = new OTelTracingTelemetry(mockOpenTelemetry, mockTracerProvider); + Attributes attributes = Attributes.create() + .addAttribute("key1", 1l) + .addAttribute("key2", 2.0) + .addAttribute("key3", true) + .addAttribute("key4", "key4"); + Span span = tracingTelemetry.createSpan(SpanCreationContext.internal().name("span_name").attributes(attributes), parentSpan); + + io.opentelemetry.api.common.Attributes otelAttributes = io.opentelemetry.api.common.Attributes.builder() + .put("key1", 1l) + .put("key2", 2.0) + .put("key3", true) + .put("key4", "key4") + .build(); + verify(mockSpanBuilder).setParent(any()); + verify(mockSpanBuilder).setAllAttributes(otelAttributes); + assertNotNull(span.getParentSpan()); + + assertEquals("parent_span", span.getParentSpan().getSpanName()); + } + + private io.opentelemetry.api.common.Attributes createAttribute(Attributes attributes) { + AttributesBuilder attributesBuilder = io.opentelemetry.api.common.Attributes.builder(); + attributes.getAttributesMap().forEach((x, y) -> attributesBuilder.put(x, (String) y)); + return attributesBuilder.build(); + } + + private io.opentelemetry.api.common.Attributes createAttributeLong(Attributes attributes) { + AttributesBuilder attributesBuilder = io.opentelemetry.api.common.Attributes.builder(); + attributes.getAttributesMap().forEach((x, y) -> attributesBuilder.put(x, (Long) y)); + return attributesBuilder.build(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void testGetContextPropagator() { + OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class); + Tracer mockTracer = mock(Tracer.class); + TracerProvider mockTracerProvider = mock(TracerProvider.class); + when(mockTracerProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME)).thenReturn(mockTracer); + + TracingTelemetry tracingTelemetry = new OTelTracingTelemetry(mockOpenTelemetry, mockTracerProvider); + + assertTrue(tracingTelemetry.getContextPropagator() instanceof OTelTracingContextPropagator); + } + +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporter.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporter.java new file mode 100644 index 0000000000000..8a47350a7c3bb --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporter.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.exporter; + +import java.util.Collection; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +public class DummySpanExporter implements SpanExporter { + @Override + public CompletableResultCode export(Collection spans) { + return null; + } + + @Override + public CompletableResultCode flush() { + return null; + } + + @Override + public CompletableResultCode shutdown() { + return null; + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporterWithGetDefault.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporterWithGetDefault.java new file mode 100644 index 0000000000000..225cfa6ab2d1a --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/DummySpanExporterWithGetDefault.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.exporter; + +import java.util.Collection; + +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +public class DummySpanExporterWithGetDefault implements SpanExporter { + + public static DummySpanExporterWithGetDefault getDefault() { + return new DummySpanExporterWithGetDefault(); + } + + @Override + public CompletableResultCode export(Collection spans) { + return null; + } + + @Override + public CompletableResultCode flush() { + return null; + } + + @Override + public CompletableResultCode shutdown() { + return null; + } +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactoryTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactoryTests.java new file mode 100644 index 0000000000000..d71aef9366e21 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/exporter/OTelSpanExporterFactoryTests.java @@ -0,0 +1,77 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.exporter; + +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.OTelTelemetrySettings; +import org.opensearch.test.OpenSearchTestCase; + +import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.sdk.trace.export.SpanExporter; + +public class OTelSpanExporterFactoryTests extends OpenSearchTestCase { + + public void testSpanExporterDefault() { + Settings settings = Settings.builder().build(); + SpanExporter spanExporter = OTelSpanExporterFactory.create(settings); + assertTrue(spanExporter instanceof LoggingSpanExporter); + } + + public void testSpanExporterLogging() { + Settings settings = Settings.builder() + .put( + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), + "io.opentelemetry.exporter.logging.LoggingSpanExporter" + ) + .build(); + SpanExporter spanExporter = OTelSpanExporterFactory.create(settings); + assertTrue(spanExporter instanceof LoggingSpanExporter); + } + + public void testSpanExporterInvalid() { + Settings settings = Settings.builder().put(OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), "abc").build(); + assertThrows(IllegalArgumentException.class, () -> OTelSpanExporterFactory.create(settings)); + } + + public void testSpanExporterNoCreateFactoryMethod() { + Settings settings = Settings.builder() + .put( + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), + "org.opensearch.telemetry.tracing.exporter.DummySpanExporter" + ) + .build(); + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> OTelSpanExporterFactory.create(settings)); + assertEquals( + "SpanExporter instantiation failed for class [org.opensearch.telemetry.tracing.exporter.DummySpanExporter]", + exception.getMessage() + ); + } + + public void testSpanExporterNonSpanExporterClass() { + Settings settings = Settings.builder() + .put(OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), "java.lang.String") + .build(); + IllegalStateException exception = assertThrows(IllegalStateException.class, () -> OTelSpanExporterFactory.create(settings)); + assertEquals("SpanExporter instantiation failed for class [java.lang.String]", exception.getMessage()); + assertTrue(exception.getCause() instanceof NoSuchMethodError); + + } + + public void testSpanExporterGetDefaultMethod() { + Settings settings = Settings.builder() + .put( + OTelTelemetrySettings.OTEL_TRACER_SPAN_EXPORTER_CLASS_SETTING.getKey(), + "org.opensearch.telemetry.tracing.exporter.DummySpanExporterWithGetDefault" + ) + .build(); + + assertTrue(OTelSpanExporterFactory.create(settings) instanceof DummySpanExporterWithGetDefault); + } + +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/sampler/ProbabilisticSamplerTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/sampler/ProbabilisticSamplerTests.java new file mode 100644 index 0000000000000..639dc341ef0db --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/sampler/ProbabilisticSamplerTests.java @@ -0,0 +1,64 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.sampler; + +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.telemetry.TelemetrySettings; +import org.opensearch.test.OpenSearchTestCase; + +import java.util.Set; + +import io.opentelemetry.sdk.trace.samplers.Sampler; + +import static org.opensearch.telemetry.OTelTelemetrySettings.TRACER_EXPORTER_DELAY_SETTING; +import static org.opensearch.telemetry.TelemetrySettings.TRACER_ENABLED_SETTING; +import static org.opensearch.telemetry.TelemetrySettings.TRACER_SAMPLER_PROBABILITY; + +public class ProbabilisticSamplerTests extends OpenSearchTestCase { + + // When ProbabilisticSampler is created with OTelTelemetrySettings as null + public void testProbabilisticSamplerWithNullSettings() { + // Verify that the constructor throws IllegalArgumentException when given null settings + assertThrows(NullPointerException.class, () -> { new ProbabilisticSampler(null); }); + } + + public void testDefaultGetSampler() { + Settings settings = Settings.builder().put(TRACER_EXPORTER_DELAY_SETTING.getKey(), "1s").build(); + TelemetrySettings telemetrySettings = new TelemetrySettings( + Settings.EMPTY, + new ClusterSettings(settings, Set.of(TRACER_SAMPLER_PROBABILITY, TRACER_ENABLED_SETTING)) + ); + + // Probabilistic Sampler + ProbabilisticSampler probabilisticSampler = new ProbabilisticSampler(telemetrySettings); + + assertNotNull(probabilisticSampler.getSampler()); + assertEquals(0.01, probabilisticSampler.getSamplingRatio(), 0.0d); + } + + public void testGetSamplerWithUpdatedSamplingRatio() { + Settings settings = Settings.builder().put(TRACER_EXPORTER_DELAY_SETTING.getKey(), "1s").build(); + TelemetrySettings telemetrySettings = new TelemetrySettings( + Settings.EMPTY, + new ClusterSettings(settings, Set.of(TRACER_SAMPLER_PROBABILITY, TRACER_ENABLED_SETTING)) + ); + + // Probabilistic Sampler + ProbabilisticSampler probabilisticSampler = new ProbabilisticSampler(telemetrySettings); + assertEquals(0.01d, probabilisticSampler.getSamplingRatio(), 0.0d); + + telemetrySettings.setSamplingProbability(0.02); + + // Need to call getSampler() to update the value of tracerHeadSamplerSamplingRatio + Sampler updatedProbabilisticSampler = probabilisticSampler.getSampler(); + assertEquals(0.02, probabilisticSampler.getSamplingRatio(), 0.0d); + } + +} diff --git a/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/sampler/RequestSamplerTests.java b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/sampler/RequestSamplerTests.java new file mode 100644 index 0000000000000..facf04623ec46 --- /dev/null +++ b/plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/tracing/sampler/RequestSamplerTests.java @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.sampler; + +import org.opensearch.test.OpenSearchTestCase; + +import java.util.Collections; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class RequestSamplerTests extends OpenSearchTestCase { + + public void testShouldSampleWithTraceAttributeAsTrue() { + + // Create a mock default sampler + Sampler defaultSampler = mock(Sampler.class); + when(defaultSampler.shouldSample(any(), anyString(), anyString(), any(), any(), any())).thenReturn(SamplingResult.drop()); + + // Create an instance of HeadSampler with the mock default sampler + RequestSampler requestSampler = new RequestSampler(defaultSampler); + + // Create a mock Context and Attributes + Context parentContext = mock(Context.class); + Attributes attributes = Attributes.of(AttributeKey.stringKey("trace"), "true"); + + // Call shouldSample on HeadSampler + SamplingResult result = requestSampler.shouldSample( + parentContext, + "traceId", + "spanName", + SpanKind.INTERNAL, + attributes, + Collections.emptyList() + ); + + assertEquals(SamplingResult.recordAndSample(), result); + + // Verify that the default sampler's shouldSample method was not called + verify(defaultSampler, never()).shouldSample(any(), anyString(), anyString(), any(), any(), any()); + } + + public void testShouldSampleWithoutTraceAttribute() { + + // Create a mock default sampler + Sampler defaultSampler = mock(Sampler.class); + when(defaultSampler.shouldSample(any(), anyString(), anyString(), any(), any(), any())).thenReturn( + SamplingResult.recordAndSample() + ); + + // Create an instance of HeadSampler with the mock default sampler + RequestSampler requestSampler = new RequestSampler(defaultSampler); + + // Create a mock Context and Attributes + Context parentContext = mock(Context.class); + Attributes attributes = Attributes.empty(); + + // Call shouldSample on HeadSampler + SamplingResult result = requestSampler.shouldSample( + parentContext, + "traceId", + "spanName", + SpanKind.INTERNAL, + attributes, + Collections.emptyList() + ); + + // Verify that HeadSampler returned SamplingResult.recordAndSample() + assertEquals(SamplingResult.recordAndSample(), result); + + // Verify that the default sampler's shouldSample method was called + verify(defaultSampler).shouldSample(any(), anyString(), anyString(), any(), any(), any()); + } + +} diff --git a/plugins/transport-nio/build.gradle b/plugins/transport-nio/build.gradle index 88355cdf22728..8c0ee8ba718ac 100644 --- a/plugins/transport-nio/build.gradle +++ b/plugins/transport-nio/build.gradle @@ -61,15 +61,9 @@ thirdPartyAudit { 'com.aayushatharva.brotli4j.Brotli4jLoader', 'com.aayushatharva.brotli4j.decoder.DecoderJNI$Status', 'com.aayushatharva.brotli4j.decoder.DecoderJNI$Wrapper', - 'com.aayushatharva.brotli4j.encoder.Encoder', + 'com.aayushatharva.brotli4j.encoder.BrotliEncoderChannel', 'com.aayushatharva.brotli4j.encoder.Encoder$Mode', 'com.aayushatharva.brotli4j.encoder.Encoder$Parameters', - - // from io.netty.handler.codec.protobuf.ProtobufDecoder (netty) - 'com.google.protobuf.ExtensionRegistry', - 'com.google.protobuf.MessageLite$Builder', - 'com.google.protobuf.MessageLite', - 'com.google.protobuf.Parser', // from io.netty.logging.CommonsLoggerFactory (netty) 'org.apache.commons.logging.Log', @@ -83,6 +77,12 @@ thirdPartyAudit { 'org.bouncycastle.cert.X509v3CertificateBuilder', 'org.bouncycastle.cert.jcajce.JcaX509CertificateConverter', 'org.bouncycastle.operator.jcajce.JcaContentSignerBuilder', + 'org.bouncycastle.openssl.PEMEncryptedKeyPair', + 'org.bouncycastle.openssl.PEMParser', + 'org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter', + 'org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder', + 'org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder', + 'org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo', // from io.netty.handler.ssl.JettyNpnSslEngine (netty) 'org.eclipse.jetty.npn.NextProtoNego$ClientProvider', @@ -110,15 +110,8 @@ thirdPartyAudit { 'org.slf4j.LoggerFactory', 'org.slf4j.spi.LocationAwareLogger', - 'com.github.luben.zstd.Zstd', - 'com.google.protobuf.ExtensionRegistryLite', - 'com.google.protobuf.MessageLiteOrBuilder', 'com.google.protobuf.nano.CodedOutputByteBufferNano', 'com.google.protobuf.nano.MessageNano', - 'com.jcraft.jzlib.Deflater', - 'com.jcraft.jzlib.Inflater', - 'com.jcraft.jzlib.JZlib$WrapperType', - 'com.jcraft.jzlib.JZlib', 'com.ning.compress.BufferRecycler', 'com.ning.compress.lzf.ChunkDecoder', 'com.ning.compress.lzf.ChunkEncoder', @@ -145,6 +138,7 @@ thirdPartyAudit { 'io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod', 'io.netty.internal.tcnative.AsyncTask', 'io.netty.internal.tcnative.Buffer', + 'io.netty.internal.tcnative.CertificateCompressionAlgo', 'io.netty.internal.tcnative.Library', 'io.netty.internal.tcnative.ResultCallback', 'io.netty.internal.tcnative.SSL', @@ -157,6 +151,10 @@ thirdPartyAudit { 'io.netty.internal.tcnative.SessionTicketKey', 'io.netty.internal.tcnative.SniHostNameMatcher', + // from io.netty.channel.unix (netty) + 'io.netty.channel.unix.FileDescriptor', + 'io.netty.channel.unix.UnixChannel', + 'reactor.blockhound.BlockHound$Builder', 'reactor.blockhound.integration.BlockHoundIntegration' ) @@ -167,7 +165,8 @@ thirdPartyAudit { 'io.netty.util.internal.PlatformDependent0$1', 'io.netty.util.internal.PlatformDependent0$2', 'io.netty.util.internal.PlatformDependent0$3', - 'io.netty.util.internal.PlatformDependent0$5', + 'io.netty.util.internal.PlatformDependent0$4', + 'io.netty.util.internal.PlatformDependent0$6', 'io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueConsumerNodeRef', 'io.netty.util.internal.shaded.org.jctools.queues.BaseLinkedQueueProducerNodeRef', 'io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields', @@ -180,7 +179,11 @@ thirdPartyAudit { 'io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess', 'io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess', - 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator' + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$1', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$2', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$3', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$4', + 'io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator$5' ) } - diff --git a/plugins/transport-nio/licenses/netty-buffer-4.1.73.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-buffer-4.1.73.Final.jar.sha1 deleted file mode 100644 index e5833785ebb7e..0000000000000 --- a/plugins/transport-nio/licenses/netty-buffer-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -244a569c9aae973f6f485ac9801d79c1eca36daa \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-buffer-4.1.99.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-buffer-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..5b393be40e945 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-buffer-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9f02dcb9b15a647a56af210dffdc294a57922fb0 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-4.1.73.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-4.1.73.Final.jar.sha1 deleted file mode 100644 index dcdc1e4e58afe..0000000000000 --- a/plugins/transport-nio/licenses/netty-codec-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -9496a30a349863a4c6fa10d5c36b4f3b495d3a31 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-4.1.99.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..45ea27d29a183 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-codec-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9984cbd6e5d55c768f198e975d8aaf7fd42a4602 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-http-4.1.73.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-http-4.1.73.Final.jar.sha1 deleted file mode 100644 index 374cfb98614d5..0000000000000 --- a/plugins/transport-nio/licenses/netty-codec-http-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1ceeac4429b9bd517dc05e376a144bbe6b6bd038 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-codec-http-4.1.99.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-codec-http-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..6bb7fcd68b272 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-codec-http-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +7142095066eaebd5f29b88c41af7b383b6a953f6 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-common-4.1.73.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-common-4.1.73.Final.jar.sha1 deleted file mode 100644 index e80a6e2569d81..0000000000000 --- a/plugins/transport-nio/licenses/netty-common-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -27731b58d741b6faa6a00fa3285e7a55cc47be01 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-common-4.1.99.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-common-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..d53adfa649f5f --- /dev/null +++ b/plugins/transport-nio/licenses/netty-common-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +278f6dfa49d6bd75c40ae1470eb165716f87dce0 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-handler-4.1.73.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-handler-4.1.73.Final.jar.sha1 deleted file mode 100644 index 0e227997874bf..0000000000000 --- a/plugins/transport-nio/licenses/netty-handler-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1a2231c0074f88254865c3769a4b5842939ea04d \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-handler-4.1.99.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-handler-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..258f7c957dda0 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-handler-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +742693761d7ea4c038bccfda96bb38194720b80d \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-resolver-4.1.73.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-resolver-4.1.73.Final.jar.sha1 deleted file mode 100644 index ba24531724fb5..0000000000000 --- a/plugins/transport-nio/licenses/netty-resolver-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -bfe83710f0c1739019613e81a06101020ca65def \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-resolver-4.1.99.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-resolver-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..b8bc0a4370f58 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-resolver-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +080e45397d9d5b134477de3ffd0f94283b908621 \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-transport-4.1.73.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-transport-4.1.73.Final.jar.sha1 deleted file mode 100644 index 6a8647497f210..0000000000000 --- a/plugins/transport-nio/licenses/netty-transport-4.1.73.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -abb155ddff196ccedfe85b810d4b9375ef85fcfa \ No newline at end of file diff --git a/plugins/transport-nio/licenses/netty-transport-4.1.99.Final.jar.sha1 b/plugins/transport-nio/licenses/netty-transport-4.1.99.Final.jar.sha1 new file mode 100644 index 0000000000000..247975e0a64c7 --- /dev/null +++ b/plugins/transport-nio/licenses/netty-transport-4.1.99.Final.jar.sha1 @@ -0,0 +1 @@ +9ca2e3ae19a6713b749df154622115f480b6716c \ No newline at end of file diff --git a/plugins/transport-nio/src/internalClusterTest/java/org/opensearch/http/nio/NioPipeliningIT.java b/plugins/transport-nio/src/internalClusterTest/java/org/opensearch/http/nio/NioPipeliningIT.java index ac06bf03ed8cd..4f26e8ae65259 100644 --- a/plugins/transport-nio/src/internalClusterTest/java/org/opensearch/http/nio/NioPipeliningIT.java +++ b/plugins/transport-nio/src/internalClusterTest/java/org/opensearch/http/nio/NioPipeliningIT.java @@ -32,9 +32,8 @@ package org.opensearch.http.nio; -import io.netty.handler.codec.http.FullHttpResponse; import org.opensearch.NioIntegTestCase; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.http.HttpServerTransport; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; @@ -42,6 +41,8 @@ import java.util.Collection; import java.util.Locale; +import io.netty.handler.codec.http.FullHttpResponse; + import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; diff --git a/plugins/transport-nio/src/internalClusterTest/java/org/opensearch/transport/nio/NioTransportLoggingIT.java b/plugins/transport-nio/src/internalClusterTest/java/org/opensearch/transport/nio/NioTransportLoggingIT.java index 25de433e3489f..2be7730cff9e9 100644 --- a/plugins/transport-nio/src/internalClusterTest/java/org/opensearch/transport/nio/NioTransportLoggingIT.java +++ b/plugins/transport-nio/src/internalClusterTest/java/org/opensearch/transport/nio/NioTransportLoggingIT.java @@ -34,12 +34,11 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; - import org.opensearch.NioIntegTestCase; import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsRequest; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; import org.opensearch.test.MockLogAppender; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.junit.annotations.TestLogging; import org.opensearch.transport.TcpTransport; import org.opensearch.transport.TransportLogger; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/ByteBufUtils.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/ByteBufUtils.java index db9ba4edd644f..d6665607af5d3 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/ByteBufUtils.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/ByteBufUtils.java @@ -31,19 +31,20 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefIterator; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; + class ByteBufUtils { /** diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/HttpReadWriteHandler.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/HttpReadWriteHandler.java index 561695c06effe..d44515f3dc727 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/HttpReadWriteHandler.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/HttpReadWriteHandler.java @@ -32,13 +32,6 @@ package org.opensearch.http.nio; -import io.netty.channel.ChannelHandler; -import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.http.HttpContentCompressor; -import io.netty.handler.codec.http.HttpContentDecompressor; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; import org.opensearch.common.unit.TimeValue; import org.opensearch.http.HttpHandlingSettings; import org.opensearch.http.HttpPipelinedRequest; @@ -58,6 +51,14 @@ import java.util.function.BiConsumer; import java.util.function.LongSupplier; +import io.netty.channel.ChannelHandler; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.http.HttpContentCompressor; +import io.netty.handler.codec.http.HttpContentDecompressor; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; + public class HttpReadWriteHandler implements NioChannelHandler { private final NettyAdaptor adaptor; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NettyAdaptor.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NettyAdaptor.java index 9cb224aa8decf..0b7f4ee7646d1 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NettyAdaptor.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NettyAdaptor.java @@ -32,14 +32,6 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; -import io.netty.channel.embedded.EmbeddedChannel; import org.opensearch.ExceptionsHelper; import org.opensearch.nio.FlushOperation; import org.opensearch.nio.Page; @@ -49,6 +41,15 @@ import java.util.LinkedList; import java.util.function.BiConsumer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import io.netty.channel.embedded.EmbeddedChannel; + class NettyAdaptor { private final EmbeddedChannel nettyChannel; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NettyListener.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NettyListener.java index 80b46ec99f69c..e939dbc07e471 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NettyListener.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NettyListener.java @@ -32,10 +32,6 @@ package org.opensearch.http.nio; -import io.netty.channel.Channel; -import io.netty.channel.ChannelPromise; -import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.GenericFutureListener; import org.opensearch.ExceptionsHelper; import org.opensearch.common.util.concurrent.FutureUtils; @@ -44,6 +40,11 @@ import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; +import io.netty.channel.Channel; +import io.netty.channel.ChannelPromise; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; + /** * This is an {@link BiConsumer} that interfaces with netty code. It wraps a netty promise and will * complete that promise when accept is called. It delegates the normal promise methods to the underlying diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpChannel.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpChannel.java index a20bb55458951..6c341b07f433d 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpChannel.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpChannel.java @@ -32,7 +32,7 @@ package org.opensearch.http.nio; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; import org.opensearch.http.HttpChannel; import org.opensearch.http.HttpResponse; import org.opensearch.nio.NioSocketChannel; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpPipeliningHandler.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpPipeliningHandler.java index aa173b51f61d1..ed1e95b1b8c69 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpPipeliningHandler.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpPipeliningHandler.java @@ -32,9 +32,6 @@ package org.opensearch.http.nio; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; import org.apache.logging.log4j.Logger; import org.opensearch.common.collect.Tuple; import org.opensearch.http.HttpPipelinedRequest; @@ -45,6 +42,10 @@ import java.nio.channels.ClosedChannelException; import java.util.List; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; + /** * Implements HTTP pipelining ordering, ensuring that responses are completely served in the same order as their corresponding requests. */ diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpRequest.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpRequest.java index 73c603ffc059e..d25ef33c2ce29 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpRequest.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpRequest.java @@ -32,22 +32,10 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultHttpHeaders; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.ServerCookieDecoder; -import io.netty.handler.codec.http.cookie.ServerCookieEncoder; - -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; import org.opensearch.http.HttpRequest; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; import java.util.AbstractMap; import java.util.Collection; @@ -58,6 +46,18 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.ServerCookieDecoder; +import io.netty.handler.codec.http.cookie.ServerCookieEncoder; + public class NioHttpRequest implements HttpRequest { private final FullHttpRequest request; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpRequestCreator.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpRequestCreator.java index b75323b017282..27e43f4eef386 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpRequestCreator.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpRequestCreator.java @@ -32,13 +32,14 @@ package org.opensearch.http.nio; +import org.opensearch.ExceptionsHelper; + +import java.util.List; + import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.http.FullHttpRequest; -import org.opensearch.ExceptionsHelper; - -import java.util.List; @ChannelHandler.Sharable class NioHttpRequestCreator extends MessageToMessageDecoder { diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpResponse.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpResponse.java index 4284b0e2948bc..c349ee14bc70f 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpResponse.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpResponse.java @@ -32,15 +32,15 @@ package org.opensearch.http.nio; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.http.HttpResponse; + import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.http.HttpResponse; -import org.opensearch.rest.RestStatus; - public class NioHttpResponse extends DefaultFullHttpResponse implements HttpResponse { private final HttpHeaders requestHeaders; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpResponseCreator.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpResponseCreator.java index e63ec57713b99..93822b57ec40f 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpResponseCreator.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpResponseCreator.java @@ -32,6 +32,11 @@ package org.opensearch.http.nio; +import org.opensearch.common.Booleans; +import org.opensearch.monitor.jvm.JvmInfo; + +import java.util.List; + import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; @@ -40,10 +45,6 @@ import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.DefaultLastHttpContent; import io.netty.handler.codec.http.HttpResponse; -import org.opensearch.common.Booleans; -import org.opensearch.monitor.jvm.JvmInfo; - -import java.util.List; /** * Split up large responses to prevent batch compression or other CPU intensive operations down the pipeline. diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpServerChannel.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpServerChannel.java index 781a4f03da952..2c47bf009f2a3 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpServerChannel.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpServerChannel.java @@ -32,7 +32,7 @@ package org.opensearch.http.nio; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; import org.opensearch.http.HttpServerChannel; import org.opensearch.nio.NioServerSocketChannel; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpServerTransport.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpServerTransport.java index 5d480794d3920..ecf9ad9f17f87 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpServerTransport.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/NioHttpServerTransport.java @@ -34,17 +34,16 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.util.BigArrays; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.http.AbstractHttpServerTransport; import org.opensearch.http.HttpChannel; import org.opensearch.http.HttpServerChannel; @@ -57,6 +56,7 @@ import org.opensearch.nio.NioSocketChannel; import org.opensearch.nio.ServerChannelContext; import org.opensearch.nio.SocketChannelContext; +import org.opensearch.telemetry.tracing.Tracer; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.nio.NioGroupFactory; import org.opensearch.transport.nio.PageAllocator; @@ -107,9 +107,10 @@ public NioHttpServerTransport( NamedXContentRegistry xContentRegistry, Dispatcher dispatcher, NioGroupFactory nioGroupFactory, - ClusterSettings clusterSettings + ClusterSettings clusterSettings, + Tracer tracer ) { - super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings); + super(settings, networkService, bigArrays, threadPool, xContentRegistry, dispatcher, clusterSettings, tracer); this.pageAllocator = new PageAllocator(pageCacheRecycler); this.nioGroupFactory = nioGroupFactory; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/PagedByteBuf.java b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/PagedByteBuf.java index 4dc9c0935be67..221b2104e9904 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/http/nio/PagedByteBuf.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/http/nio/PagedByteBuf.java @@ -32,17 +32,18 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.buffer.UnpooledHeapByteBuf; import org.opensearch.nio.Page; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.buffer.UnpooledHeapByteBuf; + public class PagedByteBuf extends UnpooledHeapByteBuf { private final Runnable releasable; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTcpChannel.java b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTcpChannel.java index 81a07fc646907..3aa02d11dfd75 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTcpChannel.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTcpChannel.java @@ -32,8 +32,8 @@ package org.opensearch.transport.nio; -import org.opensearch.action.ActionListener; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.nio.NioSocketChannel; import org.opensearch.transport.TcpChannel; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTcpServerChannel.java b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTcpServerChannel.java index 69218f2f1fcf5..a0fb2ef4cfdf2 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTcpServerChannel.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTcpServerChannel.java @@ -32,7 +32,7 @@ package org.opensearch.transport.nio; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; import org.opensearch.nio.NioServerSocketChannel; import org.opensearch.transport.TcpServerChannel; diff --git a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTransport.java b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTransport.java index b12247c9c711c..55920bab4efd3 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTransport.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTransport.java @@ -34,17 +34,16 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.indices.breaker.CircuitBreakerService; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.indices.breaker.CircuitBreakerService; import org.opensearch.nio.BytesChannelContext; import org.opensearch.nio.ChannelFactory; import org.opensearch.nio.Config; @@ -53,6 +52,7 @@ import org.opensearch.nio.NioSelector; import org.opensearch.nio.NioSocketChannel; import org.opensearch.nio.ServerChannelContext; +import org.opensearch.telemetry.tracing.Tracer; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TcpTransport; import org.opensearch.transport.TransportSettings; @@ -85,9 +85,10 @@ protected NioTransport( PageCacheRecycler pageCacheRecycler, NamedWriteableRegistry namedWriteableRegistry, CircuitBreakerService circuitBreakerService, - NioGroupFactory groupFactory + NioGroupFactory groupFactory, + Tracer tracer ) { - super(settings, version, threadPool, pageCacheRecycler, circuitBreakerService, namedWriteableRegistry, networkService); + super(settings, version, threadPool, pageCacheRecycler, circuitBreakerService, namedWriteableRegistry, networkService, tracer); this.pageAllocator = new PageAllocator(pageCacheRecycler); this.groupFactory = groupFactory; } diff --git a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTransportPlugin.java b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTransportPlugin.java index f8816e0686e9d..d4be876867651 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTransportPlugin.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/NioTransportPlugin.java @@ -34,9 +34,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.lucene.util.SetOnce; import org.opensearch.Version; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.SetOnce; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; @@ -44,12 +43,14 @@ import org.opensearch.common.util.BigArrays; import org.opensearch.common.util.PageCacheRecycler; import org.opensearch.common.util.concurrent.OpenSearchExecutors; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.indices.breaker.CircuitBreakerService; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.http.HttpServerTransport; import org.opensearch.http.nio.NioHttpServerTransport; -import org.opensearch.indices.breaker.CircuitBreakerService; import org.opensearch.plugins.NetworkPlugin; import org.opensearch.plugins.Plugin; +import org.opensearch.telemetry.tracing.Tracer; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.Transport; @@ -90,7 +91,8 @@ public Map> getTransports( PageCacheRecycler pageCacheRecycler, CircuitBreakerService circuitBreakerService, NamedWriteableRegistry namedWriteableRegistry, - NetworkService networkService + NetworkService networkService, + Tracer tracer ) { return Collections.singletonMap( NIO_TRANSPORT_NAME, @@ -102,7 +104,8 @@ public Map> getTransports( pageCacheRecycler, namedWriteableRegistry, circuitBreakerService, - getNioGroupFactory(settings) + getNioGroupFactory(settings), + tracer ) ); } @@ -117,7 +120,8 @@ public Map> getHttpTransports( NamedXContentRegistry xContentRegistry, NetworkService networkService, HttpServerTransport.Dispatcher dispatcher, - ClusterSettings clusterSettings + ClusterSettings clusterSettings, + Tracer tracer ) { return Collections.singletonMap( NIO_HTTP_TRANSPORT_NAME, @@ -130,7 +134,8 @@ public Map> getHttpTransports( xContentRegistry, dispatcher, getNioGroupFactory(settings), - clusterSettings + clusterSettings, + tracer ) ); } diff --git a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/TcpReadWriteHandler.java b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/TcpReadWriteHandler.java index 1427429f0b766..0c90deed6411c 100644 --- a/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/TcpReadWriteHandler.java +++ b/plugins/transport-nio/src/main/java/org/opensearch/transport/nio/TcpReadWriteHandler.java @@ -32,14 +32,14 @@ package org.opensearch.transport.nio; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.bytes.CompositeBytesReference; import org.opensearch.common.bytes.ReleasableBytesReference; import org.opensearch.common.lease.Releasable; import org.opensearch.common.lease.Releasables; import org.opensearch.common.util.PageCacheRecycler; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.CompositeBytesReference; import org.opensearch.nio.BytesWriteHandler; import org.opensearch.nio.InboundChannelBuffer; import org.opensearch.nio.Page; diff --git a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/HttpReadWriteHandlerTests.java b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/HttpReadWriteHandlerTests.java index 072664d992b78..a3f7a7822cd40 100644 --- a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/HttpReadWriteHandlerTests.java +++ b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/HttpReadWriteHandlerTests.java @@ -32,25 +32,11 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelPromise; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestEncoder; -import io.netty.handler.codec.http.HttpResponseDecoder; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; - -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.rest.RestStatus; import org.opensearch.http.CorsHandler; import org.opensearch.http.HttpChannel; import org.opensearch.http.HttpHandlingSettings; @@ -63,11 +49,8 @@ import org.opensearch.nio.SocketChannelContext; import org.opensearch.nio.TaskScheduler; import org.opensearch.rest.RestRequest; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchTestCase; - import org.junit.Before; -import org.mockito.ArgumentCaptor; import java.io.IOException; import java.nio.ByteBuffer; @@ -76,12 +59,28 @@ import java.util.List; import java.util.function.BiConsumer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelPromise; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestEncoder; +import io.netty.handler.codec.http.HttpResponseDecoder; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.HttpVersion; +import org.mockito.ArgumentCaptor; + import static org.opensearch.http.HttpTransportSettings.SETTING_HTTP_MAX_CONTENT_LENGTH; import static org.opensearch.http.HttpTransportSettings.SETTING_HTTP_READ_TIMEOUT; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NettyAdaptorTests.java b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NettyAdaptorTests.java index 9ba27802822ea..21634725d5279 100644 --- a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NettyAdaptorTests.java +++ b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NettyAdaptorTests.java @@ -32,13 +32,6 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; -import io.netty.channel.SimpleChannelInboundHandler; import org.opensearch.nio.FlushOperation; import org.opensearch.test.OpenSearchTestCase; @@ -47,6 +40,14 @@ import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import io.netty.channel.SimpleChannelInboundHandler; + public class NettyAdaptorTests extends OpenSearchTestCase { public void testBasicRead() { diff --git a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpClient.java b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpClient.java index 1229aab244a6d..45e51c6855f79 100644 --- a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpClient.java +++ b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpClient.java @@ -32,28 +32,15 @@ package org.opensearch.http.nio; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandler; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpContentDecompressor; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpRequestEncoder; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseDecoder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.nio.BytesChannelContext; import org.opensearch.nio.ChannelFactory; import org.opensearch.nio.Config; @@ -83,9 +70,23 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandler; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpContentDecompressor; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpRequestEncoder; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseDecoder; + +import static org.opensearch.common.util.concurrent.OpenSearchExecutors.daemonThreadFactory; import static io.netty.handler.codec.http.HttpHeaderNames.HOST; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static org.opensearch.common.util.concurrent.OpenSearchExecutors.daemonThreadFactory; import static org.junit.Assert.fail; /** diff --git a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpPipeliningHandlerTests.java b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpPipeliningHandlerTests.java index 70da3496c2534..46cf6ae708d1c 100644 --- a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpPipeliningHandlerTests.java +++ b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpPipeliningHandlerTests.java @@ -32,26 +32,15 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPromise; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.embedded.EmbeddedChannel; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.QueryStringDecoder; - import org.opensearch.common.Randomness; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; import org.opensearch.http.HttpPipelinedRequest; import org.opensearch.http.HttpPipelinedResponse; import org.opensearch.http.HttpRequest; import org.opensearch.http.HttpResponse; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchTestCase; - import org.junit.After; import java.nio.channels.ClosedChannelException; @@ -67,9 +56,19 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.QueryStringDecoder; + +import static org.hamcrest.core.Is.is; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static org.hamcrest.core.Is.is; public class NioHttpPipeliningHandlerTests extends OpenSearchTestCase { diff --git a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpServerTransportTests.java b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpServerTransportTests.java index 9106e9608c778..09594673de5b2 100644 --- a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpServerTransportTests.java +++ b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/NioHttpServerTransportTests.java @@ -32,49 +32,36 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; -import io.netty.handler.codec.TooLongFrameException; -import io.netty.handler.codec.http.DefaultFullHttpRequest; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaderNames; -import io.netty.handler.codec.http.HttpHeaderValues; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpUtil; -import io.netty.handler.codec.http.HttpVersion; import org.apache.logging.log4j.message.ParameterizedMessage; - import org.opensearch.OpenSearchException; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.network.NetworkAddress; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.MockBigArrays; import org.opensearch.common.util.MockPageCacheRecycler; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; import org.opensearch.http.BindHttpException; import org.opensearch.http.CorsHandler; import org.opensearch.http.HttpServerTransport; import org.opensearch.http.HttpTransportSettings; import org.opensearch.http.NullDispatcher; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; import org.opensearch.nio.NioSocketChannel; import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestChannel; import org.opensearch.rest.RestRequest; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.rest.FakeRestRequest; import org.opensearch.threadpool.TestThreadPool; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.nio.NioGroupFactory; - import org.junit.After; import org.junit.Before; @@ -86,10 +73,23 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.TooLongFrameException; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.HttpVersion; + +import static org.opensearch.core.rest.RestStatus.BAD_REQUEST; +import static org.opensearch.core.rest.RestStatus.OK; import static org.opensearch.http.HttpTransportSettings.SETTING_CORS_ALLOW_ORIGIN; import static org.opensearch.http.HttpTransportSettings.SETTING_CORS_ENABLED; -import static org.opensearch.rest.RestStatus.BAD_REQUEST; -import static org.opensearch.rest.RestStatus.OK; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -187,7 +187,8 @@ public void dispatchBadRequest(RestChannel channel, ThreadContext threadContext, xContentRegistry(), dispatcher, new NioGroupFactory(settings, logger), - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS) + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -237,7 +238,8 @@ public void testBindUnavailableAddress() { xContentRegistry(), new NullDispatcher(), new NioGroupFactory(Settings.EMPTY, logger), - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS) + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -256,7 +258,8 @@ public void testBindUnavailableAddress() { xContentRegistry(), new NullDispatcher(), new NioGroupFactory(Settings.EMPTY, logger), - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS) + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + NoopTracer.INSTANCE ) ) { BindHttpException bindHttpException = expectThrows(BindHttpException.class, () -> otherTransport.start()); @@ -299,7 +302,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th xContentRegistry(), dispatcher, new NioGroupFactory(settings, logger), - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS) + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -373,7 +377,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th xContentRegistry(), dispatcher, new NioGroupFactory(Settings.EMPTY, logger), - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS) + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -439,7 +444,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th xContentRegistry(), dispatcher, new NioGroupFactory(settings, logger), - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS) + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + NoopTracer.INSTANCE ) ) { transport.start(); @@ -501,7 +507,8 @@ public void dispatchBadRequest(final RestChannel channel, final ThreadContext th xContentRegistry(), dispatcher, new NioGroupFactory(settings, logger), - new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS) + new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), + NoopTracer.INSTANCE ) ) { transport.start(); diff --git a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/PagedByteBufTests.java b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/PagedByteBufTests.java index de53cdad104df..c540c1854e509 100644 --- a/plugins/transport-nio/src/test/java/org/opensearch/http/nio/PagedByteBufTests.java +++ b/plugins/transport-nio/src/test/java/org/opensearch/http/nio/PagedByteBufTests.java @@ -32,7 +32,6 @@ package org.opensearch.http.nio; -import io.netty.buffer.ByteBuf; import org.opensearch.nio.Page; import org.opensearch.test.OpenSearchTestCase; @@ -40,6 +39,8 @@ import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; +import io.netty.buffer.ByteBuf; + public class PagedByteBufTests extends OpenSearchTestCase { public void testReleasingPage() { diff --git a/plugins/transport-nio/src/test/java/org/opensearch/transport/nio/SimpleNioTransportTests.java b/plugins/transport-nio/src/test/java/org/opensearch/transport/nio/SimpleNioTransportTests.java index c8b9fa63383bf..f5d1c618f5ace 100644 --- a/plugins/transport-nio/src/test/java/org/opensearch/transport/nio/SimpleNioTransportTests.java +++ b/plugins/transport-nio/src/test/java/org/opensearch/transport/nio/SimpleNioTransportTests.java @@ -33,18 +33,18 @@ package org.opensearch.transport.nio; import org.opensearch.Version; -import org.opensearch.action.ActionListener; -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.network.NetworkService; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.util.MockPageCacheRecycler; -import org.opensearch.core.internal.io.IOUtils; -import org.opensearch.core.internal.net.NetUtils; -import org.opensearch.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.common.util.net.NetUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.indices.breaker.NoneCircuitBreakerService; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.transport.MockTransportService; import org.opensearch.test.transport.StubbableTransport; import org.opensearch.transport.AbstractSimpleTransportTestCase; @@ -82,7 +82,8 @@ protected Transport build(Settings settings, final Version version, ClusterSetti new MockPageCacheRecycler(settings), namedWriteableRegistry, new NoneCircuitBreakerService(), - new NioGroupFactory(settings, logger) + new NioGroupFactory(settings, logger), + NoopTracer.INSTANCE ) { @Override @@ -122,10 +123,7 @@ public void testConnectException() throws UnknownHostException { } public void testDefaultKeepAliveSettings() throws IOException { - assumeTrue( - "setting default keepalive options not supported on this platform", - (IOUtils.LINUX || IOUtils.MAC_OS_X) && JavaVersion.current().compareTo(JavaVersion.parse("11")) >= 0 - ); + assumeTrue("setting default keepalive options not supported on this platform", (IOUtils.LINUX || IOUtils.MAC_OS_X)); try ( MockTransportService serviceC = buildService("TS_C", Version.CURRENT, Settings.EMPTY); MockTransportService serviceD = buildService("TS_D", Version.CURRENT, Settings.EMPTY) diff --git a/protobuf-java-NOTICE.txt b/protobuf-java-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/qa/ccs-unavailable-clusters/src/test/java/org/opensearch/search/CrossClusterSearchUnavailableClusterIT.java b/qa/ccs-unavailable-clusters/src/test/java/org/opensearch/search/CrossClusterSearchUnavailableClusterIT.java index 5691154882c9f..c6380c69a1c95 100644 --- a/qa/ccs-unavailable-clusters/src/test/java/org/opensearch/search/CrossClusterSearchUnavailableClusterIT.java +++ b/qa/ccs-unavailable-clusters/src/test/java/org/opensearch/search/CrossClusterSearchUnavailableClusterIT.java @@ -61,12 +61,13 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.search.aggregations.InternalAggregations; import org.opensearch.search.internal.InternalSearchResponse; +import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.rest.OpenSearchRestTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.threadpool.TestThreadPool; @@ -118,7 +119,7 @@ private static MockTransportService startTransport( boolean success = false; final Settings s = Settings.builder().put("node.name", id).build(); ClusterName clusterName = ClusterName.CLUSTER_NAME_SETTING.get(s); - MockTransportService newService = MockTransportService.createNewService(s, version, threadPool, null); + MockTransportService newService = MockTransportService.createNewService(s, version, threadPool, NoopTracer.INSTANCE); try { newService.registerRequestHandler(ClusterSearchShardsAction.NAME, ThreadPool.Names.SAME, ClusterSearchShardsRequest::new, (request, channel, task) -> { @@ -341,7 +342,7 @@ private static HttpEntity buildUpdateSettingsRequestBody(Map set builder.endObject(); } builder.endObject(); - requestBody = Strings.toString(builder); + requestBody = builder.toString(); } return new NStringEntity(requestBody, ContentType.APPLICATION_JSON); } diff --git a/qa/die-with-dignity/build.gradle b/qa/die-with-dignity/build.gradle index 008e2e19bf72f..db8762fe921bf 100644 --- a/qa/die-with-dignity/build.gradle +++ b/qa/die-with-dignity/build.gradle @@ -35,4 +35,3 @@ testClusters.javaRestTest { } test.enabled = false - diff --git a/qa/die-with-dignity/src/javaRestTest/java/org/opensearch/qa/die_with_dignity/DieWithDignityIT.java b/qa/die-with-dignity/src/javaRestTest/java/org/opensearch/qa/die_with_dignity/DieWithDignityIT.java index ec891ef8d44ef..8a2bb3e8be38a 100644 --- a/qa/die-with-dignity/src/javaRestTest/java/org/opensearch/qa/die_with_dignity/DieWithDignityIT.java +++ b/qa/die-with-dignity/src/javaRestTest/java/org/opensearch/qa/die_with_dignity/DieWithDignityIT.java @@ -76,7 +76,7 @@ public void testDieWithDignity() throws Exception { try { while (it.hasNext() && (fatalError == false || fatalErrorInThreadExiting == false)) { final String line = it.next(); - if (line.matches(".*ERROR.*o\\.o\\.ExceptionsHelper.*javaRestTest-0.*fatal error.*")) { + if (line.matches(".*ERROR.*o\\.o\\.(Base)?ExceptionsHelper.*javaRestTest-0.*fatal error.*")) { fatalError = true; } else if (line.matches( ".*ERROR.*o\\.o\\.b\\.OpenSearchUncaughtExceptionHandler.*javaRestTest-0.*" diff --git a/qa/evil-tests/build.gradle b/qa/evil-tests/build.gradle index 19dc72c0c784f..fab925d887847 100644 --- a/qa/evil-tests/build.gradle +++ b/qa/evil-tests/build.gradle @@ -40,7 +40,7 @@ apply plugin: 'opensearch.testclusters' apply plugin: 'opensearch.standalone-test' dependencies { - testImplementation 'com.google.jimfs:jimfs:1.2' + testImplementation 'com.google.jimfs:jimfs:1.3.0' } // TODO: give each evil test its own fresh JVM for more isolation. @@ -62,7 +62,6 @@ thirdPartyAudit { 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$1', 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$2', - 'com.google.common.hash.LittleEndianByteArray$UnsafeByteArray$3', 'com.google.common.hash.Striped64', 'com.google.common.hash.Striped64$1', 'com.google.common.hash.Striped64$Cell', diff --git a/qa/evil-tests/src/test/java/org/opensearch/common/logging/EvilLoggerTests.java b/qa/evil-tests/src/test/java/org/opensearch/common/logging/EvilLoggerTests.java index 9319d11ebae98..a44bd0ee4b32f 100644 --- a/qa/evil-tests/src/test/java/org/opensearch/common/logging/EvilLoggerTests.java +++ b/qa/evil-tests/src/test/java/org/opensearch/common/logging/EvilLoggerTests.java @@ -62,7 +62,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Set; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.regex.Matcher; @@ -299,7 +298,7 @@ public void testNoNodeNameInPatternWarning() throws IOException, UserException { // the first message is a warning for unsupported configuration files assertLogLine(events.get(0), Level.WARN, location, "\\[" + nodeName + "\\] Some logging configurations have " + "%marker but don't have %node_name. We will automatically add %node_name to the pattern to ease the " - + "migration for users who customize log4j2.properties but will stop this behavior in 7.0. You should " + + "migration for users who customize log4j2.properties but will stop this behavior in 3.0. You should " + "manually replace `%node_name` with `\\[%node_name\\]%marker ` in these locations:"); if (Constants.WINDOWS) { assertThat(events.get(1), endsWith("no_node_name\\log4j2.properties")); diff --git a/qa/evil-tests/src/test/resources/org/opensearch/common/cli/tool-cmd1.help b/qa/evil-tests/src/test/resources/org/opensearch/common/cli/tool-cmd1.help index d083e3a65348f..60acabffb6544 100644 --- a/qa/evil-tests/src/test/resources/org/opensearch/common/cli/tool-cmd1.help +++ b/qa/evil-tests/src/test/resources/org/opensearch/common/cli/tool-cmd1.help @@ -1 +1 @@ -cmd1 help \ No newline at end of file +cmd1 help diff --git a/qa/evil-tests/src/test/resources/org/opensearch/common/cli/tool.help b/qa/evil-tests/src/test/resources/org/opensearch/common/cli/tool.help index 023b1accdffb1..2a5850ba79db2 100644 --- a/qa/evil-tests/src/test/resources/org/opensearch/common/cli/tool.help +++ b/qa/evil-tests/src/test/resources/org/opensearch/common/cli/tool.help @@ -1 +1 @@ -tool help \ No newline at end of file +tool help diff --git a/qa/full-cluster-restart/src/test/java/org/opensearch/upgrades/FullClusterRestartIT.java b/qa/full-cluster-restart/src/test/java/org/opensearch/upgrades/FullClusterRestartIT.java index a8302fdd6bc76..53bac56ab8675 100644 --- a/qa/full-cluster-restart/src/test/java/org/opensearch/upgrades/FullClusterRestartIT.java +++ b/qa/full-cluster-restart/src/test/java/org/opensearch/upgrades/FullClusterRestartIT.java @@ -43,11 +43,13 @@ import org.opensearch.cluster.metadata.MetadataIndexStateService; import org.opensearch.common.Booleans; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.test.NotEqualMessageBuilder; import org.opensearch.test.XContentTestUtils; @@ -149,7 +151,7 @@ public void testSearch() throws Exception { mappingsAndSettings.endObject(); Request createIndex = new Request("PUT", "/" + index); - createIndex.setJsonEntity(Strings.toString(mappingsAndSettings)); + createIndex.setJsonEntity(mappingsAndSettings.toString()); createIndex.setOptions(allowTypesRemovalWarnings()); client().performRequest(createIndex); @@ -208,7 +210,7 @@ public void testNewReplicasWork() throws Exception { mappingsAndSettings.endObject(); Request createIndex = new Request("PUT", "/" + index); - createIndex.setJsonEntity(Strings.toString(mappingsAndSettings)); + createIndex.setJsonEntity(mappingsAndSettings.toString()); client().performRequest(createIndex); int numDocs = randomIntBetween(2000, 3000); @@ -257,7 +259,7 @@ public void testClusterState() throws Exception { } mappingsAndSettings.endObject(); Request createTemplate = new Request("PUT", "/_template/template_1"); - createTemplate.setJsonEntity(Strings.toString(mappingsAndSettings)); + createTemplate.setJsonEntity(mappingsAndSettings.toString()); client().performRequest(createTemplate); client().performRequest(new Request("PUT", "/" + index)); } @@ -315,7 +317,7 @@ public void testShrink() throws IOException { mappingsAndSettings.endObject(); Request createIndex = new Request("PUT", "/" + index); - createIndex.setJsonEntity(Strings.toString(mappingsAndSettings)); + createIndex.setJsonEntity(mappingsAndSettings.toString()); client().performRequest(createIndex); numDocs = randomIntBetween(512, 1024); @@ -387,7 +389,7 @@ public void testShrinkAfterUpgrade() throws IOException { mappingsAndSettings.endObject(); Request createIndex = new Request("PUT", "/" + index); - createIndex.setJsonEntity(Strings.toString(mappingsAndSettings)); + createIndex.setJsonEntity(mappingsAndSettings.toString()); client().performRequest(createIndex); numDocs = randomIntBetween(512, 1024); @@ -659,7 +661,7 @@ public void testEmptyShard() throws IOException { .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) // if the node with the replica is the first to be restarted, while a replica is still recovering - // then delayed allocation will kick in. When the node comes back, the master will search for a copy + // then delayed allocation will kick in. When the node comes back, the cluster-manager will search for a copy // but the recovering copy will be seen as invalid and the cluster health won't return to GREEN // before timing out .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") @@ -857,7 +859,7 @@ public void testSnapshotRestore() throws IOException { } templateBuilder.endObject().endObject(); Request createTemplateRequest = new Request("PUT", "/_template/test_template"); - createTemplateRequest.setJsonEntity(Strings.toString(templateBuilder)); + createTemplateRequest.setJsonEntity(templateBuilder.toString()); client().performRequest(createTemplateRequest); @@ -873,7 +875,7 @@ public void testSnapshotRestore() throws IOException { } repoConfig.endObject(); Request createRepoRequest = new Request("PUT", "/_snapshot/repo"); - createRepoRequest.setJsonEntity(Strings.toString(repoConfig)); + createRepoRequest.setJsonEntity(repoConfig.toString()); client().performRequest(createRepoRequest); } @@ -900,7 +902,7 @@ public void testHistoryUUIDIsAdded() throws Exception { } mappingsAndSettings.endObject(); Request createIndex = new Request("PUT", "/" + index); - createIndex.setJsonEntity(Strings.toString(mappingsAndSettings)); + createIndex.setJsonEntity(mappingsAndSettings.toString()); client().performRequest(createIndex); } else { ensureGreenLongWait(index); @@ -943,11 +945,11 @@ public void testSoftDeletes() throws Exception { } mappingsAndSettings.endObject(); Request createIndex = new Request("PUT", "/" + index); - createIndex.setJsonEntity(Strings.toString(mappingsAndSettings)); + createIndex.setJsonEntity(mappingsAndSettings.toString()); client().performRequest(createIndex); int numDocs = between(10, 100); for (int i = 0; i < numDocs; i++) { - String doc = Strings.toString(JsonXContent.contentBuilder().startObject().field("field", "v1").endObject()); + String doc = JsonXContent.contentBuilder().startObject().field("field", "v1").endObject().toString(); Request request = new Request("POST", "/" + index + "/_doc/" + i); request.setJsonEntity(doc); client().performRequest(request); @@ -958,7 +960,7 @@ public void testSoftDeletes() throws Exception { assertTotalHits(liveDocs, entityAsMap(client().performRequest(new Request("GET", "/" + index + "/_search")))); for (int i = 0; i < numDocs; i++) { if (randomBoolean()) { - String doc = Strings.toString(JsonXContent.contentBuilder().startObject().field("field", "v2").endObject()); + String doc = JsonXContent.contentBuilder().startObject().field("field", "v2").endObject().toString(); Request request = new Request("POST", "/" + index + "/_doc/" + i); request.setJsonEntity(doc); client().performRequest(request); @@ -992,7 +994,7 @@ public void testClosedIndices() throws Exception { numDocs = between(1, 100); for (int i = 0; i < numDocs; i++) { final Request request = new Request("POST", "/" + index + "/" + type + "/" + i); - request.setJsonEntity(Strings.toString(JsonXContent.contentBuilder().startObject().field("field", "v1").endObject())); + request.setJsonEntity(JsonXContent.contentBuilder().startObject().field("field", "v1").endObject().toString()); assertOK(client().performRequest(request)); if (rarely()) { refresh(); @@ -1090,7 +1092,7 @@ private void checkSnapshot(final String snapshotName, final int count, final Ver restoreCommand.endObject(); Request restoreRequest = new Request("POST", "/_snapshot/repo/" + snapshotName + "/_restore"); restoreRequest.addParameter("wait_for_completion", "true"); - restoreRequest.setJsonEntity(Strings.toString(restoreCommand)); + restoreRequest.setJsonEntity(restoreCommand.toString()); client().performRequest(restoreRequest); // Make sure search finds all documents @@ -1165,7 +1167,7 @@ private void indexRandomDocuments( for (int i = 0; i < count; i++) { logger.debug("Indexing document [{}]", i); Request createDocument = new Request("POST", "/" + index + "/_doc/" + (specifyId ? i : "")); - createDocument.setJsonEntity(Strings.toString(docSupplier.apply(i))); + createDocument.setJsonEntity(docSupplier.apply(i).toString()); client().performRequest(createDocument); if (rarely()) { refreshAllIndices(); @@ -1182,7 +1184,7 @@ private void indexRandomDocuments( private void indexDocument(String id) throws IOException { final Request indexRequest = new Request("POST", "/" + index + "/" + type + "/" + id); - indexRequest.setJsonEntity(Strings.toString(JsonXContent.contentBuilder().startObject().field("f", "v").endObject())); + indexRequest.setJsonEntity(JsonXContent.contentBuilder().startObject().field("f", "v").endObject().toString()); assertOK(client().performRequest(indexRequest)); } @@ -1197,7 +1199,7 @@ private void saveInfoDocument(String id, String value) throws IOException { // Only create the first version so we know how many documents are created when the index is first created Request request = new Request("PUT", "/info/" + type + "/" + id); request.addParameter("op_type", "create"); - request.setJsonEntity(Strings.toString(infoDoc)); + request.setJsonEntity(infoDoc.toString()); client().performRequest(request); } @@ -1262,7 +1264,7 @@ public void testPeerRecoveryRetentionLeases() throws Exception { settings.endObject(); Request createIndex = new Request("PUT", "/" + index); - createIndex.setJsonEntity(Strings.toString(settings)); + createIndex.setJsonEntity(settings.toString()); client().performRequest(createIndex); } ensureGreen(index); @@ -1372,7 +1374,7 @@ public void testResize() throws Exception { if (randomBoolean()) { settings.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true); } - shrinkRequest.setJsonEntity("{\"settings\":" + Strings.toString(settings.build()) + "}"); + shrinkRequest.setJsonEntity("{\"settings\":" + Strings.toString(MediaTypeRegistry.JSON, settings.build()) + "}"); client().performRequest(shrinkRequest); ensureGreenLongWait(target); assertNumHits(target, numDocs + moreDocs, 1); @@ -1384,7 +1386,7 @@ public void testResize() throws Exception { settings.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true); } Request splitRequest = new Request("PUT", "/" + index + "/_split/" + target); - splitRequest.setJsonEntity("{\"settings\":" + Strings.toString(settings.build()) + "}"); + splitRequest.setJsonEntity("{\"settings\":" + Strings.toString(MediaTypeRegistry.JSON, settings.build()) + "}"); client().performRequest(splitRequest); ensureGreenLongWait(target); assertNumHits(target, numDocs + moreDocs, 6); @@ -1523,7 +1525,7 @@ public void testEnableSoftDeletesOnRestore() throws Exception { } repoConfig.endObject(); Request createRepoRequest = new Request("PUT", "/_snapshot/repo"); - createRepoRequest.setJsonEntity(Strings.toString(repoConfig)); + createRepoRequest.setJsonEntity(repoConfig.toString()); client().performRequest(createRepoRequest); // create snapshot Request createSnapshot = new Request("PUT", "/_snapshot/repo/" + snapshot); @@ -1545,7 +1547,7 @@ public void testEnableSoftDeletesOnRestore() throws Exception { restoreCommand.endObject(); Request restoreRequest = new Request("POST", "/_snapshot/repo/" + snapshot + "/_restore"); restoreRequest.addParameter("wait_for_completion", "true"); - restoreRequest.setJsonEntity(Strings.toString(restoreCommand)); + restoreRequest.setJsonEntity(restoreCommand.toString()); client().performRequest(restoreRequest); ensureGreen(restoredIndex); int numDocs = countOfIndexedRandomDocuments(); @@ -1577,7 +1579,7 @@ public void testForbidDisableSoftDeletesOnRestore() throws Exception { } repoConfig.endObject(); Request createRepoRequest = new Request("PUT", "/_snapshot/repo"); - createRepoRequest.setJsonEntity(Strings.toString(repoConfig)); + createRepoRequest.setJsonEntity(repoConfig.toString()); client().performRequest(createRepoRequest); // create snapshot Request createSnapshot = new Request("PUT", "/_snapshot/repo/" + snapshot); @@ -1598,7 +1600,7 @@ public void testForbidDisableSoftDeletesOnRestore() throws Exception { restoreCommand.endObject(); Request restoreRequest = new Request("POST", "/_snapshot/repo/" + snapshot + "/_restore"); restoreRequest.addParameter("wait_for_completion", "true"); - restoreRequest.setJsonEntity(Strings.toString(restoreCommand)); + restoreRequest.setJsonEntity(restoreCommand.toString()); final ResponseException error = expectThrows(ResponseException.class, () -> client().performRequest(restoreRequest)); assertThat(error.getMessage(), containsString("cannot disable setting [index.soft_deletes.enabled] on restore")); } diff --git a/qa/full-cluster-restart/src/test/java/org/opensearch/upgrades/QueryBuilderBWCIT.java b/qa/full-cluster-restart/src/test/java/org/opensearch/upgrades/QueryBuilderBWCIT.java index de042cb2b7634..1648f345104ea 100644 --- a/qa/full-cluster-restart/src/test/java/org/opensearch/upgrades/QueryBuilderBWCIT.java +++ b/qa/full-cluster-restart/src/test/java/org/opensearch/upgrades/QueryBuilderBWCIT.java @@ -36,16 +36,15 @@ import org.opensearch.LegacyESVersion; import org.opensearch.client.Request; import org.opensearch.client.Response; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.InputStreamStreamInput; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.InputStreamStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.Fuzziness; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.ConstantScoreQueryBuilder; import org.opensearch.index.query.DisMaxQueryBuilder; @@ -198,7 +197,7 @@ public void testQueryBuilderBWC() throws Exception { mappingsAndSettings.endObject(); Request request = new Request("PUT", "/" + index); request.setOptions(allowTypesRemovalWarnings()); - request.setJsonEntity(Strings.toString(mappingsAndSettings)); + request.setJsonEntity(mappingsAndSettings.toString()); Response rsp = client().performRequest(request); assertEquals(200, rsp.getStatusLine().getStatusCode()); diff --git a/qa/logging-config/src/test/java/org/opensearch/common/logging/JsonLoggerTests.java b/qa/logging-config/src/test/java/org/opensearch/common/logging/JsonLoggerTests.java index c760677aa3cd0..7fbfd6929ebdf 100644 --- a/qa/logging-config/src/test/java/org/opensearch/common/logging/JsonLoggerTests.java +++ b/qa/logging-config/src/test/java/org/opensearch/common/logging/JsonLoggerTests.java @@ -43,7 +43,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.env.Environment; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.tasks.Task; import org.opensearch.test.OpenSearchTestCase; import org.hamcrest.FeatureMatcher; diff --git a/qa/logging-config/src/test/java/org/opensearch/common/logging/OpenSearchJsonLayoutTests.java b/qa/logging-config/src/test/java/org/opensearch/common/logging/OpenSearchJsonLayoutTests.java index 6639d53c9c879..0a8bd46ac96f3 100644 --- a/qa/logging-config/src/test/java/org/opensearch/common/logging/OpenSearchJsonLayoutTests.java +++ b/qa/logging-config/src/test/java/org/opensearch/common/logging/OpenSearchJsonLayoutTests.java @@ -66,6 +66,46 @@ public void testLayout() { "%exceptionAsJson }" + System.lineSeparator())); } + public void testWithMaxMessageLengthLayout() { + OpenSearchJsonLayout server = OpenSearchJsonLayout.newBuilder() + .setType("server") + .setMaxMessageLength(42) + .build(); + String conversionPattern = server.getPatternLayout().getConversionPattern(); + + assertThat(conversionPattern, Matchers.equalTo( + "{" + + "\"type\": \"server\", " + + "\"timestamp\": \"%d{yyyy-MM-dd'T'HH:mm:ss,SSSZZ}\", " + + "\"level\": \"%p\", " + + "\"component\": \"%c{1.}\", " + + "\"cluster.name\": \"${sys:opensearch.logs.cluster_name}\", " + + "\"node.name\": \"%node_name\", " + + "\"message\": \"%notEmpty{%enc{%marker}{JSON} }%enc{%.-42m}{JSON}\"" + + "%notEmpty{, %node_and_cluster_id }" + + "%exceptionAsJson }" + System.lineSeparator())); + } + + public void testWithUnrestrictedMaxMessageLengthLayout() { + OpenSearchJsonLayout server = OpenSearchJsonLayout.newBuilder() + .setType("server") + .setMaxMessageLength(0) + .build(); + String conversionPattern = server.getPatternLayout().getConversionPattern(); + + assertThat(conversionPattern, Matchers.equalTo( + "{" + + "\"type\": \"server\", " + + "\"timestamp\": \"%d{yyyy-MM-dd'T'HH:mm:ss,SSSZZ}\", " + + "\"level\": \"%p\", " + + "\"component\": \"%c{1.}\", " + + "\"cluster.name\": \"${sys:opensearch.logs.cluster_name}\", " + + "\"node.name\": \"%node_name\", " + + "\"message\": \"%notEmpty{%enc{%marker}{JSON} }%enc{%m}{JSON}\"" + + "%notEmpty{, %node_and_cluster_id }" + + "%exceptionAsJson }" + System.lineSeparator())); + } + public void testLayoutWithAdditionalFields() { OpenSearchJsonLayout server = OpenSearchJsonLayout.newBuilder() .setType("server") diff --git a/qa/mixed-cluster/build.gradle b/qa/mixed-cluster/build.gradle index 7a2c37639b93e..822977c55368a 100644 --- a/qa/mixed-cluster/build.gradle +++ b/qa/mixed-cluster/build.gradle @@ -38,6 +38,10 @@ apply plugin: 'opensearch.standalone-test' apply from : "$rootDir/gradle/bwc-test.gradle" apply plugin: 'opensearch.rest-resources' +dependencies { + testImplementation project(":client:rest-high-level") +} + restResources { restTests { includeCore '*' @@ -51,6 +55,7 @@ for (Version bwcVersion : BuildParams.bwcVersions.wireCompatible) { } String baseName = "v${bwcVersion}" + String bwcVersionStr = "${bwcVersion}" /* This project runs the core REST tests against a 4 node cluster where two of the nodes has a different minor. */ @@ -81,6 +86,7 @@ for (Version bwcVersion : BuildParams.bwcVersions.wireCompatible) { nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}") nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}") } + systemProperty 'tests.upgrade_from_version', bwcVersionStr systemProperty 'tests.path.repo', "${buildDir}/cluster/shared/repo/${baseName}" onlyIf { project.bwc_tests_enabled } } diff --git a/qa/mixed-cluster/src/test/java/org/opensearch/backwards/ExceptionIT.java b/qa/mixed-cluster/src/test/java/org/opensearch/backwards/ExceptionIT.java index e0246870181c0..d8207ecb9b5c5 100644 --- a/qa/mixed-cluster/src/test/java/org/opensearch/backwards/ExceptionIT.java +++ b/qa/mixed-cluster/src/test/java/org/opensearch/backwards/ExceptionIT.java @@ -47,7 +47,13 @@ public void testOpensearchException() throws Exception { private void logClusterNodes() throws IOException { ObjectPath objectPath = ObjectPath.createFromResponse(client().performRequest(new Request("GET", "_nodes"))); Map nodes = objectPath.evaluate("nodes"); - String master = EntityUtils.toString(client().performRequest(new Request("GET", "_cat/master?h=id")).getEntity()).trim(); + Request request = new Request("GET", "_cat/master?h=id"); + String inclusiveWarning = "[GET /_cat/master] is deprecated! Use [GET /_cat/cluster_manager] instead."; + request.setOptions(expectVersionSpecificWarnings(v -> { + v.current(inclusiveWarning); + v.compatible(inclusiveWarning); + })); + String master = EntityUtils.toString(client().performRequest(request).getEntity()).trim(); logger.info("cluster discovered: master id='{}'", master); for (String id : nodes.keySet()) { logger.info("{}: id='{}', name='{}', version={}", diff --git a/qa/mixed-cluster/src/test/java/org/opensearch/backwards/IndexingIT.java b/qa/mixed-cluster/src/test/java/org/opensearch/backwards/IndexingIT.java index b133a6462a525..b31706d5e2d2f 100644 --- a/qa/mixed-cluster/src/test/java/org/opensearch/backwards/IndexingIT.java +++ b/qa/mixed-cluster/src/test/java/org/opensearch/backwards/IndexingIT.java @@ -32,6 +32,10 @@ package org.opensearch.backwards; import org.apache.http.HttpHost; +import org.apache.http.ParseException; + + +import org.apache.http.util.EntityUtils; import org.opensearch.LegacyESVersion; import org.opensearch.Version; import org.opensearch.client.Request; @@ -40,12 +44,13 @@ import org.opensearch.client.ResponseException; import org.opensearch.client.RestClient; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.Strings; import org.opensearch.index.seqno.SeqNoStats; -import org.opensearch.rest.RestStatus; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.core.rest.RestStatus; import org.opensearch.test.rest.OpenSearchRestTestCase; import org.opensearch.test.rest.yaml.ObjectPath; @@ -62,11 +67,15 @@ public class IndexingIT extends OpenSearchRestTestCase { + protected static final Version UPGRADE_FROM_VERSION = Version.fromString(System.getProperty("tests.upgrade_from_version")); + private static final String TEST_MAPPING = createTestMapping(); + + private int indexDocs(String index, final int idStart, final int numDocs) throws IOException { for (int i = 0; i < numDocs; i++) { final int id = idStart + i; Request request = new Request("PUT", index + "/_doc/" + id); - request.setJsonEntity("{\"test\": \"test_" + randomAlphaOfLength(2) + "\"}"); + request.setJsonEntity("{\"test\": \"test_" + randomAlphaOfLength(2) + "\", \"sortfield\": \""+ randomIntBetween(0, numDocs) + "\"}"); assertOK(client().performRequest(request)); } return numDocs; @@ -97,6 +106,113 @@ private int indexDocWithConcurrentUpdates(String index, final int docId, int nUp return nUpdates + 1; } + private void printClusterRouting() throws IOException, ParseException { + Request clusterStateRequest = new Request("GET", "_cluster/state/routing_nodes?pretty"); + String clusterState = EntityUtils.toString(client().performRequest(clusterStateRequest).getEntity()).trim(); + logger.info("cluster nodes: {}", clusterState); + } + + /** + * This test verifies that segment replication does not break when primary shards are on lower OS version. It does this + * by verifying replica shards contains same number of documents as primary's. + * + * @throws Exception + */ + public void testIndexingWithPrimaryOnBwcNodes() throws Exception { + if (UPGRADE_FROM_VERSION.before(Version.V_2_4_0)) { + logger.info("--> Skip test for version {} where segment replication feature is not available", UPGRADE_FROM_VERSION); + return; + } + Nodes nodes = buildNodeAndVersions(); + assumeFalse("new nodes is empty", nodes.getNewNodes().isEmpty()); + logger.info("cluster discovered:\n {}", nodes.toString()); + final List bwcNamesList = nodes.getBWCNodes().stream().map(Node::getNodeName).collect(Collectors.toList()); + final String bwcNames = bwcNamesList.stream().collect(Collectors.joining(",")); + // Update allocation settings so that primaries gets allocated only on nodes running on older version + Settings.Builder settings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .putList("index.sort.field", "sortfield") + .put("index.routing.allocation.include._name", bwcNames); + final String index = "test-index"; + createIndex(index, settings.build(), TEST_MAPPING); + ensureNoInitializingShards(); // wait for all other shard activity to finish + + int docCount = 200; + try (RestClient nodeClient = buildClient(restClientSettings(), + nodes.getNewNodes().stream().map(Node::getPublishAddress).toArray(HttpHost[]::new))) { + + logger.info("Remove allocation include settings so that shards can be allocated on current version nodes"); + updateIndexSettings(index, Settings.builder().putNull("index.routing.allocation.include._name")); + // Add replicas so that it can be assigned on higher OS version nodes. + updateIndexSettings(index, Settings.builder().put("index.number_of_replicas", 2)); + + printClusterRouting(); + ensureGreen(index); + + // Index docs + indexDocs(index, 0, docCount); + + // perform a refresh + assertOK(client().performRequest(new Request("POST", index + "/_flush"))); + + // verify replica catch up with primary + assertSeqNoOnShards(index, nodes, docCount, nodeClient); + } + } + + + /** + * This test creates a cluster with primary on higher version but due to {@link org.opensearch.cluster.routing.allocation.decider.NodeVersionAllocationDecider}; + * replica shard allocation on lower OpenSearch version is prevented. Thus, this test though cover the use case where + * primary shard containing nodes are running on higher OS version while replicas are unassigned. + * + * @throws Exception + */ + public void testIndexingWithReplicaOnBwcNodes() throws Exception { + if (UPGRADE_FROM_VERSION.before(Version.V_2_4_0)) { + logger.info("--> Skip test for version {} where segment replication feature is not available", UPGRADE_FROM_VERSION); + return; + } + Nodes nodes = buildNodeAndVersions(); + assumeFalse("new nodes is empty", nodes.getNewNodes().isEmpty()); + logger.info("cluster discovered:\n {}", nodes.toString()); + final List bwcNamesList = nodes.getBWCNodes().stream().map(Node::getNodeName).collect(Collectors.toList()); + final String bwcNames = bwcNamesList.stream().collect(Collectors.joining(",")); + // Exclude bwc nodes from allocation so that primaries gets allocated on current/higher version + Settings.Builder settings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .putList("index.sort.field", "sortfield") + .put("index.routing.allocation.exclude._name", bwcNames); + final String index = "test-index"; + createIndex(index, settings.build(), TEST_MAPPING); + ensureNoInitializingShards(); // wait for all other shard activity to finish + printClusterRouting(); + + int docCount = 200; + try (RestClient nodeClient = buildClient(restClientSettings(), + nodes.values().stream().map(Node::getPublishAddress).toArray(HttpHost[]::new))) { + + logger.info("allowing replica shards assignment on bwc nodes"); + updateIndexSettings(index, Settings.builder().putNull("index.routing.allocation.exclude._name")); + // Add replicas so that it can be assigned on lower OS version nodes, but it doesn't work as called out in test overview + updateIndexSettings(index, Settings.builder().put("index.number_of_replicas", 2)); + printClusterRouting(); + + // Index docs + indexDocs(index, 0, docCount); + + // perform a refresh + assertOK(client().performRequest(new Request("POST", index + "/_flush"))); + + // verify replica catch up with primary + assertSeqNoOnShards(index, nodes, docCount, nodeClient); + } + } + public void testIndexVersionPropagation() throws Exception { Nodes nodes = buildNodeAndVersions(); assumeFalse("new nodes is empty", nodes.getNewNodes().isEmpty()); @@ -106,11 +222,12 @@ public void testIndexVersionPropagation() throws Exception { Settings.Builder settings = Settings.builder() .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 2) + .putList("index.sort.field", "sortfield") .put("index.routing.allocation.include._name", bwcNames); final String index = "indexversionprop"; final int minUpdates = 5; final int maxUpdates = 10; - createIndex(index, settings.build()); + createIndex(index, settings.build(), TEST_MAPPING); try (RestClient newNodeClient = buildClient(restClientSettings(), nodes.getNewNodes().stream().map(Node::getPublishAddress).toArray(HttpHost[]::new))) { @@ -192,10 +309,11 @@ public void testSeqNoCheckpoints() throws Exception { Settings.Builder settings = Settings.builder() .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 2) + .putList("index.sort.field", "sortfield") .put("index.routing.allocation.include._name", bwcNames); final String index = "test"; - createIndex(index, settings.build()); + createIndex(index, settings.build(), TEST_MAPPING); try (RestClient newNodeClient = buildClient(restClientSettings(), nodes.getNewNodes().stream().map(Node::getPublishAddress).toArray(HttpHost[]::new))) { int numDocs = 0; @@ -257,15 +375,14 @@ public void testUpdateSnapshotStatus() throws Exception { // Create the repository before taking the snapshot. Request request = new Request("PUT", "/_snapshot/repo"); - request.setJsonEntity(Strings - .toString(JsonXContent.contentBuilder() + request.setJsonEntity(JsonXContent.contentBuilder() .startObject() .field("type", "fs") .startObject("settings") .field("compress", randomBoolean()) .field("location", System.getProperty("tests.path.repo")) .endObject() - .endObject())); + .endObject().toString()); assertOK(client().performRequest(request)); @@ -275,10 +392,11 @@ public void testUpdateSnapshotStatus() throws Exception { Settings.Builder settings = Settings.builder() .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), between(5, 10)) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) + .putList("index.sort.field", "sortfield") .put("index.routing.allocation.include._name", bwcNames); final String index = "test-snapshot-index"; - createIndex(index, settings.build()); + createIndex(index, settings.build(), TEST_MAPPING); indexDocs(index, 0, between(50, 100)); ensureGreen(index); assertOK(client().performRequest(new Request("POST", index + "/_refresh"))); @@ -312,7 +430,8 @@ public void testSyncedFlushTransition() throws Exception { createIndex(index, Settings.builder() .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), numShards) .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numOfReplicas) - .put("index.routing.allocation.include._name", newNodes).build()); + .putList("index.sort.field", "sortfield") + .put("index.routing.allocation.include._name", newNodes).build(), TEST_MAPPING); ensureGreen(index); indexDocs(index, randomIntBetween(0, 100), between(1, 100)); try (RestClient oldNodeClient = buildClient(restClientSettings(), @@ -322,7 +441,7 @@ public void testSyncedFlushTransition() throws Exception { ResponseException responseException = expectThrows(ResponseException.class, () -> oldNodeClient.performRequest(request)); assertThat(responseException.getResponse().getStatusLine().getStatusCode(), equalTo(RestStatus.CONFLICT.getStatus())); assertThat(responseException.getResponse().getWarnings(), - contains("Synced flush is deprecated and will be removed in 8.0. Use flush at _/flush or /{index}/_flush instead.")); + contains("Synced flush is deprecated and will be removed in 3.0. Use flush at _/flush or /{index}/_flush instead.")); Map result = ObjectPath.createFromResponse(responseException.getResponse()).evaluate("_shards"); assertThat(result.get("total"), equalTo(totalShards)); assertThat(result.get("successful"), equalTo(0)); @@ -429,23 +548,23 @@ private Nodes buildNodeAndVersions() throws IOException { HttpHost.create(objectPath.evaluate("nodes." + id + ".http.publish_address")))); } response = client().performRequest(new Request("GET", "_cluster/state")); - nodes.setMasterNodeId(ObjectPath.createFromResponse(response).evaluate("master_node")); + nodes.setClusterManagerNodeId(ObjectPath.createFromResponse(response).evaluate("master_node")); return nodes; } final class Nodes extends HashMap { - private String masterNodeId = null; + private String clusterManagerNodeId = null; - public Node getMaster() { - return get(masterNodeId); + public Node getClusterManager() { + return get(clusterManagerNodeId); } - public void setMasterNodeId(String id) { + public void setClusterManagerNodeId(String id) { if (get(id) == null) { throw new IllegalArgumentException("node with id [" + id + "] not found. got:" + toString()); } - masterNodeId = id; + clusterManagerNodeId = id; } public void add(Node node) { @@ -480,7 +599,7 @@ public Node getSafe(String id) { @Override public String toString() { return "Nodes{" + - "masterNodeId='" + masterNodeId + "'\n" + + "masterNodeId='" + clusterManagerNodeId + "'\n" + values().stream().map(Node::toString).collect(Collectors.joining("\n")) + '}'; } @@ -557,4 +676,15 @@ public String toString() { '}'; } } + + private static String createTestMapping() { + return " \"properties\": {\n" + + " \"test\": {\n" + + " \"type\": \"text\"\n" + + " },\n" + + " \"sortfield\": {\n" + + " \"type\": \"integer\"\n" + + " }\n" + + " }"; + } } diff --git a/qa/mixed-cluster/src/test/java/org/opensearch/backwards/SearchingIT.java b/qa/mixed-cluster/src/test/java/org/opensearch/backwards/SearchingIT.java new file mode 100644 index 0000000000000..7d427da9b6559 --- /dev/null +++ b/qa/mixed-cluster/src/test/java/org/opensearch/backwards/SearchingIT.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.backwards; + +import org.apache.http.HttpHost; +import org.opensearch.action.get.MultiGetRequest; +import org.opensearch.action.get.MultiGetResponse; +import org.opensearch.client.Request; +import org.opensearch.client.RequestOptions; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; +import org.opensearch.client.RestHighLevelClient; +import org.opensearch.test.rest.OpenSearchRestTestCase; +import org.opensearch.test.rest.yaml.ObjectPath; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.hamcrest.Matchers.containsString; + +public class SearchingIT extends OpenSearchRestTestCase { + public void testMultiGet() throws Exception { + final Set nodes = buildNodes(); + + final MultiGetRequest multiGetRequest = new MultiGetRequest(); + multiGetRequest.add("index", "id1"); + + try (RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(nodes.toArray(HttpHost[]::new)))) { + MultiGetResponse response = client.mget(multiGetRequest, RequestOptions.DEFAULT); + assertEquals(1, response.getResponses().length); + + assertTrue(response.getResponses()[0].isFailed()); + assertNotNull(response.getResponses()[0].getFailure()); + assertEquals(response.getResponses()[0].getFailure().getId(), "id1"); + assertEquals(response.getResponses()[0].getFailure().getIndex(), "index"); + assertThat(response.getResponses()[0].getFailure().getMessage(), containsString("no such index [index]")); + } + } + + private Set buildNodes() throws IOException, URISyntaxException { + Response response = client().performRequest(new Request("GET", "_nodes")); + ObjectPath objectPath = ObjectPath.createFromResponse(response); + Map nodesAsMap = objectPath.evaluate("nodes"); + final Set nodes = new HashSet<>(); + for (String id : nodesAsMap.keySet()) { + nodes.add(HttpHost.create((String) objectPath.evaluate("nodes." + id + ".http.publish_address"))); + } + + return nodes; + } +} diff --git a/qa/multi-cluster-search/build.gradle b/qa/multi-cluster-search/build.gradle index 64aabf602e501..907791bd6a7de 100644 --- a/qa/multi-cluster-search/build.gradle +++ b/qa/multi-cluster-search/build.gradle @@ -45,7 +45,7 @@ task 'remote-cluster'(type: RestIntegTestTask) { testClusters.'remote-cluster' { numberOfNodes = 2 - setting 'node.roles', '[data,ingest,master]' + setting 'node.roles', '[data,ingest,cluster_manager]' } task mixedClusterTest(type: RestIntegTestTask) { diff --git a/qa/multi-cluster-search/src/test/java/org/opensearch/search/CCSDuelIT.java b/qa/multi-cluster-search/src/test/java/org/opensearch/search/CCSDuelIT.java index 9913e5816e856..3dc2444d8a16e 100644 --- a/qa/multi-cluster-search/src/test/java/org/opensearch/search/CCSDuelIT.java +++ b/qa/multi-cluster-search/src/test/java/org/opensearch/search/CCSDuelIT.java @@ -36,7 +36,7 @@ import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.tests.util.TimeUnits; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.admin.indices.refresh.RefreshRequest; import org.opensearch.action.admin.indices.refresh.RefreshResponse; @@ -53,12 +53,12 @@ import org.opensearch.client.RestHighLevelClient; import org.opensearch.client.indices.CreateIndexRequest; import org.opensearch.client.indices.CreateIndexResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentHelper; import org.opensearch.index.query.InnerHitBuilder; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -198,7 +198,7 @@ private static void indexDocuments(String idPrefix) throws IOException, Interrup createIndexRequest.mapping("{\"properties\":{" + "\"id\":{\"type\":\"keyword\"}," + "\"suggest\":{\"type\":\"completion\"}," + - "\"join\":{\"type\":\"join\", \"relations\": {\"question\":\"answer\"}}}}", XContentType.JSON); + "\"join\":{\"type\":\"join\", \"relations\": {\"question\":\"answer\"}}}}", MediaTypeRegistry.JSON); CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT); assertTrue(createIndexResponse.isAcknowledged()); @@ -255,7 +255,7 @@ private static IndexRequest buildIndexRequest(String id, String type, String que if (questionId != null) { joinField.put("parent", questionId); } - indexRequest.source(XContentType.JSON, + indexRequest.source(MediaTypeRegistry.JSON, "id", id, "type", type, "votes", randomIntBetween(0, 30), @@ -726,7 +726,7 @@ public void testCompletionSuggester() throws Exception { sourceBuilder.suggest(suggestBuilder); duelSearch(searchRequest, response -> { assertMultiClusterSearchResponse(response); - assertEquals(Strings.toString(response, true, true), 3, response.getSuggest().size()); + assertEquals(Strings.toString(MediaTypeRegistry.JSON, response, true, true), 3, response.getSuggest().size()); assertThat(response.getSuggest().getSuggestion("python").getEntries().size(), greaterThan(0)); assertThat(response.getSuggest().getSuggestion("java").getEntries().size(), greaterThan(0)); assertThat(response.getSuggest().getSuggestion("ruby").getEntries().size(), greaterThan(0)); @@ -827,8 +827,8 @@ private static void assertAggs(SearchResponse response) { @SuppressWarnings("unchecked") private static Map responseToMap(SearchResponse response) throws IOException { - BytesReference bytesReference = XContentHelper.toXContent(response, XContentType.JSON, false); - Map responseMap = XContentHelper.convertToMap(bytesReference, false, XContentType.JSON).v2(); + BytesReference bytesReference = XContentHelper.toXContent(response, MediaTypeRegistry.JSON, false); + Map responseMap = org.opensearch.common.xcontent.XContentHelper.convertToMap(bytesReference, false, MediaTypeRegistry.JSON).v2(); assertNotNull(responseMap.put("took", -1)); responseMap.remove("num_reduce_phases"); Map profile = (Map)responseMap.get("profile"); diff --git a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml index 92ae11c712b25..23cd1567b49ba 100644 --- a/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml +++ b/qa/multi-cluster-search/src/test/resources/rest-api-spec/test/multi_cluster/70_skip_shards.yml @@ -220,4 +220,3 @@ # When all shards are skipped current logic returns 1 to produce a valid search result - match: { _shards.skipped : 1} - match: { _shards.failed: 0 } - diff --git a/qa/os/build.gradle b/qa/os/build.gradle index 038e3d16745c3..66c6525439dac 100644 --- a/qa/os/build.gradle +++ b/qa/os/build.gradle @@ -42,15 +42,17 @@ dependencies { api "org.apache.httpcomponents:fluent-hc:${versions.httpclient}" api "org.apache.logging.log4j:log4j-api:${versions.log4j}" api "org.apache.logging.log4j:log4j-core:${versions.log4j}" + api "org.apache.logging.log4j:log4j-jul:${versions.log4j}" api "org.apache.logging.log4j:log4j-jcl:${versions.log4j}" api "commons-codec:commons-codec:${versions.commonscodec}" api "commons-logging:commons-logging:${versions.commonslogging}" + api project(':libs:opensearch-common') api project(':libs:opensearch-core') testImplementation "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" testImplementation "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" - testImplementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" + testImplementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}" } tasks.named('forbiddenApisTest').configure { diff --git a/qa/os/src/test/java/org/opensearch/packaging/test/ArchiveTests.java b/qa/os/src/test/java/org/opensearch/packaging/test/ArchiveTests.java index 898ea12b6a6c3..29e40a4e179cb 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/test/ArchiveTests.java +++ b/qa/os/src/test/java/org/opensearch/packaging/test/ArchiveTests.java @@ -328,7 +328,7 @@ public void test54ForceBundledJdkEmptyJavaHome() throws Exception { public void test70CustomPathConfAndJvmOptions() throws Exception { withCustomConfig(tempConf -> { - final List jvmOptions = org.opensearch.common.collect.List.of("-Xms512m", "-Xmx512m", "-Dlog4j2.disable.jmx=true"); + final List jvmOptions = List.of("-Xms512m", "-Xmx512m", "-Dlog4j2.disable.jmx=true"); Files.write(tempConf.resolve("jvm.options"), jvmOptions, CREATE, APPEND); sh.getEnv().put("OPENSEARCH_JAVA_OPTS", "-XX:-UseCompressedOops"); diff --git a/qa/os/src/test/java/org/opensearch/packaging/test/DebMetadataTests.java b/qa/os/src/test/java/org/opensearch/packaging/test/DebMetadataTests.java index 299f23e83db41..0db5aec7b1d0e 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/test/DebMetadataTests.java +++ b/qa/os/src/test/java/org/opensearch/packaging/test/DebMetadataTests.java @@ -32,7 +32,6 @@ package org.opensearch.packaging.test; -import junit.framework.TestCase; import org.opensearch.packaging.util.Distribution; import org.opensearch.packaging.util.FileUtils; import org.opensearch.packaging.util.Shell; @@ -40,6 +39,8 @@ import java.util.regex.Pattern; +import junit.framework.TestCase; + import static org.opensearch.packaging.util.FileUtils.getDistributionFile; import static org.junit.Assume.assumeTrue; diff --git a/qa/os/src/test/java/org/opensearch/packaging/test/DockerTests.java b/qa/os/src/test/java/org/opensearch/packaging/test/DockerTests.java index 725e430b3634c..be9741e660223 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/test/DockerTests.java +++ b/qa/os/src/test/java/org/opensearch/packaging/test/DockerTests.java @@ -33,6 +33,7 @@ package org.opensearch.packaging.test; import com.fasterxml.jackson.databind.JsonNode; + import org.opensearch.packaging.util.Installation; import org.opensearch.packaging.util.Platforms; import org.opensearch.packaging.util.ServerUtils; @@ -43,9 +44,13 @@ import org.junit.BeforeClass; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -85,11 +90,6 @@ import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashSet; - public class DockerTests extends PackagingTestCase { private Path tempDir; diff --git a/qa/os/src/test/java/org/opensearch/packaging/test/KeystoreManagementTests.java b/qa/os/src/test/java/org/opensearch/packaging/test/KeystoreManagementTests.java index fee3b0bc501d9..4970a874ffb1a 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/test/KeystoreManagementTests.java +++ b/qa/os/src/test/java/org/opensearch/packaging/test/KeystoreManagementTests.java @@ -52,7 +52,6 @@ import java.util.List; import java.util.Map; -import static com.carrotsearch.randomizedtesting.RandomizedTest.randomBoolean; import static java.util.Collections.singletonList; import static org.opensearch.packaging.util.Archives.ARCHIVE_OWNER; import static org.opensearch.packaging.util.Archives.installArchive; @@ -76,6 +75,7 @@ import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.startsWith; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomBoolean; import static org.junit.Assume.assumeThat; import static org.junit.Assume.assumeTrue; diff --git a/qa/os/src/test/java/org/opensearch/packaging/test/PackagingTestCase.java b/qa/os/src/test/java/org/opensearch/packaging/test/PackagingTestCase.java index 259ae6e766c8e..02a613be320c2 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/test/PackagingTestCase.java +++ b/qa/os/src/test/java/org/opensearch/packaging/test/PackagingTestCase.java @@ -39,10 +39,11 @@ import com.carrotsearch.randomizedtesting.annotations.TestGroup; import com.carrotsearch.randomizedtesting.annotations.TestMethodProviders; import com.carrotsearch.randomizedtesting.annotations.Timeout; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.common.CheckedConsumer; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.packaging.util.Archives; import org.opensearch.packaging.util.Distribution; import org.opensearch.packaging.util.Docker; diff --git a/qa/os/src/test/java/org/opensearch/packaging/test/RpmMetadataTests.java b/qa/os/src/test/java/org/opensearch/packaging/test/RpmMetadataTests.java index 5eb8ffe8817ee..f95a79efa51ef 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/test/RpmMetadataTests.java +++ b/qa/os/src/test/java/org/opensearch/packaging/test/RpmMetadataTests.java @@ -32,7 +32,6 @@ package org.opensearch.packaging.test; -import junit.framework.TestCase; import org.opensearch.packaging.util.Distribution; import org.opensearch.packaging.util.Platforms; import org.opensearch.packaging.util.Shell; @@ -40,6 +39,8 @@ import java.util.regex.Pattern; +import junit.framework.TestCase; + import static org.opensearch.packaging.util.FileUtils.getDistributionFile; import static org.junit.Assume.assumeTrue; diff --git a/qa/os/src/test/java/org/opensearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/opensearch/packaging/test/WindowsServiceTests.java index 57ea853e735a9..f4bb9d70354a0 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/opensearch/packaging/test/WindowsServiceTests.java @@ -32,7 +32,6 @@ package org.opensearch.packaging.test; -import junit.framework.TestCase; import org.opensearch.packaging.util.FileUtils; import org.opensearch.packaging.util.Platforms; import org.opensearch.packaging.util.ServerUtils; @@ -46,13 +45,15 @@ import java.nio.file.Path; import java.util.Arrays; -import static com.carrotsearch.randomizedtesting.RandomizedTest.assumeTrue; +import junit.framework.TestCase; + import static org.opensearch.packaging.util.Archives.installArchive; import static org.opensearch.packaging.util.Archives.verifyArchiveInstallation; import static org.opensearch.packaging.util.FileUtils.append; import static org.opensearch.packaging.util.FileUtils.mv; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; +import static com.carrotsearch.randomizedtesting.RandomizedTest.assumeTrue; public class WindowsServiceTests extends PackagingTestCase { diff --git a/qa/os/src/test/java/org/opensearch/packaging/util/Docker.java b/qa/os/src/test/java/org/opensearch/packaging/util/Docker.java index ef5b1e590635a..6517062c0ca50 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/util/Docker.java +++ b/qa/os/src/test/java/org/opensearch/packaging/util/Docker.java @@ -34,6 +34,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.client.fluent.Request; diff --git a/qa/os/src/test/java/org/opensearch/packaging/util/FileUtils.java b/qa/os/src/test/java/org/opensearch/packaging/util/FileUtils.java index aa52c3325bee5..5169ce18fff79 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/util/FileUtils.java +++ b/qa/os/src/test/java/org/opensearch/packaging/util/FileUtils.java @@ -33,7 +33,7 @@ package org.opensearch.packaging.util; import org.apache.logging.log4j.Logger; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.hamcrest.FeatureMatcher; import org.hamcrest.Matcher; @@ -83,7 +83,7 @@ public class FileUtils { public static List lsGlob(Path directory, String glob) { List paths = new ArrayList<>(); if (Files.exists(directory) == false) { - return org.opensearch.common.collect.List.of(); + return List.of(); } try (DirectoryStream stream = Files.newDirectoryStream(directory, glob)) { diff --git a/qa/os/src/test/java/org/opensearch/packaging/util/Packages.java b/qa/os/src/test/java/org/opensearch/packaging/util/Packages.java index cea03a0b6fe70..e9ebf28042b46 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/util/Packages.java +++ b/qa/os/src/test/java/org/opensearch/packaging/util/Packages.java @@ -194,11 +194,11 @@ private static void verifyInstallation(Installation opensearch, Distribution dis // we shell out here because java's posix file permission view doesn't support special modes assertThat(opensearch.config, file(Directory, "root", "opensearch", p750)); - assertThat(sh.run("find \"" + opensearch.config + "\" -maxdepth 0 -printf \"%m\"").stdout, containsString("2750")); + assertThat(sh.run("find \"" + opensearch.config + "\" -maxdepth 0 -printf \"%m\"").stdout, containsString("750")); final Path jvmOptionsDirectory = opensearch.config.resolve("jvm.options.d"); assertThat(jvmOptionsDirectory, file(Directory, "root", "opensearch", p750)); - assertThat(sh.run("find \"" + jvmOptionsDirectory + "\" -maxdepth 0 -printf \"%m\"").stdout, containsString("2750")); + assertThat(sh.run("find \"" + jvmOptionsDirectory + "\" -maxdepth 0 -printf \"%m\"").stdout, containsString("750")); Stream.of("opensearch.keystore", "opensearch.yml", "jvm.options", "log4j2.properties") .forEach(configFile -> assertThat(opensearch.config(configFile), file(File, "root", "opensearch", p660))); @@ -322,7 +322,7 @@ private enum PackageManagerCommand { REMOVE } - private static Map RPM_OPTIONS = org.opensearch.common.collect.Map.of( + private static Map RPM_OPTIONS = Map.of( PackageManagerCommand.QUERY, "-qe", PackageManagerCommand.INSTALL, @@ -335,7 +335,7 @@ private enum PackageManagerCommand { "-e" ); - private static Map DEB_OPTIONS = org.opensearch.common.collect.Map.of( + private static Map DEB_OPTIONS = Map.of( PackageManagerCommand.QUERY, "-s", PackageManagerCommand.INSTALL, diff --git a/qa/os/src/test/java/org/opensearch/packaging/util/ServerUtils.java b/qa/os/src/test/java/org/opensearch/packaging/util/ServerUtils.java index d92feec21daaf..42eac9fdf4961 100644 --- a/qa/os/src/test/java/org/opensearch/packaging/util/ServerUtils.java +++ b/qa/os/src/test/java/org/opensearch/packaging/util/ServerUtils.java @@ -52,6 +52,7 @@ import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; + import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; diff --git a/qa/remote-clusters/docker-test-entrypoint.sh b/qa/remote-clusters/docker-test-entrypoint.sh index e08e388b8c98d..18a9363d375ab 100755 --- a/qa/remote-clusters/docker-test-entrypoint.sh +++ b/qa/remote-clusters/docker-test-entrypoint.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash +set -e -o pipefail + cd /usr/share/opensearch/bin/ ./opensearch-users useradd rest_user -p test-password -r superuser || true echo "testnode" > /tmp/password diff --git a/qa/remote-clusters/src/test/java/org/opensearch/cluster/remote/test/AbstractMultiClusterRemoteTestCase.java b/qa/remote-clusters/src/test/java/org/opensearch/cluster/remote/test/AbstractMultiClusterRemoteTestCase.java index 61ccbab95850d..4238219f2c0ed 100644 --- a/qa/remote-clusters/src/test/java/org/opensearch/cluster/remote/test/AbstractMultiClusterRemoteTestCase.java +++ b/qa/remote-clusters/src/test/java/org/opensearch/cluster/remote/test/AbstractMultiClusterRemoteTestCase.java @@ -39,7 +39,7 @@ import org.opensearch.client.RestClient; import org.opensearch.client.RestHighLevelClient; import org.opensearch.common.settings.Settings; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.test.rest.OpenSearchRestTestCase; import java.io.IOException; diff --git a/qa/repository-multi-version/src/test/java/org/opensearch/upgrades/MultiVersionRepositoryAccessIT.java b/qa/repository-multi-version/src/test/java/org/opensearch/upgrades/MultiVersionRepositoryAccessIT.java index c0b90626d7bad..7a32b92d8aa75 100644 --- a/qa/repository-multi-version/src/test/java/org/opensearch/upgrades/MultiVersionRepositoryAccessIT.java +++ b/qa/repository-multi-version/src/test/java/org/opensearch/upgrades/MultiVersionRepositoryAccessIT.java @@ -46,8 +46,8 @@ import org.opensearch.client.RestClient; import org.opensearch.client.RestHighLevelClient; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.snapshots.SnapshotsService; import org.opensearch.test.rest.OpenSearchRestTestCase; diff --git a/qa/rolling-upgrade/build.gradle b/qa/rolling-upgrade/build.gradle index 3dff452be855f..68a9dcafbdc47 100644 --- a/qa/rolling-upgrade/build.gradle +++ b/qa/rolling-upgrade/build.gradle @@ -74,6 +74,7 @@ for (Version bwcVersion : BuildParams.bwcVersions.wireCompatible) { } systemProperty 'tests.upgrade_from_version', bwcVersionStr systemProperty 'tests.rest.suite', 'old_cluster' + systemProperty 'tests.repo_location', "${buildDir}/cluster/shared/repo/${baseName}/test" nonInputProperties.systemProperty('tests.rest.cluster', "${-> testClusters."${baseName}".allHttpSocketURI.join(",")}") nonInputProperties.systemProperty('tests.clustername', "${-> testClusters."${baseName}".getName()}") } @@ -110,6 +111,7 @@ for (Version bwcVersion : BuildParams.bwcVersions.wireCompatible) { testClusters."${baseName}".nextNodeToNextVersion() } useCluster testClusters."${baseName}" + systemProperty 'tests.repo_location', "${buildDir}/cluster/shared/repo/${baseName}/test" systemProperty 'tests.rest.suite', 'upgraded_cluster' systemProperty 'tests.upgrade_from_version', bwcVersionStr diff --git a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/AbstractRollingTestCase.java b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/AbstractRollingTestCase.java index 27766e4086b91..ee1c1fd888438 100644 --- a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/AbstractRollingTestCase.java +++ b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/AbstractRollingTestCase.java @@ -56,6 +56,7 @@ public static ClusterType parse(String value) { } protected static final ClusterType CLUSTER_TYPE = ClusterType.parse(System.getProperty("tests.rest.suite")); + protected static final String REPOSITORY_LOCATION = System.getProperty("tests.repo_location"); protected static final Version UPGRADE_FROM_VERSION = Version.fromString(System.getProperty("tests.upgrade_from_version")); protected static final boolean firstMixedRound = Boolean.parseBoolean(System.getProperty("tests.first_round", "false")); @@ -74,6 +75,9 @@ protected boolean preserveTemplatesUponCompletion() { return true; } + @Override + protected final boolean preserveSnapshotsUponCompletion() { return true; } + @Override protected final Settings restClientSettings() { return Settings.builder().put(super.restClientSettings()) diff --git a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/IndexingIT.java b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/IndexingIT.java index f34e5f7bc121a..3f341537ee934 100644 --- a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/IndexingIT.java +++ b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/IndexingIT.java @@ -32,6 +32,8 @@ package org.opensearch.upgrades; import org.apache.http.util.EntityUtils; +import org.apache.http.ParseException; + import org.opensearch.LegacyESVersion; import org.opensearch.Version; import org.opensearch.client.Request; @@ -39,16 +41,26 @@ import org.opensearch.client.ResponseException; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Booleans; +import org.opensearch.common.io.Streams; import org.opensearch.common.settings.Settings; -import org.opensearch.rest.action.document.RestBulkAction; +import org.opensearch.index.codec.CodecService; +import org.opensearch.index.engine.EngineConfig; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.rest.yaml.ObjectPath; import java.io.IOException; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; +import static org.opensearch.cluster.routing.UnassignedInfo.INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING; import static org.opensearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.either; +import static org.opensearch.test.OpenSearchIntegTestCase.CODECS; /** * Basic test that indexed documents survive the rolling restart. See @@ -61,7 +73,106 @@ */ public class IndexingIT extends AbstractRollingTestCase { - public void testIndexing() throws IOException { + private void printClusterNodes() throws IOException, ParseException, URISyntaxException { + Request clusterStateRequest = new Request("GET", "_nodes"); + Response response = client().performRequest(clusterStateRequest); + + ObjectPath objectPath = ObjectPath.createFromResponse(response); + Map nodesAsMap = objectPath.evaluate("nodes"); + for (String id : nodesAsMap.keySet()) { + logger.info("--> {} {} {}", + id, + objectPath.evaluate("nodes." + id + ".name"), + Version.fromString(objectPath.evaluate("nodes." + id + ".version"))); + } + response = client().performRequest(new Request("GET", "_cluster/state")); + String cm = ObjectPath.createFromResponse(response).evaluate("master_node"); + logger.info("--> Cluster manager {}", cm); + } + + // Verifies that for each shard copy holds same document count across all containing nodes. + private void waitForSearchableDocs(String index, int shardCount, int replicaCount) throws Exception { + assertTrue(shardCount > 0); + assertTrue(replicaCount > 0); + waitForClusterHealthWithNoShardMigration(index, "green"); + logger.info("--> _cat/shards before search \n{}", EntityUtils.toString(client().performRequest(new Request("GET", "/_cat/shards?v")).getEntity())); + + // Verify segment replication stats + verifySegmentStats(index); + + // Verify segment store + assertBusy(() -> { + /** + * Use default tabular output and sort response based on shard,segment,primaryOrReplica columns to allow line by + * line parsing where records related to a segment (e.g. _0) are chunked together with first record belonging + * to primary while remaining *replicaCount* records belongs to replica copies + * */ + Request segrepStatsRequest = new Request("GET", "/_cat/segments/" + index + "?s=shard,segment,primaryOrReplica"); + segrepStatsRequest.addParameter("h", "index,shard,primaryOrReplica,segment,docs.count"); + Response segrepStatsResponse = client().performRequest(segrepStatsRequest); + List responseList = Streams.readAllLines(segrepStatsResponse.getEntity().getContent()); + logger.info("--> _cat/segments response\n {}", responseList.toString().replace(',', '\n')); + // Filter response for rows with zero doc count + List filteredList = new ArrayList<>(); + for(String row: responseList) { + String count = row.split(" +")[4]; + if (count.equals("0") == false) { + filteredList.add(row); + } + } + // Ensure there is result for replica copies before processing the result. This results in retry when there + // are not enough number of rows vs failing with IndexOutOfBoundsException + assertEquals(0, filteredList.size() % (replicaCount + 1)); + for (int segmentsIndex=0; segmentsIndex < filteredList.size();) { + String[] primaryRow = filteredList.get(segmentsIndex++).split(" +"); + String shardId = primaryRow[0] + primaryRow[1]; + assertTrue(primaryRow[2].equals("p")); + for(int replicaIndex = 1; replicaIndex <= replicaCount; replicaIndex++) { + String[] replicaRow = filteredList.get(segmentsIndex).split(" +"); + String replicaShardId = replicaRow[0] + replicaRow[1]; + // When segment has 0 doc count, not all replica copies posses that segment. Skip to next segment + if (replicaRow[2].equals("p")) { + assertTrue(primaryRow[4].equals("0")); + break; + } + // verify same shard id + assertTrue(replicaShardId.equals(shardId)); + // verify replica row + assertTrue(replicaRow[2].equals("r")); + // Verify segment name matches e.g. _0 + assertTrue(replicaRow[3].equals(primaryRow[3])); + // Verify doc count matches + assertTrue(replicaRow[4].equals(primaryRow[4])); + segmentsIndex++; + } + } + }, 1, TimeUnit.MINUTES); + } + + private void waitForClusterHealthWithNoShardMigration(String indexName, String status) throws IOException { + Request waitForStatus = new Request("GET", "/_cluster/health/" + indexName); + waitForStatus.addParameter("wait_for_status", status); + // wait for long enough that we give delayed unassigned shards to stop being delayed + waitForStatus.addParameter("timeout", "70s"); + waitForStatus.addParameter("level", "shards"); + waitForStatus.addParameter("wait_for_no_initializing_shards", "true"); + waitForStatus.addParameter("wait_for_no_relocating_shards", "true"); + client().performRequest(waitForStatus); + } + + private void verifySegmentStats(String indexName) throws Exception { + assertBusy(() -> { + Request segrepStatsRequest = new Request("GET", "/_cat/segment_replication/" + indexName); + segrepStatsRequest.addParameter("h", "shardId,target_node,checkpoints_behind"); + Response segrepStatsResponse = client().performRequest(segrepStatsRequest); + for (String statLine : Streams.readAllLines(segrepStatsResponse.getEntity().getContent())) { + String[] elements = statLine.split(" +"); + assertEquals("Replica shard " + elements[0] + "not upto date with primary ", 0, Integer.parseInt(elements[2])); + } + }, 1, TimeUnit.MINUTES); + } + + public void testIndexing() throws Exception { switch (CLUSTER_TYPE) { case OLD: break; @@ -147,6 +258,96 @@ public void testIndexing() throws IOException { } } + + /** + * This test verifies that during rolling upgrades the segment replication does not break when replica shards can + * be running on older codec versions. + * + * @throws Exception + */ + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/7679") + public void testIndexingWithSegRep() throws Exception { + if (UPGRADE_FROM_VERSION.before(Version.V_2_4_0)) { + logger.info("--> Skip test for version {} where segment replication feature is not available", UPGRADE_FROM_VERSION); + return; + } + final String indexName = "test-index-segrep"; + final int shardCount = 3; + final int replicaCount = 2; + logger.info("--> Case {}", CLUSTER_TYPE); + printClusterNodes(); + logger.info("--> _cat/shards before test execution \n{}", EntityUtils.toString(client().performRequest(new Request("GET", "/_cat/shards?v")).getEntity())); + switch (CLUSTER_TYPE) { + case OLD: + Settings.Builder settings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), shardCount) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), replicaCount) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put( + EngineConfig.INDEX_CODEC_SETTING.getKey(), + randomFrom(new ArrayList<>(CODECS) { + { + add(CodecService.LUCENE_DEFAULT_CODEC); + } + }) + ) + .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms"); + createIndex(indexName, settings.build()); + waitForClusterHealthWithNoShardMigration(indexName, "green"); + bulk(indexName, "_OLD", 5); + break; + case MIXED: + waitForClusterHealthWithNoShardMigration(indexName, "yellow"); + break; + case UPGRADED: + waitForClusterHealthWithNoShardMigration(indexName, "green"); + break; + default: + throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + } + + int expectedCount; + switch (CLUSTER_TYPE) { + case OLD: + expectedCount = 5; + break; + case MIXED: + if (Booleans.parseBoolean(System.getProperty("tests.first_round"))) { + expectedCount = 5; + } else { + expectedCount = 10; + } + break; + case UPGRADED: + expectedCount = 15; + break; + default: + throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + } + + waitForSearchableDocs(indexName, shardCount, replicaCount); + assertCount(indexName, expectedCount); + + if (CLUSTER_TYPE != ClusterType.OLD) { + logger.info("--> Bulk index 5 documents"); + bulk(indexName, "_" + CLUSTER_TYPE, 5); + logger.info("--> Index one doc (to be deleted next) and verify doc count"); + Request toBeDeleted = new Request("PUT", "/" + indexName + "/_doc/to_be_deleted"); + toBeDeleted.addParameter("refresh", "true"); + toBeDeleted.setJsonEntity("{\"f1\": \"delete-me\"}"); + client().performRequest(toBeDeleted); + waitForSearchableDocs(indexName, shardCount, replicaCount); + assertCount(indexName, expectedCount + 6); + + logger.info("--> Delete previously added doc and verify doc count"); + Request delete = new Request("DELETE", "/" + indexName + "/_doc/to_be_deleted"); + delete.addParameter("refresh", "true"); + client().performRequest(delete); + waitForSearchableDocs(indexName, shardCount, replicaCount); + assertCount(indexName, expectedCount + 5); + } + } + public void testAutoIdWithOpTypeCreate() throws IOException { final String indexName = "auto_id_and_op_type_create_index"; StringBuilder b = new StringBuilder(); @@ -214,12 +415,14 @@ private void bulk(String index, String valueSuffix, int count) throws IOExceptio client().performRequest(bulk); } - private void assertCount(String index, int count) throws IOException { - Request searchTestIndexRequest = new Request("POST", "/" + index + "/_search"); - searchTestIndexRequest.addParameter(TOTAL_HITS_AS_INT_PARAM, "true"); - searchTestIndexRequest.addParameter("filter_path", "hits.total"); - Response searchTestIndexResponse = client().performRequest(searchTestIndexRequest); - assertEquals("{\"hits\":{\"total\":" + count + "}}", + private void assertCount(String index, int count) throws Exception { + assertBusy(() -> { + Request searchTestIndexRequest = new Request("POST", "/" + index + "/_search"); + searchTestIndexRequest.addParameter(TOTAL_HITS_AS_INT_PARAM, "true"); + searchTestIndexRequest.addParameter("filter_path", "hits.total"); + Response searchTestIndexResponse = client().performRequest(searchTestIndexRequest); + assertEquals("{\"hits\":{\"total\":" + count + "}}", EntityUtils.toString(searchTestIndexResponse.getEntity(), StandardCharsets.UTF_8)); + }, 30, TimeUnit.SECONDS); } } diff --git a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/JodaCompatibilityIT.java b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/JodaCompatibilityIT.java index 0ef1e3a5050af..32d545481c523 100644 --- a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/JodaCompatibilityIT.java +++ b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/JodaCompatibilityIT.java @@ -40,7 +40,7 @@ import org.opensearch.client.Response; import org.opensearch.client.WarningsHandler; import org.opensearch.common.Booleans; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.search.DocValueFormat; import org.junit.BeforeClass; diff --git a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/MappingTypeRemovalIT.java b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/MappingTypeRemovalIT.java new file mode 100644 index 0000000000000..e36abcff11661 --- /dev/null +++ b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/MappingTypeRemovalIT.java @@ -0,0 +1,310 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.upgrades; + +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.util.EntityUtils; +import org.opensearch.Version; +import org.opensearch.client.Request; +import org.opensearch.client.RequestOptions; +import org.opensearch.client.Response; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.regex.Pattern; + +import static org.opensearch.rest.action.search.RestSearchAction.TOTAL_HITS_AS_INT_PARAM; + +/** + * Tests to ensure indices, snapshots, templates created using type mapping before {@link Version#V_2_0_0} + * survive the rolling upgrade to {@link Version#V_2_0_0}. The tests include indexing, re-indexing, + * snapshot and restore, template based index creation for type mapped indices to ensure they are accessible + * and searchable in {@link Version#V_2_0_0}. + */ +public class MappingTypeRemovalIT extends AbstractRollingTestCase { + + public void testTypeRemovalIndexing() throws IOException { + final String indexName = "test_index_with_mapping"; + final String indexWithoutTypeName = "test_index_without_mapping"; + final String templateIndexName = "template_test_index"; + final String templateName = "test_template"; + final String indexNamePattern = "template_test*"; + + final String mapping = "\"properties\":{\"f1\":{\"type\":\"keyword\"},\"f2\":{\"type\":\"keyword\"}}"; + final String typeMapping = "\"_doc\":{" + mapping +"}"; + + switch (CLUSTER_TYPE) { + case OLD: + if (UPGRADE_FROM_VERSION.before(Version.V_2_0_0)) { + Settings.Builder settings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0); + createIndexWithDocMappings(indexName, settings.build(), typeMapping); + assertIndexMapping(indexName, mapping); + + createTemplate(templateName, indexNamePattern, typeMapping); + createIndex(templateIndexName, settings.build()); + assertIndexMapping(templateIndexName, mapping); + bulk(indexName, CLUSTER_TYPE.name(), 1); + bulk(templateIndexName, CLUSTER_TYPE.name(), 1); + + createIndex(indexWithoutTypeName, settings.build()); + bulk(indexWithoutTypeName, CLUSTER_TYPE.name(), 1); + } + break; + case MIXED: + waitForClusterGreenStatus(); + if (UPGRADE_FROM_VERSION.before(Version.V_2_0_0)) { + assertCount(indexName, 1, 1); + assertCount(templateIndexName, 1, 1); + assertCount(indexWithoutTypeName, 1, 1); + } + break; + case UPGRADED: + if (UPGRADE_FROM_VERSION.before(Version.V_2_0_0)) { + // Assert documents created with mapping prior to OS 2.0 are accessible. + assertCount(indexName, 1, 1); + bulk(indexName, CLUSTER_TYPE.name(), 1); + // Assert the newly ingested documents are accessible + assertCount(indexName, 2, 1); + + // Assert documents created with mapping on index with template prior to OS 2.0 are accessible. + assertCount(templateIndexName, 1, 1); + bulk(templateIndexName, CLUSTER_TYPE.name(), 1); + // Assert the newly ingested documents with template are accessible + assertCount(templateIndexName, 2, 1); + + // Assert documents created prior to OS 2.0 are accessible. + assertCount(indexWithoutTypeName, 1, 1); + // Test ingestion of new documents created using < OS2.0 + bulk(indexWithoutTypeName, CLUSTER_TYPE.name(), 1); + assertCount(indexWithoutTypeName, 2, 1); + } + break; + default: + throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + } + } + + public void testTypeRemovalReindexing() throws IOException { + final String indexName = "test_reindex_with_mapping"; + final String reindexName = "test_reindex_with_mapping_v2"; + + final String originalMapping = "\"properties\":{\"f1\":{\"type\":\"keyword\"},\"f2\":{\"type\":\"keyword\"}}"; + final String originalTypeMapping = "\"_doc\":{" + originalMapping +"}"; + + final String newMapping = "\"properties\":{\"f1\":{\"type\":\"text\"},\"f2\":{\"type\":\"text\"}}"; + final String newTypeMapping = "\"_doc\":{" + newMapping +"}"; + + switch (CLUSTER_TYPE) { + case OLD: + if (UPGRADE_FROM_VERSION.before(Version.V_2_0_0)) { + Settings.Builder settings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0); + createIndexWithDocMappings(indexName, settings.build(), originalTypeMapping); + assertIndexMapping(indexName, originalMapping); + + createIndexWithDocMappings(reindexName, settings.build(), newTypeMapping); + assertIndexMapping(reindexName, newMapping); + bulk(indexName, CLUSTER_TYPE.name(), 1); + } + break; + case MIXED: + waitForClusterGreenStatus(); + if (UPGRADE_FROM_VERSION.before(Version.V_2_0_0)) { + assertCount(indexName, 1, 1); + } + break; + case UPGRADED: + if (UPGRADE_FROM_VERSION.before(Version.V_2_0_0)) { + // Assert documents created with mapping prior to OS 2.0 are accessible. + assertCount(indexName, 1, 1); + bulk(indexName, CLUSTER_TYPE.name(), 1); + // Assert the newly ingested documents are accessible + assertCount(indexName, 2, 1); + + reindex(indexName, reindexName); + assertCount(reindexName, 2, 1); + assertIndexMapping(reindexName, newMapping); + } + break; + default: + throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + } + } + + public void testTypeRemovalSnapshots() throws IOException { + final String indexName = "test_snapshot_index"; + final String repositoryName = "test_repository"; + final String snapshotName = "force_preserve_test_snapshot"; + + final String mapping = "\"properties\":{\"f1\":{\"type\":\"keyword\"},\"f2\":{\"type\":\"keyword\"}}"; + final String typeMapping = "\"_doc\":{" + mapping +"}"; + + switch (CLUSTER_TYPE) { + case OLD: + if (UPGRADE_FROM_VERSION.before(Version.V_2_0_0)) { + Settings.Builder settings = Settings.builder() + .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) + .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0); + createIndexWithDocMappings(indexName, settings.build(), typeMapping); + assertIndexMapping(indexName, mapping); + bulk(indexName, CLUSTER_TYPE.name(), 1); + + registerRepository(repositoryName,"fs", true, Settings.builder() + .put("location", REPOSITORY_LOCATION) + .build()); + createSnapshotIfNotExists(repositoryName, snapshotName, true, indexName); + deleteIndex(indexName); + } + break; + case MIXED: + waitForClusterGreenStatus(); + break; + case UPGRADED: + if (UPGRADE_FROM_VERSION.before(Version.V_2_0_0)) { + registerRepository(repositoryName,"fs", true, Settings.builder() + .put("location", REPOSITORY_LOCATION) + .build()); + assertFalse(indexExists(indexName));; + restoreSnapshot(repositoryName, snapshotName, true); + assertCount(indexName, 1, 1); + } + break; + default: + throw new UnsupportedOperationException("Unknown cluster type [" + CLUSTER_TYPE + "]"); + } + } + + private void bulk(String index, String valueSuffix, int count) throws IOException { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < count; i++) { + b.append("{\"index\": {\"_index\": \"").append(index).append("\"}}\n"); + b.append("{\"f1\": \"v").append(i).append(valueSuffix).append("\", \"f2\": ").append(i).append("}\n"); + } + Request bulk = new Request(HttpPost.METHOD_NAME, "/_bulk"); + bulk.addParameter("refresh", "true"); + bulk.setJsonEntity(b.toString()); + client().performRequest(bulk); + } + + private void createIndexWithDocMappings(String index, Settings settings, String mapping) throws IOException { + Request createIndexWithMappingsRequest = new Request(HttpPut.METHOD_NAME, "/" + index); + String entity = "{\"settings\": " + Strings.toString(MediaTypeRegistry.JSON, settings); + if (mapping != null) { + entity += ",\"mappings\" : {" + mapping + "}"; + } + entity += "}"; + createIndexWithMappingsRequest.addParameter("include_type_name", "true"); + createIndexWithMappingsRequest.setJsonEntity(entity); + useIgnoreTypesRemovalWarningsHandler(createIndexWithMappingsRequest); + client().performRequest(createIndexWithMappingsRequest); + } + + private void createTemplate(String templateName, String indexPattern, String mapping) throws IOException { + Request templateRequest = new Request(HttpPut.METHOD_NAME, "/_template/" + templateName); + String entity = "{\"index_patterns\": \"" + indexPattern + "\""; + if (mapping != null) { + entity += ",\"mappings\" : {" + mapping + "}"; + } + entity += "}"; + templateRequest.addParameter("include_type_name", "true"); + templateRequest.setJsonEntity(entity); + useIgnoreTypesRemovalWarningsHandler(templateRequest); + client().performRequest(templateRequest); + } + + private void reindex(String originalIndex, String newIndex) throws IOException { + Request reIndexRequest = new Request(HttpPost.METHOD_NAME, "/_reindex/"); + String entity = "{ \"source\": { \"index\": \"" + originalIndex + "\" }, \"dest\": { \"index\": \"" + newIndex + "\" } }"; + reIndexRequest.setJsonEntity(entity); + reIndexRequest.addParameter("refresh", "true"); + client().performRequest(reIndexRequest); + } + + private void assertIndexMapping(String index, String mappings) throws IOException { + Request testIndexMappingRequest = new Request(HttpGet.METHOD_NAME, "/" + index + "/_mapping"); + Response testIndexMappingResponse = client().performRequest(testIndexMappingRequest); + assertEquals("{\""+index+"\":{\"mappings\":{"+mappings+"}}}", + EntityUtils.toString(testIndexMappingResponse.getEntity(), StandardCharsets.UTF_8)); + } + + private void assertCount(String index, int count, Integer totalShards) throws IOException { + Request searchTestIndexRequest = new Request(HttpPost.METHOD_NAME, "/" + index + "/_search"); + searchTestIndexRequest.addParameter(TOTAL_HITS_AS_INT_PARAM, "true"); + if (totalShards != null) { + searchTestIndexRequest.addParameter("filter_path", "hits.total,_shards"); + } else { + searchTestIndexRequest.addParameter("filter_path", "hits.total"); + } + + Response searchTestIndexResponse = client().performRequest(searchTestIndexRequest); + String expectedResponse; + if (totalShards != null) { + expectedResponse = "{\"_shards\":{\"total\":" + totalShards + ",\"successful\":" + totalShards + ",\"skipped\":0,\"failed\":0},\"hits\":{\"total\":" + count + "}}"; + } else { + expectedResponse = "{\"hits\":{\"total\":" + count + "}}"; + } + assertEquals(expectedResponse, + EntityUtils.toString(searchTestIndexResponse.getEntity(), StandardCharsets.UTF_8)); + } + + private final Pattern TYPE_REMOVAL_WARNING = Pattern.compile( + "^\\[types removal\\] (.+) include_type_name (.+) is deprecated\\. The parameter will be removed in the next major version\\.$" + ); + + private void useIgnoreTypesRemovalWarningsHandler(Request request) { + RequestOptions.Builder options = request.getOptions().toBuilder(); + options.setWarningsHandler(warnings -> { + if (warnings.size() > 0) { + boolean matches = warnings.stream() + .anyMatch( + message -> TYPE_REMOVAL_WARNING.matcher(message).matches() + ); + return matches == false; + } else { + return false; + } + }); + request.setOptions(options); + } + + private void createSnapshotIfNotExists(String repository, String snapshot, boolean waitForCompletion, String indexName) throws IOException { + final Request getSnapshotsRequest = new Request(HttpGet.METHOD_NAME, "_cat/snapshots/" + repository); + final Response getSnapshotsResponse = client().performRequest(getSnapshotsRequest); + if (!EntityUtils.toString(getSnapshotsResponse.getEntity(), StandardCharsets.UTF_8).contains(snapshot)) { + final Request request = new Request(HttpPut.METHOD_NAME, "_snapshot/" + repository + '/' + snapshot); + request.addParameter("wait_for_completion", Boolean.toString(waitForCompletion)); + if (indexName != null) { + String entity = "{\"indices\" : \"" + indexName + "\"}"; + request.setJsonEntity(entity); + } + + final Response response = client().performRequest(request); + assertEquals( + "Failed to create snapshot [" + snapshot + "] in repository [" + repository + "]: " + response, + response.getStatusLine().getStatusCode(), RestStatus.OK.getStatus() + ); + } + } + + private void waitForClusterGreenStatus() throws IOException { + Request waitForGreen = new Request(HttpGet.METHOD_NAME, "/_cluster/health"); + waitForGreen.addParameter("wait_for_nodes", "3"); + client().performRequest(waitForGreen); + } +} diff --git a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/RecoveryIT.java b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/RecoveryIT.java index 687fd1743c3d3..71ea1b95a1786 100644 --- a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/RecoveryIT.java +++ b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/RecoveryIT.java @@ -42,12 +42,13 @@ import org.opensearch.cluster.metadata.MetadataIndexStateService; import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.common.Booleans; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.AbstractRunnable; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.rest.RestStatus; import org.opensearch.index.IndexSettings; -import org.opensearch.rest.RestStatus; import org.opensearch.test.rest.yaml.ObjectPath; import org.hamcrest.Matcher; import org.hamcrest.Matchers; @@ -90,7 +91,7 @@ public void testHistoryUUIDIsGenerated() throws Exception { .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) // if the node with the replica is the first to be restarted, while a replica is still recovering - // then delayed allocation will kick in. When the node comes back, the master will search for a copy + // then delayed allocation will kick in. When the node comes back, the cluster-manager will search for a copy // but the recovering copy will be seen as invalid and the cluster health won't return to GREEN // before timing out .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms"); @@ -158,7 +159,7 @@ public void testRecoveryWithConcurrentIndexing() throws Exception { .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 2) // if the node with the replica is the first to be restarted, while a replica is still recovering - // then delayed allocation will kick in. When the node comes back, the master will search for a copy + // then delayed allocation will kick in. When the node comes back, the cluster-manager will search for a copy // but the recovering copy will be seen as invalid and the cluster health won't return to GREEN // before timing out .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") @@ -256,7 +257,7 @@ public void testRelocationWithConcurrentIndexing() throws Exception { .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 2) // if the node with the replica is the first to be restarted, while a replica is still recovering - // then delayed allocation will kick in. When the node comes back, the master will search for a copy + // then delayed allocation will kick in. When the node comes back, the cluster-manager will search for a copy // but the recovering copy will be seen as invalid and the cluster health won't return to GREEN // before timing out .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") @@ -266,7 +267,7 @@ public void testRelocationWithConcurrentIndexing() throws Exception { indexDocs(index, 0, 10); ensureGreen(index); // make sure that no shards are allocated, so we can make sure the primary stays on the old node (when one - // node stops, we lose the master too, so a replica will not be promoted) + // node stops, we lose the cluster-manager too, so a replica will not be promoted) updateIndexSettings(index, Settings.builder().put(INDEX_ROUTING_ALLOCATION_ENABLE_SETTING.getKey(), "none")); break; case MIXED: @@ -330,7 +331,7 @@ public void testRecovery() throws Exception { .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) // if the node with the replica is the first to be restarted, while a replica is still recovering - // then delayed allocation will kick in. When the node comes back, the master will search for a copy + // then delayed allocation will kick in. When the node comes back, the cluster-manager will search for a copy // but the recovering copy will be seen as invalid and the cluster health won't return to GREEN // before timing out .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") @@ -448,7 +449,7 @@ public void testRecoveryClosedIndex() throws Exception { .put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1) .put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1) // if the node with the replica is the first to be restarted, while a replica is still recovering - // then delayed allocation will kick in. When the node comes back, the master will search for a copy + // then delayed allocation will kick in. When the node comes back, the cluster-manager will search for a copy // but the recovering copy will be seen as invalid and the cluster health won't return to GREEN // before timing out .put(INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.getKey(), "100ms") @@ -800,7 +801,7 @@ public void testSoftDeletesDisabledWarning() throws Exception { settings.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), softDeletesEnabled); } Request request = new Request("PUT", "/" + indexName); - request.setJsonEntity("{\"settings\": " + Strings.toString(settings.build()) + "}"); + request.setJsonEntity("{\"settings\": " + Strings.toString(MediaTypeRegistry.JSON, settings.build()) + "}"); if (softDeletesEnabled == false) { expectSoftDeletesWarning(request, indexName); } diff --git a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/SystemIndicesUpgradeIT.java b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/SystemIndicesUpgradeIT.java index c50af0084b000..22b6b4a201877 100644 --- a/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/SystemIndicesUpgradeIT.java +++ b/qa/rolling-upgrade/src/test/java/org/opensearch/upgrades/SystemIndicesUpgradeIT.java @@ -34,13 +34,17 @@ import org.opensearch.LegacyESVersion; import org.opensearch.Version; +import org.hamcrest.MatcherAssert; import org.opensearch.client.Request; +import org.opensearch.client.Response; import org.opensearch.client.ResponseException; import org.opensearch.test.XContentTestUtils.JsonMapView; +import java.io.IOException; import java.util.Map; import static org.opensearch.cluster.metadata.IndexNameExpressionResolver.SYSTEM_INDEX_ENFORCEMENT_VERSION; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -68,25 +72,7 @@ public void testSystemIndicesUpgrades() throws Exception { } client().performRequest(bulk); - // start a async reindex job - Request reindex = new Request("POST", "/_reindex"); - reindex.setJsonEntity( - "{\n" + - " \"source\":{\n" + - " \"index\":\"test_index_old\"\n" + - " },\n" + - " \"dest\":{\n" + - " \"index\":\"test_index_reindex\"\n" + - " }\n" + - "}"); - reindex.addParameter("wait_for_completion", "false"); - Map response = entityAsMap(client().performRequest(reindex)); - String taskId = (String) response.get("task"); - - // wait for task - Request getTask = new Request("GET", "/_tasks/" + taskId); - getTask.addParameter("wait_for_completion", "true"); - client().performRequest(getTask); + createAndVerifyStoredTask(); // make sure .tasks index exists Request getTasksIndex = new Request("GET", "/.tasks"); @@ -121,6 +107,8 @@ public void testSystemIndicesUpgrades() throws Exception { assertThat(client().performRequest(putAliasRequest).getStatusLine().getStatusCode(), is(200)); } } else if (CLUSTER_TYPE == ClusterType.UPGRADED) { + createAndVerifyStoredTask(); + assertBusy(() -> { Request clusterStateRequest = new Request("GET", "/_cluster/state/metadata"); Map indices = new JsonMapView(entityAsMap(client().performRequest(clusterStateRequest))) @@ -152,4 +140,29 @@ public void testSystemIndicesUpgrades() throws Exception { }); } } + + /** + * Completed tasks get persisted into the .tasks index, so this method waits + * until the task is completed in order to verify that it has been successfully + * written to the index and can be retrieved. + */ + private static void createAndVerifyStoredTask() throws Exception { + // Use update by query to create an async task + final Request updateByQueryRequest = new Request("POST", "/test_index_old/_update_by_query"); + updateByQueryRequest.addParameter("wait_for_completion", "false"); + final Response updateByQueryResponse = client().performRequest(updateByQueryRequest); + MatcherAssert.assertThat(updateByQueryResponse.getStatusLine().getStatusCode(), equalTo(200)); + final String taskId = (String) entityAsMap(updateByQueryResponse).get("task"); + + // wait for task to complete + waitUntil(() -> { + try { + final Response getTaskResponse = client().performRequest(new Request("GET", "/_tasks/" + taskId)); + MatcherAssert.assertThat(getTaskResponse.getStatusLine().getStatusCode(), equalTo(200)); + return (Boolean) entityAsMap(getTaskResponse).get("completed"); + } catch (IOException e) { + throw new AssertionError(e); + } + }); + } } diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml index e1ffcea930a42..840c7f5f6297e 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/10_basic.yml @@ -204,4 +204,3 @@ tasks.get: wait_for_completion: true task_id: $task - diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_date_range.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_date_range.yml index 89992eeba616f..6427a45e19f58 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_date_range.yml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/old_cluster/20_date_range.yml @@ -111,5 +111,3 @@ gte: "2019-02-01T00:00+01:00" lte: "2019-02-01T00:00+01:00" - match: { hits.total: 1 } - - diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml index cb74c33cbd31a..f1c00ee896f92 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/10_basic.yml @@ -133,5 +133,3 @@ wait_for_completion: true task_id: $task_id - match: { task.headers.X-Opaque-Id: "Reindexing Again" } - - diff --git a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/20_date_range.yml b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/20_date_range.yml index 7dae2a4d6241a..026dcff32e175 100644 --- a/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/20_date_range.yml +++ b/qa/rolling-upgrade/src/test/resources/rest-api-spec/test/upgraded_cluster/20_date_range.yml @@ -38,4 +38,3 @@ time_frame: gte: "2019-02-01T00:00+01:00" lte: "2019-02-01T00:00+01:00" - diff --git a/qa/smoke-test-http/build.gradle b/qa/smoke-test-http/build.gradle index 2402c9b807283..f48ddc26d929b 100644 --- a/qa/smoke-test-http/build.gradle +++ b/qa/smoke-test-http/build.gradle @@ -35,7 +35,8 @@ apply plugin: 'opensearch.test-with-dependencies' dependencies { testImplementation project(path: ':modules:transport-netty4') // for http - testImplementation project(path: ':plugins:transport-nio') // for http + testImplementation project(path: ':plugins:transport-nio') + testImplementation project(path: ':plugins:identity-shiro') // for http } integTest { diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/AwarenessAttributeDecommissionRestIT.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/AwarenessAttributeDecommissionRestIT.java new file mode 100644 index 0000000000000..61afb3ffdcf98 --- /dev/null +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/AwarenessAttributeDecommissionRestIT.java @@ -0,0 +1,102 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.http; + +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingResponse; +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.client.ResponseException; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.routing.WeightedRouting; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static org.opensearch.test.NodeRoles.onlyRole; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class AwarenessAttributeDecommissionRestIT extends HttpSmokeTestCase{ + + public void testRestStatusForDecommissioningFailedException() { + internalCluster().startNodes(3); + Request request = new Request("PUT", "/_cluster/decommission/awareness/zone/zone-1"); + ResponseException exception = expectThrows( + ResponseException.class, + () -> getRestClient().performRequest(request) + ); + assertEquals(exception.getResponse().getStatusLine().getStatusCode(), RestStatus.BAD_REQUEST.getStatus()); + assertTrue(exception.getMessage().contains("invalid awareness attribute requested for decommissioning")); + } + + public void testRestStatusForAcknowledgedDecommission() throws IOException { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'a' & 'b' & 'c'"); + List clusterManagerNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + + logger.info("--> start 3 data nodes on zones 'a' & 'b' & 'c'"); + List dataNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build() + ); + + ensureStableCluster(6); + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + Request request = new Request("PUT", "/_cluster/decommission/awareness/zone/c"); + Response response = getRestClient().performRequest(request); + assertEquals(response.getStatusLine().getStatusCode(), RestStatus.OK.getStatus()); + } +} diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/DanglingIndicesRestIT.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/DanglingIndicesRestIT.java index d5dcde2492046..42c7fd667fd8f 100644 --- a/qa/smoke-test-http/src/test/java/org/opensearch/http/DanglingIndicesRestIT.java +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/DanglingIndicesRestIT.java @@ -54,8 +54,8 @@ import static org.opensearch.cluster.metadata.IndexGraveyard.SETTING_MAX_TOMBSTONES; import static org.opensearch.gateway.DanglingIndicesState.AUTO_IMPORT_DANGLING_INDICES_SETTING; import static org.opensearch.indices.IndicesService.WRITE_DANGLING_INDICES_INFO_SETTING; -import static org.opensearch.rest.RestStatus.ACCEPTED; -import static org.opensearch.rest.RestStatus.OK; +import static org.opensearch.core.rest.RestStatus.ACCEPTED; +import static org.opensearch.core.rest.RestStatus.OK; import static org.opensearch.test.XContentTestUtils.createJsonMapView; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -91,7 +91,7 @@ private Settings buildSettings(int maxTombstones) { * Check that when dangling indices are discovered, then they can be listed via the REST API. */ public void testDanglingIndicesCanBeListed() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(1); + internalCluster().setBootstrapClusterManagerNodeIndex(1); internalCluster().startNodes(3, buildSettings(0)); final DanglingIndexDetails danglingIndexDetails = createDanglingIndices(INDEX_NAME); @@ -121,7 +121,7 @@ public void testDanglingIndicesCanBeListed() throws Exception { * Check that dangling indices can be imported. */ public void testDanglingIndicesCanBeImported() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(1); + internalCluster().setBootstrapClusterManagerNodeIndex(1); internalCluster().startNodes(3, buildSettings(0)); createDanglingIndices(INDEX_NAME); @@ -135,7 +135,7 @@ public void testDanglingIndicesCanBeImported() throws Exception { importRequest.addParameter("accept_data_loss", "true"); // Ensure this parameter is accepted importRequest.addParameter("timeout", "20s"); - importRequest.addParameter("master_timeout", "20s"); + importRequest.addParameter("cluster_manager_timeout", "20s"); final Response importResponse = restClient.performRequest(importRequest); assertThat(importResponse.getStatusLine().getStatusCode(), equalTo(ACCEPTED.getStatus())); @@ -157,7 +157,7 @@ public void testDanglingIndicesCanBeImported() throws Exception { * deleted through the API */ public void testDanglingIndicesCanBeDeleted() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(1); + internalCluster().setBootstrapClusterManagerNodeIndex(1); internalCluster().startNodes(3, buildSettings(1)); createDanglingIndices(INDEX_NAME, OTHER_INDEX_NAME); @@ -171,7 +171,7 @@ public void testDanglingIndicesCanBeDeleted() throws Exception { deleteRequest.addParameter("accept_data_loss", "true"); // Ensure these parameters is accepted deleteRequest.addParameter("timeout", "20s"); - deleteRequest.addParameter("master_timeout", "20s"); + deleteRequest.addParameter("cluster_manager_timeout", "20s"); final Response deleteResponse = restClient.performRequest(deleteRequest); assertThat(deleteResponse.getStatusLine().getStatusCode(), equalTo(ACCEPTED.getStatus())); diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/DetailedErrorsEnabledIT.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/DetailedErrorsEnabledIT.java index 090a572ef0d6a..d91a96e0174fc 100644 --- a/qa/smoke-test-http/src/test/java/org/opensearch/http/DetailedErrorsEnabledIT.java +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/DetailedErrorsEnabledIT.java @@ -57,7 +57,7 @@ public void testThatErrorTraceWorksByDefault() throws IOException { Response response = e.getResponse(); assertThat(response.getHeader("Content-Type"), containsString("application/json")); assertThat(EntityUtils.toString(response.getEntity()), - containsString("\"stack_trace\":\"[Validation Failed: 1: index / indices is missing;]; " + + containsString("\"stack_trace\":\"OpenSearchException[Validation Failed: 1: index / indices is missing;]; " + "nested: ActionRequestValidationException[Validation Failed: 1:")); } diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/IdentityAuthenticationIT.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/IdentityAuthenticationIT.java new file mode 100644 index 0000000000000..2772f902e7ea1 --- /dev/null +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/IdentityAuthenticationIT.java @@ -0,0 +1,91 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.http; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import org.junit.Assert; +import org.opensearch.client.Request; +import org.opensearch.client.RequestOptions; +import org.opensearch.client.Response; +import org.opensearch.client.ResponseException; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.identity.shiro.ShiroIdentityPlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.core.rest.RestStatus; + +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.transport.Netty4Plugin; +import org.opensearch.transport.nio.NioTransportPlugin; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.core.StringContains.containsString; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 1) +public class IdentityAuthenticationIT extends HttpSmokeTestCase { + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(FeatureFlags.IDENTITY, "true") + .build(); + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(OpenSearchTestCase.getTestTransportPlugin(), Netty4Plugin.class, NioTransportPlugin.class, ShiroIdentityPlugin.class); + } + + + public void testBasicAuthSuccess() throws Exception { + final Response response = createHealthRequest("Basic YWRtaW46YWRtaW4="); // admin:admin + final String content = new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8); + + Assert.assertThat(content, response.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus())); + Assert.assertThat(content, containsString("green")); + } + + public void testBasicAuthUnauthorized_invalidHeader() throws Exception { + final Response response = createHealthRequest("Basic aaaa"); // invalid username password + final String content = new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8); + + Assert.assertThat(content, response.getStatusLine().getStatusCode(), equalTo(RestStatus.UNAUTHORIZED.getStatus())); + Assert.assertThat(content, containsString("Illegally formed basic authorization header")); + } + + public void testBasicAuthUnauthorized_wrongPassword() throws Exception { + final Response response = createHealthRequest("Basic YWRtaW46aW52YWxpZFBhc3N3b3Jk"); // admin:invalidPassword + final String content = new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8); + + Assert.assertThat(content, response.getStatusLine().getStatusCode(), equalTo(RestStatus.UNAUTHORIZED.getStatus())); + } + + public void testBasicAuthUnauthorized_unknownUser() throws Exception { + final Response response = createHealthRequest("Basic dXNlcjpkb2VzTm90RXhpc3Q="); // user:doesNotExist + final String content = new String(response.getEntity().getContent().readAllBytes(), StandardCharsets.UTF_8); + + Assert.assertThat(content, response.getStatusLine().getStatusCode(), equalTo(RestStatus.UNAUTHORIZED.getStatus())); + } + + private Response createHealthRequest(final String authorizationHeaderValue) throws Exception { + final Request request = new Request("GET", "/_cluster/health"); + final RequestOptions options = RequestOptions.DEFAULT.toBuilder().addHeader("Authorization", authorizationHeaderValue).build(); + request.setOptions(options); + + try { + final Response response = OpenSearchIntegTestCase.getRestClient().performRequest(request); + return response; + } catch (final ResponseException re) { + return re.getResponse(); + } + } +} diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/IndexingPressureRestIT.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/IndexingPressureRestIT.java index bac4304db1ef6..6ff2dee2812e9 100644 --- a/qa/smoke-test-http/src/test/java/org/opensearch/http/IndexingPressureRestIT.java +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/IndexingPressureRestIT.java @@ -46,9 +46,9 @@ import java.util.ArrayList; import java.util.Map; -import static org.opensearch.rest.RestStatus.CREATED; -import static org.opensearch.rest.RestStatus.OK; -import static org.opensearch.rest.RestStatus.TOO_MANY_REQUESTS; +import static org.opensearch.core.rest.RestStatus.CREATED; +import static org.opensearch.core.rest.RestStatus.OK; +import static org.opensearch.core.rest.RestStatus.TOO_MANY_REQUESTS; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/SearchRestCancellationIT.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/SearchRestCancellationIT.java index a13d406f7b133..2126652865330 100644 --- a/qa/smoke-test-http/src/test/java/org/opensearch/http/SearchRestCancellationIT.java +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/SearchRestCancellationIT.java @@ -34,7 +34,6 @@ import org.apache.http.entity.ContentType; import org.apache.http.nio.entity.NByteArrayEntity; import org.apache.logging.log4j.LogManager; -import org.apache.lucene.util.SetOnce; import org.opensearch.action.admin.cluster.node.info.NodeInfo; import org.opensearch.action.admin.cluster.node.info.NodesInfoResponse; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; @@ -48,8 +47,10 @@ import org.opensearch.client.Request; import org.opensearch.client.Response; import org.opensearch.client.ResponseListener; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.SetOnce; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.PluginsService; import org.opensearch.script.MockScriptPlugin; @@ -59,7 +60,7 @@ import org.opensearch.search.lookup.LeafFieldsLookup; import org.opensearch.tasks.CancellableTask; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.tasks.TaskManager; import org.opensearch.transport.TransportService; @@ -98,12 +99,12 @@ public void testAutomaticCancellationDuringQueryPhase() throws Exception { Request searchRequest = new Request("GET", "/test/_search"); SearchSourceBuilder searchSource = new SearchSourceBuilder().query(scriptQuery( new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap()))); - searchRequest.setJsonEntity(Strings.toString(searchSource)); + searchRequest.setJsonEntity(Strings.toString(MediaTypeRegistry.JSON, searchSource)); verifyCancellationDuringQueryPhase(SearchAction.NAME, searchRequest); } public void testAutomaticCancellationMultiSearchDuringQueryPhase() throws Exception { - XContentType contentType = XContentType.JSON; + MediaType contentType = MediaTypeRegistry.JSON; MultiSearchRequest multiSearchRequest = new MultiSearchRequest().add(new SearchRequest("test") .source(new SearchSourceBuilder().scriptField("test_field", new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap())))); @@ -147,12 +148,12 @@ public void testAutomaticCancellationDuringFetchPhase() throws Exception { Request searchRequest = new Request("GET", "/test/_search"); SearchSourceBuilder searchSource = new SearchSourceBuilder().scriptField("test_field", new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap())); - searchRequest.setJsonEntity(Strings.toString(searchSource)); + searchRequest.setJsonEntity(Strings.toString(MediaTypeRegistry.JSON, searchSource)); verifyCancellationDuringFetchPhase(SearchAction.NAME, searchRequest); } public void testAutomaticCancellationMultiSearchDuringFetchPhase() throws Exception { - XContentType contentType = XContentType.JSON; + MediaType contentType = MediaTypeRegistry.JSON; MultiSearchRequest multiSearchRequest = new MultiSearchRequest().add(new SearchRequest("test") .source(new SearchSourceBuilder().scriptField("test_field", new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap())))); @@ -183,6 +184,7 @@ public void onFailure(Exception exception) { } }); + latch.await(2, TimeUnit.SECONDS); awaitForBlock(plugins); cancellable.cancel(); ensureSearchTaskIsCancelled(searchAction, nodeIdToName::get); @@ -297,7 +299,7 @@ public Map, Object>> pluginScripts() { } } - private static ContentType createContentType(final XContentType xContentType) { - return ContentType.create(xContentType.mediaTypeWithoutParameters(), (Charset) null); + private static ContentType createContentType(final MediaType mediaType) { + return ContentType.create(mediaType.mediaTypeWithoutParameters(), (Charset) null); } } diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/ShardIndexingPressureRestIT.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/ShardIndexingPressureRestIT.java index c018bc68e051a..9e663ac042d91 100644 --- a/qa/smoke-test-http/src/test/java/org/opensearch/http/ShardIndexingPressureRestIT.java +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/ShardIndexingPressureRestIT.java @@ -19,9 +19,9 @@ import java.util.ArrayList; import java.util.Map; -import static org.opensearch.rest.RestStatus.CREATED; -import static org.opensearch.rest.RestStatus.OK; -import static org.opensearch.rest.RestStatus.TOO_MANY_REQUESTS; +import static org.opensearch.core.rest.RestStatus.CREATED; +import static org.opensearch.core.rest.RestStatus.OK; +import static org.opensearch.core.rest.RestStatus.TOO_MANY_REQUESTS; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/SystemIndexRestIT.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/SystemIndexRestIT.java index e687e5eb8a151..9f2d686251947 100644 --- a/qa/smoke-test-http/src/test/java/org/opensearch/http/SystemIndexRestIT.java +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/SystemIndexRestIT.java @@ -157,7 +157,7 @@ public List getRestHandlers(Settings settings, RestController restC IndexScopedSettings indexScopedSettings, SettingsFilter settingsFilter, IndexNameExpressionResolver indexNameExpressionResolver, Supplier nodesInCluster) { - return org.opensearch.common.collect.List.of(new AddDocRestHandler()); + return List.of(new AddDocRestHandler()); } @Override @@ -178,7 +178,7 @@ public String getName() { @Override public List routes() { - return org.opensearch.common.collect.List.of(new Route(RestRequest.Method.POST, "/_sys_index_test/add_doc/{id}")); + return List.of(new Route(RestRequest.Method.POST, "/_sys_index_test/add_doc/{id}")); } @Override @@ -186,7 +186,7 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli IndexRequest indexRequest = new IndexRequest(SYSTEM_INDEX_NAME); indexRequest.id(request.param("id")); indexRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); - indexRequest.source(org.opensearch.common.collect.Map.of("some_field", "some_value")); + indexRequest.source(Map.of("some_field", "some_value")); return channel -> client.index(indexRequest, new RestStatusToXContentListener<>(channel, r -> r.getLocation(indexRequest.routing()))); } diff --git a/qa/smoke-test-http/src/test/java/org/opensearch/http/TestResponseHeaderRestAction.java b/qa/smoke-test-http/src/test/java/org/opensearch/http/TestResponseHeaderRestAction.java index c95b4f0070a80..6cf92626e335a 100644 --- a/qa/smoke-test-http/src/test/java/org/opensearch/http/TestResponseHeaderRestAction.java +++ b/qa/smoke-test-http/src/test/java/org/opensearch/http/TestResponseHeaderRestAction.java @@ -36,7 +36,7 @@ import org.opensearch.rest.BytesRestResponse; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestResponse; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import java.util.List; diff --git a/qa/smoke-test-ingest-disabled/build.gradle b/qa/smoke-test-ingest-disabled/build.gradle index d7676a5b5ecbd..79abbf84fbc09 100644 --- a/qa/smoke-test-ingest-disabled/build.gradle +++ b/qa/smoke-test-ingest-disabled/build.gradle @@ -38,5 +38,5 @@ dependencies { } testClusters.integTest { - setting 'node.roles', '[data,master,remote_cluster_client]' + setting 'node.roles', '[data,cluster_manager,remote_cluster_client]' } diff --git a/qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/10_ingest_disabled.yml b/qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/10_ingest_disabled.yml index 7a0cdcbef0786..c8fcebfba67ab 100644 --- a/qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/10_ingest_disabled.yml +++ b/qa/smoke-test-ingest-disabled/src/test/resources/rest-api-spec/test/ingest_mustache/10_ingest_disabled.yml @@ -112,4 +112,3 @@ _id: test_id2 pipeline: my_pipeline_1 - f1: v2 - diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yml index e6a2a3d52e116..c043015281a9a 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yml +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/10_pipeline_with_mustache_templates.yml @@ -208,7 +208,7 @@ id: 1 - length: { _source: 2 } - match: { _source.do_nothing: "foo" } - - match: { _source.error: "processor first_processor [remove]: field [field_to_remove] not present as part of path [field_to_remove]" } + - match: { _source.error: "processor first_processor [remove]: field [field_to_remove] doesn't exist" } --- "Test rolling up json object arrays": diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yml index eaf6b24030a06..c58735f7862e6 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yml +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/50_script_processor_using_painless.yml @@ -102,4 +102,3 @@ - match: { error.processor_type: "script" } - match: { error.type: "script_exception" } - match: { error.reason: "compile error" } - diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yml b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yml index 0f514f2213492..d7f565f30c93d 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yml +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/rest-api-spec/test/ingest/60_pipeline_timestamp_date_mapping.yml @@ -34,4 +34,3 @@ id: 1 pipeline: "my_timely_pipeline" body: {} - diff --git a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/scripts/master.painless b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/scripts/master.painless index 29880e8fd5f57..82f007e8e4dac 100644 --- a/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/scripts/master.painless +++ b/qa/smoke-test-ingest-with-all-dependencies/src/test/resources/scripts/master.painless @@ -1 +1 @@ -ctx.bytes_total = ctx.bytes_in + ctx.bytes_out \ No newline at end of file +ctx.bytes_total = ctx.bytes_in + ctx.bytes_out diff --git a/qa/smoke-test-plugins/src/test/java/org/opensearch/smoketest/SmokeTestPluginsClientYamlTestSuiteIT.java b/qa/smoke-test-plugins/src/test/java/org/opensearch/smoketest/SmokeTestPluginsClientYamlTestSuiteIT.java index a7e50601ad9df..a1c61ca97f877 100644 --- a/qa/smoke-test-plugins/src/test/java/org/opensearch/smoketest/SmokeTestPluginsClientYamlTestSuiteIT.java +++ b/qa/smoke-test-plugins/src/test/java/org/opensearch/smoketest/SmokeTestPluginsClientYamlTestSuiteIT.java @@ -49,4 +49,3 @@ public static Iterable parameters() throws Exception { return OpenSearchClientYamlSuiteTestCase.createParameters(); } } - diff --git a/qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_basic.yml b/qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_basic.yml index 6a92845a062aa..a4c8d4baabd1b 100644 --- a/qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_basic.yml +++ b/qa/smoke-test-plugins/src/test/resources/rest-api-spec/test/smoke_test_plugins/10_basic.yml @@ -4,10 +4,10 @@ - do: cluster.state: {} - # Get master node id - - set: { master_node: master } + # Get cluster-manager node id + - set: { cluster_manager_node: cluster_manager } - do: nodes.info: {} - - length: { nodes.$master.plugins: ${expected.plugins.count} } + - length: { nodes.$cluster_manager.plugins: ${expected.plugins.count} } diff --git a/qa/translog-policy/src/test/java/org/opensearch/upgrades/TranslogPolicyIT.java b/qa/translog-policy/src/test/java/org/opensearch/upgrades/TranslogPolicyIT.java index 0dc62b160ff3f..5f0f468898c47 100644 --- a/qa/translog-policy/src/test/java/org/opensearch/upgrades/TranslogPolicyIT.java +++ b/qa/translog-policy/src/test/java/org/opensearch/upgrades/TranslogPolicyIT.java @@ -35,7 +35,7 @@ import org.opensearch.LegacyESVersion; import org.opensearch.client.Request; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.index.IndexSettings; diff --git a/qa/wildfly/build.gradle b/qa/wildfly/build.gradle index 7cb08a9de6f08..5d37be47e782e 100644 --- a/qa/wildfly/build.gradle +++ b/qa/wildfly/build.gradle @@ -30,6 +30,7 @@ import org.opensearch.gradle.Architecture import org.opensearch.gradle.VersionProperties +import org.opensearch.gradle.test.TestTask apply plugin: 'war' apply plugin: 'opensearch.build' @@ -39,25 +40,31 @@ apply plugin: 'opensearch.internal-distribution-download' testFixtures.useFixture() dependencies { - providedCompile 'javax.enterprise:cdi-api:1.2' - providedCompile 'org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec:1.0.0.Final' - providedCompile 'org.jboss.spec.javax.ws.rs:jboss-jaxrs-api_2.0_spec:1.0.0.Final' - api('org.jboss.resteasy:resteasy-jackson2-provider:3.0.19.Final') { - exclude module: 'jackson-annotations' - exclude module: 'jackson-core' - exclude module: 'jackson-databind' - exclude module: 'jackson-jaxrs-json-provider' + providedCompile('jakarta.enterprise:jakarta.enterprise.cdi-api:4.0.1') { + exclude module: 'jakarta.annotation-api' + } + providedCompile 'jakarta.ws.rs:jakarta.ws.rs-api:3.1.0' + providedCompile "org.jboss.resteasy:resteasy-core:${versions.resteasy}" + providedCompile "org.jboss.resteasy:resteasy-core-spi:${versions.resteasy}" + api("org.jboss.resteasy:resteasy-jackson2-provider:${versions.resteasy}") { + exclude module: 'jakarta.activation-api' + exclude group: 'com.fasterxml.jackson' + exclude group: 'com.fasterxml.jackson.core' + exclude group: 'com.fasterxml.jackson.dataformat' + exclude group: 'com.fasterxml.jackson.module' } api "com.fasterxml.jackson.core:jackson-annotations:${versions.jackson}" - api "com.fasterxml.jackson.core:jackson-core:${versions.jackson}" api "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}" - api "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:${versions.jackson}" - api "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:${versions.jackson}" - api "com.fasterxml.jackson.module:jackson-module-jaxb-annotations:${versions.jackson}" - api "org.apache.logging.log4j:log4j-api:${versions.log4j}" - api "org.apache.logging.log4j:log4j-core:${versions.log4j}" - api project(path: ':client:rest-high-level') - testImplementation project(':test:framework') + api "com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-base:${versions.jackson}" + api "com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:${versions.jackson}" + api "com.github.fge:json-patch:1.9" + api(project(path: ':client:rest-high-level')) { + exclude module: 'jakarta.annotation-api' + } + testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:${versions.log4j}" + testImplementation(project(':test:framework')) { + exclude module: 'jakarta.annotation-api' + } } war { @@ -81,7 +88,7 @@ dockerCompose { useComposeFiles = ['docker-compose.yml'] } -tasks.register("integTest", Test) { +tasks.register("integTest", TestTask) { outputs.doNotCacheIf('Build cache is disabled for Docker tests') { true } maxParallelForks = '1' include '**/*IT.class' diff --git a/qa/wildfly/docker-compose.yml b/qa/wildfly/docker-compose.yml index 96f168ba3505c..b0f1609f01e72 100644 --- a/qa/wildfly/docker-compose.yml +++ b/qa/wildfly/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.7' services: wildfly: - image: jboss/wildfly:18.0.1.Final + image: quay.io/wildfly/wildfly:28.0.1.Final-jdk11 environment: JAVA_OPTS: -Dopensearch.uri=opensearch:9200 -Djboss.http.port=8080 -Djava.net.preferIPv4Stack=true volumes: diff --git a/qa/wildfly/src/main/java/org/opensearch/wildfly/model/Employee.java b/qa/wildfly/src/main/java/org/opensearch/wildfly/model/Employee.java index 5cf40ce941636..8f8d6635d568d 100644 --- a/qa/wildfly/src/main/java/org/opensearch/wildfly/model/Employee.java +++ b/qa/wildfly/src/main/java/org/opensearch/wildfly/model/Employee.java @@ -34,12 +34,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; -import javax.ws.rs.Consumes; -import javax.ws.rs.core.MediaType; - import java.util.List; -@Consumes(MediaType.APPLICATION_JSON) public class Employee { @JsonProperty(value = "first_name") diff --git a/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientActivator.java b/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientActivator.java index bf91346933e02..14b8cd6730531 100644 --- a/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientActivator.java +++ b/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientActivator.java @@ -32,18 +32,10 @@ package org.opensearch.wildfly.transport; -import javax.ws.rs.ApplicationPath; -import javax.ws.rs.core.Application; - -import java.util.Collections; -import java.util.Set; +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; @ApplicationPath("/transport") public class RestHighLevelClientActivator extends Application { - @Override - public Set> getClasses() { - return Collections.singleton(RestHighLevelClientEmployeeResource.class); - } - } diff --git a/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientEmployeeResource.java b/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientEmployeeResource.java index da751df3fbeee..d2bc0a81768d7 100644 --- a/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientEmployeeResource.java +++ b/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientEmployeeResource.java @@ -38,17 +38,9 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.client.RequestOptions; import org.opensearch.client.RestHighLevelClient; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.wildfly.model.Employee; -import javax.inject.Inject; -import javax.ws.rs.GET; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -56,6 +48,16 @@ import java.util.Map; import java.util.Objects; +import jakarta.inject.Inject; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; @Path("/employees") @@ -88,6 +90,7 @@ public Response getEmployeeById(final @PathParam("id") Long id) throws IOExcepti @PUT @Path("/{id}") + @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response putEmployeeById(final @PathParam("id") Long id, final Employee employee) throws URISyntaxException, IOException { Objects.requireNonNull(id); diff --git a/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientProducer.java b/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientProducer.java index f85c3efcbb6e8..a6e2f0e10491c 100644 --- a/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientProducer.java +++ b/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelClientProducer.java @@ -38,9 +38,10 @@ import org.opensearch.common.SuppressForbidden; import org.opensearch.common.io.PathUtils; -import javax.enterprise.inject.Produces; import java.nio.file.Path; +import jakarta.enterprise.inject.Produces; + @SuppressWarnings("unused") public final class RestHighLevelClientProducer { diff --git a/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelJacksonJsonProvider.java b/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelJacksonJsonProvider.java index 604d975f53280..7f27e69813971 100644 --- a/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelJacksonJsonProvider.java +++ b/qa/wildfly/src/main/java/org/opensearch/wildfly/transport/RestHighLevelJacksonJsonProvider.java @@ -32,10 +32,9 @@ package org.opensearch.wildfly.transport; +import jakarta.ws.rs.ext.Provider; import org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider; -import javax.ws.rs.ext.Provider; - @Provider public class RestHighLevelJacksonJsonProvider extends ResteasyJackson2Provider { diff --git a/qa/wildfly/src/main/resources/log4j2.properties b/qa/wildfly/src/main/resources/log4j2.properties deleted file mode 100644 index 4fd5d69daf65b..0000000000000 --- a/qa/wildfly/src/main/resources/log4j2.properties +++ /dev/null @@ -1,20 +0,0 @@ -# -# SPDX-License-Identifier: Apache-2.0 -# -# The OpenSearch Contributors require contributions made to -# this file be licensed under the Apache-2.0 license or a -# compatible open source license. -# -# Modifications Copyright OpenSearch Contributors. See -# GitHub history for details. -# - -status = error - -appender.console.type = Console -appender.console.name = console -appender.console.layout.type = PatternLayout -appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] %marker%m%n - -rootLogger.level = info -rootLogger.appenderRef.console.ref = console diff --git a/qa/wildfly/src/main/webapp/WEB-INF/jboss-deployment-structure.xml b/qa/wildfly/src/main/webapp/WEB-INF/jboss-deployment-structure.xml index 7191bfe1268aa..a08090100989a 100644 --- a/qa/wildfly/src/main/webapp/WEB-INF/jboss-deployment-structure.xml +++ b/qa/wildfly/src/main/webapp/WEB-INF/jboss-deployment-structure.xml @@ -1,9 +1,6 @@ - - - diff --git a/qa/wildfly/src/test/java/org/opensearch/wildfly/WildflyIT.java b/qa/wildfly/src/test/java/org/opensearch/wildfly/WildflyIT.java index 7961ca69c2d29..ab24dfaddd893 100644 --- a/qa/wildfly/src/test/java/org/opensearch/wildfly/WildflyIT.java +++ b/qa/wildfly/src/test/java/org/opensearch/wildfly/WildflyIT.java @@ -45,10 +45,10 @@ import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.lucene.tests.util.TestRuleLimitSysouts; import org.opensearch.cluster.ClusterModule; -import org.opensearch.common.xcontent.DeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.net.URI; diff --git a/release-notes/opensearch.release-notes-1.0.0-rc1.md b/release-notes/opensearch.release-notes-1.0.0-rc1.md index 2223d732abb98..205873fd282b2 100644 --- a/release-notes/opensearch.release-notes-1.0.0-rc1.md +++ b/release-notes/opensearch.release-notes-1.0.0-rc1.md @@ -411,5 +411,3 @@ Signed-off-by: Abbas Hussain <abbas_10690@yahoo.com> - - diff --git a/release-notes/opensearch.release-notes-1.1.0.md b/release-notes/opensearch.release-notes-1.1.0.md index 0545e106a15a5..ba5a5d4c95c60 100644 --- a/release-notes/opensearch.release-notes-1.1.0.md +++ b/release-notes/opensearch.release-notes-1.1.0.md @@ -386,5 +386,3 @@ Signed-off-by: Sooraj Sinha <soosinha@amazon.com> - - diff --git a/release-notes/opensearch.release-notes-1.2.0.md b/release-notes/opensearch.release-notes-1.2.0.md index 86860e5f872da..d7c75f3c0eaf2 100644 --- a/release-notes/opensearch.release-notes-1.2.0.md +++ b/release-notes/opensearch.release-notes-1.2.0.md @@ -458,4 +458,3 @@ Signed-off-by: Nicholas Walter Knize <nknize@apache.org> - diff --git a/release-notes/opensearch.release-notes-1.2.4.md b/release-notes/opensearch.release-notes-1.2.4.md index dc2852a102c44..dc0bce20a2a00 100644 --- a/release-notes/opensearch.release-notes-1.2.4.md +++ b/release-notes/opensearch.release-notes-1.2.4.md @@ -72,5 +72,3 @@ Signed-off-by: dblock <dblock@amazon.com> - - diff --git a/release-notes/opensearch.release-notes-1.3.0.md b/release-notes/opensearch.release-notes-1.3.0.md index 62c5be8413943..7dd71fa47b72f 100644 --- a/release-notes/opensearch.release-notes-1.3.0.md +++ b/release-notes/opensearch.release-notes-1.3.0.md @@ -1295,5 +1295,3 @@ [Nick Knize](mailto:nknize@apache.org) - Thu, 4 Nov 2021 14:46:57 -0500 Signed-off-by: Nicholas Walter Knize <nknize@apache.org> - - diff --git a/release-notes/opensearch.release-notes-2.0.0.md b/release-notes/opensearch.release-notes-2.0.0.md new file mode 100644 index 0000000000000..ab38069bbf67d --- /dev/null +++ b/release-notes/opensearch.release-notes-2.0.0.md @@ -0,0 +1,210 @@ +## 2022-05-19 Version 2.0.0 Release Notes + +### Breaking Changes in 2.0 + +#### Remove Mapping types +* [Type removal] Remove redundant _type in pipeline simulate action ([#3371](https://github.com/opensearch-project/OpenSearch/pull/3371)) +* [Type removal] Remove _type deprecation from script and conditional processor ([#3239](https://github.com/opensearch-project/OpenSearch/pull/3239)) +* [Type removal] Remove _type from _bulk yaml test, scripts, unused constants ([#3372](https://github.com/opensearch-project/OpenSearch/pull/3372)) +* [Type removal] _type removal from mocked responses of scroll hit tests ([#3377](https://github.com/opensearch-project/OpenSearch/pull/3377)) +* [Remove] TypeFieldMapper ([#3196](https://github.com/opensearch-project/OpenSearch/pull/3196)) +* [Type Removal] Remove TypeFieldMapper usage, remove support of `_type` in searches and from LeafFieldsLookup ([#3016](https://github.com/opensearch-project/OpenSearch/pull/3016)) +* [Type removal] Remove _type support in NOOP bulk indexing from client benchmark ([#3076](https://github.com/opensearch-project/OpenSearch/pull/3076)) +* [Type removal] Remove deprecation warning on use of _type in doc scripts ([#2564](https://github.com/opensearch-project/OpenSearch/pull/2564)) +* [Remove] AliasesExistAction ([#3149](https://github.com/opensearch-project/OpenSearch/pull/3149)) +* [Remove] TypesExist Action ([#3139](https://github.com/opensearch-project/OpenSearch/pull/3139)) +* [Remove] Type from nested fields using new metadata field mapper([#3004](https://github.com/opensearch-project/OpenSearch/pull/3004)) +* [Remove] types from rest-api-spec endpoints ([#2689](https://github.com/opensearch-project/OpenSearch/pull/2689)) +* [Remove] Types from PutIndexTemplateRequest and builder to reduce mapping to a string ([#2510](https://github.com/opensearch-project/OpenSearch/pull/2510)) +* [Remove] Type from Percolate query API ([#2490](https://github.com/opensearch-project/OpenSearch/pull/2490)) +* [Remove] types from CreateIndexRequest and companion Builder's mapping method ([#2498](https://github.com/opensearch-project/OpenSearch/pull/2498)) +* [Remove] Type from PutIndexTemplateRequest and PITRB ([#2497](https://github.com/opensearch-project/OpenSearch/pull/2497)) +* [Remove] Type metadata from ingest documents ([#2491](https://github.com/opensearch-project/OpenSearch/pull/2491)) +* [Remove] type from CIR.mapping and CIRB.mapping ([#2478](https://github.com/opensearch-project/OpenSearch/pull/2478)) +* [Remove] types based addMapping method from CreateIndexRequest and Builder ([#2460](https://github.com/opensearch-project/OpenSearch/pull/2460)) +* [Remove] type from TaskResults index and IndexMetadata.getMappings ([#2469](https://github.com/opensearch-project/OpenSearch/pull/2469)) +* [Remove] Type query ([#2448](https://github.com/opensearch-project/OpenSearch/pull/2448)) +* [Remove] Type from TermsLookUp ([#2459](https://github.com/opensearch-project/OpenSearch/pull/2459)) +* [Remove] types from Uid and remaining types/Uid from translog ([#2450](https://github.com/opensearch-project/OpenSearch/pull/2450)) +* [Remove] types from translog ([#2439](https://github.com/opensearch-project/OpenSearch/pull/2439)) +* [Remove] Multiple Types from IndexTemplateMetadata ([#2400](https://github.com/opensearch-project/OpenSearch/pull/2400)) +* Remove type mapping from document index API ([#2026](https://github.com/opensearch-project/OpenSearch/pull/2026)) +* [Remove] Type mapping parameter from document update API ([#2204](https://github.com/opensearch-project/OpenSearch/pull/2204)) +* [Remove] Types from DocWrite Request and Response ([#2239](https://github.com/opensearch-project/OpenSearch/pull/2239)) +* [Remove] Types from GET/MGET ([#2168](https://github.com/opensearch-project/OpenSearch/pull/2168)) +* [Remove] types from SearchHit and Explain API ([#2205](https://github.com/opensearch-project/OpenSearch/pull/2205)) +* [Remove] type support from Bulk API ([#2215](https://github.com/opensearch-project/OpenSearch/pull/2215)) +* Remove type end-points from no-op bulk and search action ([#2261](https://github.com/opensearch-project/OpenSearch/pull/2261)) +* Remove type end-points from search and related APIs ([#2263](https://github.com/opensearch-project/OpenSearch/pull/2263)) +* [Remove] Type mapping end-points from RestMultiSearchTemplateAction ([#2433](https://github.com/opensearch-project/OpenSearch/pull/2433)) +* Removes type mappings from mapping APIs ([#2238](https://github.com/opensearch-project/OpenSearch/pull/2238)) +* Remove type end-points from count action ([#2379](https://github.com/opensearch-project/OpenSearch/pull/2379)) +* Remove type from validate query API ([#2255](https://github.com/opensearch-project/OpenSearch/pull/2255)) +* [Remove] Type parameter from TermVectors API ([#2104](https://github.com/opensearch-project/OpenSearch/pull/2104)) +* Remove inclue_type_name parameter from rest api spec ([#2410](https://github.com/opensearch-project/OpenSearch/pull/2410)) +* [Remove] include_type_name from HLRC ([#2397](https://github.com/opensearch-project/OpenSearch/pull/2397)) +* [Remove] Type mappings from GeoShapeQueryBuilder ([#2322](https://github.com/opensearch-project/OpenSearch/pull/2322)) +* [Remove] types from PutMappingRequest ([#2335](https://github.com/opensearch-project/OpenSearch/pull/2335)) +* [Remove] deprecated getMapping API from IndicesClient ([#2262](https://github.com/opensearch-project/OpenSearch/pull/2262)) +* [Remove] remaining type usage in Client and AbstractClient ([#2258](https://github.com/opensearch-project/OpenSearch/pull/2258)) +* [Remove] Type from Client.prepare(Index,Delete,Update) ([#2253](https://github.com/opensearch-project/OpenSearch/pull/2253)) +* [Remove] Type Specific Index Stats ([#2198](https://github.com/opensearch-project/OpenSearch/pull/2198)) +* [Remove] Type from Search Internals ([#2109](https://github.com/opensearch-project/OpenSearch/pull/2109)) + +#### Upgrades +* [Upgrade] Lucene 9.1 release ([#2560](https://github.com/opensearch-project/OpenSearch/pull/2560)) +* [Upgrade] ICU4j from 68.2 to 70.1 ([#2504](https://github.com/opensearch-project/OpenSearch/pull/2504)) + +#### Deprecations +* Deprecate setting 'cluster.no_master_block' and introduce the alternative setting 'cluster.no_cluster_manager_block' ([#2453](https://github.com/opensearch-project/OpenSearch/pull/2453)) +* Deprecate setting 'cluster.service.slow_master_task_logging_threshold' and introduce the alternative setting 'cluster.service.slow_cluster_manager_task_logging_threshold' ([#2451](https://github.com/opensearch-project/OpenSearch/pull/2451)) +* Deprecate setting 'cluster.initial_master_nodes' and introduce the alternative setting 'cluster.initial_cluster_manager_nodes' ([#2463](https://github.com/opensearch-project/OpenSearch/pull/2463)) +* Deprecated reserved node id '_must_join_elected_master_' that used by DetachClusterCommand and replace with '_must_join_elected_cluster_manager_' ([#3138](https://github.com/opensearch-project/OpenSearch/pull/3138)) + +### Security Fixes +* [CVE-2020-36518] Update jackson-databind to 2.13.2.2 ([#2599](https://github.com/opensearch-project/OpenSearch/pull/2599)) + +### Features/Enhancements +* Removing hard coded value of max concurrent shard requests ([#3364](https://github.com/opensearch-project/OpenSearch/pull/3364)) +* Update generated ANTLR lexer/parser to match runtime version ([#3297](https://github.com/opensearch-project/OpenSearch/pull/3297)) +* Rename BecomeMasterTask to BecomeClusterManagerTask in JoinTaskExecutor ([#3099](https://github.com/opensearch-project/OpenSearch/pull/3099)) +* Replace 'master' terminology with 'cluster manager' in log messages in 'server/src/main' directory - Part 2 ([#3174](https://github.com/opensearch-project/OpenSearch/pull/3174)) +* Remove deprecation warning of using REST API request parameter 'master_timeout' ([#2920](https://github.com/opensearch-project/OpenSearch/pull/2920)) +* Add deprecated API for creating History Ops Snapshot from translog ([#2886](https://github.com/opensearch-project/OpenSearch/pull/2886)) +* Add request parameter 'cluster_manager_timeout' and deprecate 'master_timeout' - in Ingest APIs and Script APIs ([#2682](https://github.com/opensearch-project/OpenSearch/pull/2682)) +* Change deprecation message for API parameter value 'master_node' of parameter 'metric' ([#2880](https://github.com/opensearch-project/OpenSearch/pull/2880)) +* Add request parameter 'cluster_manager_timeout' and deprecate 'master_timeout' - in Snapshot APIs ([#2680](https://github.com/opensearch-project/OpenSearch/pull/2680)) +* Add request parameter 'cluster_manager_timeout' and deprecate 'master_timeout' - in Index Template APIs ([#2678](https://github.com/opensearch-project/OpenSearch/pull/2678)) +* Change deprecation message for REST API parameter 'master_timeout' to specify the version of removal ([#2863](https://github.com/opensearch-project/OpenSearch/pull/2863)) +* Decouple IndexSettings from IncludeExclude ([#2860](https://github.com/opensearch-project/OpenSearch/pull/2860)) +* Remove endpoint_suffix dependency on account key ([#2485](https://github.com/opensearch-project/OpenSearch/pull/2485)) +* Replace remaining 'blacklist' with 'denylist' in internal class and method names ([#2784](https://github.com/opensearch-project/OpenSearch/pull/2784)) +* Make discovered_master field optional on the client to support compatibility for opensearch client with odfe ([#2641](https://github.com/opensearch-project/OpenSearch/pull/2641)) +* Add request parameter 'cluster_manager_timeout' and deprecate 'master_timeout' - in Index APIs except index template APIs ([#2660](https://github.com/opensearch-project/OpenSearch/pull/2660)) +* Add request parameter 'cluster_manager_timeout' and deprecate 'master_timeout' - in Cluster APIs ([#2658](https://github.com/opensearch-project/OpenSearch/pull/2658)) +* Make Rest-High-Rest-Level tests allow deprecation warning temporarily, during deprecation of request parameter 'master_timeout' ([#2702](https://github.com/opensearch-project/OpenSearch/pull/2702)) +* Add request parameter 'cluster_manager_timeout' as the alternative for 'master_timeout', and deprecate 'master_timeout' - in CAT APIs ([#2717](https://github.com/opensearch-project/OpenSearch/pull/2717)) +* Add mapping method back referenced in other repos ([#2636](https://github.com/opensearch-project/OpenSearch/pull/2636)) +* Replaced "master" terminology in Log message ([#2575](https://github.com/opensearch-project/OpenSearch/pull/2575)) +* Introduce QueryPhaseSearcher extension point (SearchPlugin) ([#1931](https://github.com/opensearch-project/OpenSearch/pull/1931)) +* Support for geo_bounding_box queries on geo_shape fields ([#2506](https://github.com/opensearch-project/OpenSearch/pull/2506)) +* Updating repository commons logging version ([#2541](https://github.com/opensearch-project/OpenSearch/pull/2541)) +* Support for geo_distance queries on geo_shape fields ([#2516](https://github.com/opensearch-project/OpenSearch/pull/2516)) +* Add 'cluster_manager_node' into ClusterState Metric as an alternative to 'master_node' ([#2415](https://github.com/opensearch-project/OpenSearch/pull/2415)) +* Add a new node role 'cluster_manager' as the alternative for 'master' role and deprecate 'master' role ([#2424](https://github.com/opensearch-project/OpenSearch/pull/2424)) +* Replace 'master' with 'cluster_manager' in 'GET Cat Nodes' API ([#2441](https://github.com/opensearch-project/OpenSearch/pull/2441)) +* Replace 'discovered_master' with 'discovered_cluster_manager' in 'GET Cat Health' API ([#2438](https://github.com/opensearch-project/OpenSearch/pull/2438)) +* Add a field discovered_cluster_manager in get cluster health api ([#2437](https://github.com/opensearch-project/OpenSearch/pull/2437)) +* Add request parameter 'cluster_manager_timeout' as the alternative for 'master_timeout', and deprecate 'master_timeout' - in CAT Nodes API ([#2435](https://github.com/opensearch-project/OpenSearch/pull/2435)) +* Add a new REST API endpoint 'GET _cat/cluster_manager' as the replacement of 'GET _cat/master' ([#2404](https://github.com/opensearch-project/OpenSearch/pull/2404)) +* Add default for EnginePlugin.getEngineFactory ([#2419](https://github.com/opensearch-project/OpenSearch/pull/2419)) + +### Bug Fixes +* Fixing PublishTests tests (running against unclean build folders) ([#3253](https://github.com/opensearch-project/OpenSearch/pull/3253)) +* Fixing Scaled float field mapper to respect ignoreMalformed setting ([#2918](https://github.com/opensearch-project/OpenSearch/pull/2918)) +* Fixing plugin installation URL to consume build qualifier ([#3193](https://github.com/opensearch-project/OpenSearch/pull/3193)) +* Fix minimum index compatibility error message ([#3159](https://github.com/opensearch-project/OpenSearch/pull/3159)) +* Added explicit 'null' check for response listener to prevent obscure NullPointerException issues ([#3048](https://github.com/opensearch-project/OpenSearch/pull/3048)) +* Adding a null pointer check to fix index_prefix query ([#2879](https://github.com/opensearch-project/OpenSearch/pull/2879)) +* Bugfix to guard against stack overflow errors caused by very large reg-ex input ([#2816](https://github.com/opensearch-project/OpenSearch/pull/2816)) +* Fix InboundDecoder version compat check ([#2570](https://github.com/opensearch-project/OpenSearch/pull/2570)) +* ignore_malformed parameter on ip_range data_type throws mapper_parsing_exception ([#2429](https://github.com/opensearch-project/OpenSearch/pull/2429)) +* Discrepancy in result from _validate/query API and actual query validity ([#2416](https://github.com/opensearch-project/OpenSearch/pull/2416)) + +### Build & Infrastructure +* Allow to configure POM for ZIP publication ([#3252](https://github.com/opensearch-project/OpenSearch/pull/3252)) +* Gradle plugin `opensearch.pluginzip` Add implicit dependency. ([#3189](https://github.com/opensearch-project/OpenSearch/pull/3189)) +* Gradle custom java zippublish plugin ([#2988](https://github.com/opensearch-project/OpenSearch/pull/2988)) +* Added Adoptium JDK8 support and updated DistroTestPlugin JDK version used by Gradle ([#3324](https://github.com/opensearch-project/OpenSearch/pull/3324)) +* Update bundled JDK to 17.0.3+7 ([#3093](https://github.com/opensearch-project/OpenSearch/pull/3093)) +* Use G1GC on JDK11+ ([#2964](https://github.com/opensearch-project/OpenSearch/pull/2964)) +* Removed java11 source folders since JDK-11 is the baseline now ([#2898](https://github.com/opensearch-project/OpenSearch/pull/2898)) +* Changed JAVA_HOME to jdk-17 ([#2656](https://github.com/opensearch-project/OpenSearch/pull/2656)) +* Fix build-tools/reaper source/target compatibility to be JDK-11 ([#2596](https://github.com/opensearch-project/OpenSearch/pull/2596)) +* Adding workflow to create documentation related issues in documentation-website repo ([#2929](https://github.com/opensearch-project/OpenSearch/pull/2929)) +* Fix issue that deprecated setting 'cluster.initial_master_nodes' is not identified in node bootstrap check ([#2779](https://github.com/opensearch-project/OpenSearch/pull/2779)) +* Replace blacklist in Gradle build environment configuration ([#2752](https://github.com/opensearch-project/OpenSearch/pull/2752)) +* Update ThirdPartyAuditTask to check for and list pointless exclusions. ([#2760](https://github.com/opensearch-project/OpenSearch/pull/2760)) +* Add Shadow jar publication to lang-painless module. ([#2681](https://github.com/opensearch-project/OpenSearch/pull/2681)) +* Add 1.3.2 to main causing gradle check failures ([#2679](https://github.com/opensearch-project/OpenSearch/pull/2679)) +* Added jenkinsfile to run gradle check in OpenSearch ([#2166](https://github.com/opensearch-project/OpenSearch/pull/2166)) +* Gradle check retry ([#2638](https://github.com/opensearch-project/OpenSearch/pull/2638)) +* Override Default Distribution Download Url with Custom Distribution Url when it is passed from Plugin ([#2420](https://github.com/opensearch-project/OpenSearch/pull/2420)) + +### Documentation +* [Javadocs] add remaining internal classes and reenable missingJavadoc on server ([#3296](https://github.com/opensearch-project/OpenSearch/pull/3296)) +* [Javadocs] add to o.o.cluster ([#3170](https://github.com/opensearch-project/OpenSearch/pull/3170)) +* [Javadocs] add to o.o.bootstrap, cli, and client ([#3163](https://github.com/opensearch-project/OpenSearch/pull/3163)) +* [Javadocs] add to o.o.search.rescore,searchafter,slice, sort, and suggest ([#3264](https://github.com/opensearch-project/OpenSearch/pull/3264)) +* [Javadocs] add to o.o.transport ([#3220](https://github.com/opensearch-project/OpenSearch/pull/3220)) +* [Javadocs] add to o.o.action, index, and transport ([#3277](https://github.com/opensearch-project/OpenSearch/pull/3277)) +* [Javadocs] add to internal classes in o.o.http, indices, and search ([#3288](https://github.com/opensearch-project/OpenSearch/pull/3288)) +* [Javadocs] Add to remaining o.o.action classes ([#3182](https://github.com/opensearch-project/OpenSearch/pull/3182)) +* [Javadocs] add to o.o.rest, snapshots, and tasks packages ([#3219](https://github.com/opensearch-project/OpenSearch/pull/3219)) +* [Javadocs] add to o.o.common ([#3289](https://github.com/opensearch-project/OpenSearch/pull/3289)) +* [Javadocs] add to o.o.dfs,fetch,internal,lookup,profile, and query packages ([#3261](https://github.com/opensearch-project/OpenSearch/pull/3261)) +* [Javadocs] add to o.o.search.aggs, builder, and collapse packages ([#3254](https://github.com/opensearch-project/OpenSearch/pull/3254)) +* [Javadocs] add to o.o.index and indices ([#3209](https://github.com/opensearch-project/OpenSearch/pull/3209)) +* [Javadocs] add to o.o.monitor,persistance,plugins,repo,script,threadpool,usage,watcher ([#3186](https://github.com/opensearch-project/OpenSearch/pull/3186)) +* [Javadocs] Add to o.o.disovery, env, gateway, http, ingest, lucene and node pkgs ([#3185](https://github.com/opensearch-project/OpenSearch/pull/3185)) +* [Javadocs] add to o.o.action.admin ([#3155](https://github.com/opensearch-project/OpenSearch/pull/3155)) +* [Javadocs] Add missing package-info.java files to server ([#3128](https://github.com/opensearch-project/OpenSearch/pull/3128)) + +### Maintenance +* Bump re2j from 1.1 to 1.6 in /plugins/repository-hdfs ([#3337](https://github.com/opensearch-project/OpenSearch/pull/3337)) +* Bump google-oauth-client from 1.33.1 to 1.33.2 in /plugins/discovery-gce ([#2828](https://github.com/opensearch-project/OpenSearch/pull/2828)) +* Bump protobuf-java-util from 3.19.3 to 3.20.0 in /plugins/repository-gcs ([#2834](https://github.com/opensearch-project/OpenSearch/pull/2834)) +* Bump cdi-api from 1.2 to 2.0 in /qa/wildfly ([#2835](https://github.com/opensearch-project/OpenSearch/pull/2835)) +* Bump azure-core from 1.26.0 to 1.27.0 in /plugins/repository-azure ([#2837](https://github.com/opensearch-project/OpenSearch/pull/2837)) +* Bump asm-analysis from 9.2 to 9.3 in /test/logger-usage ([#2829](https://github.com/opensearch-project/OpenSearch/pull/2829)) +* Bump protobuf-java from 3.19.3 to 3.20.0 in /plugins/repository-hdfs ([#2836](https://github.com/opensearch-project/OpenSearch/pull/2836)) +* Bump joni from 2.1.41 to 2.1.43 in /libs/grok ([#2832](https://github.com/opensearch-project/OpenSearch/pull/2832)) +* Bump geoip2 from 2.16.1 to 3.0.1 in /modules/ingest-geoip ([#2646](https://github.com/opensearch-project/OpenSearch/pull/2646)) +* Bump jettison from 1.1 to 1.4.1 in /plugins/discovery-azure-classic ([#2614](https://github.com/opensearch-project/OpenSearch/pull/2614)) +* Bump google-oauth-client from 1.31.0 to 1.33.1 in /plugins/repository-gcs ([#2616](https://github.com/opensearch-project/OpenSearch/pull/2616)) +* Bump jboss-annotations-api_1.2_spec in /qa/wildfly ([#2615](https://github.com/opensearch-project/OpenSearch/pull/2615)) +* Bump forbiddenapis in /buildSrc/src/testKit/thirdPartyAudit ([#2611](https://github.com/opensearch-project/OpenSearch/pull/2611)) +* Bump json-schema-validator from 1.0.67 to 1.0.68 in /buildSrc ([#2610](https://github.com/opensearch-project/OpenSearch/pull/2610)) +* Bump htrace-core4 from 4.1.0-incubating to 4.2.0-incubating in /plugins/repository-hdfs ([#2618](https://github.com/opensearch-project/OpenSearch/pull/2618)) +* Bump asm-tree from 7.2 to 9.2 in /modules/lang-painless ([#2617](https://github.com/opensearch-project/OpenSearch/pull/2617)) +* Bump antlr4 from 4.5.3 to 4.9.3 in /modules/lang-painless ([#2537](https://github.com/opensearch-project/OpenSearch/pull/2537)) +* Bump commons-lang3 from 3.7 to 3.12.0 in /plugins/repository-hdfs ([#2552](https://github.com/opensearch-project/OpenSearch/pull/2552)) +* Bump gson from 2.8.9 to 2.9.0 in /plugins/repository-gcs ([#2550](https://github.com/opensearch-project/OpenSearch/pull/2550)) +* Bump google-oauth-client from 1.31.0 to 1.33.1 in /plugins/discovery-gce ([#2524](https://github.com/opensearch-project/OpenSearch/pull/2524)) +* Bump google-cloud-core from 1.93.3 to 2.5.10 in /plugins/repository-gcs ([#2536](https://github.com/opensearch-project/OpenSearch/pull/2536)) +* Bump wiremock-jre8-standalone from 2.23.2 to 2.32.0 in /buildSrc ([#2525](https://github.com/opensearch-project/OpenSearch/pull/2525)) +* Bump com.gradle.enterprise from 3.8.1 to 3.9 ([#2523](https://github.com/opensearch-project/OpenSearch/pull/2523)) +* Bump commons-io from 2.7 to 2.11.0 in /plugins/discovery-azure-classic ([#2527](https://github.com/opensearch-project/OpenSearch/pull/2527)) +* Bump asm-analysis from 7.1 to 9.2 in /test/logger-usage ([#2273](https://github.com/opensearch-project/OpenSearch/pull/2273)) +* Bump asm-commons from 7.2 to 9.2 in /modules/lang-painless ([#2234](https://github.com/opensearch-project/OpenSearch/pull/2234)) +* Bump jna from 5.5.0 to 5.10.0 in /buildSrc ([#2512](https://github.com/opensearch-project/OpenSearch/pull/2512)) +* Bump jsr305 from 1.3.9 to 3.0.2 in /plugins/discovery-gce ([#2137](https://github.com/opensearch-project/OpenSearch/pull/2137)) +* Bump json-schema-validator from 1.0.36 to 1.0.67 in /buildSrc ([#2454](https://github.com/opensearch-project/OpenSearch/pull/2454)) +* Bump woodstox-core from 6.1.1 to 6.2.8 in /plugins/repository-azure ([#2456](https://github.com/opensearch-project/OpenSearch/pull/2456)) +* Bump commons-lang3 from 3.4 to 3.12.0 in /plugins/repository-azure ([#2455](https://github.com/opensearch-project/OpenSearch/pull/2455)) +* Update azure-storage-blob to 12.15.0 ([#2774](https://github.com/opensearch-project/OpenSearch/pull/2774)) +* Move Jackson-databind to 2.13.2 ([#2548](https://github.com/opensearch-project/OpenSearch/pull/2548)) +* Add trademark notice ([#2473](https://github.com/opensearch-project/OpenSearch/pull/2473)) +* adds ToC ([#2546](https://github.com/opensearch-project/OpenSearch/pull/2546)) +* Sync maintainers with actual permissions. ([#3127](https://github.com/opensearch-project/OpenSearch/pull/3127)) + +### Refactoring +* [Remove] remaining AllFieldMapper references ([#3007](https://github.com/opensearch-project/OpenSearch/pull/3007)) +* Clear up some confusing code in IndexShardHotSpotTests ([#1534](https://github.com/opensearch-project/OpenSearch/pull/1534)) +* [Remove] ShrinkAction, ShardUpgradeRequest, UpgradeSettingsRequestBuilder ([#3169](https://github.com/opensearch-project/OpenSearch/pull/3169)) +* [Rename] ESTestCase stragglers to OpenSearchTestCase ([#3053](https://github.com/opensearch-project/OpenSearch/pull/3053)) +* [Remove] MainResponse version override cluster setting ([#3031](https://github.com/opensearch-project/OpenSearch/pull/3031)) +* [Version] Don't spoof major for 3.0+ clusters ([#2722](https://github.com/opensearch-project/OpenSearch/pull/2722)) +* Centralize codes related to 'master_timeout' deprecation for eaiser removal - in CAT Nodes API ([#2670](https://github.com/opensearch-project/OpenSearch/pull/2670)) +* Rename reference to project OpenSearch was forked from ([#2483](https://github.com/opensearch-project/OpenSearch/pull/2483)) +* Remove the IndexCommitRef class ([#2421](https://github.com/opensearch-project/OpenSearch/pull/2421)) +* Refactoring gated and ref-counted interfaces and their implementations ([#2396](https://github.com/opensearch-project/OpenSearch/pull/2396)) +* [Refactor] LuceneChangesSnapshot to use accurate ops history ([#2452](https://github.com/opensearch-project/OpenSearch/pull/2452)) + +### Tests +* Add type mapping removal bwc tests for indexing, searching, snapshots ([#2901](https://github.com/opensearch-project/OpenSearch/pull/2901)) +* Removing SLM check in tests for OpenSearch versions ([#2604](https://github.com/opensearch-project/OpenSearch/pull/2604)) +* [Unmute] NumberFieldTypeTests ([#2531](https://github.com/opensearch-project/OpenSearch/pull/2531)) +* Use Hamcrest matchers and assertThat() in ReindexRenamedSettingTests ([#2503](https://github.com/opensearch-project/OpenSearch/pull/2503)) +* [Unmute] IndexPrimaryRelocationIT ([#2488](https://github.com/opensearch-project/OpenSearch/pull/2488)) +* Fixing PluginsServiceTests (post Lucene 9 update) ([#2484](https://github.com/opensearch-project/OpenSearch/pull/2484)) diff --git a/release-notes/opensearch.release-notes-2.0.1.md b/release-notes/opensearch.release-notes-2.0.1.md new file mode 100644 index 0000000000000..e2918d14a055c --- /dev/null +++ b/release-notes/opensearch.release-notes-2.0.1.md @@ -0,0 +1,9 @@ +## 2022-06-15 Version 2.0.1 Release Notes + +### Bug Fixes +* Fixing the Node Sniffer RestClient support for OpenSearch 2.x ([#3522](https://github.com/opensearch-project/OpenSearch/pull/3522)) +* Adding MainResponse version override cluster setting ([#3536](https://github.com/opensearch-project/OpenSearch/pull/3536)) + +### Tests +* Fixing Docker test for Ubuntu ([#3465](https://github.com/opensearch-project/OpenSearch/pull/3465)) +* [Type removal] Fixing the cluster upgrade backward compatibility test ([#3531](https://github.com/opensearch-project/OpenSearch/pull/3531)) diff --git a/release-notes/opensearch.release-notes-2.1.0.md b/release-notes/opensearch.release-notes-2.1.0.md new file mode 100644 index 0000000000000..b32864990b82e --- /dev/null +++ b/release-notes/opensearch.release-notes-2.1.0.md @@ -0,0 +1,120 @@ +## 2022-07-07 Version 2.1.0 Release Notes + +### Features/Enhancements + +* Rename package 'o.o.action.support.master' to 'o.o.action.support.clustermanager' (#3556) ([#3597](https://github.com/opensearch-project/opensearch/pull/3597)) +* Support dynamic node role (#3436) ([#3585](https://github.com/opensearch-project/opensearch/pull/3585)) +* Support use of IRSA for repository-s3 plugin credentials (#3475) ([#3486](https://github.com/opensearch-project/opensearch/pull/3486)) +* Filter out invalid URI and HTTP method in the error message of no handler found for a REST request (#3459) ([#3485](https://github.com/opensearch-project/opensearch/pull/3485)) +* Set term vector flags to false for ._index_prefix field (#1901). (#3119) ([#3447](https://github.com/opensearch-project/opensearch/pull/3447)) +* Replace internal usages of 'master' term in 'server/src/test' directory (#2520) ([#3444](https://github.com/opensearch-project/opensearch/pull/3444)) +* Rename master to cluster_manager in the XContent Parser of ClusterHealthResponse (#3432) ([#3438](https://github.com/opensearch-project/opensearch/pull/3438)) +* Replace internal usages of 'master' term in 'server/src/internalClusterTest' directory (#2521) ([#3407](https://github.com/opensearch-project/opensearch/pull/3407)) +* Replace 'master' terminology with 'cluster manager' in 'qa' directory (#3330) ([#3332](https://github.com/opensearch-project/opensearch/pull/3332)) +* Replace 'master' terminology with 'cluster manager' in 'modules' directory (#3328) ([#3333](https://github.com/opensearch-project/opensearch/pull/3333)) +* Replace 'master' terminology with 'cluster manager' in 'plugins' directory (#3329) ([#3334](https://github.com/opensearch-project/opensearch/pull/3334)) +* Replace internal usages of 'master' term in 'test' directory (#3283) ([#3293](https://github.com/opensearch-project/opensearch/pull/3293)) +* Replace internal usages of 'master' term in 'client' directory (#3088) ([#3284](https://github.com/opensearch-project/opensearch/pull/3284)) +* Replace internal usages of 'master' term in 'test' directory (#3283) ([#3293](https://github.com/opensearch-project/opensearch/pull/3293)) +* Replace internal usages of 'master' term in 'client' directory (#3088) ([#3284](https://github.com/opensearch-project/opensearch/pull/3284)) +* Replace internal usages of 'master' term in 'server/src/main' directory (#2519) ([#3165](https://github.com/opensearch-project/opensearch/pull/3165)) +* Rename ClusterBlock description 'no master' to 'no cluster-manager' (#3133) ([#3151](https://github.com/opensearch-project/opensearch/pull/3151)) +* Correct the skip version, multi_terms aggregation is supported on 2.1 (#3072) ([#3073](https://github.com/opensearch-project/opensearch/pull/3073)) +* Adds the replication type index setting, alongside a formal notion of feature flags (#3037) ([#3071](https://github.com/opensearch-project/opensearch/pull/3071)) +* Add new multi_term aggregation (#2687) ([#3022](https://github.com/opensearch-project/opensearch/pull/3022)) +* Add `positive_score_impact` support for `rank_features` (#2725) ([#2968](https://github.com/opensearch-project/opensearch/pull/2968)) +* [Backport 2.x] Add resource stats to task framework ([#2655](https://github.com/opensearch-project/opensearch/pull/2655)) +* Optimize Node, remove duplicate Settings (#2703) ([#2742](https://github.com/opensearch-project/opensearch/pull/2742)) +* [Backport 2.x] Add request parameter 'cluster_manager_timeout' as the alternative for 'master_timeout', and deprecate 'master_timeout' - in CAT APIs ([#2716](https://github.com/opensearch-project/opensearch/pull/2716)) +* Enable merge on refresh and merge on commit on Opensearch (#2535) ([#2600](https://github.com/opensearch-project/opensearch/pull/2600)) +* Concurrent Searching (Experimental) (#1500) ([#2584](https://github.com/opensearch-project/opensearch/pull/2584)) +* [Type removal] Remove type from BulkRequestParser (#3423) ([#3431](https://github.com/opensearch-project/opensearch/pull/3431)) +* [Type removal] _type removal from tests of yaml tests (#3406) ([#3414](https://github.com/opensearch-project/opensearch/pull/3414)) +* [2.x] Deprecate public methods and variables with master term in package 'org.opensearch.action.support.master' (#3617) ([#3643](https://github.com/opensearch-project/opensearch/pull/3643)) +* Deprecate classes in org.opensearch.action.support.master (#3593) ([#3609](https://github.com/opensearch-project/opensearch/pull/3609)) +* [TEST] Add back necessary tests for deprecated settings that are replaced during applying inclusive naming (#2825) ([#3353](https://github.com/opensearch-project/opensearch/pull/3353)) +* Support use of IRSA for repository-s3 plugin credentials: added YAML Rest test case (#3499) ([#3520](https://github.com/opensearch-project/opensearch/pull/3520)) +* [Backport 2.x] Add type mapping removal bwc tests for indexing, searching, snapshots ([#2921](https://github.com/opensearch-project/opensearch/pull/2921)) + + +### Bug Fixes + +* [BUG] Custom POM configuration for ZIP publication produces duplicit tags (url, scm) (#3656) ([#3680](https://github.com/opensearch-project/opensearch/pull/3680)) +* [BUG] opensearch crashes on closed client connection before search reply (#3626) ([#3645](https://github.com/opensearch-project/opensearch/pull/3645)) +* Fix false positive query timeouts due to using cached time (#3454) ([#3624](https://github.com/opensearch-project/opensearch/pull/3624)) +* Fix NPE when minBound/maxBound is not set before being called. (#3605) ([#3610](https://github.com/opensearch-project/opensearch/pull/3610)) +* Fix for bug showing incorrect awareness attributes count in AwarenessAllocationDecider (#3428) ([#3580](https://github.com/opensearch-project/opensearch/pull/3580)) +* Add flat_skew setting to node overload decider (#3563) ([#3582](https://github.com/opensearch-project/opensearch/pull/3582)) +* Fix the support of RestClient Node Sniffer for version 2.x and update tests (#3487) ([#3521](https://github.com/opensearch-project/opensearch/pull/3521)) +* move bash flag to set statement (#3494) ([#3519](https://github.com/opensearch-project/opensearch/pull/3519)) +* [BUG] Fixing org.opensearch.monitor.os.OsProbeTests > testLogWarnCpuMessageOnlyOnes when cgroups are available but cgroup stats is not (#3448) ([#3464](https://github.com/opensearch-project/opensearch/pull/3464)) +* Removing unused method from TransportSearchAction (#3437) ([#3445](https://github.com/opensearch-project/opensearch/pull/3445)) +* Fix Lucene-snapshots repo for jdk 17. (#3396) ([#3404](https://github.com/opensearch-project/opensearch/pull/3404)) +* Temporary adding Apache Lucene repositories for snapshots (#3042) ([#3047](https://github.com/opensearch-project/opensearch/pull/3047)) +* Excluding system indices from max shard limit validator (#2894) ([#2911](https://github.com/opensearch-project/opensearch/pull/2911)) +* [Backport] [2.x] Bugfix to guard against stack overflow errors caused by very large reg-ex input ([#2817](https://github.com/opensearch-project/opensearch/pull/2817)) + +### Infrastructure + +* Move gradle-check code to its own scripts and upload codecov (#3742) ([#3747](https://github.com/opensearch-project/opensearch/pull/3747)) +* Optimize Gradle builds by enabling local build caching (#3718) ([#3737](https://github.com/opensearch-project/opensearch/pull/3737)) +* Update github action gradle-check to use pull_request_target for accessing token (#3728) ([#3731](https://github.com/opensearch-project/opensearch/pull/3731)) +* Add gradle check test for github workflows (#3717) ([#3723](https://github.com/opensearch-project/opensearch/pull/3723)) +* Used set to make shell scripts more strict (#3278) ([#3344](https://github.com/opensearch-project/opensearch/pull/3344)) +* Bootstrap should implement a denylist of Java versions (ranges) (#3164) ([#3292](https://github.com/opensearch-project/opensearch/pull/3292)) +* Add Github Workflow to build and publish lucene snapshots. (#2906) ([#3038](https://github.com/opensearch-project/opensearch/pull/3038)) +* Remove JavaVersion in favour of standard Runtime.Version (java-version-checker) (#3027) ([#3034](https://github.com/opensearch-project/opensearch/pull/3034)) +* Remove JavaVersion, use builtin Runtime.Version to deal with runtime versions (#3006) ([#3013](https://github.com/opensearch-project/opensearch/pull/3013)) +* [Backport 2.x] Added a new line linter ([#2892](https://github.com/opensearch-project/opensearch/pull/2892)) + +### Maintenance + +* Added bwc version 2.0.2 ([#3612](https://github.com/opensearch-project/opensearch/pull/3612)) +* Added bwc version 1.3.4 (#3551) ([#3583](https://github.com/opensearch-project/opensearch/pull/3583)) +* Bump guava from 18.0 to 23.0 in /plugins/ingest-attachment (#3357) ([#3534](https://github.com/opensearch-project/opensearch/pull/3534)) +* Added bwc version 2.0.1 ([#3451](https://github.com/opensearch-project/opensearch/pull/3451)) +* Bump google-auth-library-oauth2-http from 0.20.0 to 1.7.0 in /plugins/repository-gcs (#3473) ([#3488](https://github.com/opensearch-project/opensearch/pull/3488)) +* Bump protobuf-java from 3.20.1 to 3.21.1 in /plugins/repository-hdfs (#3472) ([#3480](https://github.com/opensearch-project/opensearch/pull/3480)) +* Bump reactor-core from 3.4.17 to 3.4.18 in /plugins/repository-azure (#3427) ([#3430](https://github.com/opensearch-project/opensearch/pull/3430)) +* Bump com.gradle.enterprise from 3.10 to 3.10.1 (#3425) ([#3429](https://github.com/opensearch-project/opensearch/pull/3429)) +* Bump reactor-netty-core from 1.0.16 to 1.0.19 in /plugins/repository-azure (#3360) ([#3386](https://github.com/opensearch-project/opensearch/pull/3386)) +* Bump xz from 1.8 to 1.9 in /plugins/ingest-attachment (#3248) ([#3280](https://github.com/opensearch-project/opensearch/pull/3280)) +* Upgrading ingest-attachment dependencies (#3111) ([#3279](https://github.com/opensearch-project/opensearch/pull/3279)) +* Bump reactor-netty from 1.0.17 to 1.0.18 in /plugins/repository-azure (#3246) ([#3258](https://github.com/opensearch-project/opensearch/pull/3258)) +* Bump google-http-client-appengine from 1.35.0 to 1.41.8 in /plugins/repository-gcs (#3247) ([#3259](https://github.com/opensearch-project/opensearch/pull/3259)) +* Bump HdrHistogram from 2.1.9 to 2.1.12 in /server (#2135) ([#3200](https://github.com/opensearch-project/opensearch/pull/3200)) +* Add bwc version 1.3.3 ([#3221](https://github.com/opensearch-project/opensearch/pull/3221)) +* Bump commons-lang3 from 3.9 to 3.12.0 in /plugins/ingest-attachment (#3190) ([#3194](https://github.com/opensearch-project/opensearch/pull/3194)) +* Bump google-api-client from 1.30.10 to 1.34.0 in /plugins/repository-gcs (#3161) ([#3191](https://github.com/opensearch-project/opensearch/pull/3191)) +* Bump reactor-netty-http from 1.0.16 to 1.0.18 in /plugins/repository-azure (#3057) ([#3070](https://github.com/opensearch-project/opensearch/pull/3070)) +* Bump com.gradle.enterprise from 3.9 to 3.10 (#3055) ([#3069](https://github.com/opensearch-project/opensearch/pull/3069)) +* Bump protobuf-java from 3.20.0 to 3.20.1 in /plugins/repository-hdfs (#3062) ([#3068](https://github.com/opensearch-project/opensearch/pull/3068)) +* Bump protobuf-java from 3.20.0 to 3.20.1 in /plugins/repository-hdfs (#3062) ([#3068](https://github.com/opensearch-project/opensearch/pull/3068)) +* Bump json-schema-validator from 1.0.68 to 1.0.69 in /buildSrc (#3060) ([#3066](https://github.com/opensearch-project/opensearch/pull/3066)) +* Add Gradle 7.4 Aggregated Test Reports (#2821) ([#2983](https://github.com/opensearch-project/opensearch/pull/2983)) +* Bump google-oauth-client from 1.33.2 to 1.33.3 in /plugins/discovery-gce (#2943) ([#2966](https://github.com/opensearch-project/opensearch/pull/2966)) +* Bump org.gradle.test-retry from 1.3.1 to 1.3.2 (#2940) ([#2960](https://github.com/opensearch-project/opensearch/pull/2960)) +* Bump reactor-core from 3.4.15 to 3.4.17 in /plugins/repository-azure (#2947) ([#2961](https://github.com/opensearch-project/opensearch/pull/2961)) +* Bump grpc-context from 1.29.0 to 1.45.1 in /plugins/repository-gcs (#2944) ([#2962](https://github.com/opensearch-project/opensearch/pull/2962)) +* Bump guava from 30.1.1-jre to 31.1-jre in /plugins/repository-hdfs (#2948) ([#2959](https://github.com/opensearch-project/opensearch/pull/2959)) +* Bump reactor-netty from 1.0.16 to 1.0.17 in /plugins/repository-azure (#2613) ([#2958](https://github.com/opensearch-project/opensearch/pull/2958)) +* Bump hadoop-minicluster from 3.3.1 to 3.3.2 in /test/fixtures/hdfs-fixture (#2381) ([#2957](https://github.com/opensearch-project/opensearch/pull/2957)) +* Bump com.diffplug.spotless from 6.4.1 to 6.4.2 (#2827) ([#2956](https://github.com/opensearch-project/opensearch/pull/2956)) +* Bump jna from 5.10.0 to 5.11.0 in /buildSrc (#2946) ([#2955](https://github.com/opensearch-project/opensearch/pull/2955)) +* Adding asm to version file and upgrading (#2933) ([#2951](https://github.com/opensearch-project/opensearch/pull/2951)) +* [Backport 2.x] Update commons-logging to 1.2 ([#2823](https://github.com/opensearch-project/opensearch/pull/2823)) +* Bump gradle-info-plugin from 7.1.3 to 11.3.3 in /buildSrc (#2831) ([#2842](https://github.com/opensearch-project/opensearch/pull/2842)) +* Update azure-storage-blob to 12.15.0: fix test flakiness (#2795) ([#2798](https://github.com/opensearch-project/opensearch/pull/2798)) +* Bump proto-google-common-protos from 1.16.0 to 2.8.0 in /plugins/repository-gcs (#2738) ([#2743](https://github.com/opensearch-project/opensearch/pull/2743)) +* Update to Gradle 7.4.2 (#2688) ([#2692](https://github.com/opensearch-project/opensearch/pull/2692)) +* Update Gradle to 7.4.1 (#2078) ([#2645](https://github.com/opensearch-project/opensearch/pull/2645)) +* Lock 2.1 release to Lucene 9.2.0. ([#3676](https://github.com/opensearch-project/opensearch/pull/3676)) +* [Dependency upgrade] google-oauth-client to 1.33.3 (#3500) ([#3501](https://github.com/opensearch-project/opensearch/pull/3501)) + +### Refactoring + +* [Refactor] XContentType to parse Accept or Content-Type headers (#3077) ([#3103](https://github.com/opensearch-project/opensearch/pull/3103)) +* Remove usages of MultiTermQuery.setRewriteMethodsetRewriteMethod (#2997) ([#3017](https://github.com/opensearch-project/opensearch/pull/3017)) +* Refactoring GatedAutoCloseable and moving RecoveryState.Timer (#2965) ([#3014](https://github.com/opensearch-project/opensearch/pull/3014)) +* Fixing flakiness of ShuffleForcedMergePolicyTests (#3591) ([#3592](https://github.com/opensearch-project/opensearch/pull/3592)) +* Fix testSetAdditionalRolesCanAddDeprecatedMasterRole() by removing the initial assertion (#3441) ([#3443](https://github.com/opensearch-project/opensearch/pull/3443)) diff --git a/release-notes/opensearch.release-notes-2.10.0.md b/release-notes/opensearch.release-notes-2.10.0.md new file mode 100644 index 0000000000000..9d5f75d61ee2a --- /dev/null +++ b/release-notes/opensearch.release-notes-2.10.0.md @@ -0,0 +1,136 @@ +## 2023-09-08 Version 2.10.0 Release Notes + +## [2.10] + +### Added +- Add server version as REST response header [#6583](https://github.com/opensearch-project/OpenSearch/issues/6583) +- Start replication checkpointTimers on primary before segments upload to remote store. ([#8221]()https://github.com/opensearch-project/OpenSearch/pull/8221) +- Introduce new static cluster setting to control slice computation for concurrent segment search. ([#8847](https://github.com/opensearch-project/OpenSearch/pull/8884)) +- Add configuration for file cache size to max remote data ratio to prevent oversubscription of file cache ([#8606](https://github.com/opensearch-project/OpenSearch/pull/8606)) +- Disallow compression level to be set for default and best_compression index codecs ([#8737]()https://github.com/opensearch-project/OpenSearch/pull/8737) +- [distribution/archives] [Linux] [x64] Provide the variant of the distributions bundled with JRE ([#8195]()https://github.com/opensearch-project/OpenSearch/pull/8195) +- Prioritize replica shard movement during shard relocation ([#8875](https://github.com/opensearch-project/OpenSearch/pull/8875)) +- Introducing Default and Best Compression codecs as their algorithm name ([#9123](https://github.com/opensearch-project/OpenSearch/pull/9123)) +- Make SearchTemplateRequest implement IndicesRequest.Replaceable ([#9122](https://github.com/opensearch-project/OpenSearch/pull/9122)) +- [BWC and API enforcement] Define the initial set of annotations, their meaning and relations between them ([#9223](https://github.com/opensearch-project/OpenSearch/pull/9223)) +- [Remote Store] Add Segment download stats to remotestore stats API ([#8718](https://github.com/opensearch-project/OpenSearch/pull/8718)) +- [Remote Store] Add remote segment transfer stats on NodesStats API ([#9168](https://github.com/opensearch-project/OpenSearch/pull/9168) [#9393](https://github.com/opensearch-project/OpenSearch/pull/9393) [#9454](https://github.com/opensearch-project/OpenSearch/pull/9454)) +- [Segment Replication] Support realtime reads for GET requests ([#9212](https://github.com/opensearch-project/OpenSearch/pull/9212)) +- Allow test clusters to run with TLS ([#8900](https://github.com/opensearch-project/OpenSearch/pull/8900)) +- Add jdk.incubator.vector module support for JDK 20+ ([#8601](https://github.com/opensearch-project/OpenSearch/pull/8601)) +- [Feature] Expose term frequency in Painless script score context ([#9081](https://github.com/opensearch-project/OpenSearch/pull/9081)) +- Add support for reading partial files to HDFS repository ([#9513](https://github.com/opensearch-project/OpenSearch/issues/9513)) +- [Remote Store] Rate limiter integration for remote store uploads and downloads([#9448](https://github.com/opensearch-project/OpenSearch/pull/9448/)) +- [BWC and API enforcement] Decorate the existing APIs with proper annotations (part 1) ([#9520](https://github.com/opensearch-project/OpenSearch/pull/9520)) +- Add support for extensions to search responses using SearchExtBuilder ([#9379](https://github.com/opensearch-project/OpenSearch/pull/9379)) +- [Remote State] Create service to publish cluster state to remote store ([#9160](https://github.com/opensearch-project/OpenSearch/pull/9160)) +- Core crypto library to perform encryption and decryption of source content ([#8466](https://github.com/opensearch-project/OpenSearch/pull/8466)) +- Expose DelimitedTermFrequencyTokenFilter to allow providing term frequencies along with terms ([#9479](https://github.com/opensearch-project/OpenSearch/pull/9479)) +- APIs for performing async blob reads and async downloads from the repository using multiple streams ([#9592](https://github.com/opensearch-project/OpenSearch/issues/9592)) +- Add concurrent segment search related metrics to node and index stats ([#9622](https://github.com/opensearch-project/OpenSearch/issues/9622)) +- Add average concurrency metric for concurrent segment search ([#9670](https://github.com/opensearch-project/OpenSearch/issues/9670)) +- Introduce cluster default remote translog buffer interval setting ([#9584](https://github.com/opensearch-project/OpenSearch/pull/9584)) +- Added encryption-sdk lib to provide encryption and decryption capabilities ([#8466](https://github.com/opensearch-project/OpenSearch/pull/8466) [#9289](https://github.com/opensearch-project/OpenSearch/pull/9289)) +- [Segment Replication] Adding segment replication statistics rolled up at index, node and cluster level ([#9709](https://github.com/opensearch-project/OpenSearch/pull/9709)) +- Added crypto-kms plugin to provide AWS KMS based key providers for encryption/decryption. ([#8465](https://github.com/opensearch-project/OpenSearch/pull/8465)) +- [Remote state] Integrate remote cluster state in publish/commit flow ([#9665](https://github.com/opensearch-project/OpenSearch/pull/9665)) +- [Remote Store] Changes to introduce repository registration during bootstrap via node attributes. ([#9105](https://github.com/opensearch-project/OpenSearch/pull/9105)) +- [Remote state] Auto restore index metadata from last known cluster state ([#9831](https://github.com/opensearch-project/OpenSearch/pull/9831)) + +### Dependencies +- Bump `org.apache.logging.log4j:log4j-core` from 2.17.1 to 2.20.0 ([#8307](https://github.com/opensearch-project/OpenSearch/pull/8307)) +- Bump `io.grpc:grpc-context` from 1.46.0 to 1.57.1 ([#8726](https://github.com/opensearch-project/OpenSearch/pull/8726), [#9145](https://github.com/opensearch-project/OpenSearch/pull/9145)) +- Bump `com.netflix.nebula:gradle-info-plugin` from 12.1.5 to 12.1.6 ([#8724](https://github.com/opensearch-project/OpenSearch/pull/8724)) +- Bump `commons-codec:commons-codec` from 1.15 to 1.16.0 ([#8725](https://github.com/opensearch-project/OpenSearch/pull/8725)) +- Bump `org.apache.zookeeper:zookeeper` from 3.8.1 to 3.9.0 ([#8844](https://github.com/opensearch-project/OpenSearch/pull/8844), [#9146](https://github.com/opensearch-project/OpenSearch/pull/9146)) +- Bump `org.gradle.test-retry` from 1.5.3 to 1.5.4 ([#8842](https://github.com/opensearch-project/OpenSearch/pull/8842)) +- Bump `com.netflix.nebula.ospackage-base` from 11.3.0 to 11.4.0 ([#8838](https://github.com/opensearch-project/OpenSearch/pull/8838)) +- Bump `com.google.http-client:google-http-client-gson` from 1.43.2 to 1.43.3 ([#8840](https://github.com/opensearch-project/OpenSearch/pull/8840)) +- OpenJDK Update (July 2023 Patch releases) ([#8869](https://github.com/opensearch-project/OpenSearch/pull/8869)) +- Bump `hadoop` libraries from 3.3.4 to 3.3.6 ([#6995](https://github.com/opensearch-project/OpenSearch/pull/6995)) +- Bump `com.gradle.enterprise` from 3.13.3 to 3.14.1 ([#8996](https://github.com/opensearch-project/OpenSearch/pull/8996)) +- Bump `org.apache.commons:commons-lang3` from 3.12.0 to 3.13.0 ([#8995](https://github.com/opensearch-project/OpenSearch/pull/8995)) +- Bump `com.google.cloud:google-cloud-core-http` from 2.21.0 to 2.21.1 ([#8999](https://github.com/opensearch-project/OpenSearch/pull/8999)) +- Bump `com.maxmind.geoip2:geoip2` from 4.0.1 to 4.1.0 ([#8998](https://github.com/opensearch-project/OpenSearch/pull/8998)) +- Bump `org.apache.commons:commons-lang3` from 3.12.0 to 3.13.0 in /plugins/repository-hdfs ([#8997](https://github.com/opensearch-project/OpenSearch/pull/8997)) +- Bump `netty` from 4.1.94.Final to 4.1.96.Final ([#9030](https://github.com/opensearch-project/OpenSearch/pull/9030)) +- Bump `com.google.jimfs:jimfs` from 1.2 to 1.3.0 ([#9080](https://github.com/opensearch-project/OpenSearch/pull/9080)) +- Bump `io.projectreactor.netty:reactor-netty-http` from 1.1.8 to 1.1.9 ([#9147](https://github.com/opensearch-project/OpenSearch/pull/9147)) +- Bump `org.apache.maven:maven-model` from 3.9.3 to 3.9.4 ([#9148](https://github.com/opensearch-project/OpenSearch/pull/9148)) +- Bump `com.azure:azure-storage-blob` from 12.22.3 to 12.23.0 ([#9231](https://github.com/opensearch-project/OpenSearch/pull/9231)) +- Bump `com.diffplug.spotless` from 6.19.0 to 6.20.0 ([#9227](https://github.com/opensearch-project/OpenSearch/pull/9227)) +- Bump `org.xerial.snappy:snappy-java` from 1.1.8.2 to 1.1.10.3 ([#9252](https://github.com/opensearch-project/OpenSearch/pull/9252)) +- Bump `com.squareup.okhttp3:okhttp` from 4.9.3 to 4.11.0 ([#9252](https://github.com/opensearch-project/OpenSearch/pull/9252)) +- Bump `com.squareup.okio:okio` from 2.8.0 to 3.5.0 ([#9252](https://github.com/opensearch-project/OpenSearch/pull/9252)) +- Bump `com.google.code.gson:gson` from 2.9.0 to 2.10.1 ([#9230](https://github.com/opensearch-project/OpenSearch/pull/9230)) +- Bump `lycheeverse/lychee-action` from 1.2.0 to 1.8.0 ([#9228](https://github.com/opensearch-project/OpenSearch/pull/9228)) +- Bump `snakeyaml` from 2.0 to 2.1 ([#9269](https://github.com/opensearch-project/OpenSearch/pull/9269)) +- Bump `aws-actions/configure-aws-credentials` from 1 to 2 ([#9302](https://github.com/opensearch-project/OpenSearch/pull/9302)) +- Bump `com.github.luben:zstd-jni` from 1.5.5-3 to 1.5.5-5 ([#9431](https://github.com/opensearch-project/OpenSearch/pull/9431) +- Bump `netty` from 4.1.96.Final to 4.1.97.Final ([#9553](https://github.com/opensearch-project/OpenSearch/pull/9553)) +- Bump `io.grpc:grpc-api` from 1.57.1 to 1.57.2 ([#9578](https://github.com/opensearch-project/OpenSearch/pull/9578)) +- Add Encryption SDK dependencies ([#8466](https://github.com/opensearch-project/OpenSearch/pull/8466)) + +### Changed +- Default to mmapfs within hybridfs ([#8508](https://github.com/opensearch-project/OpenSearch/pull/8508)) +- Perform aggregation postCollection in ContextIndexSearcher after searching leaves ([#8303](https://github.com/opensearch-project/OpenSearch/pull/8303)) +- Make Span exporter configurable ([#8620](https://github.com/opensearch-project/OpenSearch/issues/8620)) +- Perform aggregation postCollection in ContextIndexSearcher after searching leaves ([#8303](https://github.com/opensearch-project/OpenSearch/pull/8303)) +- [Refactor] StreamIO from common to core.common namespace in core lib ([#8157](https://github.com/opensearch-project/OpenSearch/pull/8157)) +- [Refactor] Remaining HPPC to java.util collections ([#8730](https://github.com/opensearch-project/OpenSearch/pull/8730)) +- Remote Segment Store Repository setting moved from `index.remote_store.repository` to `index.remote_store.segment.repository` and `cluster.remote_store.repository` to `cluster.remote_store.segment.repository` respectively for Index and Cluster level settings ([#8719](https://github.com/opensearch-project/OpenSearch/pull/8719)) +- Change InternalSignificantTerms to sum shard-level superset counts only in final reduce ([#8735](https://github.com/opensearch-project/OpenSearch/pull/8735)) +- Exclude 'benchmarks' from codecov report ([#8805](https://github.com/opensearch-project/OpenSearch/pull/8805)) +- Create separate SourceLookup instance per segment slice in SignificantTextAggregatorFactory ([#8807](https://github.com/opensearch-project/OpenSearch/pull/8807)) +- Replace the deprecated IndexReader APIs with new storedFields() & termVectors() ([#7792](https://github.com/opensearch-project/OpenSearch/pull/7792)) +- [Remote Store] Add support to restore only unassigned shards of an index ([#8792](https://github.com/opensearch-project/OpenSearch/pull/8792)) +- Add safeguard limits for file cache during node level allocation ([#8208](https://github.com/opensearch-project/OpenSearch/pull/8208)) +- Performance improvements for BytesRefHash ([#8788](https://github.com/opensearch-project/OpenSearch/pull/8788)) +- Add support for aggregation profiler with concurrent aggregation ([#8801](https://github.com/opensearch-project/OpenSearch/pull/8801)) +- [Remove] Deprecated Fractional ByteSizeValue support #9005 ([#9005](https://github.com/opensearch-project/OpenSearch/pull/9005)) +- Add support for aggregation profiler with concurrent aggregation ([#8801](https://github.com/opensearch-project/OpenSearch/pull/8801)) +- [Remote Store] Restrict user override for remote store index level settings ([#8812](https://github.com/opensearch-project/OpenSearch/pull/8812)) +- [Refactor] MediaTypeParser to MediaTypeParserRegistry ([#8636](https://github.com/opensearch-project/OpenSearch/pull/8636)) +- Make MultiBucketConsumerService thread safe to use across slices during search ([#9047](https://github.com/opensearch-project/OpenSearch/pull/9047)) +- Removed blocking wait in TransportGetSnapshotsAction which was exhausting generic threadpool ([#8377](https://github.com/opensearch-project/OpenSearch/pull/8377)) +- Adds support for tracing runnable scenarios ([#8831](https://github.com/opensearch-project/OpenSearch/pull/8831)) +- Change shard_size and shard_min_doc_count evaluation to happen in shard level reduce phase ([#9085](https://github.com/opensearch-project/OpenSearch/pull/9085)) +- Add attributes to startSpan methods ([#9199](https://github.com/opensearch-project/OpenSearch/pull/9199)) +- [Refactor] Task foundation classes to core library - pt 1 ([#9082](https://github.com/opensearch-project/OpenSearch/pull/9082)) +- Add support for wrapping CollectorManager with profiling during concurrent execution ([#9129](https://github.com/opensearch-project/OpenSearch/pull/9129)) +- Add base class for parameterizing the search based tests #9083 ([#9083](https://github.com/opensearch-project/OpenSearch/pull/9083)) +- Add support for wrapping CollectorManager with profiling during concurrent execution ([#9129](https://github.com/opensearch-project/OpenSearch/pull/9129)) +- Rethrow OpenSearch exception for non-concurrent path while using concurrent search ([#9177](https://github.com/opensearch-project/OpenSearch/pull/9177)) +- Improve performance of encoding composite keys in multi-term aggregations ([#9412](https://github.com/opensearch-project/OpenSearch/pull/9412)) +- Refactor Compressors from CompressorFactory to CompressorRegistry for extensibility ([#9262](https://github.com/opensearch-project/OpenSearch/pull/9262)) +- Fix sort related ITs for concurrent search ([#9177](https://github.com/opensearch-project/OpenSearch/pull/9466) +- [Remote Store] Implicitly use replication type SEGMENT for remote store clusters ([#9264](https://github.com/opensearch-project/OpenSearch/pull/9264)) +- Add support to use trace propagated from client ([#9506](https://github.com/opensearch-project/OpenSearch/pull/9506)) +- Separate request-based and settings-based concurrent segment search controls and introduce AggregatorFactory method to determine concurrent search support ([#9469](https://github.com/opensearch-project/OpenSearch/pull/9469)) +- [Remote Store] Rate limiter integration for remote store uploads and downloads([#9448](https://github.com/opensearch-project/OpenSearch/pull/9448/)) +- [Remote Store] Implicitly use replication type SEGMENT for remote store clusters ([#9264](https://github.com/opensearch-project/OpenSearch/pull/9264)) +- Redefine telemetry context restoration and propagation ([#9617](https://github.com/opensearch-project/OpenSearch/pull/9617)) +- Use non-concurrent path for sort request on timeseries index and field([#9562](https://github.com/opensearch-project/OpenSearch/pull/9562)) +- Added sampler based on `Blanket Probabilistic Sampling rate` and `Override for on demand` ([#9621](https://github.com/opensearch-project/OpenSearch/issues/9621)) +- Decouple replication lag from logic to fail stale replicas ([#9507](https://github.com/opensearch-project/OpenSearch/pull/9507)) +- Improve performance of rounding dates in date_histogram aggregation ([#9727](https://github.com/opensearch-project/OpenSearch/pull/9727)) +- [Remote Store] Add support for Remote Translog Store stats in `_remotestore/stats/` API ([#9263](https://github.com/opensearch-project/OpenSearch/pull/9263)) +- Removing the vec file extension from INDEX_STORE_HYBRID_NIO_EXTENSIONS, to ensure the no performance degradation for vector search via Lucene Engine.([#9528](https://github.com/opensearch-project/OpenSearch/pull/9528))) +- Cleanup Unreferenced file on segment merge failure ([#9503](https://github.com/opensearch-project/OpenSearch/pull/9503)) +- Move zstd compression codec to external custom-codecs repository ([#9422](https://github.com/opensearch-project/OpenSearch/issues/9422]) +- [Remote Store] Add support for Remote Translog Store upload stats in `_nodes/stats/` API ([#8908](https://github.com/opensearch-project/OpenSearch/pull/8908)) +- [Remote Store] Removing feature flag to mark feature GA ([#9761](https://github.com/opensearch-project/OpenSearch/pull/9761)) + +### Removed +- Remove provision to create Remote Indices without Remote Translog Store ([#8719](https://github.com/opensearch-project/OpenSearch/pull/8719)) + +### Fixed +- Fix flaky ResourceAwareTasksTests.testBasicTaskResourceTracking test ([#8993](https://github.com/opensearch-project/OpenSearch/pull/8993)) +- Fix null_pointer_exception when creating or updating ingest pipeline ([#9259](https://github.com/opensearch-project/OpenSearch/pull/9259)) +- Fix memory leak when using Zstd Dictionary ([#9403](https://github.com/opensearch-project/OpenSearch/pull/9403)) +- Fix condition to remove index create block ([#9437](https://github.com/opensearch-project/OpenSearch/pull/9437)) +- Add support to clear archived index setting ([#9019](https://github.com/opensearch-project/OpenSearch/pull/9019)) +- Fix range reads in respository-s3 ([9512](https://github.com/opensearch-project/OpenSearch/issues/9512)) +- [Segment Replication] Fixed bug where replica shard temporarily serves stale data during an engine reset ([#9495](https://github.com/opensearch-project/OpenSearch/pull/9495)) +- Disable shard/segment level search_after short cutting if track_total_hits != false ([#9683](https://github.com/opensearch-project/OpenSearch/pull/9683)) +- [Segment Replication] Fixed bug where bytes behind metric is not accurate ([#9686](https://github.com/opensearch-project/OpenSearch/pull/9686)) diff --git a/release-notes/opensearch.release-notes-2.2.0.md b/release-notes/opensearch.release-notes-2.2.0.md new file mode 100644 index 0000000000000..74e76cfe46b5a --- /dev/null +++ b/release-notes/opensearch.release-notes-2.2.0.md @@ -0,0 +1,79 @@ +## 2022-08-05 Version 2.2.0 Release Notes + +### Features/Enhancements + +* Task consumer Integration ([#2293](https://github.com/opensearch-project/opensearch/pull/2293)) ([#4141](https://github.com/opensearch-project/opensearch/pull/4141)) +* [Backport 2.x] [Segment Replication] Add SegmentReplicationTargetService to orchestrate replication events. ([#4074](https://github.com/opensearch-project/opensearch/pull/4074)) +* Support task resource tracking in OpenSearch ([#3982](https://github.com/opensearch-project/opensearch/pull/3982)) ([#4087](https://github.com/opensearch-project/opensearch/pull/4087)) +* Making shard copy count a multiple of attribute count ([#3462](https://github.com/opensearch-project/opensearch/pull/3462)) ([#4086](https://github.com/opensearch-project/opensearch/pull/4086)) +* [Backport 2.x] [Segment Rreplication] Adding CheckpointRefreshListener to trigger when Segment replication is turned on and Primary shard refreshes ([#4044](https://github.com/opensearch-project/opensearch/pull/4044)) +* Add doc_count field mapper ([#3985](https://github.com/opensearch-project/opensearch/pull/3985)) ([#4037](https://github.com/opensearch-project/opensearch/pull/4037)) +* Parallelize stale blobs deletion during snapshot delete ([#3796](https://github.com/opensearch-project/opensearch/pull/3796)) ([#3990](https://github.com/opensearch-project/opensearch/pull/3990)) +* [Backport 2.x] [Segment Replication] Add a new Engine implementation for replicas with segment replication enabled. ([#4003](https://github.com/opensearch-project/opensearch/pull/4003)) +* [Backport 2.x] Adds a new parameter, max_analyzer_offset, for the highlighter ([#4031](https://github.com/opensearch-project/opensearch/pull/4031)) +* Update merge on refresh and merge on commit defaults in Opensearch (Lucene 9.3) ([#3561](https://github.com/opensearch-project/opensearch/pull/3561)) ([#4013](https://github.com/opensearch-project/opensearch/pull/4013)) +* Make HybridDirectory MMAP Extensions Configurable ([#3837](https://github.com/opensearch-project/opensearch/pull/3837)) ([#3970](https://github.com/opensearch-project/opensearch/pull/3970)) +* Add option to disable chunked transfer-encoding ([#3864](https://github.com/opensearch-project/opensearch/pull/3864)) ([#3885](https://github.com/opensearch-project/opensearch/pull/3885)) +* Introducing TranslogManager implementations decoupled from the Engine [2.x] ([#3820](https://github.com/opensearch-project/opensearch/pull/3820)) +* Changing default no_master_block from write to metadata_write ([#3621](https://github.com/opensearch-project/opensearch/pull/3621)) ([#3756](https://github.com/opensearch-project/opensearch/pull/3756)) + +### Bug Fixes + +* OpenSearch crashes on closed client connection before search reply when total ops higher compared to expected ([#4143](https://github.com/opensearch-project/opensearch/pull/4143)) ([#4145](https://github.com/opensearch-project/opensearch/pull/4145)) +* Binding empty instance of SegmentReplicationCheckpointPublisher when Feature Flag is off in IndicesModule.java file. ([#4119](https://github.com/opensearch-project/opensearch/pull/4119)) +* Fix the bug that masterOperation(with task param) is bypassed ([#4103](https://github.com/opensearch-project/opensearch/pull/4103)) ([#4115](https://github.com/opensearch-project/opensearch/pull/4115)) +* Fixing flaky org.opensearch.cluster.routing.allocation.decider.DiskThresholdDeciderIT.testHighWatermarkNotExceeded test case ([#4012](https://github.com/opensearch-project/opensearch/pull/4012)) ([#4014](https://github.com/opensearch-project/opensearch/pull/4014)) +* Correct typo: Rutime -> Runtime ([#3896](https://github.com/opensearch-project/opensearch/pull/3896)) ([#3898](https://github.com/opensearch-project/opensearch/pull/3898)) +* Fixing implausibly old time stamp 1970-01-01 00:00:00 by using the timestamp from the Git revision instead of default 0 value ([#3883](https://github.com/opensearch-project/opensearch/pull/3883)) ([#3891](https://github.com/opensearch-project/opensearch/pull/3891)) + +### Infrastructure + +* Correctly ignore depandabot branches during push ([#4077](https://github.com/opensearch-project/opensearch/pull/4077)) ([#4113](https://github.com/opensearch-project/opensearch/pull/4113)) +* Build performance improvements ([#3926](https://github.com/opensearch-project/opensearch/pull/3926)) ([#3937](https://github.com/opensearch-project/opensearch/pull/3937)) +* PR coverage requirement and default settings ([#3931](https://github.com/opensearch-project/opensearch/pull/3931)) ([#3938](https://github.com/opensearch-project/opensearch/pull/3938)) +* [Backport 2.x] Fail build on wildcard imports ([#3940](https://github.com/opensearch-project/opensearch/pull/3940)) +* Don't run EmptyDirTaskTests in a Docker container ([#3792](https://github.com/opensearch-project/opensearch/pull/3792)) ([#3912](https://github.com/opensearch-project/opensearch/pull/3912)) +* Add coverage, gha, jenkins server, documentation and forum badges ([#3886](https://github.com/opensearch-project/opensearch/pull/3886)) +* Unable to use Systemd module with tar distribution ([#3755](https://github.com/opensearch-project/opensearch/pull/3755)) ([#3903](https://github.com/opensearch-project/opensearch/pull/3903)) +* Ignore backport / autocut / dependentbot branches for gradle checks ([#3816](https://github.com/opensearch-project/opensearch/pull/3816)) ([#3825](https://github.com/opensearch-project/opensearch/pull/3825)) +* Setup branch push coverage and fix coverage uploads ([#3793](https://github.com/opensearch-project/opensearch/pull/3793)) ([#3811](https://github.com/opensearch-project/opensearch/pull/3811)) +* Enable XML test reports for Jenkins integration ([#3799](https://github.com/opensearch-project/opensearch/pull/3799)) ([#3803](https://github.com/opensearch-project/opensearch/pull/3803)) + +### Maintenance + +* OpenJDK Update (July 2022 Patch releases) ([#4023](https://github.com/opensearch-project/opensearch/pull/4023)) ([#4092](https://github.com/opensearch-project/opensearch/pull/4092)) +* Update to Lucene 9.3.0 ([#4043](https://github.com/opensearch-project/opensearch/pull/4043)) ([#4088](https://github.com/opensearch-project/opensearch/pull/4088)) +* Bump commons-configuration2 from 2.7 to 2.8.0 in /plugins/repository-hdfs ([#3764](https://github.com/opensearch-project/opensearch/pull/3764)) ([#3783](https://github.com/opensearch-project/opensearch/pull/3783)) +* Use bash in systemd-entrypoint shebang ([#4008](https://github.com/opensearch-project/opensearch/pull/4008)) ([#4009](https://github.com/opensearch-project/opensearch/pull/4009)) +* Bump com.gradle.enterprise from 3.10.1 to 3.10.2 ([#3568](https://github.com/opensearch-project/opensearch/pull/3568)) ([#3934](https://github.com/opensearch-project/opensearch/pull/3934)) +* Bump log4j-core in /buildSrc/src/testKit/thirdPartyAudit/sample_jars ([#3763](https://github.com/opensearch-project/opensearch/pull/3763)) ([#3784](https://github.com/opensearch-project/opensearch/pull/3784)) +* Added bwc version 1.3.5 ([#3911](https://github.com/opensearch-project/opensearch/pull/3911)) ([#3913](https://github.com/opensearch-project/opensearch/pull/3913)) +* Update to Gradle 7.5 ([#3594](https://github.com/opensearch-project/opensearch/pull/3594)) ([#3904](https://github.com/opensearch-project/opensearch/pull/3904)) +* Update Netty to 4.1.79.Final ([#3868](https://github.com/opensearch-project/opensearch/pull/3868)) ([#3874](https://github.com/opensearch-project/opensearch/pull/3874)) +* Upgrade MinIO image version ([#3541](https://github.com/opensearch-project/opensearch/pull/3541)) ([#3867](https://github.com/opensearch-project/opensearch/pull/3867)) +* Add netty-transport-native-unix-common to modules/transport-netty4/bu… ([#3848](https://github.com/opensearch-project/opensearch/pull/3848)) ([#3853](https://github.com/opensearch-project/opensearch/pull/3853)) +* Update outdated dependencies ([#3821](https://github.com/opensearch-project/opensearch/pull/3821)) ([#3854](https://github.com/opensearch-project/opensearch/pull/3854)) +* Added bwc version 2.1.1 ([#3806](https://github.com/opensearch-project/opensearch/pull/3806)) +* Upgrade netty from 4.1.73.Final to 4.1.78.Final ([#3772](https://github.com/opensearch-project/opensearch/pull/3772)) ([#3778](https://github.com/opensearch-project/opensearch/pull/3778)) +* Bump protobuf-java from 3.21.1 to 3.21.2 in /plugins/repository-hdfs ([#3711](https://github.com/opensearch-project/opensearch/pull/3711)) ([#3726](https://github.com/opensearch-project/opensearch/pull/3726)) +* Upgrading AWS SDK dependency for native plugins ([#3694](https://github.com/opensearch-project/opensearch/pull/3694)) ([#3701](https://github.com/opensearch-project/opensearch/pull/3701)) + +### Refactoring + +* [Backport 2.x] Changes to encapsulate Translog into TranslogManager ([#4095](https://github.com/opensearch-project/opensearch/pull/4095)) ([#4142](https://github.com/opensearch-project/opensearch/pull/4142)) +* Deprecate and rename abstract methods in interfaces that contain 'master' in name ([#4121](https://github.com/opensearch-project/opensearch/pull/4121)) ([#4123](https://github.com/opensearch-project/opensearch/pull/4123)) +* [Backport 2.x] Integrate Engine with decoupled Translog interfaces ([#3822](https://github.com/opensearch-project/opensearch/pull/3822)) +* Deprecate class FakeThreadPoolMasterService, BlockMasterServiceOnMaster and BusyMasterServiceDisruption ([#4058](https://github.com/opensearch-project/opensearch/pull/4058)) ([#4068](https://github.com/opensearch-project/opensearch/pull/4068)) +* Rename classes with name 'MasterService' to 'ClusterManagerService' in directory 'test/framework' ([#4051](https://github.com/opensearch-project/opensearch/pull/4051)) ([#4057](https://github.com/opensearch-project/opensearch/pull/4057)) +* Deprecate class 'MasterService' and create alternative class 'ClusterManagerService' ([#4022](https://github.com/opensearch-project/opensearch/pull/4022)) ([#4050](https://github.com/opensearch-project/opensearch/pull/4050)) +* Deprecate and Rename abstract methods from 'Master' terminology to 'ClusterManager'. ([#4032](https://github.com/opensearch-project/opensearch/pull/4032)) ([#4048](https://github.com/opensearch-project/opensearch/pull/4048)) +* Deprecate public methods and variables that contain 'master' terminology in class 'NoMasterBlockService' and 'MasterService' ([#4006](https://github.com/opensearch-project/opensearch/pull/4006)) ([#4038](https://github.com/opensearch-project/opensearch/pull/4038)) +* Deprecate public methods and variables that contain 'master' terminology in 'client' directory ([#3966](https://github.com/opensearch-project/opensearch/pull/3966)) ([#3981](https://github.com/opensearch-project/opensearch/pull/3981)) +* [segment replication]Introducing common Replication interfaces for segment replication and recovery code paths ([#3234](https://github.com/opensearch-project/opensearch/pull/3234)) ([#3984](https://github.com/opensearch-project/opensearch/pull/3984)) +* Deprecate public methods and variables that contain 'master' terminology in 'test/framework' directory ([#3978](https://github.com/opensearch-project/opensearch/pull/3978)) ([#3987](https://github.com/opensearch-project/opensearch/pull/3987)) +* [Backport 2.x] [Segment Replication] Moving RecoveryState.Index to a top-level class and renaming ([#3971](https://github.com/opensearch-project/opensearch/pull/3971)) +* Rename and deprecate public methods that contains 'master' in the name in 'server' directory ([#3647](https://github.com/opensearch-project/opensearch/pull/3647)) ([#3964](https://github.com/opensearch-project/opensearch/pull/3964)) +* [2.x] Deprecate public class names with master terminology ([#3871](https://github.com/opensearch-project/opensearch/pull/3871)) ([#3914](https://github.com/opensearch-project/opensearch/pull/3914)) +* [Backport 2.x] Rename public classes with 'Master' to 'ClusterManager' ([#3870](https://github.com/opensearch-project/opensearch/pull/3870)) +* Revert renaming masterOperation() to clusterManagerOperation() ([#3681](https://github.com/opensearch-project/opensearch/pull/3681)) ([#3714](https://github.com/opensearch-project/opensearch/pull/3714)) +* Revert renaming method onMaster() and offMaster() in interface LocalNodeMasterListener ([#3686](https://github.com/opensearch-project/opensearch/pull/3686)) ([#3693](https://github.com/opensearch-project/opensearch/pull/3693)) diff --git a/release-notes/opensearch.release-notes-2.3.0.md b/release-notes/opensearch.release-notes-2.3.0.md new file mode 100644 index 0000000000000..1532ab31106f7 --- /dev/null +++ b/release-notes/opensearch.release-notes-2.3.0.md @@ -0,0 +1,55 @@ +## 2022-09-08 Version 2.3.0 Release Notes + +### Features/Enhancements +* [Backport to 2.x] [Segment Replication] - Update replicas to commit SegmentInfos instead of relying on segments_N from primary shards. ([#4450](https://github.com/opensearch-project/opensearch/pull/4450)) +* [Segment Replication] [Backport] Fix timeout issue by calculating time needed to process getSegmentFiles. ([#4434](https://github.com/opensearch-project/opensearch/pull/4434)) +* [Semgnet Replication] Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test ([#4414](https://github.com/opensearch-project/opensearch/pull/4414)) ([#4425](https://github.com/opensearch-project/opensearch/pull/4425)) +* [Segment Replication] Extend FileChunkWriter to allow cancel on transport client ([#4386](https://github.com/opensearch-project/opensearch/pull/4386)) ([#4424](https://github.com/opensearch-project/opensearch/pull/4424)) +* Segment Replication - Fix NoSuchFileException errors caused when computing metadata snapshot on primary shards. ([#4366](https://github.com/opensearch-project/opensearch/pull/4366)) ([#4422](https://github.com/opensearch-project/opensearch/pull/4422)) +* [Remote Store] Add index specific setting for remote repository ([#4253](https://github.com/opensearch-project/opensearch/pull/4253)) ([#4418](https://github.com/opensearch-project/opensearch/pull/4418)) +* [Segment Replication] Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/opensearch/pull/4363)) ([#4396](https://github.com/opensearch-project/opensearch/pull/4396)) +* [Segment Replication] Bump segment infos counter before commit during replica promotion ([#4365](https://github.com/opensearch-project/opensearch/pull/4365)) ([#4397](https://github.com/opensearch-project/opensearch/pull/4397)) +* Segment Replication - Implement segment replication event cancellation. ([#4225](https://github.com/opensearch-project/opensearch/pull/4225)) ([#4387](https://github.com/opensearch-project/opensearch/pull/4387)) +* [Backport 2.x] [Remote Store] Backport remote segment store changes ([#4380](https://github.com/opensearch-project/opensearch/pull/4380)) +* [Backport 2.x] Added timing data and more granular stages to SegmentReplicationState ([#4367](https://github.com/opensearch-project/opensearch/pull/4367)) +* [Backport 2.x] Support shard promotion with Segment Replication. ([#4135](https://github.com/opensearch-project/opensearch/pull/4135)) ([#4325](https://github.com/opensearch-project/opensearch/pull/4325)) +* [Segment Replication] Update PrimaryShardAllocator to prefer replicas with higher replication checkpoint ([#4041](https://github.com/opensearch-project/opensearch/pull/4041)) ([#4252](https://github.com/opensearch-project/opensearch/pull/4252)) +* [Backport 2.x] [Segment Replication] Backport all PR's containing remaining segment replication changes ([#4243](https://github.com/opensearch-project/opensearch/pull/4243)) +* [Backport 2.x] [Segment Replication] Backport PR's : #3525 #3533 #3540 #3943 #3963 From main branch ([#4181](https://github.com/opensearch-project/opensearch/pull/4181)) +* [Backport 2.x] [Segment Replication] Added source-side classes for orchestrating replication events. ([#4128](https://github.com/opensearch-project/opensearch/pull/4128)) + +### Bug Fixes +* [Bug]: gradle check failing with java heap OutOfMemoryError ([#4328](https://github.com/opensearch-project/opensearch/pull/4328)) ([#4442](https://github.com/opensearch-project/opensearch/pull/4442)) +* [Backport 2.x] Revert to Netty 4.1.79.Final ([#4432](https://github.com/opensearch-project/opensearch/pull/4432)) +* Bug fixes for dependabot changelog verifier ([#4364](https://github.com/opensearch-project/opensearch/pull/4364)) ([#4395](https://github.com/opensearch-project/opensearch/pull/4395)) +* [BUG] Create logs directory before running OpenSearch on Windows ([#4305](https://github.com/opensearch-project/opensearch/pull/4305)) ([#4335](https://github.com/opensearch-project/opensearch/pull/4335)) +* [BUG] Running "opensearch-service.bat start" and "opensearch-service.bat manager" ([#4289](https://github.com/opensearch-project/opensearch/pull/4289)) ([#4293](https://github.com/opensearch-project/opensearch/pull/4293)) +* [Backport 2.x] Do not fail replica shard due to primary closure ([#4309](https://github.com/opensearch-project/opensearch/pull/4309)) +* [Bug]: gradle check failing with java heap OutOfMemoryError ([#4150](https://github.com/opensearch-project/opensearch/pull/4150)) ([#4167](https://github.com/opensearch-project/opensearch/pull/4167)) +* OpenSearch crashes on closed client connection before search reply when total ops higher compared to expected ([#4143](https://github.com/opensearch-project/opensearch/pull/4143)) ([#4144](https://github.com/opensearch-project/opensearch/pull/4144)) + +### Infrastructure +* Add workflow for changelog verification ([#4085](https://github.com/opensearch-project/opensearch/pull/4085)) ([#4284](https://github.com/opensearch-project/opensearch/pull/4284)) +* Add 2.x version to CHANGELOG ([#4297](https://github.com/opensearch-project/opensearch/pull/4297)) ([#4303](https://github.com/opensearch-project/opensearch/pull/4303)) +* Update the head ref to changelog verifier ([#4296](https://github.com/opensearch-project/opensearch/pull/4296)) ([#4298](https://github.com/opensearch-project/opensearch/pull/4298)) +* Publish transport-netty4 module to central repository ([#4054](https://github.com/opensearch-project/opensearch/pull/4054)) ([#4078](https://github.com/opensearch-project/opensearch/pull/4078)) + +### Maintenance +* Add bwcVersion 1.3.6 to 2.x ([#4452](https://github.com/opensearch-project/opensearch/pull/4452)) +* [AUTO] [2.x] Added bwc version 2.2.2. ([#4385](https://github.com/opensearch-project/opensearch/pull/4385)) +* Update to Netty 4.1.80.Final ([#4359](https://github.com/opensearch-project/opensearch/pull/4359)) ([#4374](https://github.com/opensearch-project/opensearch/pull/4374)) +* Adding @dreamer-89 to Opensearch maintainers. ([#4342](https://github.com/opensearch-project/opensearch/pull/4342)) ([#4345](https://github.com/opensearch-project/opensearch/pull/4345)) +* [CVE] Update snakeyaml dependency ([#4341](https://github.com/opensearch-project/opensearch/pull/4341)) ([#4347](https://github.com/opensearch-project/opensearch/pull/4347)) +* Some dependency updates ([#4308](https://github.com/opensearch-project/opensearch/pull/4308)) ([#4311](https://github.com/opensearch-project/opensearch/pull/4311)) +* Added bwc version 2.2.1 ([#4193](https://github.com/opensearch-project/opensearch/pull/4193)) +* Update Gradle to 7.5.1 ([#4211](https://github.com/opensearch-project/opensearch/pull/4211)) ([#4213](https://github.com/opensearch-project/opensearch/pull/4213)) +* [Backport] Upgrade dependencies ([#4165](https://github.com/opensearch-project/opensearch/pull/4165)) +* Bumping 2.x to 2.3.0 ([#4098](https://github.com/opensearch-project/opensearch/pull/4098)) + +### Refactoring +* Refactored the src and test of GeoHashGrid and GeoTileGrid Aggregations on GeoPoint from server folder to geo module.([#4071](https://github.com/opensearch-project/opensearch/pull/4071)) ([#4072](https://github.com/opensearch-project/opensearch/pull/4072)) ([#4180](https://github.com/opensearch-project/opensearch/pull/4180)) ([#4281](https://github.com/opensearch-project/opensearch/pull/4281)) +* Update the head ref to changelog verifier ([#4296](https://github.com/opensearch-project/opensearch/pull/4296)) ([#4298](https://github.com/opensearch-project/opensearch/pull/4298)) +* [2.x] Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses ([#4307](https://github.com/opensearch-project/opensearch/pull/4307)) ([#4324](https://github.com/opensearch-project/opensearch/pull/4324)) +* Refactored the src and test of GeoHashGrid and GeoTileGrid Aggregations on GeoPoint from server folder to geo module.([#4071](https://github.com/opensearch-project/opensearch/pull/4071)) ([#4072](https://github.com/opensearch-project/opensearch/pull/4072)) ([#4180](https://github.com/opensearch-project/opensearch/pull/4180)) ([#4281](https://github.com/opensearch-project/opensearch/pull/4281)) +* Refactors the GeoBoundsAggregation for geo_point types from the core server to the geo module. ([#4179](https://github.com/opensearch-project/opensearch/pull/4179)) +* Backporting multiple 2.* release notes from main to the 2.x branch ([#4154](https://github.com/opensearch-project/opensearch/pull/4154)) diff --git a/release-notes/opensearch.release-notes-2.4.0.md b/release-notes/opensearch.release-notes-2.4.0.md new file mode 100644 index 0000000000000..2a377aa8fce0b --- /dev/null +++ b/release-notes/opensearch.release-notes-2.4.0.md @@ -0,0 +1,101 @@ +## 2022-11-04 Version 2.4.0 Release Notes + +## [2.4] +### Added +- Introduce point in time search feature ([#3959](https://github.com/opensearch-project/OpenSearch/issues/3959)) +- Introduce experimental searchable snapshot feature ([#2919](https://github.com/opensearch-project/OpenSearch/issues/2919)) +- Add API for decommissioning/recommissioning zone and weighted zonal search request routing policy ([#3639](https://github.com/opensearch-project/OpenSearch/issues/3639)) +- Introduce cluster manager task throttling framework [#479](https://github.com/opensearch-project/OpenSearch/issues/479) +- Add support for s390x architecture ([#4001](https://github.com/opensearch-project/OpenSearch/pull/4001)) +- Github workflow for changelog verification ([#4085](https://github.com/opensearch-project/OpenSearch/pull/4085)) +- Add failover support with Segment Replication enabled. ([#4325](https://github.com/opensearch-project/OpenSearch/pull/4325) +- Add a new node role 'search' which is dedicated to provide search capability ([#4689](https://github.com/opensearch-project/OpenSearch/pull/4689)) +- Introduce Remote translog feature flag([#4158](https://github.com/opensearch-project/OpenSearch/pull/4158)) +- Add groupId value propagation tests for ZIP publication task ([#4848](https://github.com/opensearch-project/OpenSearch/pull/4848)) +- Add support for GeoJson Point type in GeoPoint field ([#4597](https://github.com/opensearch-project/OpenSearch/pull/4597)) +- Add missing no-jdk distributions ([#4722](https://github.com/opensearch-project/OpenSearch/pull/4722)) +- Copy `build.sh` over from opensearch-build ([#4887](https://github.com/opensearch-project/OpenSearch/pull/4887)) +- Update GeoGrid base class access modifier to support extensibility ([#4921](https://github.com/opensearch-project/OpenSearch/pull/4921)) +- Build no-jdk distributions as part of release build ([#4902](https://github.com/opensearch-project/OpenSearch/pull/4902)) +- Add in-flight cancellation of SearchShardTask based on resource consumption ([#4565](https://github.com/opensearch-project/OpenSearch/pull/4565)) +- Add resource usage trackers for in-flight cancellation of SearchShardTask ([#4805](https://github.com/opensearch-project/OpenSearch/pull/4805)) +- Add search backpressure stats API ([#4932](https://github.com/opensearch-project/OpenSearch/pull/4932)) +- Add feature to ignore indexes starting with dot during shard limit validation.([#4695](https://github.com/opensearch-project/OpenSearch/pull/4695)) + +### Dependencies +- Bump `com.diffplug.spotless` from 6.9.1 to 6.10.0 +- Bump `xmlbeans` from 5.1.0 to 5.1.1 +- Bump `hadoop-hdfs` from 3.3.3 to 3.3.4 +- Exclude jettison version brought in with hadoop-minicluster. ([#4787](https://github.com/opensearch-project/OpenSearch/pull/4787)) +- Bump protobuf-java to 3.21.7 in repository-gcs and repository-hdfs ([#4790](https://github.com/opensearch-project/OpenSearch/pull/4790)) +- Bump reactor-netty-http to 1.0.24 in repository-azure ([#4880](https://github.com/opensearch-project/OpenSearch/pull/4880)) +- Add dev help in gradle check CI failures ([4872](https://github.com/opensearch-project/OpenSearch/pull/4872)) +- Upgrade netty to 4.1.84.Final ([#4893](https://github.com/opensearch-project/OpenSearch/pull/4893)) +- Dependency updates: asm 9.3 -> 9.4, bytebuddy 1.12.12 -> 1.12.18 ([#4889](https://github.com/opensearch-project/OpenSearch/pull/4889)) +- Bump `tika` from 2.4.0 to 2.5.0 ([#4791](https://github.com/opensearch-project/OpenSearch/pull/4791)) +- Update Apache Lucene to 9.4.1 ([#4922](https://github.com/opensearch-project/OpenSearch/pull/4922)) +- Bump `woodstox-core` to 6.4.0 ([#4951](https://github.com/opensearch-project/OpenSearch/pull/4951)) +- Upgrade jetty-http, kotlin-stdlib and snakeyaml ([#4982](https://github.com/opensearch-project/OpenSearch/pull/4982)) +- OpenJDK Update (October 2022 Patch releases) ([#4997](https://github.com/opensearch-project/OpenSearch/pull/4997)) +- Upgrade zookeeper dependency in hdfs-fixture ([#5007](https://github.com/opensearch-project/OpenSearch/pull/5007)) +- Dependency updates (httpcore, mockito, slf4j, httpasyncclient, commons-codec) ([#4308](https://github.com/opensearch-project/OpenSearch/pull/4308)) +- Update to Apache Lucene 9.4.0 ([#4661](https://github.com/opensearch-project/OpenSearch/pull/4661)) +- Updated jackson to 2.13.4 and snakeyml to 1.32 ([#4556](https://github.com/opensearch-project/OpenSearch/pull/4556)) +- Update Jackson Databind to 2.13.4.2 (addressing CVE-2022-42003) ([#4781](https://github.com/opensearch-project/OpenSearch/pull/4781)) +- Bump protobuf-java to 3.21.8 ([#5005](https://github.com/opensearch-project/OpenSearch/pull/5005)) +- Upgrade zookeeper dependency in hdfs-fixture ([#5047](https://github.com/opensearch-project/OpenSearch/pull/5047)) + +### Changed +- Use RemoteSegmentStoreDirectory instead of RemoteDirectory ([#4240](https://github.com/opensearch-project/OpenSearch/pull/4240)) +- Weighted round-robin scheduling policy for shard coordination traffic ([#4241](https://github.com/opensearch-project/OpenSearch/pull/4241)) +- Add index specific setting for remote repository ([#4253](https://github.com/opensearch-project/OpenSearch/pull/4253)) +- Update replicas to commit SegmentInfos instead of relying on SIS files from primary shards. ([#4402](https://github.com/opensearch-project/OpenSearch/pull/4402)) +- Change the version to remove deprecated code of adding node name into log pattern of log4j property file ([#4569](https://github.com/opensearch-project/OpenSearch/pull/4569)) +- Load the deprecated master role in a dedicated method instead of in setAdditionalRoles() ([#4582](https://github.com/opensearch-project/OpenSearch/pull/4582)) +- Plugin ZIP publication groupId value is configurable ([#4156](https://github.com/opensearch-project/OpenSearch/pull/4156)) +- Further simplification of the ZIP publication implementation ([#4360](https://github.com/opensearch-project/OpenSearch/pull/4360)) +- Change behaviour in replica recovery for remote translog enabled indices ([#4318](https://github.com/opensearch-project/OpenSearch/pull/4318)) +- PUT api for weighted shard routing ([#4272](https://github.com/opensearch-project/OpenSearch/pull/4272)) +- GET api for weighted shard routing([#4275](https://github.com/opensearch-project/OpenSearch/pull/4275/)) +- Delete api for weighted shard routing([#4400](https://github.com/opensearch-project/OpenSearch/pull/4400/)) +- Fix weighted routing metadata deserialization error on process restart ([#4691](https://github.com/opensearch-project/OpenSearch/pull/4691)) +- Install and configure Log4j JUL Adapter for Lucene 9.4 ([#4754](https://github.com/opensearch-project/OpenSearch/pull/4754)) +- Use getParameterCount instead of getParameterTypes ([#4821](https://github.com/opensearch-project/OpenSearch/pull/4821)) + +### Removed +- Remove RepositoryData.MIN_VERSION support for next major release ([4729](https://github.com/opensearch-project/OpenSearch/pull/4729)) + +### Fixed +- `opensearch-service.bat start` and `opensearch-service.bat manager` failing to run ([#4289](https://github.com/opensearch-project/OpenSearch/pull/4289)) +- PR reference to checkout code for changelog verifier ([#4296](https://github.com/opensearch-project/OpenSearch/pull/4296)) +- `opensearch.bat` and `opensearch-service.bat install` failing to run, missing logs directory ([#4305](https://github.com/opensearch-project/OpenSearch/pull/4305)) +- Restore using the class ClusterInfoRequest and ClusterInfoRequestBuilder from package 'org.opensearch.action.support.master.info' for subclasses ([#4307](https://github.com/opensearch-project/OpenSearch/pull/4307)) +- Do not fail replica shard due to primary closure ([#4133](https://github.com/opensearch-project/OpenSearch/pull/4133)) +- Fixed cancellation of segment replication events ([#4225](https://github.com/opensearch-project/OpenSearch/pull/4225)) +- `opensearch.bat` fails to execute when install path includes spaces ([#4362](https://github.com/opensearch-project/OpenSearch/pull/4362)) +- Getting security exception due to access denied 'java.lang.RuntimePermission' 'accessDeclaredMembers' when trying to get snapshot with S3 IRSA ([#4469](https://github.com/opensearch-project/OpenSearch/pull/4469)) +- Fix the ignore_malformed setting to also ignore objects ([#4494](https://github.com/opensearch-project/OpenSearch/pull/4494)) +- Fix day of year defaulting for round up parser ([#4627](https://github.com/opensearch-project/OpenSearch/pull/4627)) +- Fix the SnapshotsInProgress error during index deletion ([#4570](https://github.com/opensearch-project/OpenSearch/pull/4570)) +- Fix invalid location of JDK dependency for arm64 architecture([#4613](https://github.com/opensearch-project/OpenSearch/pull/4613)) +- Alias filter lost after rollover ([#4499](https://github.com/opensearch-project/OpenSearch/pull/4499)) +- Fix Gradle warnings associated with publishPluginZipPublicationToXxx tasks ([#4696](https://github.com/opensearch-project/OpenSearch/pull/4696)) +- Fix a bug on handling an invalid array value for point type field #4900([#4900](https://github.com/opensearch-project/OpenSearch/pull/4900)) +- Set analyzer to regex query string search ([4219](https://github.com/opensearch-project/OpenSearch/pull/4219)) +- Better plural stemmer than minimal_english ([#4738](https://github.com/opensearch-project/OpenSearch/pull/4738)) +- Add check to cancel ongoing replication with old primary on onNewCheckpoint on replica ([#4363](https://github.com/opensearch-project/OpenSearch/pull/4363)) +- Bump segment infos counter before commit during replica promotion ([#4365](https://github.com/opensearch-project/OpenSearch/pull/4365)) +- Update flaky testOnNewCheckpointFromNewPrimaryCancelOngoingReplication unit test ([#4414](https://github.com/opensearch-project/OpenSearch/pull/4414)) +- Extend FileChunkWriter to allow cancel on transport client ([#4386](https://github.com/opensearch-project/OpenSearch/pull/4386)) +- Fix NoSuchFileExceptions with segment replication when computing primary metadata snapshots ([#4366](https://github.com/opensearch-project/OpenSearch/pull/4366)) +- Fix timeout issue by calculating time needed to process getSegmentFiles ([#4434](https://github.com/opensearch-project/OpenSearch/pull/4434)) +- Update replicas to commit SegmentInfos instead of relying on segments_N from primary shards ([#4450](https://github.com/opensearch-project/OpenSearch/pull/4450)) +- Adding check to make sure checkpoint is not processed when a shard's shard routing is primary ([#4716](https://github.com/opensearch-project/OpenSearch/pull/4716)) +- Fix bug of replica shard's translog not purging on index flush when segment replication is enabled ([4975](https://github.com/opensearch-project/OpenSearch/pull/4975)) +- Fix message "No OpenSearchException found" when detailed_error disabled by return meaningful messages ([#4708](https://github.com/opensearch-project/OpenSearch/pull/4708)) +- Add fix for auto expand replica validation ([#4994](https://github.com/opensearch-project/OpenSearch/pull/4994)) +- Fix build failures on the Windows platform ([#4924](https://github.com/opensearch-project/OpenSearch/issues/4924)) +- Fix error handling while reading analyzer mapping rules ([6d20423](https://github.com/opensearch-project/OpenSearch/commit/6d20423f5920745463b1abc5f1daf6a786c41aa0)) + +### Security +- CVE-2022-25857 org.yaml:snakeyaml DOS vulnerability ([#4341](https://github.com/opensearch-project/OpenSearch/pull/4341)) diff --git a/release-notes/opensearch.release-notes-2.4.1.md b/release-notes/opensearch.release-notes-2.4.1.md new file mode 100644 index 0000000000000..a2e885f1f1282 --- /dev/null +++ b/release-notes/opensearch.release-notes-2.4.1.md @@ -0,0 +1,23 @@ +## 2022-12-13 Version 2.4.1 Release Notes + +### Bug Fixes +* Fix 1.x compatibility bug with stored Tasks ([#5412](https://github.com/opensearch-project/opensearch/pull/5412)) ([#5440](https://github.com/opensearch-project/opensearch/pull/5440)) +* Use BuildParams.isCi() instead of checking env var ([#5368](https://github.com/opensearch-project/opensearch/pull/5368)) ([#5373](https://github.com/opensearch-project/opensearch/pull/5373)) +* [BUG] org.opensearch.repositories.s3.RepositoryS3ClientYamlTestSuiteIT/test {yaml=repository_s3/20_repository_permanent_credentials/Snapshot and Restore with repository-s3 using permanent credentials} flaky ([#5325](https://github.com/opensearch-project/opensearch/pull/5325)) ([#5336](https://github.com/opensearch-project/opensearch/pull/5336)) +* [BUG] Gradle Check Failed on Windows due to JDK19 pulling by gradle ([#5188](https://github.com/opensearch-project/opensearch/pull/5188)) ([#5192](https://github.com/opensearch-project/opensearch/pull/5192)) +* Fix test to use a file from another temp directory ([#5158](https://github.com/opensearch-project/opensearch/pull/5158)) ([#5163](https://github.com/opensearch-project/opensearch/pull/5163)) +* Fix boundary condition in indexing pressure test ([#5168](https://github.com/opensearch-project/opensearch/pull/5168)) ([#5182](https://github.com/opensearch-project/opensearch/pull/5182)) +* [Backport 2.x] Fix: org.opensearch.clustermanager.ClusterManagerTaskThrottlingIT is flaky. ([#5153](https://github.com/opensearch-project/opensearch/pull/5153)) ([#5171](https://github.com/opensearch-project/opensearch/pull/5171)) +* [Backport 2.4] Raise error on malformed CSV ([#5141](https://github.com/opensearch-project/opensearch/pull/5141)) + +### Features/Enhancements +* Change the output error message back to use OpenSearchException in the cause chain ([#5081](https://github.com/opensearch-project/opensearch/pull/5081)) ([#5085](https://github.com/opensearch-project/opensearch/pull/5085)) +* Revert changes in AbstractPointGeometryFieldMapper ([#5250](https://github.com/opensearch-project/opensearch/pull/5250)) +* Add support for skipping changelog ([#5088](https://github.com/opensearch-project/opensearch/pull/5088)) ([#5160](https://github.com/opensearch-project/opensearch/pull/5160)) +* [Backport 2.4]Revert "Cluster manager task throttling feature [Final PR] ([#5071](https://github.com/opensearch-project/opensearch/pull/5071)) ([#5203](https://github.com/opensearch-project/opensearch/pull/5203)) + +### Maintenance +* Update Apache Lucene to 9.4.2 ([#5354](https://github.com/opensearch-project/opensearch/pull/5354)) ([#5361](https://github.com/opensearch-project/opensearch/pull/5361)) +* Update Jackson to 2.14.1 ([#5346](https://github.com/opensearch-project/opensearch/pull/5346)) ([#5358](https://github.com/opensearch-project/opensearch/pull/5358)) +* Bump nebula-publishing-plugin from v4.4.0 to v4.6.0. ([#5127](https://github.com/opensearch-project/opensearch/pull/5127)) ([#5131](https://github.com/opensearch-project/opensearch/pull/5131)) +* Bump commons-compress from 1.21 to 1.22. ([#5520](https://github.com/opensearch-project/OpenSearch/pull/5520)) ([#5522](https://github.com/opensearch-project/opensearch/pull/5522)) diff --git a/release-notes/opensearch.release-notes-2.5.0.md b/release-notes/opensearch.release-notes-2.5.0.md new file mode 100644 index 0000000000000..5bc08a3bf07e6 --- /dev/null +++ b/release-notes/opensearch.release-notes-2.5.0.md @@ -0,0 +1,57 @@ +## 2023-01-10 Version 2.5.0 Release Notes + +## [2.5] +### Added +- Prevent deletion of snapshots that are backing searchable snapshot indexes ([#5365](https://github.com/opensearch-project/OpenSearch/pull/5365)) +- Reject bulk requests with invalid actions ([#5384](https://github.com/opensearch-project/OpenSearch/issues/5384)) +- Add max_shard_size parameter for shrink API ([#5229](https://github.com/opensearch-project/OpenSearch/pull/5229)) +- Add experimental support for extensions ([#5347](https://github.com/opensearch-project/OpenSearch/pull/5347)), ([#5518](https://github.com/opensearch-project/OpenSearch/pull/5518)), ([#5597](https://github.com/opensearch-project/OpenSearch/pull/5597)), ([#5615](https://github.com/opensearch-project/OpenSearch/pull/5615))) +- Add support to register settings dynamically ([#5495](https://github.com/opensearch-project/OpenSearch/pull/5495)) +- Add auto release workflow ([#5582](https://github.com/opensearch-project/OpenSearch/pull/5582)) +- Add CI bundle pattern to distribution download ([#5348](https://github.com/opensearch-project/OpenSearch/pull/5348)) +- Experimental support for extended backward compatiblity in searchable snapshots ([#5429](https://github.com/opensearch-project/OpenSearch/pull/5429)) +- Add support of default replica count cluster setting ([#5610](https://github.com/opensearch-project/OpenSearch/pull/5610)) +- Add support for refresh level durability ([#5253](https://github.com/opensearch-project/OpenSearch/pull/5253)) +- Add Request level Durability using Remote Translog functionality ([#5757](https://github.com/opensearch-project/OpenSearch/pull/5757)) +- Support to fail open requests on search shard failures with weighted traffic routing ([#5072](https://github.com/opensearch-project/OpenSearch/pull/5072)) +- Support versioning for Weighted routing apis([#5255](https://github.com/opensearch-project/OpenSearch/pull/5255)) +- Add support for discovered cluster manager and remove local weights ([#5680](https://github.com/opensearch-project/OpenSearch/pull/5680)) +- Add new level to get health per awareness attribute in _cluster/health ([#5694](https://github.com/opensearch-project/OpenSearch/pull/5694)) + +### Changed +- Change http code for DecommissioningFailedException from 500 to 400 ([#5283](https://github.com/opensearch-project/OpenSearch/pull/5283)) +- Pre conditions check before updating weighted routing metadata ([#4955](https://github.com/opensearch-project/OpenSearch/pull/4955)) +- Support remote translog transfer for request level durability ([#4480](https://github.com/opensearch-project/OpenSearch/pull/4480)) +- Gracefully handle concurrent zone decommission action ([#5542](https://github.com/opensearch-project/OpenSearch/pull/5542)) + +### Deprecated +- Refactor fuzziness interface on query builders ([#5433](https://github.com/opensearch-project/OpenSearch/pull/5433)) + +### Fixed +- Fix case sensitivity for wildcard queries ([#5462](https://github.com/opensearch-project/OpenSearch/pull/5462)) +- Apply cluster manager throttling settings during bootstrap ([#5524](https://github.com/opensearch-project/OpenSearch/pull/5524)) +- Update thresholds map when cluster manager throttling setting is removed ([#5524](https://github.com/opensearch-project/OpenSearch/pull/5524)) +- Fix backward compatibility for static cluster manager throttling threshold setting ([#5633](https://github.com/opensearch-project/OpenSearch/pull/5633)) +- Fix index exclusion behavior in snapshot restore and clone APIs ([#5626](https://github.com/opensearch-project/OpenSearch/pull/5626)) +- Fix graph filter error in search ([#5665](https://github.com/opensearch-project/OpenSearch/pull/5665)) + +### Dependencies +- Bumps `bcpg-fips` from 1.0.5.1 to 1.0.7.1 ([#5148](https://github.com/opensearch-project/OpenSearch/pull/5148)) +- Bumps `commons-compress` from 1.21 to 1.22 ([#5104](https://github.com/opensearch-project/OpenSearch/pull/5104)) +- Bumps `geoip2` from 3.0.1 to 3.0.2 in /modules/ingest-geoip ([#5201](https://github.com/opensearch-project/OpenSearch/pull/5201)) +- Bumps `gson` from 2.9.0 to 2.10 in /plugins/repository-hdfs ([#5184](https://github.com/opensearch-project/OpenSearch/pull/5184)) +- Bumps `protobuf-java` from 3.21.8 to 3.21.9 in /test/fixtures/hdfs-fixture ([#5185](https://github.com/opensearch-project/OpenSearch/pull/5185)) +- Bumps `gradle-extra-configurations-plugin` from 7.0.0 to 8.0.0 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `jcodings` from 1.0.57 to 1.0.58 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `google-http-client-jackson2` from 1.35.0 to 1.42.3 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `azure-core` from 1.33.0 to 1.34.0 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `azure-core-http-netty` from 1.12.4 to 1.12.7 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `maxmind-db` from 2.0.0 to 2.1.0 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `json-schema-validator` from 1.0.69 to 1.0.73 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `proto-google-common-protos` from 2.8.0 to 2.10.0 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `protobuf-java` from 3.21.7 to 3.21.9 ([#5330](https://github.com/opensearch-project/OpenSearch/pull/5330)) +- Bumps `gradle` from 7.5 to 7.6 ([#5382](https://github.com/opensearch-project/OpenSearch/pull/5382)) +- Bumps `jackson` from 2.14.0 to 2.14.1 ([#5355](https://github.com/opensearch-project/OpenSearch/pull/5355)) +- Bumps `apache-rat` from 0.13 to 0.15 ([#5686](https://github.com/opensearch-project/OpenSearch/pull/5686)) +- Bumps `reactor-netty` from 1.0.18 to 1.1.1 ([#5685](https://github.com/opensearch-project/OpenSearch/pull/5685)) +- Bumps `gradle-info-plugin` from 7.1.3 to 12.0.0 ([#5684](https://github.com/opensearch-project/OpenSearch/pull/5684)) diff --git a/release-notes/opensearch.release-notes-2.6.0.md b/release-notes/opensearch.release-notes-2.6.0.md new file mode 100644 index 0000000000000..860e789be06ac --- /dev/null +++ b/release-notes/opensearch.release-notes-2.6.0.md @@ -0,0 +1,57 @@ +## 2023-02-22 Version 2.6.0 Release Notes + +## [2.6] +### Added +- Add index create block when all nodes have breached high disk watermark ([#5852](https://github.com/opensearch-project/OpenSearch/pull/5852)) +- Add cluster manager throttling stats in nodes/stats API ([#5790](https://github.com/opensearch-project/OpenSearch/pull/5790)) +- Add support for feature flags in opensearch.yml ([#4959](https://github.com/opensearch-project/OpenSearch/pull/4959)) +- Add query for initialized extensions ([#5658](https://github.com/opensearch-project/OpenSearch/pull/5658)) +- Add update-index-settings allowlist for searchable snapshot ([#5907](https://github.com/opensearch-project/OpenSearch/pull/5907)) +- Add new cat/segment_replication API to surface Segment Replication metrics ([#5718](https://github.com/opensearch-project/OpenSearch/pull/5718)). +- Replace latches with CompletableFutures for extensions ([#5646](https://github.com/opensearch-project/OpenSearch/pull/5646)) +- Add support to disallow search request with preference parameter with strict weighted shard routing([#5874](https://github.com/opensearch-project/OpenSearch/pull/5874)) +- Add support to apply index create block ([#4603](https://github.com/opensearch-project/OpenSearch/issues/4603)) +- Add support for minimum compatible version for extensions ([#6003](https://github.com/opensearch-project/OpenSearch/pull/6003)) +- Add a guardrail to limit maximum number of shard on the cluster ([#6143](https://github.com/opensearch-project/OpenSearch/pull/6143)) +- Add cancellation of in-flight SearchTasks based on resource consumption ([#5606](https://github.com/opensearch-project/OpenSearch/pull/5605)) +- Add support for ppc64le architecture ([#5459](https://github.com/opensearch-project/OpenSearch/pull/5459)) +- Add a setting to control auto release of OpenSearch managed index creation block ([#6277](https://github.com/opensearch-project/OpenSearch/pull/6277)) +- Fix timeout error when adding a document to an index with extension running ([#6275](https://github.com/opensearch-project/OpenSearch/pull/6275)) +- Handle translog upload during primary relocation for remote-backed indexes ([#5804](https://github.com/opensearch-project/OpenSearch/pull/5804)) +- Batch translog sync/upload per x ms for remote-backed indexes ([#5854](https://github.com/opensearch-project/OpenSearch/pull/5854)) + +### Dependencies +- Update nebula-publishing-plugin to 19.2.0 ([#5704](https://github.com/opensearch-project/OpenSearch/pull/5704)) +- Bump `reactor-netty` from 1.1.1 to 1.1.2 ([#5878](https://github.com/opensearch-project/OpenSearch/pull/5878)) +- OpenJDK Update (January 2023 Patch releases) ([#6075](https://github.com/opensearch-project/OpenSearch/pull/6075)) +- Bump `Mockito` from 4.7.0 to 5.1.0, `ByteBuddy` from 1.12.18 to 1.12.22 ([#6088](https://github.com/opensearch-project/OpenSearch/pull/6088)) +- Bump `joda` from 2.10.13 to 2.12.2 ([#6095](https://github.com/opensearch-project/OpenSearch/pull/6095)) +- Upgrade to Lucene 9.5.0 ([#6078](https://github.com/opensearch-project/OpenSearch/pull/6078)) +- Bump antlr4 from 4.9.3 to 4.11.1 ([#6116](https://github.com/opensearch-project/OpenSearch/pull/6116)) +- Bump `Netty` from 4.1.86.Final to 4.1.87.Final ([#6130](https://github.com/opensearch-project/OpenSearch/pull/6130)) +- Bump `Jackson` from 2.14.1 to 2.14.2 ([#6129](https://github.com/opensearch-project/OpenSearch/pull/6129)) +- Bump `org.apache.ant:ant` from 1.10.12 to 1.10.13 ([#6306](https://github.com/opensearch-project/OpenSearch/pull/6306)) +- Bump `azure-core-http-netty` from 1.12.7 to 1.12.8 ([#6304](https://github.com/opensearch-project/OpenSearch/pull/6304)) +- Bump `com.azure:azure-storage-common` from 12.18.1 to 12.19.3 ([#6304](https://github.com/opensearch-project/OpenSearch/pull/6304)) +- Bump `reactor-netty-http` from 1.0.24 to 1.1.2 ([#6309](https://github.com/opensearch-project/OpenSearch/pull/6309)) +- Bump `com.google.protobuf:protobuf-java` from 3.21.12 to 3.22.0 ([#6387](https://github.com/opensearch-project/OpenSearch/pull/6387)) +- Bump `io.projectreactor.netty:reactor-netty` from 1.1.2 to 1.1.3 ([#6386](https://github.com/opensearch-project/OpenSearch/pull/6386)) +- Bump `com.diffplug.spotless` from 6.10.0 to 6.15.0 ([#6385](https://github.com/opensearch-project/OpenSearch/pull/6385)) +- Bump `jettison` from 1.5.1 to 1.5.3 ([#6391](https://github.com/opensearch-project/OpenSearch/pull/6391)) + +### Changed +- Use ReplicationFailedException instead of OpensearchException in ReplicationTarget ([#4725](https://github.com/opensearch-project/OpenSearch/pull/4725)) +- [Refactor] Use local opensearch.common.SetOnce instead of lucene's utility class ([#5947](https://github.com/opensearch-project/OpenSearch/pull/5947)) +- Cluster health call to throw decommissioned exception for local decommissioned node([#6008](https://github.com/opensearch-project/OpenSearch/pull/6008)) +- [Refactor] core.common to new opensearch-common library ([#5976](https://github.com/opensearch-project/OpenSearch/pull/5976)) +- Update API spec for cluster health API ([#6399](https://github.com/opensearch-project/OpenSearch/pull/6399)) + +### Removed +- Remove deprecated org.gradle.util.DistributionLocator usage ([#6212](https://github.com/opensearch-project/OpenSearch/pull/6212)) + +### Fixed +- [Segment Replication] Fix for peer recovery ([#5344](https://github.com/opensearch-project/OpenSearch/pull/5344)) +- Fix weighted shard routing state across search requests([#6004](https://github.com/opensearch-project/OpenSearch/pull/6004)) +- [Segment Replication] Fix bug where inaccurate sequence numbers are sent during replication ([#6122](https://github.com/opensearch-project/OpenSearch/pull/6122)) +- Enable numeric sort optimisation for few numerical sort types ([#6321](https://github.com/opensearch-project/OpenSearch/pull/6321)) +- Fix Opensearch repository-s3 plugin cannot read ServiceAccount token ([#6390](https://github.com/opensearch-project/OpenSearch/pull/6390) diff --git a/release-notes/opensearch.release-notes-2.7.0.md b/release-notes/opensearch.release-notes-2.7.0.md new file mode 100644 index 0000000000000..fb886029683a6 --- /dev/null +++ b/release-notes/opensearch.release-notes-2.7.0.md @@ -0,0 +1,62 @@ +## 2023-04-18 Version 2.7.0 Release Notes + +## [2.7] +### Added +- Add GeoTile and GeoHash Grid aggregations on GeoShapes. ([#5589](https://github.com/opensearch-project/OpenSearch/pull/5589)) +- Disallow multiple data paths for search nodes ([#6427](https://github.com/opensearch-project/OpenSearch/pull/6427)) +- [Segment Replication] Allocation and rebalancing based on average primary shard count per index ([#6422](https://github.com/opensearch-project/OpenSearch/pull/6422)) +- The truncation limit of the OpenSearchJsonLayout logger is now configurable ([#6569](https://github.com/opensearch-project/OpenSearch/pull/6569)) +- Add 'base_path' setting to File System Repository ([#6558](https://github.com/opensearch-project/OpenSearch/pull/6558)) +- Return success on DeletePits when no PITs exist. ([#6544](https://github.com/opensearch-project/OpenSearch/pull/6544)) +- Add initial search pipelines ([#6587](https://github.com/opensearch-project/OpenSearch/pull/6587)) +- Add node repurpose command for search nodes ([#6517](https://github.com/opensearch-project/OpenSearch/pull/6517)) +- Add wait_for_completion parameter to resize, open, and forcemerge APIs ([#6434](https://github.com/opensearch-project/OpenSearch/pull/6434)) +- [Segment Replication] Apply backpressure when replicas fall behind ([#6563](https://github.com/opensearch-project/OpenSearch/pull/6563)) +- [Remote Store] Integrate remote segment store in peer recovery flow ([#6664](https://github.com/opensearch-project/OpenSearch/pull/6664)) +- Enable sort optimization for all NumericTypes ([#6464](https://github.com/opensearch-project/OpenSearch/pull/6464) +- Remove 'cluster_manager' role attachment when using 'node.master' deprecated setting ([#6331](https://github.com/opensearch-project/OpenSearch/pull/6331)) +- Add new cluster settings to ignore weighted round-robin routing and fallback to default behaviour. ([#6834](https://github.com/opensearch-project/OpenSearch/pull/6834)) +- Add experimental support for ZSTD compression. ([#3577](https://github.com/opensearch-project/OpenSearch/pull/3577)) +- [Segment Replication] Add point in time and scroll query compatibility. ([#6644](https://github.com/opensearch-project/OpenSearch/pull/6644)) +- Add retry delay as dynamic setting for cluster maanger throttling. ([#6998](https://github.com/opensearch-project/OpenSearch/pull/6998)) +- Introduce full support for searchable snapshots ([#5087](https://github.com/opensearch-project/OpenSearch/issues/5087)) +- Introduce full support for Segment Replication ([#5147](https://github.com/opensearch-project/OpenSearch/issues/5147)) +- Introduce a new field type: flat_object to help prevent mapping explosions ([#1018](https://github.com/opensearch-project/OpenSearch/issues/1018)) + +### Dependencies +- Bump `org.apache.logging.log4j:log4j-core` from 2.18.0 to 2.20.0 ([#6490](https://github.com/opensearch-project/OpenSearch/pull/6490)) +- Bump `com.azure:azure-storage-common` from 12.19.3 to 12.20.0 ([#6492](https://github.com/opensearch-project/OpenSearch/pull/6492) +- Bump `snakeyaml` from 1.33 to 2.0 ([#6511](https://github.com/opensearch-project/OpenSearch/pull/6511)) +- Bump `io.projectreactor.netty:reactor-netty` from 1.1.3 to 1.1.4 +- Bump `com.avast.gradle:gradle-docker-compose-plugin` from 0.15.2 to 0.16.11 +- Bump `net.minidev:json-smart` from 2.4.8 to 2.4.9 +- Bump `com.google.protobuf:protobuf-java` to 3.22.2 ([#6994](https://github.com/opensearch-project/OpenSearch/pull/6994)) +- Bump Netty to 4.1.90.Final ([#6677](https://github.com/opensearch-project/OpenSearch/pull/6677) +- Bump `com.diffplug.spotless` from 6.15.0 to 6.17.0 +- Bump `org.apache.zookeeper:zookeeper` from 3.8.0 to 3.8.1 +- Bump `net.minidev:json-smart` from 2.4.7 to 2.4.10 +- Bump `org.apache.maven:maven-model` from 3.6.2 to 3.9.1 +- Bump `org.codehaus.jettison:jettison` from 1.5.3 to 1.5.4 ([#6878](https://github.com/opensearch-project/OpenSearch/pull/6878)) +- Add `com.github.luben:zstd-jni:1.5.5-1` ([#3577](https://github.com/opensearch-project/OpenSearch/pull/3577)) +- Bump: Netty from 4.1.90.Final to 4.1.91.Final , ASM 9.4 to ASM 9.5, ByteBuddy 1.14.2 to 1.14.3 ([#6981](https://github.com/opensearch-project/OpenSearch/pull/6981)) +- Bump `com.azure:azure-storage-blob` from 12.15.0 to 12.21.1 +- Bump `org.gradle.test-retry` from 1.5.1 to 1.5.2 +- Bump `org.apache.hadoop:hadoop-minicluster` from 3.3.4 to 3.3.5 + +### Changed +- Require MediaType in Strings.toString API ([#6009](https://github.com/opensearch-project/OpenSearch/pull/6009)) +- [Refactor] XContent base classes from xcontent to core library ([#5902](https://github.com/opensearch-project/OpenSearch/pull/5902)) +- Changed `opensearch-env` to respect already set `OPENSEARCH_HOME` environment variable ([#6956](https://github.com/opensearch-project/OpenSearch/pull/6956/)) +- Increased visibility of BaseRestHandler’s `unrecognized` method using a new public `unrecognizedStrings` method. ([#7125](https://github.com/opensearch-project/OpenSearch/pull/7125)) + +### Deprecated +- Map, List, and Set in org.opensearch.common.collect ([#6609](https://github.com/opensearch-project/OpenSearch/pull/6609)) + +### Fixed +- Added depth check in doc parser for deep nested document ([#5199](https://github.com/opensearch-project/OpenSearch/pull/5199)) +- Added equals/hashcode for named DocValueFormat.DateTime inner class ([#6357](https://github.com/opensearch-project/OpenSearch/pull/6357)) +- Fixed bug for searchable snapshot to take 'base_path' of blob into account ([#6558](https://github.com/opensearch-project/OpenSearch/pull/6558)) +- Fix fuzziness validation ([#5805](https://github.com/opensearch-project/OpenSearch/pull/5805)) +- Avoid negative memory result in IndicesQueryCache stats calculation ([#6917](https://github.com/opensearch-project/OpenSearch/pull/6917)) +- Fix GetSnapshots to not return non-existent snapshots with ignore_unavailable=true ([#6839](https://github.com/opensearch-project/OpenSearch/pull/6839)) +- Fix GlobalAggregation with profile option enabled returns incorrect result ([#7114](https://github.com/opensearch-project/OpenSearch/pull/7114)) diff --git a/release-notes/opensearch.release-notes-2.8.0.md b/release-notes/opensearch.release-notes-2.8.0.md new file mode 100644 index 0000000000000..556e1f0047595 --- /dev/null +++ b/release-notes/opensearch.release-notes-2.8.0.md @@ -0,0 +1,71 @@ +## 2023-06-06 Version 2.8.0 Release Notes + +## [2.8] +### Added +- [Extensions] Moving Extensions APIs to support cross versions via protobuf. ([#7402](https://github.com/opensearch-project/OpenSearch/issues/7402)) +- [Extensions] Add IdentityPlugin into core to support Extension identities ([#7246](https://github.com/opensearch-project/OpenSearch/pull/7246)) +- Add connectToNodeAsExtension in TransportService ([#6866](https://github.com/opensearch-project/OpenSearch/pull/6866)) +- [Search Pipelines] Accept pipelines defined in search source ([#7253](https://github.com/opensearch-project/OpenSearch/pull/7253)) +- [Search Pipelines] Add `default_search_pipeline` index setting ([#7470](https://github.com/opensearch-project/OpenSearch/pull/7470)) +- [Search Pipelines] Add RenameFieldResponseProcessor for Search Pipelines ([#7377](https://github.com/opensearch-project/OpenSearch/pull/7377)) +- [Search Pipelines] Split search pipeline processor factories by type ([#7597](https://github.com/opensearch-project/OpenSearch/pull/7597)) +- [Search Pipelines] Add script processor ([#7607](https://github.com/opensearch-project/OpenSearch/pull/7607)) +- Add 'unsigned_long' numeric field type ([#6237](https://github.com/opensearch-project/OpenSearch/pull/6237)) +- Add back primary shard preference for queries ([#7375](https://github.com/opensearch-project/OpenSearch/pull/7375)) +- Add task cancellation timestamp in task API ([#7455](https://github.com/opensearch-project/OpenSearch/pull/7455)) +- Adds ExtensionsManager.lookupExtensionSettingsById ([#7466](https://github.com/opensearch-project/OpenSearch/pull/7466)) +- SegRep with Remote: Add hook for publishing checkpoint notifications after segment upload to remote store ([#7394](https://github.com/opensearch-project/OpenSearch/pull/7394)) +- Add search_after query optimizations with shard/segment short cutting ([#7453](https://github.com/opensearch-project/OpenSearch/pull/7453)) +- Provide mechanism to configure XContent parsing constraints (after update to Jackson 2.15.0 and above) ([#7550](https://github.com/opensearch-project/OpenSearch/pull/7550)) +- Support to clear filecache using clear indices cache API ([#7498](https://github.com/opensearch-project/OpenSearch/pull/7498)) +- Create NamedRoute to map extension routes to a shortened name ([#6870](https://github.com/opensearch-project/OpenSearch/pull/6870)) +- Added @dbwiddis as on OpenSearch maintainer ([#7665](https://github.com/opensearch-project/OpenSearch/pull/7665)) +- [Extensions] Add ExtensionAwarePlugin extension point to add custom settings for extensions ([#7526](https://github.com/opensearch-project/OpenSearch/pull/7526)) +- Add new cluster setting to set default index replication type ([#7420](https://github.com/opensearch-project/OpenSearch/pull/7420)) + +### Dependencies +- Bump `com.netflix.nebula:gradle-info-plugin` from 12.0.0 to 12.1.3 (#7564) +- Bump `com.netflix.nebula:nebula-publishing-plugin` from 19.2.0 to 20.2.0 +- Bump `com.google.protobuf:protobuf-java` from 3.22.2 to 3.22.3 +- Bump `jackson` from 2.14.2 to 2.15.0 ([#7286](https://github.com/opensearch-project/OpenSearch/pull/7286)) +- Bump `com.netflix.nebula:nebula-publishing-plugin` from 20.2.0 to 20.3.0 +- Bump `com.netflix.nebula.ospackage-base` from 11.0.0 to 11.3.0 +- Bump `gradle.plugin.com.github.johnrengelman:shadow` from 7.1.2 to 8.0.0 +- Bump `jna` from 5.11.0 to 5.13.0 +- Bump `commons-io:commons-io` from 2.7 to 2.12.0 (#7661, #7658, #7656) +- Bump `org.apache.shiro:shiro-core` from 1.9.1 to 1.11.0 ([#7397](https://github.com/opensearch-project/OpenSearch/pull/7397)) +- Bump `jetty-server` in hdfs-fixture from 9.4.49.v20220914 to 9.4.51.v20230217 ([#7405](https://github.com/opensearch-project/OpenSearch/pull/7405)) +- OpenJDK Update (April 2023 Patch releases) ([#7448](https://github.com/opensearch-project/OpenSearch/pull/7448) +- Bump `org.apache.commons:commons-compress` from 1.22 to 1.23.0 (#7462) +- Bump `com.azure:azure-core` from 1.34.0 to 1.39.0 +- Bump `com.networknt:json-schema-validator` from 1.0.78 to 1.0.81 (#7460) +- Bump Apache Lucene to 9.6.0 ([#7505](https://github.com/opensearch-project/OpenSearch/pull/7505)) +- Bump `com.google.cloud:google-cloud-core-http` from 1.93.3 to 2.17.0 (#7488) +- Bump `com.google.guava:guava` from 30.1.1-jre to 32.0.0-jre (#7565, #7811, #7808, #7807) +- Bump `com.azure:azure-storage-common` from 12.20.0 to 12.21.1 (#7566, #7814) +- Bump `org.apache.commons:commons-compress` from 1.22 to 1.23.0 (#7563) +- Bump `jackson` from 2.15.0 to 2.15.1 ([#7603](https://github.com/opensearch-project/OpenSearch/pull/7603)) +- Bump `net.minidev:json-smart` from 2.4.10 to 2.4.11 (#7660, #7812) +- Bump `io.projectreactor.netty:reactor-netty-core` from 1.1.5 to 1.1.7 (#7657) +- Bump `org.apache.maven:maven-model` from 3.9.1 to 3.9.2 (#7655) +- Bump `com.google.api:gax` from 2.17.0 to 2.27.0 (#7697) +- Bump `io.projectreactor.netty:reactor-netty` from 1.1.4 to 1.1.7 ([#7725](https://github.com/opensearch-project/OpenSearch/pull/7725)) +- Bump `io.projectreactor.netty:reactor-netty-http` from 1.1.4 to 1.1.7 ([#7725](https://github.com/opensearch-project/OpenSearch/pull/7725)) +- Bump `com.google.http-client:google-http-client-appengine` from 1.41.8 to 1.43.2 (#7813) +- Bump `org.gradle.test-retry` from 1.5.2 to 1.5.3 (#7810) + +### Changed +- Enable `./gradlew build` on MacOS by disabling bcw tests ([#7303](https://github.com/opensearch-project/OpenSearch/pull/7303)) +- Moved concurrent-search from sandbox plugin to server module behind feature flag ([#7203](https://github.com/opensearch-project/OpenSearch/pull/7203)) +- Allow access to indices cache clear APIs for read only indexes ([#7303](https://github.com/opensearch-project/OpenSearch/pull/7303)) +- Changed concurrent-search threadpool type to be resizable and support task resource tracking ([#7502](https://github.com/opensearch-project/OpenSearch/pull/7502)) +- Default search preference to _primary for searchable snapshot indices ([#7628](https://github.com/opensearch-project/OpenSearch/pull/7628)) +- [Segment Replication] Remove codec name string match check for checkpoints ([#7741](https://github.com/opensearch-project/OpenSearch/pull/7741)) + +### Fixed +- Add more index blocks check for resize APIs ([#6774](https://github.com/opensearch-project/OpenSearch/pull/6774)) +- Replaces ZipInputStream with ZipFile to fix Zip Slip vulnerability ([#7230](https://github.com/opensearch-project/OpenSearch/pull/7230)) +- Add missing validation/parsing of SearchBackpressureMode of SearchBackpressureSettings ([#7541](https://github.com/opensearch-project/OpenSearch/pull/7541)) +- [Search Pipelines] Better exception handling in search pipelines ([#7735](https://github.com/opensearch-project/OpenSearch/pull/7735)) +- Fix input validation in segments and delete pit request ([#6645](https://github.com/opensearch-project/OpenSearch/pull/6645)) + diff --git a/release-notes/opensearch.release-notes-2.9.0.md b/release-notes/opensearch.release-notes-2.9.0.md new file mode 100644 index 0000000000000..76d2d9f4593d6 --- /dev/null +++ b/release-notes/opensearch.release-notes-2.9.0.md @@ -0,0 +1,109 @@ +## 2023-07-13 Version 2.9.0 Release Notes + +## [2.9] + +### Added +- [SearchPipeline] Add new search pipeline processor type, SearchPhaseResultsProcessor, that can modify the result of one search phase before starting the next phase.([#7283](https://github.com/opensearch-project/OpenSearch/pull/7283)) +- Add task cancellation monitoring service ([#7642](https://github.com/opensearch-project/OpenSearch/pull/7642)) +- Add TokenManager Interface ([#7452](https://github.com/opensearch-project/OpenSearch/pull/7452)) +- Add Remote store as a segment replication source ([#7653](https://github.com/opensearch-project/OpenSearch/pull/7653)) +- Implement concurrent aggregations support without profile option ([#7514](https://github.com/opensearch-project/OpenSearch/pull/7514)) +- Add dynamic index and cluster setting for concurrent segment search ([#7956](https://github.com/opensearch-project/OpenSearch/pull/7956)) +- Add descending order search optimization through reverse segment read. ([#7967](https://github.com/opensearch-project/OpenSearch/pull/7967)) +- [Search pipelines] Added search pipelines output to node stats ([#8053](https://github.com/opensearch-project/OpenSearch/pull/8053)) +- Update components of segrep backpressure to support remote store. ([#8020](https://github.com/opensearch-project/OpenSearch/pull/8020)) +- Make remote cluster connection setup in async ([#8038](https://github.com/opensearch-project/OpenSearch/pull/8038)) +- Add API to initialize extensions ([#8029]()https://github.com/opensearch-project/OpenSearch/pull/8029) +- Add distributed tracing framework ([#7543](https://github.com/opensearch-project/OpenSearch/issues/7543)) +- Enable Point based optimization for custom comparators ([#8168](https://github.com/opensearch-project/OpenSearch/pull/8168)) +- [Extensions] Support extension additional settings with extension REST initialization ([#8414](https://github.com/opensearch-project/OpenSearch/pull/8414)) +- Adds mock implementation for TelemetryPlugin ([#7545](https://github.com/opensearch-project/OpenSearch/issues/7545)) +- Support transport action names when registering NamedRoutes ([#7957](https://github.com/opensearch-project/OpenSearch/pull/7957)) +- Create concept of persistent ThreadContext headers that are unstashable ([#8291]()https://github.com/opensearch-project/OpenSearch/pull/8291) +- [Search pipelines] Add Global Ignore_failure options for Processors ([#8373](https://github.com/opensearch-project/OpenSearch/pull/8373)) +- Enable Partial Flat Object ([#7997](https://github.com/opensearch-project/OpenSearch/pull/7997)) +- Add jdk.incubator.vector module support for JDK 20+ ([#8601](https://github.com/opensearch-project/OpenSearch/pull/8601)) +- Introduce full support for Search Pipeline ([#8613](https://github.com/opensearch-project/OpenSearch/pull/8613)) +- Add partial results support for concurrent segment search ([#8306](https://github.com/opensearch-project/OpenSearch/pull/8306)) + +### Dependencies +- Bump `com.azure:azure-storage-common` from 12.21.0 to 12.21.1 (#7566, #7814) +- Bump `com.google.guava:guava` from 30.1.1-jre to 32.1.1-jre (#7565, #7811, #7807, #7808, #8402, #8400, #8401, #8581) +- Bump `net.minidev:json-smart` from 2.4.10 to 2.4.11 (#7660, #7812) +- Bump `org.gradle.test-retry` from 1.5.2 to 1.5.3 (#7810) +- Bump `com.diffplug.spotless` from 6.17.0 to 6.18.0 (#7896) +- Bump `jackson` from 2.15.1 to 2.15.2 ([#7897](https://github.com/opensearch-project/OpenSearch/pull/7897)) +- Add `com.github.luben:zstd-jni` version 1.5.5-3 ([#2996](https://github.com/opensearch-project/OpenSearch/pull/2996)) +- Bump `netty` from 4.1.91.Final to 4.1.93.Final ([#7901](https://github.com/opensearch-project/OpenSearch/pull/7901)) +- Bump `com.amazonaws` 1.12.270 to `software.amazon.awssdk` 2.20.55 ([7372](https://github.com/opensearch-project/OpenSearch/pull/7372/)) +- Add `org.reactivestreams` 1.0.4 ([7372](https://github.com/opensearch-project/OpenSearch/pull/7372/)) +- Bump `com.networknt:json-schema-validator` from 1.0.81 to 1.0.85 ([7968], #8255) +- Bump `com.netflix.nebula:gradle-extra-configurations-plugin` from 9.0.0 to 10.0.0 in /buildSrc ([#7068](https://github.com/opensearch-project/OpenSearch/pull/7068)) +- Bump `com.google.guava:guava` from 32.0.0-jre to 32.0.1-jre (#8009) +- Bump `commons-io:commons-io` from 2.12.0 to 2.13.0 (#8014, #8013, #8010) +- Bump `com.diffplug.spotless` from 6.18.0 to 6.19.0 (#8007) +- Bump `'com.azure:azure-storage-blob` to 12.22.2 from 12.21.1 ([#8043](https://github.com/opensearch-project/OpenSearch/pull/8043)) +- Bump `org.jruby.joni:joni` from 2.1.48 to 2.2.1 (#8015, #8254) +- Bump `com.google.guava:guava` from 32.0.0-jre to 32.0.1-jre ([#8011](https://github.com/opensearch-project/OpenSearch/pull/8011), [#8012](https://github.com/opensearch-project/OpenSearch/pull/8012), [#8107](https://github.com/opensearch-project/OpenSearch/pull/8107)) +- Bump `io.projectreactor:reactor-core` from 3.4.18 to 3.5.6 in /plugins/repository-azure ([#8016](https://github.com/opensearch-project/OpenSearch/pull/8016)) +- Bump `spock-core` from 2.1-groovy-3.0 to 2.3-groovy-3.0 ([#8122](https://github.com/opensearch-project/OpenSearch/pull/8122)) +- Bump `com.networknt:json-schema-validator` from 1.0.83 to 1.0.84 (#8141) +- Bump `com.netflix.nebula:gradle-info-plugin` from 12.1.3 to 12.1.5 (#8139, #8568) +- Bump `commons-io:commons-io` from 2.12.0 to 2.13.0 in /plugins/discovery-azure-classic ([#8140](https://github.com/opensearch-project/OpenSearch/pull/8140)) +- Bump `mockito` from 5.2.0 to 5.4.0 ([#8181](https://github.com/opensearch-project/OpenSearch/pull/8181)) +- Bump `netty` from 4.1.93.Final to 4.1.94.Final ([#8191](https://github.com/opensearch-project/OpenSearch/pull/8191)) +- Bump `org.apache.hadoop:hadoop-minicluster` from 3.3.5 to 3.3.6 (#8257) +- Bump `io.projectreactor.netty:reactor-netty-http` from 1.1.7 to 1.1.8 (#8256) +- [Upgrade] Lucene 9.7.0 release (#8272) +- Bump `org.jboss.resteasy:resteasy-jackson2-provider` from 3.0.26.Final to 6.2.4.Final in /qa/wildfly ([#8209](https://github.com/opensearch-project/OpenSearch/pull/8209)) +- Bump `com.google.api-client:google-api-client` from 1.34.0 to 2.2.0 ([#8276](https://github.com/opensearch-project/OpenSearch/pull/8276)) +- Update Apache HttpCore/ HttpClient and Apache HttpCore5 / HttpClient5 dependencies ([#8434](https://github.com/opensearch-project/OpenSearch/pull/8434)) +- Bump `org.apache.maven:maven-model` from 3.9.2 to 3.9.3 (#8403) +- Bump `io.projectreactor.netty:reactor-netty` and `io.projectreactor.netty:reactor-netty-core` from 1.1.7 to 1.1.8 (#8405) +- Bump `com.azure:azure-storage-blob` from 12.22.2 to 12.22.3 (#8572) +- Bump `net.minidev:json-smart` from 2.4.11 to 2.5.0 (#8575, #8576) +- Bump `com.google.jimfs:jimfs` from 1.2 to 1.3.0 (#8577, #8571) +- Bump `com.networknt:json-schema-validator` from 1.0.85 to 1.0.86 ([#8573](https://github.com/opensearch-project/OpenSearch/pull/8573)) +- Bump `com.google.cloud:google-cloud-core-http` from 2.17.0 to 2.21.0 ([#8586](https://github.com/opensearch-project/OpenSearch/pull/8586)) +- Bump `com.google.jimfs:jimfs` from 1.2 to 1.3.0 ([#8585](https://github.com/opensearch-project/OpenSearch/pull/8585)) + +### Changed +- Replace jboss-annotations-api_1.2_spec with jakarta.annotation-api ([#7836](https://github.com/opensearch-project/OpenSearch/pull/7836)) +- Reduce memory copy in zstd compression ([#7681](https://github.com/opensearch-project/OpenSearch/pull/7681)) +- Add min, max, average and thread info to resource stats in tasks API ([#7673](https://github.com/opensearch-project/OpenSearch/pull/7673)) +- Add ZSTD compression for snapshotting ([#2996](https://github.com/opensearch-project/OpenSearch/pull/2996)) +- Change `com.amazonaws.sdk.ec2MetadataServiceEndpointOverride` to `aws.ec2MetadataServiceEndpoint` ([7372](https://github.com/opensearch-project/OpenSearch/pull/7372/)) +- Change `com.amazonaws.sdk.stsEndpointOverride` to `aws.stsEndpointOverride` ([7372](https://github.com/opensearch-project/OpenSearch/pull/7372/)) +- Add new query profile collector fields with concurrent search execution ([#7898](https://github.com/opensearch-project/OpenSearch/pull/7898)) +- Align range and default value for deletes_pct_allowed in merge policy ([#7730](https://github.com/opensearch-project/OpenSearch/pull/7730)) +- Rename QueryPhase actors like Suggest, Rescore to be processors rather than phase ([#8025](https://github.com/opensearch-project/OpenSearch/pull/8025)) +- Compress and cache cluster state during validate join request ([#7321](https://github.com/opensearch-project/OpenSearch/pull/7321)) +- [Snapshot Interop] Add Changes in Create Snapshot Flow for remote store interoperability. ([#7118](https://github.com/opensearch-project/OpenSearch/pull/7118)) +- Allow insecure string settings to warn-log usage and advise to migration of a newer secure variant ([#5496](https://github.com/opensearch-project/OpenSearch/pull/5496)) +- Add self-organizing hash table to improve the performance of bucket aggregations ([#7652](https://github.com/opensearch-project/OpenSearch/pull/7652)) +- Check UTF16 string size before converting to String to avoid OOME ([#7963](https://github.com/opensearch-project/OpenSearch/pull/7963)) +- Move ZSTD compression codecs out of the sandbox ([#7908](https://github.com/opensearch-project/OpenSearch/pull/7908)) +- Update ZSTD default compression level ([#8471](https://github.com/opensearch-project/OpenSearch/pull/8471)) +- [Search Pipelines] Pass pipeline creation context to processor factories ([#8164](https://github.com/opensearch-project/OpenSearch/pull/8164)) +- Enabling compression levels for zstd and zstd_no_dict ([#8312](https://github.com/opensearch-project/OpenSearch/pull/8312)) +- Optimize Metadata build() to skip redundant computations as part of ClusterState build ([#7853](https://github.com/opensearch-project/OpenSearch/pull/7853)) +- Add safeguard limits for file cache during node level allocation ([#8208](https://github.com/opensearch-project/OpenSearch/pull/8208)) +- Move span actions to Scope ([#8411](https://github.com/opensearch-project/OpenSearch/pull/8411)) +- Add wrapper tracer implementation ([#8565](https://github.com/opensearch-project/OpenSearch/pull/8565)) +- Improved performance of parsing floating point numbers ([#7909](https://github.com/opensearch-project/OpenSearch/pull/7909)) + +### Removed +- Remove `COMPRESSOR` variable from `CompressorFactory` and use `DEFLATE_COMPRESSOR` instead ([7907](https://github.com/opensearch-project/OpenSearch/pull/7907)) +- Remove concurrency based minimum file cache size restriction ([#8294](https://github.com/opensearch-project/OpenSearch/pull/8294)) + +### Fixed +- Fixing error: adding a new/forgotten parameter to the configuration for checking the config on startup in plugins/repository-s3 #7924 +- Enforce 512 byte document ID limit in bulk updates ([#8039](https://github.com/opensearch-project/OpenSearch/pull/8039)) +- With only GlobalAggregation in request causes unnecessary wrapping with MultiCollector ([#8125](https://github.com/opensearch-project/OpenSearch/pull/8125)) +- Fix mapping char_filter when mapping a hashtag ([#7591](https://github.com/opensearch-project/OpenSearch/pull/7591)) +- Fix NPE in multiterms aggregations involving empty buckets ([#7318](https://github.com/opensearch-project/OpenSearch/pull/7318)) +- Precise system clock time in MasterService debug logs ([#7902](https://github.com/opensearch-project/OpenSearch/pull/7902)) +- Improve indexing performance for flat_object type ([#7855](https://github.com/opensearch-project/OpenSearch/pull/7855)) +- Adds log4j configuration for telemetry LogSpanExporter ([#8393](https://github.com/opensearch-project/OpenSearch/pull/8393)) +- Fix painless casting bug, which crashes the OpenSearch process ([#8315](https://github.com/opensearch-project/OpenSearch/pull/8315)) +- Add missing validation/parsing of SearchBackpressureMode of SearchBackpressureSettings ([#7541](https://github.com/opensearch-project/OpenSearch/pull/7541)) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json b/rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json index f7c0d69805caf..bb066cd131480 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json @@ -26,23 +26,6 @@ "description":"Default index for items which don't provide one" } } - }, - { - "path":"/{index}/{type}/_bulk", - "methods":[ - "POST", - "PUT" - ], - "parts":{ - "index":{ - "type":"string", - "description":"Default index for items which don't provide one" - }, - "type":{ - "type":"string", - "description":"Default document type for items which don't provide one" - } - } } ] }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.allocation.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.allocation.json index 7b3dc70b03c38..717c1c49808f6 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.allocation.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.allocation.json @@ -55,7 +55,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.cluster_manager.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.cluster_manager.json index c1084825546bf..cd96038ad0693 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.cluster_manager.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.cluster_manager.json @@ -36,7 +36,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.indices.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.indices.json index a92189134f88f..2491ab309531d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.indices.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.indices.json @@ -55,7 +55,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.nodeattrs.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.nodeattrs.json index e688e23cab089..c8afa4cb17039 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.nodeattrs.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.nodeattrs.json @@ -26,7 +26,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.pending_tasks.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.pending_tasks.json index 36fa33be495cd..9c0edf8c53d90 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.pending_tasks.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.pending_tasks.json @@ -26,7 +26,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.plugins.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.plugins.json index d5346c6d9e7b4..0b5b39b01ee58 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.plugins.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.plugins.json @@ -26,7 +26,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.repositories.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.repositories.json index 84d9965907ff3..58960709a99bb 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.repositories.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.repositories.json @@ -27,7 +27,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.segment_replication.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.segment_replication.json new file mode 100644 index 0000000000000..a815cd5b1101b --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.segment_replication.json @@ -0,0 +1,103 @@ +{ + "cat.segment_replication":{ + "documentation":{ + "url":"https://github.com/opensearch-project/documentation-website/issues/2627", + "description":"Returns information about both on-going and latest completed Segment Replication events" + }, + "stability":"experimental", + "url":{ + "paths":[ + { + "path":"/_cat/segment_replication", + "methods":[ + "GET" + ] + }, + { + "path":"/_cat/segment_replication/{index}", + "methods":[ + "GET" + ], + "parts":{ + "index":{ + "type":"list", + "description":"Comma-separated list or wildcard expression of index names to limit the returned information" + } + } + } + ] + }, + "params":{ + "format":{ + "type":"string", + "description":"a short version of the Accept header, e.g. json, yaml" + }, + "active_only":{ + "type":"boolean", + "description":"If `true`, the response only includes ongoing segment replication events", + "default":false + }, + "bytes":{ + "type":"enum", + "description":"The unit in which to display byte values", + "options":[ + "b", + "k", + "kb", + "m", + "mb", + "g", + "gb", + "t", + "tb", + "p", + "pb" + ] + }, + "detailed":{ + "type":"boolean", + "description":"If `true`, the response includes detailed information about segment replications", + "default":false + }, + "shards":{ + "type":"list", + "description":"Comma-separated list of shards to display" + }, + "h":{ + "type":"list", + "description":"Comma-separated list of column names to display" + }, + "help":{ + "type":"boolean", + "description":"Return help information", + "default":false + }, + "index":{ + "type":"list", + "description":"Comma-separated list or wildcard expression of index names to limit the returned information" + }, + "s":{ + "type":"list", + "description":"Comma-separated list of column names or column aliases to sort by" + }, + "time":{ + "type":"enum", + "description":"The unit in which to display time values", + "options":[ + "d", + "h", + "m", + "s", + "ms", + "micros", + "nanos" + ] + }, + "v":{ + "type":"boolean", + "description":"Verbose mode. Display column headers", + "default":false + } + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.segments.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.segments.json index 472ef7fd22eee..5107353c7b14f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.segments.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.segments.json @@ -49,6 +49,18 @@ "pb" ] }, + "master_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" + }, "h":{ "type":"list", "description":"Comma-separated list of column names to display" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.shards.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.shards.json index a13c0f6bf6d4a..fab381a098e3f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.shards.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.shards.json @@ -55,7 +55,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.snapshots.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.snapshots.json index 757c2cfbe7dc6..1320207abfe75 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.snapshots.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.snapshots.json @@ -39,7 +39,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.templates.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.templates.json index 53fc872b5dae2..d45593b7bb2c8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.templates.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.templates.json @@ -38,7 +38,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.thread_pool.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.thread_pool.json index 710c297dbbe75..1165703490d1a 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cat.thread_pool.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cat.thread_pool.json @@ -54,7 +54,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "h":{ "type":"list", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_component_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_component_template.json index 9beea52c86b37..43e14ad0e2dd8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_component_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_component_template.json @@ -28,7 +28,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json new file mode 100644 index 0000000000000..13ea101169e60 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_decommission_awareness.json @@ -0,0 +1,19 @@ +{ + "cluster.delete_decommission_awareness": { + "documentation": { + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/decommission/", + "description": "Delete any existing decommission." + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path": "/_cluster/decommission/awareness/", + "methods": [ + "DELETE" + ] + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_weighted_routing.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_weighted_routing.json new file mode 100644 index 0000000000000..274c32f2a91be --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.delete_weighted_routing.json @@ -0,0 +1,19 @@ +{ + "cluster.delete_weighted_routing": { + "documentation": { + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/weighted-routing/delete", + "description": "Delete weighted shard routing weights" + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path": "/_cluster/routing/awareness/weights", + "methods": [ + "DELETE" + ] + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_component_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_component_template.json index ecf32f50c0a6c..aa4e395672ef3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_component_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_component_template.json @@ -30,7 +30,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "local":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json new file mode 100644 index 0000000000000..302dea4ec31a7 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_decommission_awareness.json @@ -0,0 +1,25 @@ +{ + "cluster.get_decommission_awareness": { + "documentation": { + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/decommission/", + "description": "Get details and status of decommissioned attribute" + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path":"/_cluster/decommission/awareness/{awareness_attribute_name}/_status", + "methods":[ + "GET" + ], + "parts":{ + "awareness_attribute_name":{ + "type":"string", + "description":"Awareness attribute name" + } + } + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_settings.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_settings.json index 6f91fbbedf5de..c60230dbc43b3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_settings.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_settings.json @@ -22,7 +22,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_weighted_routing.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_weighted_routing.json new file mode 100644 index 0000000000000..95e776d2ffb8b --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.get_weighted_routing.json @@ -0,0 +1,25 @@ +{ + "cluster.get_weighted_routing": { + "documentation": { + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/weighted-routing/get", + "description": "Fetches weighted shard routing weights" + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path": "/_cluster/routing/awareness/{attribute}/weights", + "methods": [ + "GET" + ], + "parts": { + "attribute": { + "type": "string", + "description": "Awareness attribute name" + } + } + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.health.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.health.json index 894b141f2f3b3..b96340d682546 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.health.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.health.json @@ -45,7 +45,8 @@ "options":[ "cluster", "indices", - "shards" + "shards", + "awareness_attributes" ], "default":"cluster", "description":"Specify the level of detail for returned information" @@ -56,7 +57,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", @@ -98,6 +107,14 @@ "red" ], "description":"Wait until cluster is in a specific state" + }, + "awareness_attribute":{ + "type":"string", + "description":"The awareness attribute for which the health is required" + }, + "ensure_node_weighed_in":{ + "type":"boolean", + "description": "Ensures whether the local node is commissioned and weighed in or not. (default: false)" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.pending_tasks.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.pending_tasks.json index d940adf9aef5d..22cfbac7ff447 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.pending_tasks.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.pending_tasks.json @@ -22,7 +22,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_component_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_component_template.json index abc83fb15f48a..05558bc7bfc50 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_component_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_component_template.json @@ -34,7 +34,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json new file mode 100644 index 0000000000000..bf4ffd454d9df --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_decommission_awareness.json @@ -0,0 +1,29 @@ +{ + "cluster.put_decommission_awareness": { + "documentation": { + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/decommission/", + "description": "Decommissions an awareness attribute" + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path": "/_cluster/decommission/awareness/{awareness_attribute_name}/{awareness_attribute_value}", + "methods": [ + "PUT" + ], + "parts": { + "awareness_attribute_name": { + "type": "string", + "description": "Awareness attribute name" + }, + "awareness_attribute_value": { + "type": "string", + "description": "Awareness attribute value" + } + } + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_settings.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_settings.json index f6b9a0863380e..1e36acc51544d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_settings.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_settings.json @@ -22,7 +22,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_weighted_routing.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_weighted_routing.json new file mode 100644 index 0000000000000..b8ce13ab33621 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.put_weighted_routing.json @@ -0,0 +1,25 @@ +{ + "cluster.put_weighted_routing": { + "documentation": { + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/weighted-routing/put", + "description": "Updates weighted shard routing weights" + }, + "stability": "experimental", + "url": { + "paths": [ + { + "path": "/_cluster/routing/awareness/{attribute}/weights", + "methods": [ + "PUT" + ], + "parts": { + "attribute": { + "type": "string", + "description": "Awareness attribute name" + } + } + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.reroute.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.reroute.json index bcf2704110664..285da40dd0245 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.reroute.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.reroute.json @@ -44,7 +44,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.state.json b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.state.json index c17e5b073e361..b43ab901785bd 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.state.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/cluster.state.json @@ -71,7 +71,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "flat_settings":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/create.json b/rest-api-spec/src/main/resources/rest-api-spec/api/create.json index 171f3da44d36d..767af84b82258 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/create.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/create.json @@ -23,32 +23,6 @@ "description":"The name of the index" } } - }, - { - "path":"/{index}/{type}/{id}/_create", - "methods":[ - "PUT", - "POST" - ], - "parts":{ - "id":{ - "type":"string", - "description":"Document ID" - }, - "index":{ - "type":"string", - "description":"The name of the index" - }, - "type":{ - "type":"string", - "description":"The type of the document", - "deprecated":true - } - }, - "deprecated":{ - "version":"7.0.0", - "description":"Specifying types in urls has been deprecated" - } } ] }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/create_pit.json b/rest-api-spec/src/main/resources/rest-api-spec/api/create_pit.json new file mode 100644 index 0000000000000..d3a2104c01bc0 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/create_pit.json @@ -0,0 +1,44 @@ + +{ + "create_pit":{ + "documentation":{ + "url":"https://opensearch.org/docs/latest/opensearch/rest-api/point_in_time/", + "description":"Creates point in time context." + }, + "stability":"stable", + "url":{ + "paths":[ + { + "path":"/{index}/_search/point_in_time", + "methods":[ + "POST" + ], + "parts":{ + "index":{ + "type":"list", + "description":"A comma-separated list of index names to search; use `_all` or empty string to perform the operation on all indices" + } + } + } + ] + }, + "params":{ + "allow_partial_pit_creation":{ + "type":"boolean", + "description":"Allow if point in time can be created with partial failures" + }, + "keep_alive":{ + "type":"string", + "description":"Specify the keep alive for point in time" + }, + "preference":{ + "type":"string", + "description":"Specify the node or shard the operation should be performed on (default: random)" + }, + "routing":{ + "type":"list", + "description":"A comma-separated list of specific routing values" + } + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/dangling_indices.delete_dangling_index.json b/rest-api-spec/src/main/resources/rest-api-spec/api/dangling_indices.delete_dangling_index.json index 1e3d74784591b..5d832fc794f4f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/dangling_indices.delete_dangling_index.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/dangling_indices.delete_dangling_index.json @@ -30,9 +30,17 @@ "type": "time", "description": "Explicit operation timeout" }, - "master_timeout": { - "type": "time", - "description": "Specify timeout for connection to master" + "master_timeout":{ + "type":"time", + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/dangling_indices.import_dangling_index.json b/rest-api-spec/src/main/resources/rest-api-spec/api/dangling_indices.import_dangling_index.json index e9dce01a76727..5b056e1fa145f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/dangling_indices.import_dangling_index.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/dangling_indices.import_dangling_index.json @@ -30,9 +30,17 @@ "type": "time", "description": "Explicit operation timeout" }, - "master_timeout": { - "type": "time", - "description": "Specify timeout for connection to master" + "master_timeout":{ + "type":"time", + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/delete.json b/rest-api-spec/src/main/resources/rest-api-spec/api/delete.json index 0d82bca9d4173..76dceb455627f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/delete.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/delete.json @@ -22,31 +22,6 @@ "description":"The name of the index" } } - }, - { - "path":"/{index}/{type}/{id}", - "methods":[ - "DELETE" - ], - "parts":{ - "id":{ - "type":"string", - "description":"The document ID" - }, - "index":{ - "type":"string", - "description":"The name of the index" - }, - "type":{ - "type":"string", - "description":"The type of the document", - "deprecated":true - } - }, - "deprecated":{ - "version":"7.0.0", - "description":"Specifying types in urls has been deprecated" - } } ] }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/delete_all_pits.json b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_all_pits.json new file mode 100644 index 0000000000000..5ff01aa746df9 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_all_pits.json @@ -0,0 +1,19 @@ +{ + "delete_all_pits":{ + "documentation":{ + "url":"https://opensearch.org/docs/latest/opensearch/rest-api/point_in_time/", + "description":"Deletes all active point in time searches." + }, + "stability":"stable", + "url":{ + "paths":[ + { + "path":"/_search/point_in_time/_all", + "methods":[ + "DELETE" + ] + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/delete_pit.json b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_pit.json new file mode 100644 index 0000000000000..b54d9f76204f4 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_pit.json @@ -0,0 +1,23 @@ +{ + "delete_pit":{ + "documentation":{ + "url":"https://opensearch.org/docs/latest/opensearch/rest-api/point_in_time/", + "description":"Deletes one or more point in time searches based on the IDs passed." + }, + "stability":"stable", + "url":{ + "paths":[ + { + "path":"/_search/point_in_time", + "methods":[ + "DELETE" + ] + } + ] + }, + "body":{ + "description":"A comma-separated list of pit IDs to clear", + "required":true + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/delete_script.json b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_script.json index b38b97ae57c2e..acaa389738606 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/delete_script.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/delete_script.json @@ -28,7 +28,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/exists_source.json b/rest-api-spec/src/main/resources/rest-api-spec/api/exists_source.json index 143ee406025ce..bdbf818fb5d81 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/exists_source.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/exists_source.json @@ -22,31 +22,6 @@ "description":"The name of the index" } } - }, - { - "path":"/{index}/{type}/{id}/_source", - "methods":[ - "HEAD" - ], - "parts":{ - "id":{ - "type":"string", - "description":"The document ID" - }, - "index":{ - "type":"string", - "description":"The name of the index" - }, - "type":{ - "type":"string", - "description":"The type of the document; deprecated and optional starting with 7.0", - "deprecated":true - } - }, - "deprecated":{ - "version":"7.0.0", - "description":"Specifying types in urls has been deprecated" - } } ] }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/explain.json b/rest-api-spec/src/main/resources/rest-api-spec/api/explain.json index c7c393a6a1cba..7f630f7666f30 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/explain.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/explain.json @@ -23,32 +23,6 @@ "description":"The name of the index" } } - }, - { - "path":"/{index}/{type}/{id}/_explain", - "methods":[ - "GET", - "POST" - ], - "parts":{ - "id":{ - "type":"string", - "description":"The document ID" - }, - "index":{ - "type":"string", - "description":"The name of the index" - }, - "type":{ - "type":"string", - "description":"The type of the document", - "deprecated":true - } - }, - "deprecated":{ - "version":"7.0.0", - "description":"Specifying types in urls has been deprecated" - } } ] }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/get_all_pits.json b/rest-api-spec/src/main/resources/rest-api-spec/api/get_all_pits.json new file mode 100644 index 0000000000000..544a8cb11b002 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/get_all_pits.json @@ -0,0 +1,19 @@ +{ + "get_all_pits":{ + "documentation":{ + "url":"https://opensearch.org/docs/latest/opensearch/rest-api/point_in_time/", + "description":"Lists all active point in time searches." + }, + "stability":"stable", + "url":{ + "paths":[ + { + "path":"/_search/point_in_time/_all", + "methods":[ + "GET" + ] + } + ] + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json b/rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json index 14307bea2ef0b..9cdac886b1b27 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/get_script.json @@ -24,7 +24,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.add_block.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.add_block.json index 7389fb1322824..af10b9f50091f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.add_block.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.add_block.json @@ -32,7 +32,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "ignore_unavailable":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.clear_cache.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.clear_cache.json index 64c10a520c7c4..0c7eca8c8e6f5 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.clear_cache.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.clear_cache.json @@ -67,6 +67,10 @@ "request":{ "type":"boolean", "description":"Clear request cache" + }, + "file":{ + "type":"boolean", + "description":"Clear filecache" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.clone.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.clone.json index d3a249583bd84..2d874f4933768 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.clone.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.clone.json @@ -31,13 +31,29 @@ "type" : "time", "description" : "Explicit operation timeout" }, - "master_timeout": { - "type" : "time", - "description" : "Specify timeout for connection to master" + "master_timeout":{ + "type":"time", + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "wait_for_active_shards": { "type" : "string", "description" : "Set the number of active shards to wait for on the cloned index before the operation returns." + }, + "wait_for_completion": { + "type" : "boolean", + "description" : "If false, the request will return a task immediately and the operation will run in background. Defaults to true." + }, + "task_execution_timeout": { + "type" : "time", + "description" : "Explicit task execution timeout, only useful when wait_for_completion is false, defaults to 1h." } }, "body": { diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.close.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.close.json index f26c8e77a06a6..1182b73541f93 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.close.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.close.json @@ -28,7 +28,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "ignore_unavailable":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json index 922183d628ac6..53ea4cbd80803 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json @@ -32,7 +32,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_alias.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_alias.json index 13abf70ca739b..049a397c6b3e2 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_alias.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_alias.json @@ -48,7 +48,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_index_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_index_template.json index d037b03dc5277..c74771ffe4b81 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_index_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_index_template.json @@ -28,7 +28,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_template.json index ca484a73e99f9..74dbb1822b64a 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.delete_template.json @@ -28,7 +28,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.exists_type.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.exists_type.json deleted file mode 100644 index c854d0e8fd841..0000000000000 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.exists_type.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "indices.exists_type":{ - "documentation":{ - "url":"https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-types-exists.html", - "description":"Returns information about whether a particular document type exists. (DEPRECATED)" - }, - "stability":"stable", - "url":{ - "paths":[ - { - "path":"/{index}/_mapping/{type}", - "methods":[ - "HEAD" - ], - "parts":{ - "index":{ - "type":"list", - "description":"A comma-separated list of index names; use `_all` to check the types across all indices" - }, - "type":{ - "type":"list", - "description":"A comma-separated list of document types to check" - } - } - } - ] - }, - "params":{ - "ignore_unavailable":{ - "type":"boolean", - "description":"Whether specified concrete indices should be ignored when unavailable (missing or closed)" - }, - "allow_no_indices":{ - "type":"boolean", - "description":"Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)" - }, - "expand_wildcards":{ - "type":"enum", - "options":[ - "open", - "closed", - "hidden", - "none", - "all" - ], - "default":"open", - "description":"Whether to expand wildcard expression to concrete indices that are open, closed or both." - }, - "local":{ - "type":"boolean", - "description":"Return local information, do not retrieve the state from master node (default: false)" - } - } - } -} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.forcemerge.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.forcemerge.json index 6036b75bb83e4..02fbcc36dfe64 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.forcemerge.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.forcemerge.json @@ -59,6 +59,10 @@ "only_expunge_deletes":{ "type":"boolean", "description":"Specify whether the operation should only expunge deleted documents" + }, + "wait_for_completion": { + "type" : "boolean", + "description" : "If false, the request will return a task immediately and the operation will run in background. Defaults to true." } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get.json index 90a1274ecb059..0a43f6481d86d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get.json @@ -57,7 +57,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_index_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_index_template.json index 7ea6dd2944c79..fbd03f99d2547 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_index_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_index_template.json @@ -34,7 +34,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "local":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json index 24fd668069697..321bfaba4f941 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json @@ -50,7 +50,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "local":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_settings.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_settings.json index 68e325446d3dc..1bdaea01f87bf 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_settings.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_settings.json @@ -58,7 +58,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "ignore_unavailable":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json index 337016763ad0a..52aeb17913db4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_template.json @@ -34,7 +34,15 @@ }, "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "local":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.open.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.open.json index 1dab468ce4ff4..f44fb04102a7f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.open.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.open.json @@ -28,7 +28,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "ignore_unavailable":{ "type":"boolean", @@ -53,6 +61,14 @@ "wait_for_active_shards":{ "type":"string", "description":"Sets the number of active shards to wait for before the operation returns." + }, + "wait_for_completion": { + "type" : "boolean", + "description" : "If false, the request will return a task immediately and the operation will run in background. Defaults to true." + }, + "task_execution_timeout": { + "type" : "time", + "description" : "Explicit task execution timeout, only useful when wait_for_completion is false, defaults to 1h." } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_alias.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_alias.json index 603f24b665eb7..00767afbaec04 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_alias.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_alias.json @@ -50,7 +50,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_index_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_index_template.json index 3f758e18737e2..a2ceb259a4376 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_index_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_index_template.json @@ -35,7 +35,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json index 451cbccd8d329..c8b63d4e1cee1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json @@ -29,7 +29,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "ignore_unavailable":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_settings.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_settings.json index 66fe23bab8ba2..ca245ec543da3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_settings.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_settings.json @@ -30,7 +30,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json index 75a328af929ef..3b1c230178bb8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_template.json @@ -34,7 +34,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.rollover.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.rollover.json index fef1f03d1c9a7..303b7c7b03c19 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.rollover.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.rollover.json @@ -48,7 +48,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "wait_for_active_shards":{ "type":"string", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.shrink.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.shrink.json index fd6d705d6a5fa..a20014a1444ec 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.shrink.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.shrink.json @@ -35,13 +35,29 @@ "type" : "time", "description" : "Explicit operation timeout" }, - "master_timeout": { - "type" : "time", - "description" : "Specify timeout for connection to master" + "master_timeout":{ + "type":"time", + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "wait_for_active_shards": { "type" : "string", "description" : "Set the number of active shards to wait for on the shrunken index before the operation returns." + }, + "wait_for_completion": { + "type" : "boolean", + "description" : "If false, the request will return a task immediately and the operation will run in background. Defaults to true." + }, + "task_execution_timeout": { + "type" : "time", + "description" : "Explicit task execution timeout, only useful when wait_for_completion is false, defaults to 1h." } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.simulate_index_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.simulate_index_template.json index 2b81572f0aaaf..0e42ba6028a9f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.simulate_index_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.simulate_index_template.json @@ -34,7 +34,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.simulate_template.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.simulate_template.json index 364547dd318a2..65b555082c3b1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.simulate_template.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.simulate_template.json @@ -40,7 +40,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.split.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.split.json index 02df3cdedf01f..d399bf9dbdb8a 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.split.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.split.json @@ -35,13 +35,29 @@ "type" : "time", "description" : "Explicit operation timeout" }, - "master_timeout": { - "type" : "time", - "description" : "Specify timeout for connection to master" + "master_timeout":{ + "type":"time", + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "wait_for_active_shards": { "type" : "string", "description" : "Set the number of active shards to wait for on the shrunken index before the operation returns." + }, + "wait_for_completion": { + "type" : "boolean", + "description" : "If false, the request will return a task immediately and the operation will run in background. Defaults to true." + }, + "task_execution_timeout": { + "type" : "time", + "description" : "Explicit task execution timeout, only useful when wait_for_completion is false, defaults to 1h." } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.stats.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.stats.json index 0a8960f2f9e89..382bb9efde0ff 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.stats.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.stats.json @@ -118,10 +118,6 @@ ], "default":"indices" }, - "types":{ - "type":"list", - "description":"A comma-separated list of document types for the `indexing` index metric" - }, "include_segment_file_sizes":{ "type":"boolean", "description":"Whether to report the aggregated disk usage of each one of the Lucene index files (only applies if segment stats are requested)", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.update_aliases.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.update_aliases.json index d4a222f2061c8..c31cb8fe59c0f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.update_aliases.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.update_aliases.json @@ -22,7 +22,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To promote inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.validate_query.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.validate_query.json index 3becec003a9e6..cc0386ee3b972 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.validate_query.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.validate_query.json @@ -26,28 +26,6 @@ "description":"A comma-separated list of index names to restrict the operation; use `_all` or empty string to perform the operation on all indices" } } - }, - { - "path":"/{index}/{type}/_validate/query", - "methods":[ - "GET", - "POST" - ], - "parts":{ - "index":{ - "type":"list", - "description":"A comma-separated list of index names to restrict the operation; use `_all` or empty string to perform the operation on all indices" - }, - "type":{ - "type":"list", - "description":"A comma-separated list of document types to restrict the operation; leave empty to perform the operation on all types", - "deprecated":true - } - }, - "deprecated":{ - "version":"7.0.0", - "description":"Specifying types in urls has been deprecated" - } } ] }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.delete_pipeline.json b/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.delete_pipeline.json index 29b4219038cd2..3e40136f556fa 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.delete_pipeline.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.delete_pipeline.json @@ -24,7 +24,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.get_pipeline.json b/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.get_pipeline.json index 65fc4f91b2b42..cde980e67c8c9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.get_pipeline.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.get_pipeline.json @@ -30,7 +30,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.put_pipeline.json b/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.put_pipeline.json index 4d2105866791c..5475905e7b99f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.put_pipeline.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/ingest.put_pipeline.json @@ -24,7 +24,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/put_script.json b/rest-api-spec/src/main/resources/rest-api-spec/api/put_script.json index 750f7fdf4eb62..c8413d1476402 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/put_script.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/put_script.json @@ -46,7 +46,15 @@ }, "master_timeout":{ "type":"time", - "description":"Specify timeout for connection to master" + "description":"Specify timeout for connection to master", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Specify timeout for connection to cluster-manager node" }, "context":{ "type":"string", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/remote_store.restore.json b/rest-api-spec/src/main/resources/rest-api-spec/api/remote_store.restore.json new file mode 100644 index 0000000000000..6af49f75b9f6e --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/remote_store.restore.json @@ -0,0 +1,34 @@ +{ + "remote_store.restore":{ + "documentation":{ + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/remote-store#restore", + "description":"Restores from remote store." + }, + "stability":"experimental", + "url":{ + "paths":[ + { + "path":"/_remotestore/_restore", + "methods":[ + "POST" + ] + } + ] + }, + "params":{ + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" + }, + "wait_for_completion":{ + "type":"boolean", + "description":"Should this request wait until the operation has completed before returning", + "default":false + } + }, + "body":{ + "description":"A comma separated list of index IDs", + "required":true + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/remote_store.stats.json b/rest-api-spec/src/main/resources/rest-api-spec/api/remote_store.stats.json new file mode 100644 index 0000000000000..437a4439bbcb5 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/remote_store.stats.json @@ -0,0 +1,48 @@ +{ + "remote_store.stats":{ + "documentation":{ + "url": "https://opensearch.org/docs/latest/tuning-your-cluster/availability-and-recovery/remote", + "description":"Stats for remote store." + }, + "stability":"experimental", + "url":{ + "paths":[ + { + "path":"/_remotestore/stats/{index}", + "methods":[ + "GET" + ], + "parts":{ + "index":{ + "type":"string", + "description": "Index name to fetch stats" + } + } + }, + { + "path":"/_remotestore/stats/{index}/{shard_id}", + "methods":[ + "GET" + ], + "parts":{ + "index":{ + "type":"string", + "description":"Index name to fetch stats" + }, + "shard_id":{ + "type":"string", + "description":"Specific shard to fetch stats" + } + } + } + ] + }, + "params":{ + "timeout":{ + "type":"time", + "default":"10s", + "description":"Max time each individual stats request should take." + } + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json index ac321acf8907b..e0fbeeb83ffc4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json @@ -225,6 +225,10 @@ "type":"boolean", "description":"Indicates whether hits.total should be rendered as an integer or an object in the rest search response", "default":false + }, + "search_pipeline": { + "type": "string", + "description": "The search pipeline to use to execute this request" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.delete.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.delete.json new file mode 100644 index 0000000000000..1fa7060b974dc --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.delete.json @@ -0,0 +1,35 @@ +{ + "search_pipeline.delete": { + "documentation": { + "description": "Deletes a search pipeline.", + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/search_pipelines/" + }, + "stability": "stable", + "url": { + "paths": [ + { + "path": "/_search/pipeline/{id}", + "methods": [ + "DELETE" + ], + "parts": { + "id": { + "type": "string", + "description": "Pipeline ID" + } + } + } + ] + }, + "params": { + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" + }, + "timeout":{ + "type":"time", + "description":"Explicit operation timeout" + } + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.get.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.get.json new file mode 100644 index 0000000000000..7cac6e7aa4bcf --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.get.json @@ -0,0 +1,37 @@ +{ + "search_pipeline.get": { + "documentation": { + "description": "Returns a search pipeline", + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/search_pipelines/" + }, + "stability": "stable", + "url": { + "paths": [ + { + "path": "/_search/pipeline", + "methods": [ + "GET" + ] + }, + { + "path": "/_search/pipeline/{id}", + "methods": [ + "GET" + ], + "parts": { + "id": { + "type": "string", + "description": "Comma-separated list of search pipeline ids. Wildcards supported." + } + } + } + ] + }, + "params": { + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" + } + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.put.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.put.json new file mode 100644 index 0000000000000..b7375d36825a2 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search_pipeline.put.json @@ -0,0 +1,39 @@ +{ + "search_pipeline.put": { + "documentation": { + "description": "Creates or updates a search pipeline.", + "url": "https://opensearch.org/docs/latest/opensearch/rest-api/search_pipelines/" + }, + "stability": "stable", + "url": { + "paths": [ + { + "path": "/_search/pipeline/{id}", + "methods": [ + "PUT" + ], + "parts": { + "id": { + "type": "string", + "description": "Pipeline ID" + } + } + } + ] + }, + "params": { + "cluster_manager_timeout": { + "type": "time", + "description": "Explicit operation timeout for connection to cluster-manager node" + }, + "timeout": { + "type": "time", + "description": "Explicit operation timeout" + } + }, + "body": { + "description": "The search pipeline definition", + "required": true + } + } +} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.cleanup_repository.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.cleanup_repository.json index 727fe79176797..05eb3309b11e6 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.cleanup_repository.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.cleanup_repository.json @@ -24,7 +24,15 @@ "params": { "master_timeout": { "type" : "time", - "description" : "Explicit operation timeout for connection to master node" + "description" : "Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout": { "type" : "time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.clone.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.clone.json index 18122bc209b0e..c79460fc30a48 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.clone.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.clone.json @@ -32,7 +32,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.create.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.create.json index da8cb9916f584..64aaeaef9d897 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.create.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.create.json @@ -29,7 +29,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "wait_for_completion":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.create_repository.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.create_repository.json index 431ac3c68c0bd..4965162bcd86c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.create_repository.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.create_repository.json @@ -25,7 +25,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.delete.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.delete.json index 30053cd5b94d3..2e21a08219942 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.delete.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.delete.json @@ -28,7 +28,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.delete_repository.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.delete_repository.json index b60aeba83a329..3fc22f969784c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.delete_repository.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.delete_repository.json @@ -24,7 +24,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json index 20006f6f499b6..e084a997a61b1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get.json @@ -28,7 +28,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "ignore_unavailable":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get_repository.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get_repository.json index 8c91caa4fe81f..cf03bab18c03f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get_repository.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.get_repository.json @@ -30,7 +30,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "local":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.restore.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.restore.json index 697ea395dcc2b..07148c7d261f4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.restore.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.restore.json @@ -28,12 +28,24 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "wait_for_completion":{ "type":"boolean", "description":"Should this request wait until the operation has completed before returning", "default":false + }, + "source_remote_store_repository": { + "type":"string", + "description":"Remote Store Repository of Remote Store Indices" } }, "body":{ diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.status.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.status.json index 70a7ba23ef506..4f22c24fd9a56 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.status.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.status.json @@ -46,7 +46,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "ignore_unavailable":{ "type":"boolean", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.verify_repository.json b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.verify_repository.json index de638c19d4a0b..865eb15d11310 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.verify_repository.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/snapshot.verify_repository.json @@ -24,7 +24,15 @@ "params":{ "master_timeout":{ "type":"time", - "description":"Explicit operation timeout for connection to master node" + "description":"Explicit operation timeout for connection to master node", + "deprecated":{ + "version":"2.0.0", + "description":"To support inclusive language, use 'cluster_manager_timeout' instead." + } + }, + "cluster_manager_timeout":{ + "type":"time", + "description":"Explicit operation timeout for connection to cluster-manager node" }, "timeout":{ "type":"time", diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/update.json b/rest-api-spec/src/main/resources/rest-api-spec/api/update.json index 81bc101600aeb..c8d1ed435756b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/update.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/update.json @@ -22,31 +22,6 @@ "description":"The name of the index" } } - }, - { - "path":"/{index}/{type}/{id}/_update", - "methods":[ - "POST" - ], - "parts":{ - "id":{ - "type":"string", - "description":"Document ID" - }, - "index":{ - "type":"string", - "description":"The name of the index" - }, - "type":{ - "type":"string", - "description":"The type of the document", - "deprecated":true - } - }, - "deprecated":{ - "version":"7.0.0", - "description":"Specifying types in urls has been deprecated" - } } ] }, diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/10_basic.yml index 8c8c6d50abf41..c91ec511a0fdb 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/10_basic.yml @@ -223,4 +223,3 @@ - match: { items.0.index.status: 400 } - match: { items.0.index.error.type: illegal_argument_exception } - match: { items.0.index.error.reason: "no write index is defined for alias [test_index]. The write index may be explicitly disabled using is_write_index=false or the alias points to multiple indices without one being designated as a write index" } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/20_list_of_strings.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/20_list_of_strings.yml index 3d956dce54289..cb3553abbd435 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/20_list_of_strings.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/20_list_of_strings.yml @@ -14,4 +14,3 @@ index: test_index - match: {count: 2} - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/30_big_string.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/30_big_string.yml index 8b6467eeed975..fabe674697cde 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/30_big_string.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/30_big_string.yml @@ -14,4 +14,3 @@ index: test_index - match: {count: 2} - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/40_source.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/40_source.yml index e29e84740ee5c..fb9554619a818 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/40_source.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/40_source.yml @@ -68,4 +68,3 @@ - match: { items.0.update.get._source.foo: garply } - is_false: items.0.update.get._source.bar - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/60_deprecated.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/60_deprecated.yml deleted file mode 100644 index 8c8a840eb3f47..0000000000000 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/bulk/60_deprecated.yml +++ /dev/null @@ -1,20 +0,0 @@ - ---- -"Deprecated parameters should fail in Bulk query": - - do: - catch: bad_request - bulk: - body: | - { "update": { "_index": "test_index", "_type": "test_type", "_id": "test_id_1", "_version": 1 } } - { "doc": { "f1": "v1" } } - { "update": { "_index": "test_index", "_type": "test_type", "_id": "test_id_2", "_version": 1 } } - { "doc": { "f1": "v2" } } - - - do: - catch: bad_request - bulk: - body: | - { "update": { "_index": "test_index", "_type": "test_type", "_id": "test_id_1", "_routing": "test1" } } - { "doc": { "f1": "v1" } } - { "update": { "_index": "test_index", "_type": "test_type", "_id": "test_id_2", "_routing": "test1" } } - { "doc": { "f1": "v2" } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.cluster_manager/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.cluster_manager/10_basic.yml index b0f1c81b56a0e..6354c751aa5f3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.cluster_manager/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.cluster_manager/10_basic.yml @@ -20,7 +20,7 @@ setup: host .+ \n ip .+ \n node .+ \n - + $/ --- diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/10_basic.yml index 0d945d01a6ddb..52b27837a61ac 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.health/10_basic.yml @@ -9,9 +9,9 @@ help: true node_selector: # Only send request to nodes in <2.0 versions, especially during ':qa:mixed-cluster:v1.x.x#mixedClusterTest'. - # Because YAML REST test takes the minimum OpenSearch version in the cluster to apply the filter in 'skip' section, + # Because YAML REST test takes the minimum OpenSearch version in the cluster to apply the filter in 'skip' section, # see OpenSearchClientYamlSuiteTestCase#initAndResetContext() for detail. - # During 'mixedClusterTest', the cluster can be mixed with nodes in 1.x and 2.x versions, + # During 'mixedClusterTest', the cluster can be mixed with nodes in 1.x and 2.x versions, # so node_selector is required, and only filtering version in 'skip' is not enough. version: "1.0.0 - 1.4.99" @@ -61,7 +61,7 @@ pending_tasks .+ \n max_task_wait_time .+ \n active_shards_percent .+ \n - + $/ --- diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/10_basic.yml index 789ea5fc19c3f..525d705de88f1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.nodes/10_basic.yml @@ -10,9 +10,9 @@ v: true node_selector: # Only send request to nodes in <2.0 versions, especially during ':qa:mixed-cluster:v1.x.x#mixedClusterTest'. - # Because YAML REST test takes the minimum OpenSearch version in the cluster to apply the filter in 'skip' section, + # Because YAML REST test takes the minimum OpenSearch version in the cluster to apply the filter in 'skip' section, # see OpenSearchClientYamlSuiteTestCase#initAndResetContext() for detail. - # During 'mixedClusterTest', the cluster can be mixed with nodes in 1.x and 2.x versions, + # During 'mixedClusterTest', the cluster can be mixed with nodes in 1.x and 2.x versions, # so node_selector is required, and only filtering version in 'skip' is not enough. version: "1.0.0 - 1.4.99" @@ -32,8 +32,8 @@ - match: $body: | - / #ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role cluster_manager name - ^ ((\d{1,3}\.){3}\d{1,3} \s+ \d+ \s+ \d* \s+ (-)?\d* \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)?\s+ ((-)?\d*(\.\d+)?)? \s+ (-|[cdhilmrstvw]{1,11}) \s+ [-*x] \s+ (\S+\s?)+ \n)+ $/ + / #ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role node.roles cluster_manager name + ^ ((\d{1,3}\.){3}\d{1,3} \s+ \d+ \s+ \d* \s+ (-)?\d* \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)?\s+ ((-)?\d*(\.\d+)?)? \s+ (-|[cdhilmrstvw]{1,11}) (\s+ (-|\w+(,\w+)*+))? \s+ [-*x] \s+ (\S+\s?)+ \n)+ $/ - do: cat.nodes: @@ -41,8 +41,8 @@ - match: $body: | - /^ ip \s+ heap\.percent \s+ ram\.percent \s+ cpu \s+ load_1m \s+ load_5m \s+ load_15m \s+ node\.role \s+ cluster_manager \s+ name \n - ((\d{1,3}\.){3}\d{1,3} \s+ \d+ \s+ \d* \s+ (-)?\d* \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)? \s+ (-|[cdhilmrstvw]{1,11}) \s+ [-*x] \s+ (\S+\s?)+ \n)+ $/ + /^ ip \s+ heap\.percent \s+ ram\.percent \s+ cpu \s+ load_1m \s+ load_5m \s+ load_15m \s+ node\.role (\s+ node\.roles)? \s+ cluster_manager \s+ name \n + ((\d{1,3}\.){3}\d{1,3} \s+ \d+ \s+ \d* \s+ (-)?\d* \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)? \s+ ((-)?\d*(\.\d+)?)? \s+ (-|[cdhilmrstvw]{1,11}) (\s+ (-|\w+(,\w+)*+ ))? \s+ [-*x] \s+ (\S+\s?)+ \n)+ $/ - do: cat.nodes: @@ -131,4 +131,3 @@ - match: $body: | /^(\S{5,}\n)+$/ - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml index aa4abc7a11eae..f80c9f9c0bc80 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml @@ -1,11 +1,13 @@ ---- "Help": - skip: - version: " - 7.99.99" - reason: shard path stats were added in 8.0.0 + version: " - 2.3.99" + reason: point in time stats were added in 2.4.0 + features: node_selector - do: cat.shards: help: true + node_selector: + version: "2.4.0 - " - match: $body: | @@ -67,6 +69,9 @@ search.scroll_current .+ \n search.scroll_time .+ \n search.scroll_total .+ \n + search.point_in_time_current .+ \n + search.point_in_time_time .+ \n + search.point_in_time_total .+ \n segments.count .+ \n segments.memory .+ \n segments.index_writer_memory .+ \n @@ -82,6 +87,92 @@ path.state .+ \n $/ --- +"Help before - 2.4.0": + - skip: + version: "2.4.0 - " + reason: point in time stats were added in 2.4.0 + features: node_selector + - do: + cat.shards: + help: true + node_selector: + version: " - 2.3.99" + + - match: + $body: | + /^ index .+ \n + shard .+ \n + prirep .+ \n + state .+ \n + docs .+ \n + store .+ \n + ip .+ \n + id .+ \n + node .+ \n + sync_id .+ \n + unassigned.reason .+ \n + unassigned.at .+ \n + unassigned.for .+ \n + unassigned.details .+ \n + recoverysource.type .+ \n + completion.size .+ \n + fielddata.memory_size .+ \n + fielddata.evictions .+ \n + query_cache.memory_size .+ \n + query_cache.evictions .+ \n + flush.total .+ \n + flush.total_time .+ \n + get.current .+ \n + get.time .+ \n + get.total .+ \n + get.exists_time .+ \n + get.exists_total .+ \n + get.missing_time .+ \n + get.missing_total .+ \n + indexing.delete_current .+ \n + indexing.delete_time .+ \n + indexing.delete_total .+ \n + indexing.index_current .+ \n + indexing.index_time .+ \n + indexing.index_total .+ \n + indexing.index_failed .+ \n + merges.current .+ \n + merges.current_docs .+ \n + merges.current_size .+ \n + merges.total .+ \n + merges.total_docs .+ \n + merges.total_size .+ \n + merges.total_time .+ \n + refresh.total .+ \n + refresh.time .+ \n + refresh.external_total .+ \n + refresh.external_time .+ \n + refresh.listeners .+ \n + search.fetch_current .+ \n + search.fetch_time .+ \n + search.fetch_total .+ \n + search.open_contexts .+ \n + search.query_current .+ \n + search.query_time .+ \n + search.query_total .+ \n + search.scroll_current .+ \n + search.scroll_time .+ \n + search.scroll_total .+ \n + segments.count .+ \n + segments.memory .+ \n + segments.index_writer_memory .+ \n + segments.version_map_memory .+ \n + segments.fixed_bitset_memory .+ \n + seq_no.max .+ \n + seq_no.local_checkpoint .+ \n + seq_no.global_checkpoint .+ \n + warmer.current .+ \n + warmer.total .+ \n + warmer.total_time .+ \n + path.data .+ \n + path.state .+ \n + $/ +--- "Test cat shards output": - do: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/10_basic.yml index 1ce8468cb51f9..00ec838489f63 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cat.thread_pool/10_basic.yml @@ -1,3 +1,29 @@ +"Test cat thread_pool total_wait_time output": + - skip: + version: " - 2.10.99" + reason: thread_pool total_wait_time stats were introduced in V_2.11.0 + + - do: + cat.thread_pool: {} + + - match: + $body: | + / #node_name name active queue rejected + ^ (\S+ \s+ \S+ \s+ \d+ \s+ \d+ \s+ \d+ \n)+ $/ + + - do: + cat.thread_pool: + thread_pool_patterns: search,search_throttled,index_searcher,generic + h: name,total_wait_time,twt + v: true + + - match: + $body: | + /^ name \s+ total_wait_time \s+ twt \n + (generic \s+ -1 \s+ -1 \n + search \s+ \d*\.*\d*\D+ \s+ \d*\.*\d*\D+ \n + search_throttled \s+ \d*\.*\d*\D+ \s+ \d*\.*\d*\D+ \n)+ $/ + --- "Test cat thread_pool output": - skip: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/10_basic.yml index f8fd8ebef170d..885f6c4a97912 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.pending_tasks/10_basic.yml @@ -11,4 +11,3 @@ local: true - is_true: tasks - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/10_basic.yml index 825bac9f91649..60081783bb346 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.put_settings/10_basic.yml @@ -69,3 +69,36 @@ include_defaults: true - match: {defaults.node.attr.testattr: "test"} + +--- +"Test set search backpressure mode": + + - skip: + version: "- 2.3.99" + reason: "Search backpressure was added in 2.4" + + - do: + cluster.put_settings: + body: + persistent: + search_backpressure.mode: "monitor_only" + + - match: {persistent: {search_backpressure: {mode: "monitor_only"}}} + +--- +"Test set invalid search backpressure mode": + + - skip: + version: "- 2.7.99" + reason: "Parsing and validation of SearchBackpressureMode does not exist in versions < 2.8" + + - do: + catch: bad_request + cluster.put_settings: + body: + persistent: + search_backpressure.mode: "monitor-only" + + - match: {error.root_cause.0.type: "illegal_argument_exception"} + - match: { error.root_cause.0.reason: "Invalid SearchBackpressureMode: monitor-only" } + - match: { status: 400 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.remote_info/10_info.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.remote_info/10_info.yml index e11eff2b78a3c..75058cefd5c53 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.remote_info/10_info.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.remote_info/10_info.yml @@ -3,4 +3,3 @@ - do: cluster.remote_info: {} - is_true: '' - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/10_basic.yml index fa973454cfba5..294f00bdd822b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/10_basic.yml @@ -17,18 +17,18 @@ - is_true: cluster_uuid - is_true: master_node - + --- "Get cluster state returns cluster_manager_node": - skip: version: " - 1.4.99" reason: "The metric cluster_manager_node is added to cluster state in version 2.0.0" - + - do: cluster.state: {} - + - set: cluster_manager_node: node_id - + - match: {master_node: $node_id} - match: {cluster_manager_node: $node_id} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/20_filtering.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/20_filtering.yml index 3d20f1d0f7e52..b17201a911290 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/20_filtering.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.state/20_filtering.yml @@ -169,7 +169,7 @@ setup: cluster.state: metric: [ master_node, version ] allowed_warnings: - - 'Deprecated value [master_node] used for parameter [metric]. To promote inclusive language, please use [cluster_manager_node] instead. It will be unsupported in a future major version.' + - 'Assigning [master_node] to parameter [metric] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_node] instead.' - match: { cluster_uuid: $cluster_uuid } - is_true: master_node diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.voting_config_exclusions/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.voting_config_exclusions/10_basic.yml index 5474c9bdf4da0..23eebacabf3f3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.voting_config_exclusions/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.voting_config_exclusions/10_basic.yml @@ -104,4 +104,3 @@ teardown: cluster.post_voting_config_exclusions: node_ids: nodeId node_names: nodeName - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/create/40_routing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/create/40_routing.yml index 9c048c361bd5c..e1341ac2b5380 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/create/40_routing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/create/40_routing.yml @@ -39,4 +39,3 @@ get: index: test_1 id: 1 - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/30_routing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/30_routing.yml index 27e9350caed70..c3c407cd9173a 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/delete/30_routing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/delete/30_routing.yml @@ -30,4 +30,3 @@ index: test_1 id: 1 routing: 5 - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/15_default_values.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/15_default_values.yml index 921397b238f51..fabf8fb87a7b6 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/get/15_default_values.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/get/15_default_values.yml @@ -14,4 +14,3 @@ - match: { _index: test_1 } - match: { _id: '1' } - match: { _source: { foo: "bar" } } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/20_stored_fields.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/20_stored_fields.yml index 23c7e5cbc90a6..1bafdc3dab21f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/get/20_stored_fields.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/get/20_stored_fields.yml @@ -48,5 +48,3 @@ - match: { fields.foo: [bar] } - match: { fields.count: [1] } - match: { _source.foo: bar } - - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/40_routing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/40_routing.yml index 9ba546d6ef942..7f45b39add8a7 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/get/40_routing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/get/40_routing.yml @@ -41,4 +41,3 @@ get: index: test_1 id: 1 - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/get/90_versions.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/get/90_versions.yml index 9037a9113e937..3f45a1da09dce 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/get/90_versions.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/get/90_versions.yml @@ -80,4 +80,3 @@ id: 1 version: 1 version_type: external_gte - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/100_partial_flat_object.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/100_partial_flat_object.yml new file mode 100644 index 0000000000000..91e4127da9c32 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/index/100_partial_flat_object.yml @@ -0,0 +1,606 @@ +--- +# The test setup includes: +# - Create flat_object mapping for test_partial_flat_object index +# - Index two example documents +# - Refresh the index so it is ready for search tests + +setup: + - do: + indices.create: + index: test_partial_flat_object + body: + mappings: + properties: + issue: + properties: + number: + type: "integer" + labels: + type: "flat_object" + - do: + index: + index: test_partial_flat_object + id: 1 + body: { + "issue": { + "number": 123, + "labels": { + "version": "2.2", + "backport": [ + "2.0", + "1.9" + ], + "category": { + "type": "API", + "level": "bug" + }, + "createdDate": "2023-01-01", + "comment": [ [ "Doe","Shipped" ],[ "John","Approved" ] ], + "views": 288, + "priority": 5.00 + } + } + } + + - do: + index: + index: test_partial_flat_object + id: 2 + body: { + "issue": { + "number": 456, + "labels": { + "author": "Liu", + "version": "2.1", + "backport": [ + "2.0", + "1.3" + ], + "category": { + "type": "API", + "level": "enhancement" + }, + "createdDate": "2023-02-01", + "comment": [ [ "Mike","LGTM" ],[ "John","Approved" ] ], + "views": 3333, + "priority": 1.50 + } + } + } + + - do: + index: + index: test_partial_flat_object + id: 3 + body: { + "issue": { + "number": 999, + "labels": [ { + "version": "1.1", + "backport": [ + "1.0", + "0.9" + ], + "category": { + "type": "Module", + "level": "feature" + } + } ] + } + } + + - do: + indices.refresh: + index: test_partial_flat_object +--- +# Delete Index when connection is teardown +teardown: + - do: + indices.delete: + index: test_partial_flat_object + + +--- +# Verify that mappings under the catalog field did not expand +# and no dynamic fields were created. +"Mappings": + - skip: + version: " - 2.99.99" + reason: "flat_object is introduced in 3.0.0 in main branch" + + - do: + indices.get_mapping: + index: test_partial_flat_object + - is_true: test_partial_flat_object.mappings + - match: { test_partial_flat_object.mappings.properties.issue.properties.number.type: integer } + - match: { test_partial_flat_object.mappings.properties.issue.properties.labels.type: flat_object } + # https://github.com/opensearch-project/OpenSearch/tree/main/rest-api-spec/src/main/resources/rest-api-spec/test#length + - length: { test_partial_flat_object.mappings.properties.issue.properties: 2 } + - length: { test_partial_flat_object.mappings.properties.issue.properties.labels: 1 } + + +--- +"Supported queries": + - skip: + version: " - 2.99.99" + reason: "flat_object is introduced in 3.0.0 in main branch" + + + # Verify Document Count + - do: + search: + body: { + query: { + match_all: { } + } + } + + - length: { hits.hits: 3 } + + # Match Query with exact dot path. + - do: + search: + body: { + _source: true, + query: { + match: { "issue.labels.version": "2.1" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.version: "2.1" } + + # Match Query without exact dot path. + - do: + search: + body: { + _source: true, + query: { + match: { issue.labels: "2.1" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.version: "2.1" } + + # Multi Match Query with exact dot path. + - do: + search: + body: { + _source: true, + query: { + multi_match: { + "query": "2.0", + "fields": [ "issue.labels.version", "issue.labels.backport" ] + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.backport: [ "2.0", "1.9" ] } + - match: { hits.hits.1._source.issue.labels.backport: [ "2.0", "1.3" ] } + + # Term Query1 with exact dot path for date + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels.createdDate: "2023-01-01" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.createdDate: "2023-01-01" } + + # Term Query1 without exact dot path for date + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels: "2023-01-01" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.createdDate: "2023-01-01" } + + + # Term Query2 with dot path for string + - do: + search: + body: { + _source: true, + query: { + term: { "issue.labels.category.type": "API" } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.category.type: "API" } + - match: { hits.hits.1._source.issue.labels.category.type: "API" } + + # Term Query2 without exact dot path. + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels: "API" } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.category.type: "API" } + - match: { hits.hits.1._source.issue.labels.category.type: "API" } + + # Term Query3 with dot path for array + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels.backport: "1.9" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.backport: [ "2.0", "1.9" ] } + + # Term Query3 without dot path for array + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels: "1.9" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.backport: [ "2.0", "1.9" ] } + + # Term Query4 with dot path for nested-array + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels.comment: "LGTM" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Term Query4 without dot path for nested-array + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels: "LGTM" } + } + } + + # Term Query5 with dot path for array + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels.category.type: "Module" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.0.category.type: "Module" } + + # Term Query5 without dot path for array + - do: + search: + body: { + _source: true, + query: { + term: { issue.labels: "Module" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.0.category.type: "Module" } + + # Terms Query without dot path. + - do: + search: + body: { + _source: true, + query: { + terms: { issue.labels: [ "John","Mike" ] } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + - match: { hits.hits.1._source.issue.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Terms Query with dot path. + - do: + search: + body: { + _source: true, + query: { + terms: { issue.labels.comment: [ "John","Mike" ] } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + - match: { hits.hits.1._source.issue.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Prefix Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "prefix": { + "issue.labels.comment": { + "value": "Mi" + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Prefix Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "prefix": { + "issue.labels": { + "value": "Mi" + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Range Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "issue.labels.version": { + "gte": "2.1", + "lte": "3.0" + } + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.version: "2.2" } + - match: { hits.hits.1._source.issue.labels.version: "2.1" } + + # Range Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "issue.labels": { + "gte": "2.1", + "lte": "3.0" + } + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.version: "2.2" } + - match: { hits.hits.1._source.issue.labels.version: "2.1" } + + # Range Query with integer input with dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "issue.labels.views": { + "gte": 3000, + "lte": 4000 + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.views: 3333 } + + # Range Query with integer input without dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "issue.labels": { + "gte": 3000, + "lte": 4000 + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.views: 3333 } + + + # Range Query with double input with dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "issue.labels.priority": { + "gte": 4.1234, + "lte": 5.1234 + } + } + } + } + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.priority: 5.00 } + + # Range Query with double input without dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "issue.labels": { + "gte": 4.1234, + "lte": 5.1234 + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.priority: 5.00 } + + + # Exists Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "exists": { + "field": issue.labels.priority + } + } + } + + - length: { hits.hits: 2 } + + # Exists Query with nested dot path, use the flat_object_field_name.last_key + - do: + search: + body: { + _source: true, + query: { + "exists": { + "field": issue.labels.type + } + } + } + + - length: { hits.hits: 3 } + + # Exists Query without dot path for the flat_object_field_name + - do: + search: + body: { + _source: true, + query: { + "exists": { + "field": issue.labels + } + } + } + + - length: { hits.hits: 3 } + + # Exists Query2 with dot path for one hit + - do: + search: + body: { + _source: true, + query: { + "exists": { + "field": issue.labels.author + } + } + } + + - length: { hits.hits: 1 } + + # Query_string Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "query_string": { + "fields": [ "issue.labels" ], + "query": "Doe OR Mike" + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + - match: { hits.hits.1._source.issue.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Query_string Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "query_string": { + "fields": [ "issue.labels.comment" ], + "query": "Doe OR Mike" + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + - match: { hits.hits.1._source.issue.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Simple_query_string Query without full dot path. + - do: + search: + body: { + _source: true, + query: { + "simple_query_string": { + "query": "Doe", + "fields": [ "issue.labels" ] + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + + # Simple_query_string Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "simple_query_string": { + "query": "Doe", + "fields": [ "issue.labels.comment" ] + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/105_partial_flat_object_nested.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/105_partial_flat_object_nested.yml new file mode 100644 index 0000000000000..ce172c2773e1f --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/index/105_partial_flat_object_nested.yml @@ -0,0 +1,636 @@ +--- +# The test setup includes: +# - Create flat_object mapping for test_partial_flat_object_nested index +# - Index two example documents +# - Refresh the index so it is ready for search tests + +setup: + - do: + indices.create: + index: test_partial_flat_object_nested + body: + mappings: + properties: + issue: + type: "nested" + properties: + number: + type: "integer" + labels: + type: "flat_object" + - do: + index: + index: test_partial_flat_object_nested + id: 1 + body: { + "issue": [ + { + "number": 123, + "labels": { + "version": "2.2", + "backport": [ + "2.0", + "1.9" + ], + "category": { + "type": "API", + "level": "bug" + }, + "createdDate": "2023-01-01", + "comment": [ [ "Doe","Shipped" ],[ "John","Approved" ] ], + "views": 288, + "priority": 5.00 + } + } + ] + } + + - do: + index: + index: test_partial_flat_object_nested + id: 2 + body: { + "issue": [ + { + "number": 456, + "labels": { + "author": "Liu", + "version": "2.1", + "backport": [ + "2.0", + "1.3" + ], + "category": { + "type": "API", + "level": "enhancement" + }, + "createdDate": "2023-02-01", + "comment": [ [ "Mike","LGTM" ],[ "John","Approved" ] ], + "views": 3333, + "priority": 1.50 + } + } + ] + } + + - do: + indices.refresh: + index: test_partial_flat_object_nested +--- +# Delete Index when connection is teardown +teardown: + - do: + indices.delete: + index: test_partial_flat_object_nested + + +--- +# Verify that mappings under the catalog field did not expand +# and no dynamic fields were created. +"Mappings": + - skip: + version: " - 2.99.99" + reason: "flat_object is introduced in 3.0.0 in main branch" + + - do: + indices.get_mapping: + index: test_partial_flat_object_nested + - is_true: test_partial_flat_object_nested.mappings + - match: { test_partial_flat_object_nested.mappings.properties.issue.properties.number.type: integer } + - match: { test_partial_flat_object_nested.mappings.properties.issue.properties.labels.type: flat_object } + # https://github.com/opensearch-project/OpenSearch/tree/main/rest-api-spec/src/main/resources/rest-api-spec/test#length + - length: { test_partial_flat_object_nested.mappings.properties.issue.properties: 2 } + - length: { test_partial_flat_object_nested.mappings.properties.issue.properties.labels: 1 } + + +--- +"Supported queries": + - skip: + version: " - 2.99.99" + reason: "flat_object is introduced in 3.0.0 in main branch" + + + # Verify Document Count + - do: + search: + body: { + query: { + match_all: { } + } + } + + - length: { hits.hits: 2 } + + # Match Query with exact dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + match: { "issue.labels.version": "2.1" } + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.version: "2.1" } + + # Match Query without exact dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + match: { "issue.labels": "2.1" } + } + } + } + } + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.version: "2.1" } + + # Term Query1 with exact dot path for date + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + term: { issue.labels.createdDate: "2023-01-01" } + } + } } } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.createdDate: "2023-01-01" } + + # Term Query1 without exact dot path for date + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + term: { issue.labels: "2023-01-01" } + } } } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.createdDate: "2023-01-01" } + + # Term Query2 with dot path for string + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + term: { "issue.labels.category.type": "API" } + } } } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.0.labels.category.type: "API" } + - match: { hits.hits.1._source.issue.0.labels.category.type: "API" } + + # Term Query2 without exact dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + term: { issue.labels: "API" } + } } } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.0.labels.category.type: "API" } + - match: { hits.hits.1._source.issue.0.labels.category.type: "API" } + + # Term Query3 with dot path for array + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + term: { issue.labels.backport: "1.9" } + } } } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.backport: [ "2.0", "1.9" ] } + + # Term Query3 without dot path for array + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + term: { issue.labels: "1.9" } + } } } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.backport: [ "2.0", "1.9" ] } + + # Term Query4 with dot path for nested-array + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + term: { issue.labels.comment: "LGTM" } + } } } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Term Query4 without dot path for nested-array + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + term: { issue.labels: "LGTM" } } } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Terms Query without dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + terms: { issue.labels: [ "John","Mike" ] } } } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + - match: { hits.hits.1._source.issue.0.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Terms Query with dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + terms: { issue.labels.comment: [ "John","Mike" ] } } } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + - match: { hits.hits.1._source.issue.0.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Prefix Query with dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "prefix": { + "issue.labels.comment": { + "value": "Mi" + } } } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Prefix Query without dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "prefix": { + "issue.labels": { + "value": "Mi" + } + } + } } } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Range Query with dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "range": { + "issue.labels.version": { + "gte": "2.1", + "lte": "3.0" + } } } + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.0.labels.version: "2.2" } + - match: { hits.hits.1._source.issue.0.labels.version: "2.1" } + + # Range Query without dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "range": { + "issue.labels": { + "gte": "2.1", + "lte": "3.0" + } } } + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.0.labels.version: "2.2" } + - match: { hits.hits.1._source.issue.0.labels.version: "2.1" } + + # Range Query with integer input with dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "range": { + "issue.labels.views": { + "gte": 3000, + "lte": 4000 + } } } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.views: 3333 } + + # Range Query with integer input without dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "range": { + "issue.labels": { + "gte": 3000, + "lte": 4000 + } } } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.views: 3333 } + + + # Range Query with double input with dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "range": { + "issue.labels.priority": { + "gte": 4.1234, + "lte": 5.1234 + } } } + } + } + } + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.priority: 5.00 } + + # Range Query with double input without dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "range": { + "issue.labels": { + "gte": 4.1234, + "lte": 5.1234 + } } } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.priority: 5.00 } + + + # Exists Query with dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "exists": { + "field": issue.labels.priority + } } } + } + } + + - length: { hits.hits: 2 } + + # Exists Query with nested dot path, use the flat_object_field_name.last_key + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "exists": { + "field": issue.labels.type + } } } + } + } + + - length: { hits.hits: 2 } + + # Exists Query without dot path for the flat_object_field_name + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "exists": { + "field": issue.labels + } } } + } + } + + - length: { hits.hits: 2 } + + # Exists Query2 with dot path for one hit + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "exists": { + "field": issue.labels.author + } } } + } + } + + - length: { hits.hits: 1 } + + # Query_string Query without dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "query_string": { + "fields": [ "issue.labels" ], + "query": "Doe OR Mike" + } } } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + - match: { hits.hits.1._source.issue.0.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Query_string Query with dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "query_string": { + "fields": [ "issue.labels.comment" ], + "query": "Doe OR Mike" + } } } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + - match: { hits.hits.1._source.issue.0.labels.comment: [ [ "Mike","LGTM" ],[ "John","Approved" ] ] } + + # Simple_query_string Query without full dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "simple_query_string": { + "query": "Doe", + "fields": [ "issue.labels" ] + } } } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } + + # Simple_query_string Query with dot path. + - do: + search: + body: { + _source: true, + query: { + nested: { + path: "issue", + query: { + "simple_query_string": { + "query": "Doe", + "fields": [ "issue.labels.comment" ] + } } } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.issue.0.labels.comment: [ [ "Doe","Shipped" ],[ "John","Approved" ] ] } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/40_routing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/40_routing.yml index 630cf39dbe65c..f6f497269b043 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/index/40_routing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/index/40_routing.yml @@ -40,4 +40,3 @@ get: index: test_1 id: 1 - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/80_geo_point.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/80_geo_point.yml new file mode 100644 index 0000000000000..7d6c2b835f1f7 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/index/80_geo_point.yml @@ -0,0 +1,151 @@ +setup: + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + location: + type: geo_point + +--- +"Single point test": + - skip: + version: " - 2.3.99" + reason: "geojson format is supported in 2.4 and above" + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - location: + lon: 52.374081 + lat: 4.912350 + - index: + _index: test_1 + _id: 2 + - location: "4.901618,52.369219" + - index: + _index: test_1 + _id: 3 + - location: [ 52.371667, 4.914722 ] + - index: + _index: test_1 + _id: 4 + - location: "POINT (52.371667 4.914722)" + - index: + _index: test_1 + _id: 5 + - location: "t0v5zsq1gpzf" + - index: + _index: test_1 + _id: 6 + - location: + type: Point + coordinates: [ 52.371667, 4.914722 ] + + - do: + search: + index: test_1 + rest_total_hits_as_int: true + body: + query: + geo_shape: + location: + shape: + type: "envelope" + coordinates: [ [ 51, 5 ], [ 53, 3 ] ] + + - match: { hits.total: 6 } + + - do: + search: + index: test_1 + rest_total_hits_as_int: true + body: + query: + geo_shape: + location: + shape: + type: "envelope" + coordinates: [ [ 151, 15 ], [ 153, 13 ] ] + + - match: { hits.total: 0 } + +--- +"Multi points test": + - skip: + version: " - 2.3.99" + reason: "geojson format is supported in 2.4 and above" + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - location: + - {lon: 52.374081, lat: 4.912350} + - {lon: 152.374081, lat: 14.912350} + - index: + _index: test_1 + _id: 2 + - location: + - "4.901618,52.369219" + - "14.901618,152.369219" + - index: + _index: test_1 + _id: 3 + - location: + - [ 52.371667, 4.914722 ] + - [ 152.371667, 14.914722 ] + - index: + _index: test_1 + _id: 4 + - location: + - "POINT (52.371667 4.914722)" + - "POINT (152.371667 14.914722)" + - index: + _index: test_1 + _id: 5 + - location: + - "t0v5zsq1gpzf" + - "x6skg0zbhnum" + - index: + _index: test_1 + _id: 6 + - location: + - {type: Point, coordinates: [ 52.371667, 4.914722 ]} + - {type: Point, coordinates: [ 152.371667, 14.914722 ]} + + - do: + search: + index: test_1 + rest_total_hits_as_int: true + body: + query: + geo_shape: + location: + shape: + type: "envelope" + coordinates: [ [ 51, 5 ], [ 53, 3 ] ] + + - match: { hits.total: 6 } + + - do: + search: + index: test_1 + rest_total_hits_as_int: true + body: + query: + geo_shape: + location: + shape: + type: "envelope" + coordinates: [ [ 151, 15 ], [ 153, 13 ] ] + + - match: { hits.total: 6 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_flat_object.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_flat_object.yml new file mode 100644 index 0000000000000..0a5f7444efd17 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_flat_object.yml @@ -0,0 +1,746 @@ +--- +# The initial test setup includes: +# - Create flat_object mapping +# - Index two example documents +# - Refresh the index so it is ready for search tests +setup: + - do: + indices.create: + index: test + body: + mappings: + properties: + ISBN13: + type : "keyword" + catalog: + type : "flat_object" + required_matches: + type : "long" + + - do: + index: + index: test + id: 1 + body: { + "ISBN13": "V9781933988177", + "catalog": { + "title": "Lucene in Action", + "author": + { + "surname": "McCandless", + "given": "Mike" + }, + "catalogId":"c-0002", + "quantity": 1234, + "rating": 9.2, + "location": [-81.7982,41.3847 ], + "review": [["great",99.8],["ok",80.0]], + "publishDate": "2015-01-01" + }, + "required_matches": 1 + } + + - do: + index: + index: test + id: 2 + body: { + "ISBN13": "V12154942129175", + "catalog": { + "title": "Mock in Action", + "author": + { + "surname": "Doe", + "given": "John" + }, + "catalogId": "c-0050", + "quantity": 4321, + "rating": 5.2, + "location": [-12.7982,33.3847 ], + "review": [["bad",30.41],["ok",80.0]], + "publishDate": "2016-01-01" + }, + "required_matches": 1 + } + + # Do index refresh + - do: + indices.refresh: + index: test + +--- +# Delete Index when connection is teardown +teardown: + - do: + indices.delete: + index: test + +--- +# Verify that mappings under the catalog field did not expand +# and no dynamic fields were created. +"Mappings": + - skip: + version: " - 2.6.99" + reason: "flat_object is introduced in 2.7.0" + + - do: + indices.get_mapping: + index: test + - is_true: test.mappings + - match: { test.mappings.properties.ISBN13.type: keyword } + - match: { test.mappings.properties.catalog.type: flat_object } + - match: { test.mappings.properties.required_matches.type: long } + # https://github.com/opensearch-project/OpenSearch/tree/main/rest-api-spec/src/main/resources/rest-api-spec/test#length + - length: { test.mappings.properties: 3 } + - length: { test.mappings.properties.catalog: 1 } + +--- +"Supported queries": + - skip: + version: " - 2.99.99" + reason: "flat_object is introduced in 3.0.0 in main branch" + + # Verify Document Count + - do: + search: + body: { + query: { + match_all: {} + } + } + + - length: { hits.hits: 2 } + + # Match Query with dot path. + - do: + search: + body: { + _source: true, + query: { + match: { "catalog.title": "Lucene in Action"} + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.title: "Lucene in Action" } + + # Match Query without dot path. + - do: + search: + body: { + _source: true, + query: { + match: { catalog: "Lucene in Action"} + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.title: "Lucene in Action" } + + + # Multi Match Query without dot path. + - do: + search: + body: { + _source: true, + query: { + multi_match: { + "query": "Mike", + "fields": [ "ISBN13", "catalog" ] + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Multi Match Query with dot path. + - do: + search: + body: { + _source: true, + query: { + multi_match: { + "query": "Mike", + "fields": [ "ISBN13", "catalog.author.given" ] + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Term Query1 with dot path for date + - do: + search: + body: { + _source: true, + query: { + term: { catalog.publishDate: "2015-01-01"} + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.publishDate: "2015-01-01" } + + # Term Query1 without dot path for date + - do: + search: + body: { + _source: true, + query: { + term: { catalog: "2015-01-01" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.publishDate: "2015-01-01" } + + # Term Query2 with dot path for string + - do: + search: + body: { + _source: true, + query: { + term: { "catalog.author.given": "Mike" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Term Query2 without dot path. + - do: + search: + body: { + _source: true, + query: { + term: { catalog: "Mike" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Term Query3 with dot path for array + - do: + search: + body: { + _source: true, + query: { + term: { catalog.location: "-12.7982" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.location: [-12.7982,33.3847 ]} + + # Term Query3 without dot path for array + - do: + search: + body: { + _source: true, + query: { + term: { catalog: "-12.7982" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.location: [-12.7982,33.3847 ]} + + + # Term Query4 with dot path for nested-array + - do: + search: + body: { + _source: true, + query: { + term: { catalog.review: "99.8" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.review: [ [ "great",99.8 ],[ "ok",80.0 ] ] } + + # Term Query4 without dot path for nested-array + - do: + search: + body: { + _source: true, + query: { + term: { catalog: "99.8" } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.review: [["great",99.8],["ok",80.0]] } + + # Terms Query without dot path. + - do: + search: + body: { + _source: true, + query: { + terms: { catalog: ["John","Mike"] } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Terms Query with dot path. + - do: + search: + body: { + _source: true, + query: { + terms: { catalog.author.given: ["John","Mike"] } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Termset Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "terms_set": { + "catalog": { + "terms": [ "John","Mike" ], + "minimum_should_match_field": "required_matches"} + } + } + } + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Termset Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "terms_set": { + "catalog.author.given": { + "terms": [ "John","Mike" ], + "minimum_should_match_field": "required_matches"} + } + } + } + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Prefix Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "prefix": { + "catalog.author.given": { + "value": "Mi" + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Prefix Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "prefix": { + "catalog": { + "value": "Mi" + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Range Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "catalog.catalogId": { + "gte": "c-0000", + "lte": "c-0006" + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.catalogId: "c-0002" } + + # Range Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "catalog": { + "gte": "c-0000", + "lte": "c-0006" + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.catalogId: "c-0002" } + + # Range Query with integer input with dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "catalog.quantity": { + "gte": 1000, + "lte": 2000 + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.quantity: 1234 } + + # Range Query with integer input without dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "catalog": { + "gte": 1000, + "lte": 2000 + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.quantity: 1234 } + + # Range Query with date input with dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "catalog.publishDate": { + "gte": "2015-01-01", + "lte": "2015-12-31" + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.publishDate: "2015-01-01" } + + # Range Query with date input without dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "catalog": { + "gte": "2015-01-01", + "lte": "2015-12-31" + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.publishDate: "2015-01-01" } + + # Range Query with double input with dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "catalog.location": { + "gte": 40.1234, + "lte": 42.1234 + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.location: [-81.7982,41.3847] } + + # Range Query with double input without dot path. + - do: + search: + body: { + _source: true, + query: { + "range": { + "catalog": { + "gte": 40.1234, + "lte": 42.1234 + } + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.location: [ -81.7982,41.3847 ] } + + # Exists Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "exists": { + "field": catalog.catalogId + } + } + } + + - length: { hits.hits: 2 } + + # Exists Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "exists": { + "field": catalog + } + } + } + + - length: { hits.hits: 2 } + + # Query_string Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "query_string": { + "fields": [ "catalog", "ISBN13" ], + "query": "John OR Mike" + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Query_string Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "query_string": { + "fields": [ "catalog.author.given", "ISBN13" ], + "query": "John OR Mike" + } + } + } + + - length: { hits.hits: 2 } + - match: { hits.hits.0._source.catalog.author.given: "Mike" } + + # Simple_query_string Query without dot path. + - do: + search: + body: { + _source: true, + query: { + "simple_query_string" : { + "query": "Doe", + "fields": ["catalog", "ISBN13"] + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.author.surname: "Doe" } + + + # Simple_query_string Query with dot path. + - do: + search: + body: { + _source: true, + query: { + "simple_query_string": { + "query": "Doe", + "fields": [ "catalog.author.surname", "ISBN13" ] + } + } + } + + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.catalog.author.surname: "Doe" } + +--- +"Unsupported": + - skip: + version: " - 2.99.99" + reason: "flat_object is introduced in 3.0.0 in main branch" + + # Mapping parameters (such as index/search analyzers) are currently not supported + # The plan is to support them in the next version + - do: + catch: bad_request + indices.create: + index: test_analyzer + body: + mappings: + properties: + data: + type : "flat_object" + analyzer: "standard" + + - match: { error.root_cause.0.type: "mapper_parsing_exception" } + - match: { error.root_cause.0.reason: "Mapping definition for [data] has unsupported parameters: [analyzer : standard]"} + - match: { status: 400 } + + # Wildcard Query with dot path. + - do: + catch: bad_request + search: + body: { + _source: true, + query: { + "wildcard": { + "catalog.title": "Mock*" + } + } + } + - match: { error.root_cause.0.type: "query_shard_exception" } + - match: { error.root_cause.0.reason: "Can only use wildcard queries on keyword and text fields - not on [catalog.title] which is of type [flat_object]"} + - match: { status: 400 } + + # Wildcard Query without dot path. + - do: + catch: bad_request + search: + body: { + _source: true, + query: { + "wildcard": { + "catalog": "Mock*" + } + } + } + - match: { error.root_cause.0.type: "query_shard_exception" } + - match: { error.root_cause.0.reason: "Can only use wildcard queries on keyword and text fields - not on [catalog] which is of type [flat_object]" } + - match: { status: 400 } + + # Aggregation and Match Query with dot path. + - do: + catch: bad_request + search: + body: { + _source: true, + size: 0, + query: { + "match": { + "ISBN13": "V9781933988177" + } + }, + aggs: { + "avg_rating": { + "avg": { + "field": "catalog.rating" + } + } + } + } + - match: { error.root_cause.0.type: "illegal_argument_exception" } + - match: { error.root_cause.0.reason: "Field [catalog.rating] of type [flat_object] is not supported for aggregation [avg]" } + - match: { status: 400 } + + # Aggregation using average and Match Query with dot path. + - do: + catch: bad_request + search: + body: { + _source: true, + size: 0, + query: { + "match": { + "ISBN13": "V9781933988177" + } + }, + aggs: { + "avg_rating": { + "avg": { + "field": "catalog.rating" + } + } + } + } + - match: { error.root_cause.0.type: "illegal_argument_exception" } + - match: { error.root_cause.0.reason: "Field [catalog.rating] of type [flat_object] is not supported for aggregation [avg]" } + - match: { status: 400 } + + # Aggregation using geolocation and Match Query with dot path. + - do: + catch: bad_request + search: + body: { + _source: true, + size: 0, + query: { + "match": { + "ISBN13": "V9781933988177" + } + }, + aggs: { + "books_in_location": { + "geo_distance": { + "field": "catalog.location", + "origin": "41.3847,-81.7982", + "unit": "km", + "ranges": [ + { + "to": 100 + } + ] + }, + "aggs": { + "total_books": { + "sum": { + "field": "catalog.quantity" + } + } + } + } + } + } + - match: { error.root_cause.0.type: "illegal_argument_exception" } + - match: { error.root_cause.0.reason: "Field [catalog.location] of type [flat_object] is not supported for aggregation [geo_distance]" } + - match: { status: 400 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_unsigned_long.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_unsigned_long.yml new file mode 100644 index 0000000000000..02157a809d90b --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_unsigned_long.yml @@ -0,0 +1,73 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + counter: + type: unsigned_long + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - counter: "10223372036854775807.23" + +--- +"Unsigned long with decimal part test": + + - do: + search: + index: test_1 + rest_total_hits_as_int: true + body: + query: + term: + counter: + value: 10223372036854775807 + + - match: { hits.total: 1 } + +--- +"Stored unsigned long with decimal part test": + - do: + indices.create: + index: test_2 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + counter: + type: unsigned_long + store: true + + bulk: + refresh: true + body: + - index: + _index: test_2 + _id: 2 + - counter: "10223372036854775807" + + - do: + search: + index: test_* + rest_total_hits_as_int: true + body: + query: + term: + counter: + value: 10223372036854775807 + + - match: { hits.total: 2 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/10_basic.yml index a4d1841ed7108..38848945d9f1f 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/10_basic.yml @@ -31,6 +31,7 @@ setup: - skip: version: " - 7.3.99" reason: index cloning was added in 7.4.0 + features: allowed_warnings # make it read-only - do: indices.put_settings: @@ -46,6 +47,8 @@ setup: # now we do the actual clone - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.clone: index: "source" target: "target" @@ -94,9 +97,12 @@ setup: - skip: version: " - 7.3.99" reason: index cloning was added in 7.4.0 + features: allowed_warnings # try to do an illegal clone with illegal number_of_shards - do: catch: /illegal_argument_exception/ + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.clone: index: "source" target: "target" @@ -106,3 +112,99 @@ setup: settings: index.number_of_replicas: 0 index.number_of_shards: 6 + +--- +"Returns error if target index's metadata write is blocked": + + - skip: + version: " - 2.7.99" + reason: "only available in 2.8.0 and above" + + # block source index's write operations + - do: + indices.put_settings: + index: source + body: + index.blocks.write: true + index.number_of_replicas: 0 + + - do: + cluster.health: + wait_for_status: green + index: source + + # set `index.blocks.read_only` to `true` for target index + - do: + catch: /action_request_validation_exception/ + indices.clone: + index: "source" + target: "new_cloned_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.blocks.read_only: true + + # set `index.blocks.metadata` to `true` for target index + - do: + catch: /action_request_validation_exception/ + indices.clone: + index: "source" + target: "new_cloned_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.blocks.metadata: true + + # set source index's setting `index.blocks.read_only` to `true` + - do: + indices.put_settings: + index: source + body: + index.blocks.read_only: true + + - do: + catch: /illegal_argument_exception/ + indices.clone: + index: "source" + target: "new_cloned_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + + # overwrite the source index's setting, everything is fine + - do: + indices.clone: + index: "source" + target: "new_cloned_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.blocks.read_only: null + + - do: + cluster.health: + wait_for_status: green + + - do: + get: + index: new_cloned_index + id: "1" + + - match: { _index: new_cloned_index } + - match: { _id: "1" } + - match: { _source: { foo: "hello world" } } + + # clear the source index's read_only blocks because it will block deleting index + - do: + indices.put_settings: + index: source + body: + index.blocks.read_only: null diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/20_source_mapping.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/20_source_mapping.yml index 625f574fa73de..21c476c76965c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/20_source_mapping.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/20_source_mapping.yml @@ -3,6 +3,7 @@ - skip: version: " - 7.3.99" reason: index cloning was added in 7.4.0 + features: allowed_warnings # create index - do: indices.create: @@ -50,6 +51,8 @@ # now we do the actual clone - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.clone: index: "source" target: "target" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/30_copy_settings.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/30_copy_settings.yml index 503cc15609072..b0bd8056cb004 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/30_copy_settings.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/30_copy_settings.yml @@ -3,7 +3,7 @@ - skip: version: " - 7.3.99" reason: index cloning was added in 7.4.0 - features: [arbitrary_key] + features: [arbitrary_key, allowed_warnings] - do: nodes.info: @@ -36,6 +36,8 @@ # now we do an actual clone and copy settings - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.clone: index: "source" target: "copy-settings-target" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/40_wait_for_completion.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/40_wait_for_completion.yml new file mode 100644 index 0000000000000..310375ef8471e --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.clone/40_wait_for_completion.yml @@ -0,0 +1,88 @@ +--- +"Clone index with wait_for_completion": + # clone index with wait_for_completion parameter, when the parameter is set to false, the API + # will return a task immediately and the clone operation will run in background. + + - skip: + version: " - 2.6.99" + reason: "only available in 2.7+" + features: allowed_warnings + + - do: + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id + + - do: + indices.create: + index: source + wait_for_active_shards: 1 + body: + settings: + # ensure everything is allocated on the same data node + index.routing.allocation.include._id: $node_id + index.number_of_shards: 1 + index.number_of_replicas: 0 + - do: + index: + index: source + id: "1" + body: { "foo": "hello world" } + + - do: + get: + index: source + id: "1" + + - match: { _index: source } + - match: { _id: "1" } + - match: { _source: { foo: "hello world" } } + + # make it read-only + - do: + indices.put_settings: + index: source + body: + index.blocks.write: true + index.number_of_replicas: 0 + + - do: + cluster.health: + wait_for_status: green + index: source + + # clone with wait_for_completion + - do: + indices.clone: + index: "source" + target: "new_cloned_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + wait_for_completion: false + task_execution_timeout: 30s + body: + settings: + index.number_of_shards: 1 + "index.number_of_replicas": 0 + + - match: { task: /^.+$/ } + - set: { task: taskId } + + - do: + tasks.get: + wait_for_completion: true + task_id: $taskId + - match: { task.action: "indices:admin/resize" } + - match: { task.description: "clone from [source] to [new_cloned_index]" } + + # .tasks index is created when the clone index operation completes, so we should delete .tasks index finally, + # if not, the .tasks index may introduce unexpected warnings and then cause other test cases to fail. + # Delete the .tasks index directly will also introduce warning, but currently we don't have such APIs which can delete one + # specified task or clear all completed tasks, so we have to do so. Expect we can introduce more tasks related APIs in future + - do: + allowed_warnings: + - "this request accesses system indices: [.tasks], but in a future major version, direct access to system indices will be prevented by default" + indices.delete: + index: .tasks + ignore_unavailable: true diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/10_basic.yml index 74684901579a4..43a6f656c09c3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/10_basic.yml @@ -28,6 +28,6 @@ indices.get_alias: index: testind name: testali - + - match: { 'status': 404 } - match: { 'error': 'alias [testali] missing' } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/all_path_options.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/all_path_options.yml index d1d01cbaaa7e6..67369b67b3249 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/all_path_options.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.delete_alias/all_path_options.yml @@ -221,4 +221,3 @@ setup: catch: param indices.delete_alias: index: "test_index1" - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/10_basic.yml index 67592a013e8f1..32b692b16c5d6 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.exists_template/10_basic.yml @@ -6,6 +6,8 @@ setup: ignore: [404] --- "Test indices.exists_template": + - skip: + features: allowed_warnings - do: indices.exists_template: @@ -23,6 +25,8 @@ setup: number_of_replicas: 0 - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.exists_template: name: test master_timeout: 1m @@ -37,4 +41,3 @@ setup: local: true - is_false: '' - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/10_basic.yml index 137736e6823a9..d62c4c8882b13 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/10_basic.yml @@ -27,5 +27,3 @@ index: test max_num_segments: 10 only_expunge_deletes: true - - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/20_wait_for_completion.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/20_wait_for_completion.yml new file mode 100644 index 0000000000000..1d6c63558bb59 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.forcemerge/20_wait_for_completion.yml @@ -0,0 +1,39 @@ +--- +"Force merge index with wait_for_completion": + # force merge index with wait_for_completion parameter, when the parameter is set to false, the API + # will return a task immediately and the merge process will run in background. + + - skip: + version: " - 2.6.99" + reason: "only available in 2.7+" + features: allowed_warnings + + - do: + indices.create: + index: test_index + + - do: + indices.forcemerge: + index: test_index + wait_for_completion: false + max_num_segments: 1 + - match: { task: /^.+$/ } + - set: { task: taskId } + + - do: + tasks.get: + wait_for_completion: true + task_id: $taskId + - match: { task.action: "indices:admin/forcemerge" } + - match: { task.description: "Force-merge indices [test_index], maxSegments[1], onlyExpungeDeletes[false], flush[true]" } + + # .tasks index is created when the force-merge operation completes, so we should delete .tasks index finally, + # if not, the .tasks index may introduce unexpected warnings and then cause other test cases to fail. + # Delete the .tasks index directly will also introduce warning, but currently we don't have such APIs which can delete one + # specified task or clear all completed tasks, so we have to do so. Expect we can introduce more tasks related APIs in future + - do: + allowed_warnings: + - "this request accesses system indices: [.tasks], but in a future major version, direct access to system indices will be prevented by default" + indices.delete: + index: .tasks + ignore_unavailable: true diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml index 7c7b07b587849..690f83d5f3c2b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/40_missing_index.yml @@ -6,5 +6,3 @@ indices.get_field_mapping: index: test_index fields: field - - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml index 2c9ff58b445df..814bd1e3a4063 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_field_mapping/50_field_wildcards.yml @@ -127,4 +127,3 @@ setup: - match: {test_index_2.mappings.t1.full_name: t1 } - match: {test_index_2.mappings.t2.full_name: t2 } - length: {test_index_2.mappings: 2} - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_index_template/20_get_missing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_index_template/20_get_missing.yml index 2dfa466ba0eca..4c855d928d1c0 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_index_template/20_get_missing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_index_template/20_get_missing.yml @@ -17,4 +17,3 @@ setup: catch: missing indices.get_index_template: name: test - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/30_missing_index.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/30_missing_index.yml index 1bbfbc4f4c967..e9502cdc08436 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/30_missing_index.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/30_missing_index.yml @@ -29,4 +29,3 @@ index: test_index ignore_unavailable: true allow_no_indices: false - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/20_aliases.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/20_aliases.yml index da7678202ed34..4f8d1371d90b1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/20_aliases.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_settings/20_aliases.yml @@ -23,4 +23,3 @@ - match: { test-index.settings.index.number_of_replicas: "3" } - match: { test-index.settings.index.number_of_shards: "2" } - match: { test-index.settings.index.refresh_interval: "-1" } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml index 9becbd54a3773..32536f8f72650 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/10_basic.yml @@ -72,8 +72,12 @@ setup: --- "Get template with flat settings and master timeout": + - skip: + features: allowed_warnings - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.get_template: name: test flat_settings: true diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/20_get_missing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/20_get_missing.yml index 2751f57dacb6c..ee7ba62c9beb4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/20_get_missing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_template/20_get_missing.yml @@ -10,4 +10,3 @@ setup: catch: missing indices.get_template: name: test - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/20_multiple_indices.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/20_multiple_indices.yml index bef5ea8a54651..2720b08514ba3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/20_multiple_indices.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/20_multiple_indices.yml @@ -101,4 +101,3 @@ setup: search: rest_total_hits_as_int: true index: test_index3 - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/30_wait_for_completion.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/30_wait_for_completion.yml new file mode 100644 index 0000000000000..f5af642d82f77 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.open/30_wait_for_completion.yml @@ -0,0 +1,50 @@ +--- +"Open index with wait_for_completion": + # open index with wait_for_completion parameter, when the parameter is set to false, the API + # will return a task immediately and the open operation will run in background. + + - skip: + version: " - 2.6.99" + reason: "only available in 2.7+" + features: allowed_warnings + + - do: + indices.create: + index: test_index + body: + settings: + number_of_replicas: 0 + number_of_shards: 1 + + - do: + indices.close: + index: test_index + - is_true: acknowledged + + - do: + indices.open: + index: test_index + wait_for_active_shards: all + cluster_manager_timeout: 10s + wait_for_completion: false + task_execution_timeout: 30s + - match: { task: /^.+$/ } + - set: { task: taskId } + + - do: + tasks.get: + wait_for_completion: true + task_id: $taskId + - match: { task.action: "indices:admin/open" } + - match: { task.description: "open indices [test_index]" } + + # .tasks index is created when the open index operation completes, so we should delete .tasks index finally, + # if not, the .tasks index may introduce unexpected warnings and then cause other test cases to fail. + # Delete the .tasks index directly will also introduce warning, but currently we don't have such APIs which can delete one + # specified task or clear all completed tasks, so we have to do so. Expect we can introduce more tasks related APIs in future + - do: + allowed_warnings: + - "this request accesses system indices: [.tasks], but in a future major version, direct access to system indices will be prevented by default" + indices.delete: + index: .tasks + ignore_unavailable: true diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/all_path_options.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/all_path_options.yml index bef57bbddf165..47828c43350b7 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/all_path_options.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_alias/all_path_options.yml @@ -113,4 +113,3 @@ setup: - do: catch: param indices.put_alias: {} - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml index 36317c7ae173c..23f87ea1ec2b3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml @@ -143,4 +143,3 @@ - is_false: test_index.mappings.properties.foo.meta.bar - match: { test_index.mappings.properties.foo.meta.baz: "quux" } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml index c1daa76fe3d6e..ca7a21df20ea4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml @@ -159,4 +159,3 @@ setup: indices.get_mapping: {} - match: {test_index1.mappings.properties.text.type: text} - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/all_path_options.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/all_path_options.yml index 07f1956f0fcca..ac45c4e098e6e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/all_path_options.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_settings/all_path_options.yml @@ -110,4 +110,3 @@ setup: - match: {test_index1.settings.index.refresh_interval: 1s} - match: {test_index2.settings.index.refresh_interval: 1s} - match: {foo.settings.index.refresh_interval: 1s} - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/10_basic.yml index 6e493a0cce936..bf20d51bc97cd 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.refresh/10_basic.yml @@ -55,4 +55,3 @@ setup: - match: { _shards.total: 0 } - match: { _shards.successful: 0 } - match: { _shards.failed: 0 } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/10_basic.yml index dc68ffc9a3b86..a36db0cda8526 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/10_basic.yml @@ -148,4 +148,3 @@ body: conditions: max_docs: 1 - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/20_max_doc_condition.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/20_max_doc_condition.yml index f5d223259dc06..7d1b447b4e293 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/20_max_doc_condition.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.rollover/20_max_doc_condition.yml @@ -52,4 +52,3 @@ - match: { conditions: { "[max_docs: 2]": true } } - match: { rolled_over: true } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml index a5b1cb8607b3a..0c95f01c1e4b8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml @@ -1,12 +1,11 @@ --- -"Shrink index via API": +setup: # creates an index with one document solely allocated on a particular data node # and shrinks it into a new index with a single shard # we don't do the relocation to a single node after the index is created # here since in a mixed version cluster we can't identify # which node is the one with the highest version and that is the only one that can safely # be used to shrink the index. - - do: nodes.info: node_id: data:true @@ -29,14 +28,10 @@ id: "1" body: { "foo": "hello world" } - - do: - get: - index: source - id: "1" - - - match: { _index: source } - - match: { _id: "1" } - - match: { _source: { foo: "hello world" } } +--- +"Shrink index via API": + - skip: + features: allowed_warnings # make it read-only - do: @@ -53,6 +48,8 @@ # now we do the actual shrink - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.shrink: index: "source" target: "target" @@ -74,3 +71,103 @@ - match: { _index: target } - match: { _id: "1" } - match: { _source: { foo: "hello world" } } + +--- +"Returns error if target index's metadata write is blocked": + + - skip: + version: " - 2.7.99" + reason: "only available in 2.8.0 and above" + + # block source index's write operations + - do: + indices.put_settings: + index: source + body: + index.blocks.write: true + index.number_of_replicas: 0 + + - do: + cluster.health: + wait_for_status: green + index: source + + # set `index.blocks.read_only` to `true` for target index + - do: + catch: /action_request_validation_exception/ + indices.shrink: + index: "source" + target: "new_shrunken_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.number_of_shards: 1 + index.blocks.read_only: true + + # set `index.blocks.metadata` to `true` for target index + - do: + catch: /action_request_validation_exception/ + indices.shrink: + index: "source" + target: "new_shrunken_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.number_of_shards: 1 + index.blocks.metadata: true + + # set source index's setting `index.blocks.read_only` to `true` + - do: + indices.put_settings: + index: source + body: + index.blocks.read_only: true + + - do: + catch: /illegal_argument_exception/ + indices.shrink: + index: "source" + target: "new_shrunken_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.number_of_shards: 1 + + # overwrite the source index's setting, everything is fine + - do: + indices.shrink: + index: "source" + target: "new_shrunken_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.number_of_shards: 1 + index.blocks.read_only: null + + - do: + cluster.health: + wait_for_status: green + + - do: + get: + index: new_shrunken_index + id: "1" + + - match: { _index: new_shrunken_index } + - match: { _id: "1" } + - match: { _source: { foo: "hello world" } } + + # clear the source index's read_only blocks because it will block deleting index + - do: + indices.put_settings: + index: source + body: + index.blocks.read_only: null diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/20_source_mapping.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/20_source_mapping.yml index dec0760fc6b19..8d08373208216 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/20_source_mapping.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/20_source_mapping.yml @@ -3,7 +3,7 @@ - skip: version: " - 6.9.99" reason: expects warnings that pre-7.0.0 will not send - features: [warnings, arbitrary_key] + features: [warnings, arbitrary_key, allowed_warnings] - do: nodes.info: @@ -60,6 +60,8 @@ # now we do the actual shrink - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.shrink: index: "source" target: "target" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/30_copy_settings.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/30_copy_settings.yml index a744895c4ce38..07d5076550889 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/30_copy_settings.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/30_copy_settings.yml @@ -46,7 +46,8 @@ index.number_of_replicas: 0 index.merge.scheduler.max_thread_count: 2 allowed_warnings: - - "parameter [copy_settings] is deprecated and will be removed in 8.0.0" + - "parameter [copy_settings] is deprecated and will be removed in 3.0.0" + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." - do: cluster.health: @@ -64,6 +65,8 @@ # now we do a actual shrink and copy settings (by default) - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.shrink: index: "source" target: "default-copy-settings-target" @@ -91,6 +94,8 @@ # now we do a actual shrink and try to set no copy settings - do: catch: /illegal_argument_exception/ + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.shrink: index: "source" target: "explicit-no-copy-settings-target" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/40_max_shard_size.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/40_max_shard_size.yml new file mode 100644 index 0000000000000..cc8fd9b025da4 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/40_max_shard_size.yml @@ -0,0 +1,82 @@ +--- +"Shrink index with max_shard_size": + # shrink index with max_shard_size parameter, which is used to generate an optimum + # number_of_shards for the target index. + + - skip: + version: " - 2.4.99" + reason: "only available in 2.5+" + features: allowed_warnings + + - do: + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id + + - do: + indices.create: + index: source + wait_for_active_shards: 1 + body: + settings: + # ensure everything is allocated on the same data node + index.routing.allocation.include._id: $node_id + index.number_of_shards: 3 + index.number_of_replicas: 0 + - do: + index: + index: source + id: "1" + body: { "foo": "hello world" } + + - do: + get: + index: source + id: "1" + + - match: { _index: source } + - match: { _id: "1" } + - match: { _source: { foo: "hello world" } } + + # make it read-only + - do: + indices.put_settings: + index: source + body: + index.blocks.write: true + index.number_of_replicas: 0 + + - do: + cluster.health: + wait_for_status: green + index: source + + # shrink with max_shard_size + - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." + indices.shrink: + index: "source" + target: "new_shrunken_index" + wait_for_active_shards: 1 + master_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + max_shard_size: "10gb" + + - do: + cluster.health: + wait_for_status: green + + - do: + get: + index: "new_shrunken_index" + id: "1" + + - do: + indices.get_settings: + index: "new_shrunken_index" + + - match: { new_shrunken_index.settings.index.number_of_shards: "1" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/50_wait_for_completion.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/50_wait_for_completion.yml new file mode 100644 index 0000000000000..f1b06fc1fa392 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/50_wait_for_completion.yml @@ -0,0 +1,88 @@ +--- +"Shrink index with wait_for_completion": + # shrink index with wait_for_completion parameter, when the parameter is set to false, the API + # will return a task immediately and the shrink operation will run in background. + + - skip: + version: " - 2.6.99" + reason: "only available in 2.7+" + features: allowed_warnings + + - do: + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id + + - do: + indices.create: + index: source + wait_for_active_shards: 1 + body: + settings: + # ensure everything is allocated on the same data node + index.routing.allocation.include._id: $node_id + index.number_of_shards: 3 + index.number_of_replicas: 0 + - do: + index: + index: source + id: "1" + body: { "foo": "hello world" } + + - do: + get: + index: source + id: "1" + + - match: { _index: source } + - match: { _id: "1" } + - match: { _source: { foo: "hello world" } } + + # make it read-only + - do: + indices.put_settings: + index: source + body: + index.blocks.write: true + index.number_of_replicas: 0 + + - do: + cluster.health: + wait_for_status: green + index: source + + # shrink with wait_for_completion + - do: + indices.shrink: + index: "source" + target: "new_shrunken_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + wait_for_completion: false + task_execution_timeout: 30s + body: + settings: + index.number_of_shards: 1 + "index.number_of_replicas": 0 + + - match: { task: /^.+$/ } + - set: { task: taskId } + + - do: + tasks.get: + wait_for_completion: true + task_id: $taskId + - match: { task.action: "indices:admin/resize" } + - match: { task.description: "shrink from [source] to [new_shrunken_index]" } + + # .tasks index is created when the shrink index operation completes, so we should delete .tasks index finally, + # if not, the .tasks index may introduce unexpected warnings and then cause other test cases to fail. + # Delete the .tasks index directly will also introduce warning, but currently we don't have such APIs which can delete one + # specified task or clear all completed tasks, so we have to do so. Expect we can introduce more tasks related APIs in future + - do: + allowed_warnings: + - "this request accesses system indices: [.tasks], but in a future major version, direct access to system indices will be prevented by default" + indices.delete: + index: .tasks + ignore_unavailable: true diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/10_basic.yml index 4ae1d0002a237..970c97ab517cf 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/10_basic.yml @@ -29,6 +29,9 @@ setup: --- "Split index via API": + - skip: + features: allowed_warnings + # make it read-only - do: indices.put_settings: @@ -44,6 +47,8 @@ setup: # now we do the actual split - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.split: index: "source" target: "target" @@ -90,6 +95,8 @@ setup: --- "Split from 1 to N": + - skip: + features: allowed_warnings - do: indices.create: index: source_one_shard @@ -131,6 +138,8 @@ setup: # now we do the actual split from 1 to 5 - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.split: index: "source_one_shard" target: "target" @@ -176,9 +185,14 @@ setup: --- "Create illegal split indices": + - skip: + features: allowed_warnings + # try to do an illegal split with number_of_routing_shards set - do: catch: /illegal_argument_exception/ + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.split: index: "source" target: "target" @@ -193,6 +207,8 @@ setup: # try to do an illegal split with illegal number_of_shards - do: catch: /illegal_state_exception/ + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.split: index: "source" target: "target" @@ -202,3 +218,103 @@ setup: settings: index.number_of_replicas: 0 index.number_of_shards: 6 + +--- +"Returns error if target index's metadata write is blocked": + + - skip: + version: " - 2.7.99" + reason: "only available in 2.8.0 and above" + + # block source index's write operations + - do: + indices.put_settings: + index: source + body: + index.blocks.write: true + index.number_of_replicas: 0 + + - do: + cluster.health: + wait_for_status: green + index: source + + # set `index.blocks.read_only` to `true` for target index + - do: + catch: /action_request_validation_exception/ + indices.split: + index: "source" + target: "new_split_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.number_of_shards: 4 + index.blocks.read_only: true + + # set `index.blocks.metadata` to `true` for target index + - do: + catch: /action_request_validation_exception/ + indices.split: + index: "source" + target: "new_split_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.number_of_shards: 4 + index.blocks.metadata: true + + # set source index's setting `index.blocks.read_only` to `true` + - do: + indices.put_settings: + index: source + body: + index.blocks.read_only: true + + - do: + catch: /illegal_argument_exception/ + indices.split: + index: "source" + target: "new_split_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.number_of_shards: 4 + + # overwrite the source index's setting, everything is fine + - do: + indices.split: + index: "source" + target: "new_split_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + body: + settings: + index.number_of_replicas: 0 + index.number_of_shards: 4 + index.blocks.read_only: null + + - do: + cluster.health: + wait_for_status: green + + - do: + get: + index: new_split_index + id: "1" + + - match: { _index: new_split_index } + - match: { _id: "1" } + - match: { _source: { foo: "hello world" } } + + # clear the source index's read_only blocks because it will block deleting index + - do: + indices.put_settings: + index: source + body: + index.blocks.read_only: null diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/20_source_mapping.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/20_source_mapping.yml index c86e49aac0561..2c878157f468e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/20_source_mapping.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/20_source_mapping.yml @@ -1,5 +1,8 @@ --- "Split index ignores target template mapping": + - skip: + features: allowed_warnings + # create index - do: indices.create: @@ -48,6 +51,8 @@ # now we do the actual split - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.split: index: "source" target: "target" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/30_copy_settings.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/30_copy_settings.yml index 0ceacf1f064ca..b8e6b54a393b9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/30_copy_settings.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/30_copy_settings.yml @@ -48,8 +48,8 @@ index.number_of_shards: 2 index.merge.scheduler.max_thread_count: 2 allowed_warnings: - - "parameter [copy_settings] is deprecated and will be removed in 8.0.0" - + - "parameter [copy_settings] is deprecated and will be removed in 3.0.0" + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." - do: cluster.health: @@ -67,6 +67,8 @@ # now we do a actual shrink and copy settings (by default) - do: + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.split: index: "source" target: "default-copy-settings-target" @@ -94,6 +96,8 @@ - do: catch: /illegal_argument_exception/ + allowed_warnings: + - "Parameter [master_timeout] is deprecated and will be removed in 3.0. To support inclusive language, please use [cluster_manager_timeout] instead." indices.split: index: "source" target: "explicit-no-copy-settings-target" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/40_wait_for_completion.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/40_wait_for_completion.yml new file mode 100644 index 0000000000000..92c906134a5f2 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/40_wait_for_completion.yml @@ -0,0 +1,88 @@ +--- +"Split index with wait_for_completion": + # split index with wait_for_completion parameter, when the parameter is set to false, the API + # will return a task immediately and the split operation will run in background. + + - skip: + version: " - 2.6.99" + reason: "only available in 2.7+" + features: allowed_warnings + + - do: + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id + + - do: + indices.create: + index: source + wait_for_active_shards: 1 + body: + settings: + # ensure everything is allocated on the same data node + index.routing.allocation.include._id: $node_id + index.number_of_shards: 1 + index.number_of_replicas: 0 + - do: + index: + index: source + id: "1" + body: { "foo": "hello world" } + + - do: + get: + index: source + id: "1" + + - match: { _index: source } + - match: { _id: "1" } + - match: { _source: { foo: "hello world" } } + + # make it read-only + - do: + indices.put_settings: + index: source + body: + index.blocks.write: true + index.number_of_replicas: 0 + + - do: + cluster.health: + wait_for_status: green + index: source + + # split with wait_for_completion + - do: + indices.split: + index: "source" + target: "new_split_index" + wait_for_active_shards: 1 + cluster_manager_timeout: 10s + wait_for_completion: false + task_execution_timeout: 30s + body: + settings: + index.number_of_shards: 2 + "index.number_of_replicas": 0 + + - match: { task: /^.+$/ } + - set: { task: taskId } + + - do: + tasks.get: + wait_for_completion: true + task_id: $taskId + - match: { task.action: "indices:admin/resize" } + - match: { task.description: "split from [source] to [new_split_index]" } + + # .tasks index is created when the split index operation completes, so we should delete .tasks index finally, + # if not, the .tasks index may introduce unexpected warnings and then cause other test cases to fail. + # Delete the .tasks index directly will also introduce warning, but currently we don't have such APIs which can delete one + # specified task or clear all completed tasks, so we have to do so. Expect we can introduce more tasks related APIs in future + - do: + allowed_warnings: + - "this request accesses system indices: [.tasks], but in a future major version, direct access to system indices will be prevented by default" + indices.delete: + index: .tasks + ignore_unavailable: true diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/20_routing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/20_routing.yml index ecedcef0c1a48..c812d84dfe7e3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/20_routing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.update_aliases/20_routing.yml @@ -132,4 +132,3 @@ setup: index: test_index - match: {test_index.aliases.test_alias: {'index_routing': '5', 'search_routing': '5'}} - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/10_basic.yml index 55070cb8c1f97..62c1a51dace52 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.upgrade/10_basic.yml @@ -69,4 +69,3 @@ indices.upgrade: index: ["test_index", "does_not_exist"] ignore_unavailable: false - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/info/20_lucene_version.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/info/20_lucene_version.yml index 83414fbabc565..427a585815db0 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/info/20_lucene_version.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/info/20_lucene_version.yml @@ -2,6 +2,3 @@ "Lucene Version": - do: {info: {}} - is_true: version.lucene_version - - - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml index a09619b7255c3..784c7b52b18b4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml @@ -138,6 +138,35 @@ - is_false: nodes.$node_id.indices.translog - is_false: nodes.$node_id.indices.recovery +--- +"Metric - indexing doc_status": + - skip: + version: " - 2.10.99" + reason: "Doc Status Stats were introduced in v2.11.0" + - do: + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id + + - do: + nodes.stats: { metric: indices, index_metric: indexing } + + - is_false: nodes.$node_id.indices.docs + - is_false: nodes.$node_id.indices.store + - is_true: nodes.$node_id.indices.indexing + - is_true: nodes.$node_id.indices.indexing.doc_status + - is_false: nodes.$node_id.indices.get + - is_false: nodes.$node_id.indices.search + - is_false: nodes.$node_id.indices.merges + - is_false: nodes.$node_id.indices.refresh + - is_false: nodes.$node_id.indices.flush + - is_false: nodes.$node_id.indices.warmer + - is_false: nodes.$node_id.indices.query_cache + - is_false: nodes.$node_id.indices.fielddata + - is_false: nodes.$node_id.indices.completion + - is_false: nodes.$node_id.indices.segments + - is_false: nodes.$node_id.indices.translog + - is_false: nodes.$node_id.indices.recovery --- "Metric - recovery": @@ -224,4 +253,3 @@ - is_false: nodes.$node_id.indices.translog - is_false: nodes.$node_id.indices.recovery - is_true: nodes.$node_id.indices.segments.file_sizes - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/ping/10_ping.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/ping/10_ping.yml index ec07c218dabd9..da160503caab4 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/ping/10_ping.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/ping/10_ping.yml @@ -2,4 +2,3 @@ "Ping": - do: { ping: {}} - is_true: '' - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/pit/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/pit/10_basic.yml new file mode 100644 index 0000000000000..9d1ee7571f2e2 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/pit/10_basic.yml @@ -0,0 +1,144 @@ +"Create PIT, Search with PIT ID and Delete": + - skip: + version: " - 2.3.99" + reason: "mode to be introduced later than 2.4" + - do: + indices.create: + index: test_pit + - do: + index: + index: test_pit + id: 42 + body: { foo: 1 } + + - do: + index: + index: test_pit + id: 43 + body: { foo: 2 } + + - do: + indices.refresh: {} + + - do: + create_pit: + allow_partial_pit_creation: true + index: test_pit + keep_alive: 23h + + - set: {pit_id: pit_id} + - match: { _shards.failed: 0} + - do: + search: + rest_total_hits_as_int: true + size: 1 + sort: foo + body: + query: + match_all: {} + pit: {"id": "$pit_id"} + + - match: {hits.total: 2 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._id: "42" } + + - do: + index: + index: test_pit + id: 44 + body: { foo: 3 } + + - do: + indices.refresh: {} + + - do: + search: + rest_total_hits_as_int: true + size: 1 + sort: foo + body: + query: + match_all: {} + pit: {"id": "$pit_id", "keep_alive":"10m"} + + - match: {hits.total: 2 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._id: "42" } + + + - do: + search: + rest_total_hits_as_int: true + index: test_pit + size: 1 + sort: foo + body: + query: + match_all: {} + + - match: {hits.total: 3 } + - length: {hits.hits: 1 } + + - do: + get_all_pits: {} + + - match: {pits.0.pit_id: $pit_id} + - match: {pits.0.keep_alive: 82800000 } + + - do: + delete_pit: + body: + "pit_id": [$pit_id] + + - match: {pits.0.pit_id: $pit_id} + - match: {pits.0.successful: true } + +--- +"Delete all": + - skip: + version: " - 2.3.99" + reason: "mode to be introduced later than 2.4" + - do: + indices.create: + index: test_pit + - do: + index: + index: test_pit + id: 42 + body: { foo: 1 } + + - do: + index: + index: test_pit + id: 43 + body: { foo: 2 } + + - do: + indices.refresh: {} + + - do: + create_pit: + allow_partial_pit_creation: true + index: test_pit + keep_alive: 23h + + - set: {pit_id: pit_id} + - match: { _shards.failed: 0} + + - do: + get_all_pits: {} + + - match: {pits.0.pit_id: $pit_id} + - match: {pits.0.keep_alive: 82800000 } + + - do: + delete_all_pits: {} + + - match: {pits.0.pit_id: $pit_id} + - match: {pits.0.successful: true } + + - do: + delete_all_pits: {} + + - match: {pits: []} + - length: {pits: 0} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic_timeseries.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic_timeseries.yml new file mode 100644 index 0000000000000..1995bee89a0a4 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/scroll/10_basic_timeseries.yml @@ -0,0 +1,161 @@ +--- +"Basic scroll on time series workload for reversed leaf sorter": + - do: + indices.create: + index: test_scroll_time_series + body: + mappings: + properties: + name: + type: keyword + '@timestamp': + type: date + + - do: + bulk: + refresh: true + index: test_scroll_time_series + body: + - '{"index": {}}' + - '{"name": "1", "@timestamp": "2010-03-12T01:07:00"}' + - '{"index": {}}' + - '{"name": "2", "@timestamp": "2010-03-12T01:07:01"}' + - '{"index": {}}' + - '{"name": "3", "@timestamp": "2010-03-12T01:07:02"}' + - '{"index": {}}' + - '{"name": "4", "@timestamp": "2010-03-12T01:07:03"}' + - '{"index": {}}' + - '{"name": "5", "@timestamp": "2010-03-12T01:07:04"}' + - '{"index": {}}' + - '{"name": "6", "@timestamp": "2010-03-12T01:07:05"}' + - '{"index": {}}' + - '{"name": "7", "@timestamp": "2010-03-12T01:07:06"}' + - '{"index": {}}' + - '{"name": "8", "@timestamp": "2010-03-12T01:07:07"}' + - '{"index": {}}' + - '{"name": "9", "@timestamp": "2010-03-12T01:07:08"}' + - '{"index": {}}' + - '{"name": "10", "@timestamp": "2010-03-12T01:07:09"}' + - do: + indices.refresh: {} + - do: + bulk: + refresh: true + index: test_scroll_time_series + body: + - '{"index": {}}' + - '{"name": "11", "@timestamp": "2010-03-12T01:07:10"}' + - '{"index": {}}' + - '{"name": "12", "@timestamp": "2010-03-12T01:07:11"}' + - '{"index": {}}' + - '{"name": "13", "@timestamp": "2010-03-12T01:07:12"}' + - '{"index": {}}' + - '{"name": "14", "@timestamp": "2010-03-12T01:07:13"}' + - '{"index": {}}' + - '{"name": "15", "@timestamp": "2010-03-12T01:07:14"}' + - '{"index": {}}' + - '{"name": "16", "@timestamp": "2010-03-12T01:07:15"}' + - '{"index": {}}' + - '{"name": "17", "@timestamp": "2010-03-12T01:07:16"}' + - '{"index": {}}' + - '{"name": "18", "@timestamp": "2010-03-12T01:07:17"}' + - '{"index": {}}' + - '{"name": "19", "@timestamp": "2010-03-12T01:07:18"}' + - '{"index": {}}' + - '{"name": "20", "@timestamp": "2010-03-12T01:07:19"}' + - do: + indices.refresh: { } + - do: + bulk: + refresh: true + index: test_scroll_time_series + body: + - '{"index": {}}' + - '{"name": "21", "@timestamp": "2010-03-12T01:07:20"}' + - '{"index": {}}' + - '{"name": "22", "@timestamp": "2010-03-12T01:07:21"}' + - '{"index": {}}' + - '{"name": "23", "@timestamp": "2010-03-12T01:07:22"}' + - '{"index": {}}' + - '{"name": "24", "@timestamp": "2010-03-12T01:07:23"}' + - '{"index": {}}' + - '{"name": "25", "@timestamp": "2010-03-12T01:07:24"}' + - '{"index": {}}' + - '{"name": "26", "@timestamp": "2010-03-12T01:07:25"}' + - '{"index": {}}' + - '{"name": "27", "@timestamp": "2010-03-12T01:07:26"}' + - '{"index": {}}' + - '{"name": "28", "@timestamp": "2010-03-12T01:07:27"}' + - '{"index": {}}' + - '{"name": "29", "@timestamp": "2010-03-12T01:07:28"}' + - '{"index": {}}' + - '{"name": "30", "@timestamp": "2010-03-12T01:07:29"}' + - do: + indices.refresh: { } + + - do: + search: + rest_total_hits_as_int: true + index: test_scroll_time_series + size: 5 + scroll: 1m + sort: _doc + body: + query: + match_all: {} + + - set: {_scroll_id: scroll_id} + - match: {hits.total: 30 } + - length: {hits.hits: 5 } + + - do: + scroll: + rest_total_hits_as_int: true + body: { "scroll_id": "$scroll_id", "scroll": "1m"} + + - match: {hits.total: 30 } + - length: {hits.hits: 5 } + + - do: + scroll: + rest_total_hits_as_int: true + body: { "scroll_id": "$scroll_id", "scroll": "1m" } + + - match: { hits.total: 30 } + - length: { hits.hits: 5 } + + - do: + scroll: + rest_total_hits_as_int: true + body: { "scroll_id": "$scroll_id", "scroll": "1m" } + + - match: { hits.total: 30 } + - length: { hits.hits: 5 } + + - do: + scroll: + rest_total_hits_as_int: true + body: { "scroll_id": "$scroll_id", "scroll": "1m" } + + - match: { hits.total: 30 } + - length: { hits.hits: 5 } + + - do: + scroll: + rest_total_hits_as_int: true + body: { "scroll_id": "$scroll_id", "scroll": "1m" } + + - match: { hits.total: 30 } + - length: { hits.hits: 5 } + + - do: + scroll: + rest_total_hits_as_int: true + body: { "scroll_id": "$scroll_id", "scroll": "1m" } + + - match: { hits.total: 30 } + - length: { hits.hits: 0 } + + - do: + clear_scroll: + scroll_id: $scroll_id diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/100_avg_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/100_avg_metric_unsigned.yml new file mode 100644 index 0000000000000..4a04b48f55444 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/100_avg_metric_unsigned.yml @@ -0,0 +1,168 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 1101 + double_field: 1101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_avg: + avg: + field: unsigned_field + the_double_avg: + avg: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_avg.value: 2555843009213695000.0 } + - match: { aggregations.the_double_avg.value: 2555843009213695000.0 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + the_unsigned_avg: + avg: + field: unsigned_field + the_double_avg: + avg: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.the_unsigned_avg.value: 2555843009213695000.0 } + - match: { aggregations.the_double_avg.value: 2555843009213695000.0 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + the_unsigned_avg: + avg: + field: unsigned_field + the_double_avg: + avg: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { aggregations.the_unsigned_avg.value: 3407790678951593500.0 } + - match: { aggregations.the_double_avg.value: 3407790678951593500.0 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_avg: + avg: + field: foo + missing: 1 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_missing_avg.value: 1 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_avg: + avg: + field: foo + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - is_false: aggregations.the_missing_avg.value + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_avg: + meta: + foo: bar + avg: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_avg.value: 2555843009213695000.0 } + - match: { aggregations.the_unsigned_avg.meta.foo: "bar" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/110_max_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/110_max_metric_unsigned.yml new file mode 100644 index 0000000000000..e8bab4c933eb5 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/110_max_metric_unsigned.yml @@ -0,0 +1,168 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 101 + double_field: 101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_avg: + avg: + field: unsigned_field + the_double_avg: + avg: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_avg.value: 2555843009213694500.0 } + - match: { aggregations.the_double_avg.value: 2555843009213694500.0 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + the_unsigned_avg: + avg: + field: unsigned_field + the_double_avg: + avg: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.the_unsigned_avg.value: 2555843009213694500.0 } + - match: { aggregations.the_double_avg.value: 2555843009213694500.0 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + the_unsigned_avg: + avg: + field: unsigned_field + the_double_avg: + avg: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { aggregations.the_unsigned_avg.value: 3407790678951592400.0 } + - match: { aggregations.the_double_avg.value: 3407790678951592400.0 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_avg: + avg: + field: foo + missing: 1 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_missing_avg.value: 1 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_avg: + avg: + field: foo + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - is_false: aggregations.the_missing_avg.value + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_avg: + meta: + foo: bar + avg: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_avg.value: 2555843009213694500.0 } + - match: { aggregations.the_unsigned_avg.meta.foo: "bar" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/120_min_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/120_min_metric_unsigned.yml new file mode 100644 index 0000000000000..38601f3ea660d --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/120_min_metric_unsigned.yml @@ -0,0 +1,168 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 101 + double_field: 101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_min: + min: + field: unsigned_field + the_double_min: + min: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_min.value: 1.0 } + - match: { aggregations.the_double_min.value: 1.0 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + the_unsigned_min: + min: + field: unsigned_field + the_double_min: + min: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.the_unsigned_min.value: 1.0 } + - match: { aggregations.the_double_min.value: 1.0 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + the_unsigned_min: + min: + field: unsigned_field + the_double_min: + min: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { aggregations.the_unsigned_min.value: 51.0 } + - match: { aggregations.the_double_min.value: 51.0 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_min: + min: + field: foo + missing: 1 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_missing_min.value: 1.0 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_min: + min: + field: foo + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - is_false: aggregations.the_missing_min.value + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_min: + meta: + foo: bar + min: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_min.value: 1.0 } + - match: { aggregations.the_unsigned_min.meta.foo: "bar" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/130_sum_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/130_sum_metric_unsigned.yml new file mode 100644 index 0000000000000..2c2cce1a24022 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/130_sum_metric_unsigned.yml @@ -0,0 +1,168 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 1101 + double_field: 1101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_sum: + sum: + field: unsigned_field + the_double_sum: + sum: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_sum.value: 10223372036854780000.0 } + - match: { aggregations.the_double_sum.value: 10223372036854780000.0 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + the_unsigned_sum: + sum: + field: unsigned_field + the_double_sum: + sum: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.the_unsigned_sum.value: 10223372036854780000.0 } + - match: { aggregations.the_double_sum.value: 10223372036854780000.0 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + the_unsigned_sum: + sum: + field: unsigned_field + the_double_sum: + sum: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { aggregations.the_unsigned_sum.value: 10223372036854780000.0 } + - match: { aggregations.the_double_sum.value: 10223372036854780000.0 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_sum: + sum: + field: foo + missing: 1 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_missing_sum.value: 4.0 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_sum: + sum: + field: foo + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_missing_sum.value: 0.0 } + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_sum: + meta: + foo: bar + sum: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_sum.value: 10223372036854780000.0 } + - match: { aggregations.the_unsigned_sum.meta.foo: "bar" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/140_value_count_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/140_value_count_metric_unsigned.yml new file mode 100644 index 0000000000000..7f8ee96142676 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/140_value_count_metric_unsigned.yml @@ -0,0 +1,180 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 101 + double_field: 101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_value_count: + value_count: + field: unsigned_field + the_double_value_count: + value_count: + field: double_field + the_string_value_count: + value_count: + field: string_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_value_count.value: 4 } + - match: { aggregations.the_double_value_count.value: 4 } + - match: { aggregations.the_string_value_count.value: 4 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + the_unsigned_value_count: + value_count: + field: unsigned_field + the_double_value_count: + value_count: + field: double_field + the_string_value_count: + value_count: + field: string_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.the_unsigned_value_count.value: 4 } + - match: { aggregations.the_double_value_count.value: 4 } + - match: { aggregations.the_string_value_count.value: 4 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + the_unsigned_value_count: + value_count: + field: unsigned_field + the_double_value_count: + value_count: + field: double_field + the_string_value_count: + value_count: + field: string_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { aggregations.the_unsigned_value_count.value: 3 } + - match: { aggregations.the_double_value_count.value: 3 } + - match: { aggregations.the_string_value_count.value: 3 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_value_count: + value_count: + field: foo + missing: 1 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_missing_value_count.value: 4 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_value_count: + value_count: + field: foo + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - is_false: aggregations.the_missing_value_count.value + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_value_count: + meta: + foo: bar + value_count: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_value_count.value: 4 } + - match: { aggregations.the_unsigned_value_count.meta.foo: "bar" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/150_stats_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/150_stats_metric_unsigned.yml new file mode 100644 index 0000000000000..717af8fe15ae8 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/150_stats_metric_unsigned.yml @@ -0,0 +1,199 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 1101 + double_field: 1101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_stats: + stats: + field: unsigned_field + the_double_stats: + stats: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_stats.count: 4 } + - match: { aggregations.the_unsigned_stats.min: 1.0 } + - match: { aggregations.the_unsigned_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_unsigned_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_double_stats.count: 4 } + - match: { aggregations.the_double_stats.min: 1.0 } + - match: { aggregations.the_double_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_double_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_double_stats.sum: 10223372036854780000.0 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + the_unsigned_stats: + stats: + field: unsigned_field + the_double_stats: + stats: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.the_unsigned_stats.count: 4 } + - match: { aggregations.the_unsigned_stats.min: 1.0 } + - match: { aggregations.the_unsigned_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_unsigned_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_double_stats.count: 4 } + - match: { aggregations.the_double_stats.min: 1.0 } + - match: { aggregations.the_double_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_double_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_double_stats.sum: 10223372036854780000.0 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + the_unsigned_stats: + stats: + field: unsigned_field + the_double_stats: + stats: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { aggregations.the_unsigned_stats.count: 3 } + - match: { aggregations.the_unsigned_stats.min: 51.0 } + - match: { aggregations.the_unsigned_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_stats.avg: 3407790678951593500.0 } + - match: { aggregations.the_unsigned_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_double_stats.count: 3 } + - match: { aggregations.the_double_stats.min: 51.0 } + - match: { aggregations.the_double_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_double_stats.avg: 3407790678951593500.0 } + - match: { aggregations.the_double_stats.sum: 10223372036854780000.0 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_stats: + stats: + field: foo + missing: 1 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_missing_stats.count: 4 } + - match: { aggregations.the_missing_stats.min: 1.0 } + - match: { aggregations.the_missing_stats.max: 1.0 } + - match: { aggregations.the_missing_stats.avg: 1.0 } + - match: { aggregations.the_missing_stats.sum: 4.0 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_stats: + stats: + field: foo + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - is_false: aggregations.the_missing_stats.value + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_stats: + meta: + foo: bar + stats: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_stats.count: 4 } + - match: { aggregations.the_unsigned_stats.min: 1.0 } + - match: { aggregations.the_unsigned_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_unsigned_stats.sum: 10223372036854780000.0 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/160_extended_stats_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/160_extended_stats_metric_unsigned.yml new file mode 100644 index 0000000000000..f2090cd081394 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/160_extended_stats_metric_unsigned.yml @@ -0,0 +1,276 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 1101 + double_field: 1101.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_extended_stats: + extended_stats: + field: unsigned_field + the_double_extended_stats: + extended_stats: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_extended_stats.count: 4 } + - match: { aggregations.the_unsigned_extended_stats.min: 1.0 } + - match: { aggregations.the_unsigned_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_extended_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation: 4426849948127849000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.upper: 11409542905469393000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.lower: -6297856887042003000.0 } + - match: { aggregations.the_double_extended_stats.count: 4 } + - match: { aggregations.the_double_extended_stats.min: 1.0 } + - match: { aggregations.the_double_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_double_extended_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_double_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_double_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation: 4426849948127849000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation_bounds.upper: 11409542905469393000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation_bounds.lower: -6297856887042003000.0 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + the_unsigned_extended_stats: + extended_stats: + field: unsigned_field + the_double_extended_stats: + extended_stats: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.the_unsigned_extended_stats.count: 4 } + - match: { aggregations.the_unsigned_extended_stats.min: 1.0 } + - match: { aggregations.the_unsigned_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_extended_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation: 4426849948127849000.0 } + - match: { aggregations.the_unsigned_extended_stats.variance: 19597000463239538000000000000000000000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.upper: 11409542905469393000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.lower: -6297856887042003000.0 } + - match: { aggregations.the_double_extended_stats.count: 4 } + - match: { aggregations.the_double_extended_stats.min: 1.0 } + - match: { aggregations.the_double_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_double_extended_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_double_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_double_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation: 4426849948127849000.0 } + - match: { aggregations.the_double_extended_stats.variance: 19597000463239538000000000000000000000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation_bounds.upper: 11409542905469393000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation_bounds.lower: -6297856887042003000.0 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + the_unsigned_extended_stats: + extended_stats: + field: unsigned_field + the_double_extended_stats: + extended_stats: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { aggregations.the_unsigned_extended_stats.count: 3 } + - match: { aggregations.the_unsigned_extended_stats.min: 51.0 } + - match: { aggregations.the_unsigned_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_extended_stats.avg: 3407790678951593500.0 } + - match: { aggregations.the_unsigned_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_unsigned_extended_stats.variance: 23226074623098710000000000000000000000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation: 4819343795901959000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.upper: 13046478270755512000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.lower: -6230896912852324000.0 } + - match: { aggregations.the_double_extended_stats.count: 3 } + - match: { aggregations.the_double_extended_stats.min: 51.0 } + - match: { aggregations.the_double_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_double_extended_stats.avg: 3407790678951593500.0 } + - match: { aggregations.the_double_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_double_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_double_extended_stats.variance: 23226074623098710000000000000000000000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation: 4819343795901959000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation_bounds.upper: 13046478270755512000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation_bounds.lower: -6230896912852324000.0 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_extended_stats: + extended_stats: + field: foo + missing: 1 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_missing_extended_stats.count: 4 } + - match: { aggregations.the_missing_extended_stats.min: 1.0 } + - match: { aggregations.the_missing_extended_stats.max: 1.0 } + - match: { aggregations.the_missing_extended_stats.avg: 1.0 } + - match: { aggregations.the_missing_extended_stats.sum: 4.0 } + - match: { aggregations.the_missing_extended_stats.sum_of_squares: 4.0 } + - match: { aggregations.the_missing_extended_stats.variance: 0.0 } + - match: { aggregations.the_missing_extended_stats.std_deviation: 0.0 } + - match: { aggregations.the_missing_extended_stats.std_deviation_bounds.upper: 1.0 } + - match: { aggregations.the_missing_extended_stats.std_deviation_bounds.lower: 1.0 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_missing_extended_stats: + extended_stats: + field: foo + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - is_false: aggregations.the_missing_extended_stats.value + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_extended_stats: + meta: + foo: bar + extended_stats: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_extended_stats.count: 4 } + - match: { aggregations.the_unsigned_extended_stats.min: 1.0 } + - match: { aggregations.the_unsigned_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_extended_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation: 4426849948127849000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.upper: 11409542905469393000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.lower: -6297856887042003000.0 } + +--- +"Sigma test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_extended_stats: + extended_stats: + field: unsigned_field + sigma: 3 + the_double_extended_stats: + extended_stats: + field: double_field + sigma: 3 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_extended_stats.count: 4 } + - match: { aggregations.the_unsigned_extended_stats.min: 1.0 } + - match: { aggregations.the_unsigned_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_unsigned_extended_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_unsigned_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_unsigned_extended_stats.variance: 19597000463239537000000000000000000000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation: 4426849948127849000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.upper: 15836392853597241000.0 } + - match: { aggregations.the_unsigned_extended_stats.std_deviation_bounds.lower: -10724706835169853000.0 } + - match: { aggregations.the_double_extended_stats.count: 4 } + - match: { aggregations.the_double_extended_stats.min: 1.0 } + - match: { aggregations.the_double_extended_stats.max: 10223372036854778000.0 } + - match: { aggregations.the_double_extended_stats.avg: 2555843009213695000.0 } + - match: { aggregations.the_double_extended_stats.sum: 10223372036854780000.0 } + - match: { aggregations.the_double_extended_stats.sum_of_squares: 104517335803944210000000000000000000000.0 } + - match: { aggregations.the_double_extended_stats.variance: 19597000463239537000000000000000000000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation: 4426849948127849000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation_bounds.upper: 15836392853597241000.0 } + - match: { aggregations.the_double_extended_stats.std_deviation_bounds.lower: -10724706835169853000.0 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/170_cardinality_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/170_cardinality_metric_unsigned.yml new file mode 100644 index 0000000000000..27245d1aa435c --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/170_cardinality_metric_unsigned.yml @@ -0,0 +1,287 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 1101 + double_field: 1101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + distinct_unsigned: + cardinality: + field: unsigned_field + distinct_double: + cardinality: + field: double_field + distinct_string: + cardinality: + field: string_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.distinct_unsigned.value: 4 } + - match: { aggregations.distinct_double.value: 4 } + - match: { aggregations.distinct_string.value: 1 } + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + distinct_unsigned: + cardinality: + field: unsigned_field + precision_threshold: 100 + distinct_double: + cardinality: + field: double_field + precision_threshold: 100 + distinct_string: + cardinality: + field: string_field + precision_threshold: 100 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.distinct_unsigned.value: 4 } + - match: { aggregations.distinct_double.value: 4 } + - match: { aggregations.distinct_string.value: 1 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + distinct_unsigned: + cardinality: + field: unsigned_field + distinct_double: + cardinality: + field: double_field + distinct_string: + cardinality: + field: string_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.distinct_unsigned.value: 4 } + - match: { aggregations.distinct_double.value: 4 } + - match: { aggregations.distinct_string.value: 1 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + distinct_unsigned: + cardinality: + field: unsigned_field + distinct_double: + cardinality: + field: double_field + distinct_string: + cardinality: + field: string_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { aggregations.distinct_unsigned.value: 3 } + - match: { aggregations.distinct_double.value: 3 } + - match: { aggregations.distinct_string.value: 1 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + distinct_missing: + cardinality: + field: missing_field + missing: "foo" + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.distinct_missing.value: 1 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + distinct_missing: + cardinality: + field: missing_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - is_false: aggregations.distinct_missing.value + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + distinct_missing: + meta: + foo: bar + cardinality: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.distinct_missing.value: 4 } + - match: { aggregations.distinct_missing.meta.foo: "bar" } + +--- +"Invalid Precision test": + + - do: + catch: /\[precisionThreshold\] must be greater than or equal to 0. Found \[-1\] in \[distinct_unsigned\]/ + search: + rest_total_hits_as_int: true + body: + aggs: + distinct_unsigned: + cardinality: + field: unsigned_field + precision_threshold: -1 + +--- +"profiler unsigned_long": + - do: + search: + body: + profile: true + size: 0 + aggs: + distinct_unsigned: + cardinality: + field: unsigned_field + - match: { aggregations.distinct_unsigned.value: 4 } + - gt: { profile.shards.0.aggregations.0.breakdown.initialize: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.build_leaf_collector: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.collect: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.build_aggregation: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.post_collection: 0 } + - match: { profile.shards.0.aggregations.0.debug.empty_collectors_used: 0 } + - gt: { profile.shards.0.aggregations.0.debug.numeric_collectors_used: 0 } + - match: { profile.shards.0.aggregations.0.debug.ordinals_collectors_used: 0 } + - match: { profile.shards.0.aggregations.0.debug.ordinals_collectors_overhead_too_high: 0 } + - match: { profile.shards.0.aggregations.0.debug.string_hashing_collectors_used: 0 } + +--- +"profiler double": + - do: + search: + body: + profile: true + size: 0 + aggs: + distinct_double: + cardinality: + field: double_field + - match: { aggregations.distinct_double.value: 4 } + - gt: { profile.shards.0.aggregations.0.breakdown.initialize: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.build_leaf_collector: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.collect: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.build_aggregation: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.post_collection: 0 } + - match: { profile.shards.0.aggregations.0.debug.empty_collectors_used: 0 } + - gt: { profile.shards.0.aggregations.0.debug.numeric_collectors_used: 0 } + - match: { profile.shards.0.aggregations.0.debug.ordinals_collectors_used: 0 } + - match: { profile.shards.0.aggregations.0.debug.ordinals_collectors_overhead_too_high: 0 } + - match: { profile.shards.0.aggregations.0.debug.string_hashing_collectors_used: 0 } + +--- +"profiler string": + - do: + search: + body: + profile: true + size: 0 + aggs: + distinct_string: + cardinality: + field: string_field + - match: { aggregations.distinct_string.value: 1 } + - gt: { profile.shards.0.aggregations.0.breakdown.initialize: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.build_leaf_collector: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.collect: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.build_aggregation: 0 } + - gt: { profile.shards.0.aggregations.0.breakdown.post_collection: 0 } + - match: { profile.shards.0.aggregations.0.debug.empty_collectors_used: 0 } + - match: { profile.shards.0.aggregations.0.debug.numeric_collectors_used: 0 } + - gt: { profile.shards.0.aggregations.0.debug.ordinals_collectors_used: 0 } + - match: { profile.shards.0.aggregations.0.debug.ordinals_collectors_overhead_too_high: 0 } + - match: { profile.shards.0.aggregations.0.debug.string_hashing_collectors_used: 0 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/180_percentiles_tdigest_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/180_percentiles_tdigest_metric_unsigned.yml new file mode 100644 index 0000000000000..b572be74e908b --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/180_percentiles_tdigest_metric_unsigned.yml @@ -0,0 +1,588 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 1101 + double_field: 1101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + +--- +"Basic 2.x test": + - skip: + version: "3.0.0 -" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "- 2.99.99" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percentiles_double: + percentiles: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 26.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 576.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 5111686018427390000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 26.0 } + - match: { aggregations.percentiles_double.values.50\.0: 576.0 } + - match: { aggregations.percentiles_double.values.75\.0: 5111686018427390000.0 } + - match: { aggregations.percentiles_double.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.99\.0: 10223372036854778000.0 } + +--- +"Basic 3.x test": + - skip: + version: "- 2.99.99" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "3.0.0 -" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percentiles_double: + percentiles: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 51.0 } + - match: { aggregations.percentiles_double.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_double.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.99\.0: 10223372036854778000.0 } + +--- +"Compression 2.x test": + - skip: + version: "3.0.0 -" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "- 2.99.99" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + tdigest: + compression: 200 + percentiles_double: + percentiles: + field: double_field + tdigest: + compression: 200 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 26.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 576.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 5111686018427390000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 26.0 } + - match: { aggregations.percentiles_double.values.50\.0: 576.0 } + - match: { aggregations.percentiles_double.values.75\.0: 5111686018427390000.0 } + - match: { aggregations.percentiles_double.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.99\.0: 10223372036854778000.0 } + +--- +"Compression 3.x test": + - skip: + version: "- 2.99.99" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "3.0.0 -" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + tdigest: + compression: 200 + percentiles_double: + percentiles: + field: double_field + tdigest: + compression: 200 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 51.0 } + - match: { aggregations.percentiles_double.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_double.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.99\.0: 10223372036854778000.0 } + +--- +"Only aggs 2.x test": + - skip: + version: "3.0.0 -" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "- 2.99.99" + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percentiles_double: + percentiles: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 26.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 576.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 5111686018427390000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 26.0 } + - match: { aggregations.percentiles_double.values.50\.0: 576.0 } + - match: { aggregations.percentiles_double.values.75\.0: 5111686018427390000.0 } + - match: { aggregations.percentiles_double.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.99\.0: 10223372036854778000.0 } + +--- +"Only aggs 3.x test": + - skip: + version: "- 2.99.99" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "3.0.0 -" + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percentiles_double: + percentiles: + field: double_field + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 51.0 } + - match: { aggregations.percentiles_double.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_double.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.99\.0: 10223372036854778000.0 } + +--- +"Filtered 2.x test": + - skip: + version: "3.0.0 -" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "- 2.99.99" + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percentiles_double: + percentiles: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 313.5 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 7667529027641084000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + + - match: { aggregations.percentiles_double.values.1\.0: 51.0 } + - match: { aggregations.percentiles_double.values.5\.0: 51.0 } + - match: { aggregations.percentiles_double.values.25\.0: 313.5 } + - match: { aggregations.percentiles_double.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_double.values.75\.0: 7667529027641084000.0 } + - match: { aggregations.percentiles_double.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.99\.0: 10223372036854778000.0 } + +--- +"Filtered 3.x test": + - skip: + version: "- 2.99.99" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "3.0.0 -" + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percentiles_double: + percentiles: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + + - match: { aggregations.percentiles_double.values.1\.0: 51.0 } + - match: { aggregations.percentiles_double.values.5\.0: 51.0 } + - match: { aggregations.percentiles_double.values.25\.0: 51.0 } + - match: { aggregations.percentiles_double.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_double.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_double.values.99\.0: 10223372036854778000.0 } + +--- +"Metadata 2.x test": + - skip: + version: "3.0.0 -" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "- 2.99.99" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + meta: + foo: bar + percentiles: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.percentiles_unsigned.meta.foo: "bar" } + + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 26.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 576.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 5111686018427390000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + +--- +"Metadata 3.x test": + - skip: + version: "- 2.99.99" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "3.0.0 -" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + meta: + foo: bar + percentiles: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.percentiles_unsigned.meta.foo: "bar" } + + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 1101.0 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 10223372036854778000.0 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 10223372036854778000.0 } + +--- +"Explicit Percents 2.x test": + - skip: + version: "3.0.0 -" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "- 2.99.99" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percents: [5.0, 25.0, 50.0] + percentiles_double: + percentiles: + field: double_field + percents: [5.0, 25.0, 50.0] + + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 26.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 576.0 } + + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 26.0 } + - match: { aggregations.percentiles_double.values.50\.0: 576.0 } + +--- +"Explicit Percents 3.x test": + - skip: + version: "- 2.99.99" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "3.0.0 -" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percents: [5.0, 25.0, 50.0] + percentiles_double: + percentiles: + field: double_field + percents: [5.0, 25.0, 50.0] + + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 1101.0 } + + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 51.0 } + - match: { aggregations.percentiles_double.values.50\.0: 1101.0 } + +--- +"Non-keyed 2.x test": + - skip: + version: "3.0.0 -" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "- 2.99.99" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percents: [5.0, 25.0, 50.0] + keyed: false + + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.0.key: 5.0 } + - match: { aggregations.percentiles_unsigned.values.0.value: 1.0 } + - match: { aggregations.percentiles_unsigned.values.1.key: 25.0 } + - match: { aggregations.percentiles_unsigned.values.1.value: 26.0 } + - match: { aggregations.percentiles_unsigned.values.2.key: 50.0 } + - match: { aggregations.percentiles_unsigned.values.2.value: 576.0 } + +--- +"Non-keyed 3.x test": + - skip: + version: "- 2.99.99" + features: node_selector + reason: "t-digest 3.2 was interpolating leading to incorrect percentiles" + + - do: + node_selector: + version: "3.0.0 -" + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percents: [5.0, 25.0, 50.0] + keyed: false + + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.0.key: 5.0 } + - match: { aggregations.percentiles_unsigned.values.0.value: 1.0 } + - match: { aggregations.percentiles_unsigned.values.1.key: 25.0 } + - match: { aggregations.percentiles_unsigned.values.1.value: 51.0 } + - match: { aggregations.percentiles_unsigned.values.2.key: 50.0 } + - match: { aggregations.percentiles_unsigned.values.2.value: 1101.0 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/190_percentiles_hdr_metric.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/190_percentiles_hdr_metric.yml index 32c349c5e46b6..fd7e17306abd9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/190_percentiles_hdr_metric.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/190_percentiles_hdr_metric.yml @@ -160,6 +160,16 @@ setup: --- "Filtered test": + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 5 + - int_field: 126 + double_field: 126.0 + string_field: foo - do: search: rest_total_hits_as_int: true @@ -180,14 +190,14 @@ setup: field: double_field hdr: {} - - match: { hits.total: 3 } - - length: { hits.hits: 3 } + - match: { hits.total: 4 } + - length: { hits.hits: 4 } - match: { aggregations.percentiles_int.values.1\.0: 51.0 } - match: { aggregations.percentiles_int.values.5\.0: 51.0 } - match: { aggregations.percentiles_int.values.25\.0: 51.0 } - match: { aggregations.percentiles_int.values.50\.0: 101.03125 } - - match: { aggregations.percentiles_int.values.75\.0: 101.03125 } + - match: { aggregations.percentiles_int.values.75\.0: 126.03125 } - match: { aggregations.percentiles_int.values.95\.0: 151.09375 } - match: { aggregations.percentiles_int.values.99\.0: 151.09375 } @@ -195,7 +205,7 @@ setup: - match: { aggregations.percentiles_double.values.5\.0: 51.0 } - match: { aggregations.percentiles_double.values.25\.0: 51.0 } - match: { aggregations.percentiles_double.values.50\.0: 101.03125 } - - match: { aggregations.percentiles_double.values.75\.0: 101.03125 } + - match: { aggregations.percentiles_double.values.75\.0: 126.03125 } - match: { aggregations.percentiles_double.values.95\.0: 151.09375 } - match: { aggregations.percentiles_double.values.99\.0: 151.09375 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/190_percentiles_hdr_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/190_percentiles_hdr_metric_unsigned.yml new file mode 100644 index 0000000000000..f45be4632d3bd --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/190_percentiles_hdr_metric_unsigned.yml @@ -0,0 +1,344 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + number_of_shards: 5 + number_of_routing_shards: 5 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 1101 + double_field: 1101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 1425497558138880 + double_field: 1425497558138880.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + hdr: {} + percentiles_double: + percentiles: + field: double_field + hdr: {} + + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 51.0302734375 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 1101.9990234375 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 1426066581225472 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 1.0 } + - match: { aggregations.percentiles_double.values.50\.0: 51.0302734375 } + - match: { aggregations.percentiles_double.values.75\.0: 1101.9990234375 } + - match: { aggregations.percentiles_double.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_double.values.99\.0: 1426066581225472 } + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + hdr: + number_of_significant_value_digits: 3 + percentiles_double: + percentiles: + field: double_field + hdr: + number_of_significant_value_digits: 3 + + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 51.0302734375 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 1101.9990234375 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 1426066581225472 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 1.0 } + - match: { aggregations.percentiles_double.values.50\.0: 51.0302734375 } + - match: { aggregations.percentiles_double.values.75\.0: 1101.9990234375 } + - match: { aggregations.percentiles_double.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_double.values.99\.0: 1426066581225472 } + + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + hdr: {} + percentiles_double: + percentiles: + field: double_field + hdr: {} + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 51.0302734375 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 1101.9990234375 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 1426066581225472 } + + - match: { aggregations.percentiles_double.values.1\.0: 1.0 } + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 1.0 } + - match: { aggregations.percentiles_double.values.50\.0: 51.0302734375 } + - match: { aggregations.percentiles_double.values.75\.0: 1101.9990234375 } + - match: { aggregations.percentiles_double.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_double.values.99\.0: 1426066581225472 } + + +--- +"Filtered test": + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 5 + - unsigned_field: 126 + double_field: 126.0 + string_field: foo + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 50 + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + hdr: {} + percentiles_double: + percentiles: + field: double_field + hdr: {} + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 51.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 126.03125 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 1101.96875 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 1426066581225472 } + + - match: { aggregations.percentiles_double.values.1\.0: 51.0 } + - match: { aggregations.percentiles_double.values.5\.0: 51.0 } + - match: { aggregations.percentiles_double.values.25\.0: 51.0 } + - match: { aggregations.percentiles_double.values.50\.0: 126.03125 } + - match: { aggregations.percentiles_double.values.75\.0: 1101.96875 } + - match: { aggregations.percentiles_double.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_double.values.99\.0: 1426066581225472 } + + +--- +"Missing field with missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_missing: + percentiles: + field: missing_field + missing: 1.0 + hdr: {} + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_missing.values.1\.0: 1.0 } + - match: { aggregations.percentiles_missing.values.5\.0: 1.0 } + - match: { aggregations.percentiles_missing.values.25\.0: 1.0 } + - match: { aggregations.percentiles_missing.values.50\.0: 1.0 } + - match: { aggregations.percentiles_missing.values.75\.0: 1.0 } + - match: { aggregations.percentiles_missing.values.95\.0: 1.0 } + - match: { aggregations.percentiles_missing.values.99\.0: 1.0 } + +--- +"Missing field without missing param": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_missing: + percentiles: + field: missing_field + hdr: {} + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - is_false: aggregations.percentiles_missing.value + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + meta: + foo: bar + percentiles: + field: unsigned_field + hdr: {} + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.percentiles_unsigned.meta.foo: "bar" } + + - match: { aggregations.percentiles_unsigned.values.1\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 51.0302734375 } + - match: { aggregations.percentiles_unsigned.values.75\.0: 1101.9990234375 } + - match: { aggregations.percentiles_unsigned.values.95\.0: 1426066581225472 } + - match: { aggregations.percentiles_unsigned.values.99\.0: 1426066581225472 } + +--- +"Explicit Percents test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percents: [5.0, 25.0, 50.0] + hdr: {} + percentiles_double: + percentiles: + field: double_field + percents: [5.0, 25.0, 50.0] + hdr: {} + + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + - match: { aggregations.percentiles_unsigned.values.5\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.25\.0: 1.0 } + - match: { aggregations.percentiles_unsigned.values.50\.0: 51.0302734375 } + + + - match: { aggregations.percentiles_double.values.5\.0: 1.0 } + - match: { aggregations.percentiles_double.values.25\.0: 1.0 } + - match: { aggregations.percentiles_double.values.50\.0: 51.0302734375 } + + +--- +"Non-keyed test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + percentiles_unsigned: + percentiles: + field: unsigned_field + percents: [5.0, 25.0, 50.0] + keyed: false + hdr: {} + + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + + + - match: { aggregations.percentiles_unsigned.values.0.key: 5.0 } + - match: { aggregations.percentiles_unsigned.values.0.value: 1.0 } + - match: { aggregations.percentiles_unsigned.values.1.key: 25.0 } + - match: { aggregations.percentiles_unsigned.values.1.value: 1.0 } + - match: { aggregations.percentiles_unsigned.values.2.key: 50.0 } + - match: { aggregations.percentiles_unsigned.values.2.value: 51.0302734375 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yml index 7c7a223044725..0fa734b7e5fe9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/20_terms.yml @@ -245,6 +245,106 @@ setup: - match: { aggregations.integer_terms.buckets.1.doc_count: 1 } +--- +"Long test": + - do: + index: + index: test_1 + id: 1 + body: { "number": 4611686018427387903 } + + - do: + index: + index: test_1 + id: 2 + body: { "number": -4611686018427387904 } + + - do: + index: + index: test_1 + id: 3 + body: { "number": 4611686018427387903 } + + - do: + indices.refresh: {} + + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "long_terms" : { "terms" : { "field" : "number" } } } } + + - match: { hits.total: 3 } + + - length: { aggregations.long_terms.buckets: 2 } + + - match: { aggregations.long_terms.buckets.0.key: 4611686018427387903 } + + - is_false: aggregations.long_terms.buckets.0.key_as_string + + - match: { aggregations.long_terms.buckets.0.doc_count: 2 } + + - match: { aggregations.long_terms.buckets.1.key: -4611686018427387904 } + + - is_false: aggregations.long_terms.buckets.1.key_as_string + + - match: { aggregations.long_terms.buckets.1.doc_count: 1 } + +--- +"Unsigned Long test": + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.put_mapping: + index: test_1 + body: + properties: + unsigned: + type: unsigned_long + + - do: + index: + index: test_1 + id: 1 + body: { "unsigned": 18446744073709551615 } + + - do: + index: + index: test_1 + id: 2 + body: { "unsigned": 10223372036854775807 } + + - do: + index: + index: test_1 + id: 3 + body: { "unsigned": 18446744073709551615 } + + - do: + indices.refresh: {} + + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "unsigned_terms" : { "terms" : { "field" : "unsigned" } } } } + + - match: { hits.total: 3 } + + - length: { aggregations.unsigned_terms.buckets: 2 } + + - match: { aggregations.unsigned_terms.buckets.0.key: 18446744073709551615 } + + - is_false: aggregations.unsigned_terms.buckets.0.key_as_string + + - match: { aggregations.unsigned_terms.buckets.0.doc_count: 2 } + + - match: { aggregations.unsigned_terms.buckets.1.key: 10223372036854775807 } + + - is_false: aggregations.unsigned_terms.buckets.1.key_as_string + + - match: { aggregations.unsigned_terms.buckets.1.doc_count: 1 } + --- "Double test": - do: @@ -567,6 +667,41 @@ setup: - match: { aggregations.long_terms.buckets.0.doc_count: 1 } +--- +"Unmapped unsigned longs": + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.put_mapping: + index: test_1 + body: + properties: + unsigned: + type: unsigned_long + - do: + index: + index: test_1 + id: 1 + body: {} + + - do: + indices.refresh: {} + + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "unsigned_terms" : { "terms" : { "field" : "unmapped_unsigned", "value_type" : "unsigned_long", "missing": 3 } } } } + + - match: { hits.total: 1 } + + - length: { aggregations.unsigned_terms.buckets: 1 } + + - match: { aggregations.unsigned_terms.buckets.0.key: 3 } + + - match: { aggregations.unsigned_terms.buckets.0.doc_count: 1 } + --- "Unmapped doubles": @@ -649,6 +784,95 @@ setup: - match: { aggregations.number_terms.buckets.2.doc_count: 1 } +--- +"Mixing longs, unsigned long and doubles": + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.put_mapping: + index: test_1 + body: + properties: + unsigned: + type: unsigned_long + + - do: + indices.create: + index: test_3 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + number: + type: unsigned_long + + - do: + index: + index: test_1 + id: 1 + body: {"number": 100} + + - do: + index: + index: test_1 + id: 2 + body: {"number": 10} + + - do: + index: + index: test_2 + id: 3 + body: {"number": 100.0} + + - do: + index: + index: test_2 + id: 1 + body: {"number": 10.0} + + - do: + index: + index: test_2 + id: 2 + body: {"number": 14.6} + + - do: + index: + index: test_3 + id: 1 + body: {"number": 10223372036854775807} + + - do: + indices.refresh: {} + + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "number_terms" : { "terms" : { "field" : "number" } } } } + + - match: { hits.total: 6 } + + - length: { aggregations.number_terms.buckets: 4 } + + - match: { aggregations.number_terms.buckets.0.key: 10.0 } + + - match: { aggregations.number_terms.buckets.0.doc_count: 2 } + + - match: { aggregations.number_terms.buckets.1.key: 100.0 } + + - match: { aggregations.number_terms.buckets.1.doc_count: 2 } + + - match: { aggregations.number_terms.buckets.2.key: 14.6 } + + - match: { aggregations.number_terms.buckets.2.doc_count: 1 } + + - match: { aggregations.number_terms.buckets.3.key: 10223372036854775807.0 } + + - match: { aggregations.number_terms.buckets.3.doc_count: 1 } + --- "Deprecated _term order": @@ -824,6 +1048,7 @@ setup: indices.forcemerge: index: test_1 max_num_segments: 1 + flush: true - do: search: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/220_filters_bucket_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/220_filters_bucket_unsigned.yml new file mode 100644 index 0000000000000..68943bf42c2b8 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/220_filters_bucket_unsigned.yml @@ -0,0 +1,254 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + string_field: foo + - index: + _index: test_1 + _id: 2 + - unsigned_field: 51 + double_field: 51.0 + string_field: foo + - index: + _index: test_1 + _id: 3 + - unsigned_field: 1101 + double_field: 1101.0 + string_field: foo + - index: + _index: test_1 + _id: 4 + - unsigned_field: 10223372036854778000 + double_field: 10223372036854778000.0 + string_field: foo + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_filter: + filters: + filters: + first_filter: + match: + unsigned_field: 1101 + second_filter: + match: + unsigned_field: 10223372036854778000 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_filter.buckets.first_filter.doc_count: 1 } + - match: { aggregations.the_filter.buckets.second_filter.doc_count: 1 } + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_filter: + filters: + filters: + first_filter: + match: + unsigned_field: 1101 + second_filter: + match: + unsigned_field: 10223372036854778000 + aggs: + the_avg: + avg: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_filter.buckets.first_filter.doc_count: 1 } + - match: { aggregations.the_filter.buckets.first_filter.the_avg.value: 1101.0 } + - match: { aggregations.the_filter.buckets.second_filter.doc_count: 1 } + - match: { aggregations.the_filter.buckets.second_filter.the_avg.value: 10223372036854778000.0 } + +--- +"Anonymous filters test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_filter: + filters: + filters: + - match: + unsigned_field: 1101 + - match: + unsigned_field: 10223372036854778000 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_filter.buckets.0.doc_count: 1 } + - match: { aggregations.the_filter.buckets.1.doc_count: 1 } + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_filter: + filters: + filters: + - match: + unsigned_field: 1101 + - match: + unsigned_field: 10223372036854778000 + aggs: + the_avg: + avg: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_filter.buckets.0.doc_count: 1 } + - match: { aggregations.the_filter.buckets.0.the_avg.value: 1101.0 } + - match: { aggregations.the_filter.buckets.1.doc_count: 1 } + - match: { aggregations.the_filter.buckets.1.the_avg.value: 10223372036854778000.0 } + +--- +"Only aggs test": + + - do: + search: + rest_total_hits_as_int: true + body: + size: 0 + aggs: + the_filter: + filters: + filters: + first_filter: + match: + unsigned_field: 1101 + second_filter: + match: + unsigned_field: 10223372036854778000 + + - match: { hits.total: 4 } + - length: { hits.hits: 0 } + - match: { aggregations.the_filter.buckets.first_filter.doc_count: 1 } + - match: { aggregations.the_filter.buckets.second_filter.doc_count: 1 } + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_filter: + filters: + filters: + first_filter: + match: + unsigned_field: 1101 + second_filter: + match: + unsigned_field: 10223372036854778000 + aggs: + the_avg: + avg: + field: unsigned_field + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_filter.buckets.first_filter.doc_count: 1 } + - match: { aggregations.the_filter.buckets.first_filter.the_avg.value: 1101.0 } + - match: { aggregations.the_filter.buckets.second_filter.doc_count: 1 } + - match: { aggregations.the_filter.buckets.second_filter.the_avg.value: 10223372036854778000.0 } + +--- +"Filtered test": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + constant_score: + filter: + range: + unsigned_field: + gte: 1110 + aggs: + the_filter: + filters: + filters: + first_filter: + match: + unsigned_field: 1101 + second_filter: + match: + unsigned_field: 10223372036854778000 + aggs: + the_avg: + avg: + field: unsigned_field + + - match: { hits.total: 1 } + - length: { hits.hits: 1 } + - match: { aggregations.the_filter.buckets.first_filter.doc_count: 0 } + - is_false: aggregations.the_filter.buckets.first_filter.the_avg.value + - match: { aggregations.the_filter.buckets.second_filter.doc_count: 1 } + - match: { aggregations.the_filter.buckets.second_filter.the_avg.value: 10223372036854778000.0 } + + +--- +"Metadata test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_filter: + meta: + foo: bar + filters: + filters: + first_filter: + match: + unsigned_field: 1101 + second_filter: + match: + unsigned_field: 10223372036854778000 + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_filter.buckets.first_filter.doc_count: 1 } + - match: { aggregations.the_filter.buckets.second_filter.doc_count: 1 } + - match: { aggregations.the_filter.meta.foo: "bar" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml index 2e298441918bc..09278690f5d05 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite.yml @@ -651,92 +651,6 @@ setup: } ] ---- -"Simple Composite aggregation with GeoTile grid": - - skip: - version: " - 7.4.99" - reason: geotile_grid is not supported until 7.5.0 - - do: - search: - rest_total_hits_as_int: true - index: test - body: - aggregations: - test: - composite: - sources: [ - "geo": { - "geotile_grid": { - "field": "geo_point", - "precision": 12 - } - }, - { - "kw": { - "terms": { - "field": "keyword" - } - } - } - ] - - - match: {hits.total: 6} - - length: { aggregations.test.buckets: 4 } - - match: { aggregations.test.buckets.0.key.geo: "12/730/1590" } - - match: { aggregations.test.buckets.0.key.kw: "foo" } - - match: { aggregations.test.buckets.0.doc_count: 1 } - - match: { aggregations.test.buckets.1.key.geo: "12/1236/1533" } - - match: { aggregations.test.buckets.1.key.kw: "bar" } - - match: { aggregations.test.buckets.1.doc_count: 2 } - - match: { aggregations.test.buckets.2.key.geo: "12/1236/1533" } - - match: { aggregations.test.buckets.2.key.kw: "foo" } - - match: { aggregations.test.buckets.2.doc_count: 1 } - - match: { aggregations.test.buckets.3.key.geo: "12/2048/0" } - - match: { aggregations.test.buckets.3.key.kw: "bar" } - - match: { aggregations.test.buckets.3.doc_count: 1 } - ---- -"Simple Composite aggregation with geotile grid add aggregate after": - - skip: - version: " - 7.4.99" - reason: geotile_grid is not supported until 7.5.0 - - do: - search: - index: test - body: - aggregations: - test: - composite: - sources: [ - "geo": { - "geotile_grid": { - "field": "geo_point", - "precision": 12 - } - }, - { - "kw": { - "terms": { - "field": "keyword" - } - } - } - ] - after: { "geo": "12/730/1590", "kw": "foo" } - - - match: { hits.total.value: 6 } - - match: { hits.total.relation: "eq" } - - length: { aggregations.test.buckets: 3 } - - match: { aggregations.test.buckets.0.key.geo: "12/1236/1533" } - - match: { aggregations.test.buckets.0.key.kw: "bar" } - - match: { aggregations.test.buckets.0.doc_count: 2 } - - match: { aggregations.test.buckets.1.key.geo: "12/1236/1533" } - - match: { aggregations.test.buckets.1.key.kw: "foo" } - - match: { aggregations.test.buckets.1.doc_count: 1 } - - match: { aggregations.test.buckets.2.key.geo: "12/2048/0" } - - match: { aggregations.test.buckets.2.key.kw: "bar" } - - match: { aggregations.test.buckets.2.doc_count: 1 } - --- "Mixed ip and unmapped fields": - skip: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite_unsigned.yml new file mode 100644 index 0000000000000..75c951d56f2ae --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/230_composite_unsigned.yml @@ -0,0 +1,348 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test + body: + mappings: + properties: + date: + type: date + keyword: + type: keyword + unsigned_long: + type: unsigned_long + geo_point: + type: geo_point + nested: + type: nested + properties: + nested_unsigned_long: + type: unsigned_long + + - do: + indices.create: + index: other + body: + mappings: + properties: + date: + type: date + unsigned_long: + type: unsigned_long + nested: + type: nested + properties: + nested_long: + type: long + + - do: + index: + index: test + id: 1 + body: { "keyword": "foo", "unsigned_long": [10223372036854775807, 184], "geo_point": "37.2343,-115.8067", "nested": [{"nested_unsigned_long": 10223372036854775807}, {"nested_unsigned_long": 184}] } + + - do: + index: + index: test + id: 2 + body: { "keyword": ["foo", "bar"], "geo_point": "41.12,-71.34" } + + - do: + index: + index: test + id: 3 + body: { "keyword": "bar", "unsigned_long": [100, 0], "geo_point": "90.0,0.0", "nested": [{"nested_unsigned_long": 184}, {"nested_unsigned_long": 0}] } + + - do: + index: + index: test + id: 4 + body: { "keyword": "bar", "unsigned_long": [1000, 0], "geo_point": "41.12,-71.34", "nested": [{"nested_unsigned_long": 1000}, {"nested_unsigned_long": 10223372036854775807}] } + + - do: + index: + index: test + id: 5 + body: { "date": "2017-10-20T03:08:45" } + + - do: + index: + index: test + id: 6 + body: { "date": "2017-10-21T07:00:00" } + + - do: + index: + index: other + id: 0 + body: { "date": "2017-10-20T03:08:45" } + + - do: + indices.refresh: + index: [test, other] + + +--- +"Nested Composite aggregation": + - do: + search: + rest_total_hits_as_int: true + index: test + body: + aggregations: + test: + composite: + sources: [ + { + "unsigned_long": { + "terms": { + "field": "unsigned_long" + } + } + }, + { + "kw": { + "terms": { + "field": "keyword" + } + } + } + ] + + - match: { hits.total: 6 } + - length: { aggregations.test.buckets: 5 } + - match: { aggregations.test.buckets.0.key.unsigned_long: 0} + - match: { aggregations.test.buckets.0.key.kw: "bar" } + - match: { aggregations.test.buckets.0.doc_count: 2 } + - match: { aggregations.test.buckets.1.key.unsigned_long: 100 } + - match: { aggregations.test.buckets.1.key.kw: "bar"} + - match: { aggregations.test.buckets.1.doc_count: 1 } + - match: { aggregations.test.buckets.2.key.unsigned_long: 184 } + - match: { aggregations.test.buckets.2.key.kw: "foo" } + - match: { aggregations.test.buckets.2.doc_count: 1 } + - match: { aggregations.test.buckets.3.key.unsigned_long: 1000} + - match: { aggregations.test.buckets.3.key.kw: "bar" } + - match: { aggregations.test.buckets.3.doc_count: 1 } + - match: { aggregations.test.buckets.4.key.unsigned_long: 10223372036854775807 } + - match: { aggregations.test.buckets.4.key.kw: "foo" } + - match: { aggregations.test.buckets.4.doc_count: 1 } + +--- +"Aggregate After": + - do: + search: + rest_total_hits_as_int: true + index: test + body: + aggregations: + test: + composite: + sources: [ + { + "unsigned_long": { + "terms": { + "field": "unsigned_long" + } + } + }, + { + "kw": { + "terms": { + "field": "keyword" + } + } + } + ] + after: { "unsigned_long": 184, "kw": "foo" } + + - match: { hits.total: 6 } + - length: { aggregations.test.buckets: 2 } + - match: { aggregations.test.buckets.0.key.unsigned_long: 1000 } + - match: { aggregations.test.buckets.0.key.kw: "bar" } + - match: { aggregations.test.buckets.0.doc_count: 1 } + - match: { aggregations.test.buckets.1.key.unsigned_long: 10223372036854775807 } + - match: { aggregations.test.buckets.1.key.kw: "foo" } + - match: { aggregations.test.buckets.1.doc_count: 1 } + +--- +"Composite aggregation with nested parent": + - do: + search: + rest_total_hits_as_int: true + index: test + body: + aggregations: + 1: + nested: + path: nested + aggs: + 2: + composite: + sources: [ + "nested": { + "terms": { + "field": "nested.nested_unsigned_long" + } + } + ] + + - match: { hits.total: 6 } + - length: { aggregations.1.2.buckets: 4 } + - match: { aggregations.1.2.buckets.0.key.nested: 0 } + - match: { aggregations.1.2.buckets.0.doc_count: 1 } + - match: { aggregations.1.2.buckets.1.key.nested: 184 } + - match: { aggregations.1.2.buckets.1.doc_count: 2 } + - match: { aggregations.1.2.buckets.2.key.nested: 1000 } + - match: { aggregations.1.2.buckets.2.doc_count: 1 } + - match: { aggregations.1.2.buckets.3.key.nested: 10223372036854775807 } + - match: { aggregations.1.2.buckets.3.doc_count: 2 } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + aggregations: + 1: + nested: + path: nested + aggs: + 2: + composite: + after: { "nested": 184 } + sources: [ + "nested": { + "terms": { + "field": "nested.nested_unsigned_long" + } + } + ] + + - match: {hits.total: 6} + - length: { aggregations.1.2.buckets: 2 } + - match: { aggregations.1.2.buckets.0.key.nested: 1000 } + - match: { aggregations.1.2.buckets.0.doc_count: 1 } + - match: { aggregations.1.2.buckets.1.key.nested: 10223372036854775807 } + - match: { aggregations.1.2.buckets.1.doc_count: 2 } + +--- +"Composite aggregation with unmapped field": + - do: + search: + rest_total_hits_as_int: true + index: [test, other] + body: + aggregations: + test: + composite: + sources: [ + { + "unsigned_long": { + "terms": { + "field": "unsigned_long" + } + } + }, + { + "kw": { + "terms": { + "field": "keyword" + } + } + } + ] + + - match: {hits.total: 7} + - length: { aggregations.test.buckets: 5 } + - match: { aggregations.test.buckets.0.key.unsigned_long: 0} + - match: { aggregations.test.buckets.0.key.kw: "bar" } + - match: { aggregations.test.buckets.0.doc_count: 2 } + - match: { aggregations.test.buckets.1.key.unsigned_long: 100 } + - match: { aggregations.test.buckets.1.key.kw: "bar"} + - match: { aggregations.test.buckets.1.doc_count: 1 } + - match: { aggregations.test.buckets.2.key.unsigned_long: 184 } + - match: { aggregations.test.buckets.2.key.kw: "foo" } + - match: { aggregations.test.buckets.2.doc_count: 1 } + - match: { aggregations.test.buckets.3.key.unsigned_long: 1000} + - match: { aggregations.test.buckets.3.key.kw: "bar" } + - match: { aggregations.test.buckets.3.doc_count: 1 } + - match: { aggregations.test.buckets.4.key.unsigned_long: 10223372036854775807 } + - match: { aggregations.test.buckets.4.key.kw: "foo" } + - match: { aggregations.test.buckets.4.doc_count: 1 } + + - do: + search: + rest_total_hits_as_int: true + index: [test, other] + body: + aggregations: + test: + composite: + after: { "unsigned_long": 1000, "kw": "bar" } + sources: [ + { + "unsigned_long": { + "terms": { + "field": "unsigned_long" + } + } + }, + { + "kw": { + "terms": { + "field": "keyword" + } + } + } + ] + + - match: {hits.total: 7} + - length: { aggregations.test.buckets: 1 } + - match: { aggregations.test.buckets.0.key.unsigned_long: 10223372036854775807 } + - match: { aggregations.test.buckets.0.key.kw: "foo" } + - match: { aggregations.test.buckets.0.doc_count: 1 } + +--- +"Terms source from sorted": + - do: + indices.create: + index: sorted_test + body: + settings: + sort.field: keyword + mappings: + properties: + keyword: + type: keyword + unsigned_long: + type: unsigned_long + + + - do: + index: + index: sorted_test + id: 2 + refresh: true + body: { "keyword": "foo", "unsigned_long": 1 } + + - do: + search: + index: sorted_test + rest_total_hits_as_int: true + body: + aggregations: + test: + composite: + sources: + - keyword: + terms: + field: keyword + + - match: { hits.total: 1 } + - length: { aggregations.test.buckets: 1 } + - match: { aggregations.test.buckets.0.key.keyword: "foo" } + - match: { aggregations.test.buckets.0.doc_count: 1 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/240_max_buckets.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/240_max_buckets.yml index 82965bda51576..c540814a1690d 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/240_max_buckets.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/240_max_buckets.yml @@ -118,4 +118,3 @@ setup: 2: terms: field: date - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/250_moving_fn.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/250_moving_fn.yml index 339fe72b77730..c0a8d2fb4500c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/250_moving_fn.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/250_moving_fn.yml @@ -74,4 +74,3 @@ buckets_path: "the_avg" window: -1 script: "MovingFunctions.windowMax(values)" - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/260_weighted_avg_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/260_weighted_avg_unsigned.yml new file mode 100644 index 0000000000000..5cdf7726e7230 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/260_weighted_avg_unsigned.yml @@ -0,0 +1,70 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type : unsigned_long + double_field: + type : double + string_field: + type: keyword + + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + _id: 1 + - unsigned_field: 1 + double_field: 1.0 + - index: + _index: test_1 + _id: 2 + - unsigned_field: 2 + double_field: 2.0 + - index: + _index: test_1 + _id: 3 + - unsigned_field: 3 + double_field: 3.0 + - index: + _index: test_1 + _id: 4 + - unsigned_field: 4 + double_field: 4.0 + +--- +"Basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + the_unsigned_avg: + weighted_avg: + value: + field: "unsigned_field" + weight: + field: "unsigned_field" + the_double_avg: + weighted_avg: + value: + field: "double_field" + weight: + field: "double_field" + + - match: { hits.total: 4 } + - length: { hits.hits: 4 } + - match: { aggregations.the_unsigned_avg.value: 3.0 } + - match: { aggregations.the_double_avg.value: 3.0 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/270_median_absolute_deviation_metric_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/270_median_absolute_deviation_metric_unsigned.yml new file mode 100644 index 0000000000000..289b61c9c8783 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/270_median_absolute_deviation_metric_unsigned.yml @@ -0,0 +1,121 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test + body: + settings: + number_of_replicas: 0 + mappings: + properties: + unsigned_field: + type: unsigned_long + double_field: + type: double + incomplete_field: + type: integer + - do: + bulk: + refresh: true + body: + - index: + _index: test + - unsigned_field: 10223372036700000000 + double_field: 10223372036700000000 + incomplete_field: 1000 + - index: + _index: test + - unsigned_field: 10223372036800000000 + double_field: 10223372036800000000 + incomplete_field: 2000 + - index: + _index: test + - unsigned_field: 10223372036900000000 + double_field: 10223372036900000000.0 + +--- +"basic test": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + mad_unsigned: + median_absolute_deviation: + field: unsigned_field + mad_double: + median_absolute_deviation: + field: double_field + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + + - match: { aggregations.mad_unsigned.value: 99999744 } + - match: { aggregations.mad_double.value: 99999744.0 } + +--- +"with setting compression": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + mad_unsigned: + median_absolute_deviation: + field: unsigned_field + compression: 500 + mad_double: + median_absolute_deviation: + field: double_field + compression: 500 + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + + - match: { aggregations.mad_unsigned.value: 99999744 } + - match: { aggregations.mad_double.value: 99999744.0 } + +--- +"no documents": + + - do: + search: + rest_total_hits_as_int: true + body: + query: + bool: + filter: + term: + non_existent_field: non_existent_value + aggs: + mad_no_docs: + median_absolute_deviation: + field: non_existent_field + + - match: { hits.total: 0 } + - length: { hits.hits: 0 } + + - match: { aggregations.mad_no_docs.value: null } + +--- +"missing value": + + - do: + search: + rest_total_hits_as_int: true + body: + aggs: + mad_missing: + median_absolute_deviation: + field: incomplete_field + missing: 30000000000000000000 + + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + + - match: { aggregations.mad_missing.value: 1000 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/350_variable_width_histogram.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/350_variable_width_histogram.yml index 071e543e8a25e..cc41ef1fa6cd3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/350_variable_width_histogram.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/350_variable_width_histogram.yml @@ -47,4 +47,3 @@ setup: - match: { aggregations.histo.buckets.1.doc_count: 1 } - match: { aggregations.histo.buckets.2.key: 4.5 } - match: { aggregations.histo.buckets.2.doc_count: 2 } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/370_multi_terms.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/370_multi_terms.yml new file mode 100644 index 0000000000000..7db5f31d8e761 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/370_multi_terms.yml @@ -0,0 +1,762 @@ +setup: + - do: + indices.create: + index: test_1 + body: + settings: + number_of_shards: 1 + number_of_replicas: 0 + mappings: + properties: + str: + type: keyword + ip: + type: ip + boolean: + type: boolean + integer: + type: long + double: + type: double + number: + type: long + date: + type: date + + - do: + indices.create: + index: test_2 + body: + settings: + number_of_shards: 2 + number_of_replicas: 0 + mappings: + properties: + str: + type: keyword + integer: + type: long + boolean: + type: boolean + + - do: + cluster.health: + wait_for_status: green + +--- +"Basic test": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "integer": 1}' + - '{"index": {}}' + - '{"str": "a", "integer": 2}' + - '{"index": {}}' + - '{"str": "b", "integer": 1}' + - '{"index": {}}' + - '{"str": "b", "integer": 2}' + - '{"index": {}}' + - '{"str": "a", "integer": 1}' + - '{"index": {}}' + - '{"str": "b", "integer": 1}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: integer + + - length: { aggregations.m_terms.buckets: 4 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 1] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|1" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["b", 1] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "b|1" } + - match: { aggregations.m_terms.buckets.1.doc_count: 2 } + - match: { aggregations.m_terms.buckets.2.key: ["a", 2] } + - match: { aggregations.m_terms.buckets.2.key_as_string: "a|2" } + - match: { aggregations.m_terms.buckets.2.doc_count: 1 } + - match: { aggregations.m_terms.buckets.3.key: ["b", 2] } + - match: { aggregations.m_terms.buckets.3.key_as_string: "b|2" } + - match: { aggregations.m_terms.buckets.3.doc_count: 1 } + +--- +"IP test": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "ip": "::1"}' + - '{"index": {}}' + - '{"str": "a", "ip": "127.0.0.1"}' + - '{"index": {}}' + - '{"str": "b", "ip": "::1"}' + - '{"index": {}}' + - '{"str": "b", "ip": "127.0.0.1"}' + - '{"index": {}}' + - '{"str": "a", "ip": "127.0.0.1"}' + - '{"index": {}}' + - '{"str": "b", "ip": "::1"}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: ip + + - length: { aggregations.m_terms.buckets: 4 } + - match: { aggregations.m_terms.buckets.0.key: ["a", "127.0.0.1"] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|127.0.0.1" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["b", "::1"] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "b|::1" } + - match: { aggregations.m_terms.buckets.1.doc_count: 2 } + - match: { aggregations.m_terms.buckets.2.key: ["a", "::1"] } + - match: { aggregations.m_terms.buckets.2.key_as_string: "a|::1" } + - match: { aggregations.m_terms.buckets.2.doc_count: 1 } + - match: { aggregations.m_terms.buckets.3.key: ["b", "127.0.0.1"] } + - match: { aggregations.m_terms.buckets.3.key_as_string: "b|127.0.0.1" } + - match: { aggregations.m_terms.buckets.3.doc_count: 1 } + +--- +"Boolean test": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "boolean": true}' + - '{"index": {}}' + - '{"str": "a", "boolean": false}' + - '{"index": {}}' + - '{"str": "b", "boolean": false}' + - '{"index": {}}' + - '{"str": "b", "boolean": true}' + - '{"index": {}}' + - '{"str": "a", "boolean": true}' + - '{"index": {}}' + - '{"str": "b", "boolean": false}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: boolean + + - length: { aggregations.m_terms.buckets: 4 } + - match: { aggregations.m_terms.buckets.0.key: ["a", true] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|true" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["b", false] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "b|false" } + - match: { aggregations.m_terms.buckets.1.doc_count: 2 } + - match: { aggregations.m_terms.buckets.2.key: ["a", false] } + - match: { aggregations.m_terms.buckets.2.key_as_string: "a|false" } + - match: { aggregations.m_terms.buckets.2.doc_count: 1 } + - match: { aggregations.m_terms.buckets.3.key: ["b", true] } + - match: { aggregations.m_terms.buckets.3.key_as_string: "b|true" } + - match: { aggregations.m_terms.buckets.3.doc_count: 1 } + +--- +"Long test": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "number": 4611686018427387903}' + - '{"index": {}}' + - '{"str": "a", "number": -4611686018427387904}' + - '{"index": {}}' + - '{"str": "b", "number": 4611686018427387903}' + - '{"index": {}}' + - '{"str": "a", "number": 4611686018427387903}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: number + + - length: { aggregations.m_terms.buckets: 3 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 4611686018427387903] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|4611686018427387903" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["a", -4611686018427387904] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "a|-4611686018427387904" } + - match: { aggregations.m_terms.buckets.1.doc_count: 1 } + - match: { aggregations.m_terms.buckets.2.key: ["b", 4611686018427387903] } + - match: { aggregations.m_terms.buckets.2.key_as_string: "b|4611686018427387903" } + - match: { aggregations.m_terms.buckets.2.doc_count: 1 } + +--- +"Unsigned Long test": + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.put_mapping: + index: test_1 + body: + properties: + unsigned: + type: unsigned_long + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "unsigned": 18446744073709551615}' + - '{"index": {}}' + - '{"str": "a", "unsigned": 10223372036854775807}' + - '{"index": {}}' + - '{"str": "b", "unsigned": 18446744073709551615}' + - '{"index": {}}' + - '{"str": "a", "unsigned": 18446744073709551615}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: unsigned + + - length: { aggregations.m_terms.buckets: 3 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 18446744073709551615] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|18446744073709551615" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["a", 10223372036854775807] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "a|10223372036854775807" } + - match: { aggregations.m_terms.buckets.1.doc_count: 1 } + - match: { aggregations.m_terms.buckets.2.key: ["b", 18446744073709551615] } + - match: { aggregations.m_terms.buckets.2.key_as_string: "b|18446744073709551615" } + - match: { aggregations.m_terms.buckets.2.doc_count: 1 } + +--- +"Double test": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "double": 1234.5}' + - '{"index": {}}' + - '{"str": "a", "double": 5678.5}' + - '{"index": {}}' + - '{"str": "b", "double": 1234.5}' + - '{"index": {}}' + - '{"str": "a", "double": 1234.5}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: double + + - length: { aggregations.m_terms.buckets: 3 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 1234.5] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|1234.5" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["a", 5678.5] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "a|5678.5" } + - match: { aggregations.m_terms.buckets.1.doc_count: 1 } + - match: { aggregations.m_terms.buckets.2.key: ["b", 1234.5] } + - match: { aggregations.m_terms.buckets.2.key_as_string: "b|1234.5" } + - match: { aggregations.m_terms.buckets.2.doc_count: 1 } + +--- +"Date test": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "date": "2022-03-23"}' + - '{"index": {}}' + - '{"str": "a", "date": "2022-03-25"}' + - '{"index": {}}' + - '{"str": "b", "date": "2022-03-23"}' + - '{"index": {}}' + - '{"str": "a", "date": "2022-03-23"}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: date + + - length: { aggregations.m_terms.buckets: 3 } + - match: { aggregations.m_terms.buckets.0.key: ["a", "2022-03-23T00:00:00.000Z"] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|2022-03-23T00:00:00.000Z" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["a", "2022-03-25T00:00:00.000Z"] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "a|2022-03-25T00:00:00.000Z" } + - match: { aggregations.m_terms.buckets.1.doc_count: 1 } + - match: { aggregations.m_terms.buckets.2.key: ["b", "2022-03-23T00:00:00.000Z"] } + - match: { aggregations.m_terms.buckets.2.key_as_string: "b|2022-03-23T00:00:00.000Z" } + - match: { aggregations.m_terms.buckets.2.doc_count: 1 } + +--- +"Unmapped keywords": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "integer": 1}' + - '{"index": {}}' + - '{"str": "a", "integer": 2}' + - '{"index": {}}' + - '{"str": "b", "integer": 1}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: unmapped_string + value_type: string + missing: abc + + - length: { aggregations.m_terms.buckets: 2 } + - match: { aggregations.m_terms.buckets.0.key: ["a", "abc"] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|abc" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["b", "abc"] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "b|abc" } + - match: { aggregations.m_terms.buckets.1.doc_count: 1 } + +--- +"Null value": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "integer": null}' + - '{"index": {}}' + - '{"str": "a", "integer": 2}' + - '{"index": {}}' + - '{"str": null, "integer": 1}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: integer + + - length: { aggregations.m_terms.buckets: 1 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 2] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|2" } + - match: { aggregations.m_terms.buckets.0.doc_count: 1 } + +--- +"multiple multi_terms bucket": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "integer": 1, "double": 1234.5, "boolean": true}' + - '{"index": {}}' + - '{"str": "a", "integer": 1, "double": 5678.9, "boolean": false}' + - '{"index": {}}' + - '{"str": "a", "integer": 1, "double": 1234.5, "boolean": true}' + - '{"index": {}}' + - '{"str": "b", "integer": 1, "double": 1234.5, "boolean": true}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: integer + aggs: + n_terms: + multi_terms: + terms: + - field: double + - field: boolean + + - length: { aggregations.m_terms.buckets: 2 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 1] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|1" } + - match: { aggregations.m_terms.buckets.0.doc_count: 3 } + - match: { aggregations.m_terms.buckets.0.n_terms.buckets.0.key: [1234.5, true] } + - match: { aggregations.m_terms.buckets.0.n_terms.buckets.0.key_as_string: "1234.5|true" } + - match: { aggregations.m_terms.buckets.0.n_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.0.n_terms.buckets.1.key: [5678.9, false] } + - match: { aggregations.m_terms.buckets.0.n_terms.buckets.1.key_as_string: "5678.9|false" } + - match: { aggregations.m_terms.buckets.0.n_terms.buckets.1.doc_count: 1 } + - match: { aggregations.m_terms.buckets.1.key: ["b", 1] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "b|1" } + - match: { aggregations.m_terms.buckets.1.doc_count: 1 } + +--- +"ordered by metrics": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "double": 1234.5, "integer": 1}' + - '{"index": {}}' + - '{"str": "b", "double": 5678.9, "integer": 2}' + - '{"index": {}}' + - '{"str": "b", "double": 5678.9, "integer": 2}' + - '{"index": {}}' + - '{"str": "a", "double": 1234.5, "integer": 1}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: double + order: + the_int_sum: desc + aggs: + the_int_sum: + sum: + field: integer + + - length: { aggregations.m_terms.buckets: 2 } + - match: { aggregations.m_terms.buckets.0.key: ["b", 5678.9] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "b|5678.9" } + - match: { aggregations.m_terms.buckets.0.the_int_sum.value: 4.0 } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["a", 1234.5] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "a|1234.5" } + - match: { aggregations.m_terms.buckets.1.the_int_sum.value: 2.0 } + - match: { aggregations.m_terms.buckets.1.doc_count: 2 } + +--- +"top 1 ordered by metrics ": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "double": 1234.5, "integer": 1}' + - '{"index": {}}' + - '{"str": "b", "double": 5678.9, "integer": 2}' + - '{"index": {}}' + - '{"str": "b", "double": 5678.9, "integer": 2}' + - '{"index": {}}' + - '{"str": "a", "double": 1234.5, "integer": 1}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: double + order: + the_int_sum: desc + size: 1 + aggs: + the_int_sum: + sum: + field: integer + + - length: { aggregations.m_terms.buckets: 1 } + - match: { aggregations.m_terms.buckets.0.key: ["b", 5678.9] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "b|5678.9" } + - match: { aggregations.m_terms.buckets.0.the_int_sum.value: 4.0 } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + +--- +"min_doc_count": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "integer": 1}' + - '{"index": {}}' + - '{"str": "a", "integer": 1}' + - '{"index": {}}' + - '{"str": "b", "integer": 1}' + - '{"index": {}}' + - '{"str": "c", "integer": 1}' + + - do: + search: + index: test_1 + body: + size: 0 + query: + simple_query_string: + fields: [str] + query: a b + minimum_should_match: 1 + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: integer + min_doc_count: 2 + + - length: { aggregations.m_terms.buckets: 1 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 1] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|1" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + + - do: + search: + index: test_1 + body: + size: 0 + query: + simple_query_string: + fields: [str] + query: a b + minimum_should_match: 1 + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: integer + min_doc_count: 0 + + - length: { aggregations.m_terms.buckets: 3 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 1] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|1" } + - match: { aggregations.m_terms.buckets.0.doc_count: 2 } + - match: { aggregations.m_terms.buckets.1.key: ["b", 1] } + - match: { aggregations.m_terms.buckets.1.key_as_string: "b|1" } + - match: { aggregations.m_terms.buckets.1.doc_count: 1 } + - match: { aggregations.m_terms.buckets.2.key: ["c", 1] } + - match: { aggregations.m_terms.buckets.2.key_as_string: "c|1" } + - match: { aggregations.m_terms.buckets.2.doc_count: 0 } + +--- +"sum_other_doc_count": + - skip: + version: "- 2.0.99" + reason: multi_terms aggregation is introduced in 2.1.0 + + - do: + bulk: + index: test_2 + refresh: true + body: + - '{"index": {"routing": "s1"}}' + - '{"str": "a", "integer": 1}' + - '{"index": {"routing": "s1"}}' + - '{"str": "a", "integer": 1}' + - '{"index": {"routing": "s1"}}' + - '{"str": "a", "integer": 1}' + - '{"index": {"routing": "s1"}}' + - '{"str": "a", "integer": 1}' + - '{"index": {"routing": "s2"}}' + - '{"str": "b", "integer": 1}' + - '{"index": {"routing": "s2"}}' + - '{"str": "b", "integer": 1}' + - '{"index": {"routing": "s2"}}' + - '{"str": "b", "integer": 1}' + - '{"index": {"routing": "s2"}}' + - '{"str": "a", "integer": 1}' + + - do: + search: + index: test_2 + size: 0 + body: + aggs: + m_terms: + multi_terms: + size: 1 + shard_size: 1 + terms: + - field: str + - field: integer + + - length: { aggregations.m_terms.buckets: 1 } + - match: { aggregations.m_terms.sum_other_doc_count: 4 } + - match: { aggregations.m_terms.buckets.0.key: ["a", 1] } + - match: { aggregations.m_terms.buckets.0.key_as_string: "a|1" } + - match: { aggregations.m_terms.buckets.0.doc_count: 4 } + +--- +"aggregate over multi-terms test": + - skip: + version: "- 2.8.99" + reason: "multi_terms aggregation was introduced in 2.1.0, NPE bug checked by this test case will manifest in any version < 3.0" + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"str": "a", "ip": "127.0.0.1", "date": "2022-03-23"}' + - '{"index": {}}' + - '{"str": "a", "ip": "127.0.0.1", "date": "2022-03-25"}' + - '{"index": {}}' + - '{"str": "b", "ip": "127.0.0.1", "date": "2022-03-23"}' + - '{"index": {}}' + - '{"str": "b", "ip": "127.0.0.1", "date": "2022-03-25"}' + + - do: + search: + index: test_1 + size: 0 + body: + aggs: + histo: + date_histogram: + field: date + calendar_interval: day + aggs: + m_terms: + multi_terms: + terms: + - field: str + - field: ip + + - match: { hits.total.value: 4 } + - length: { aggregations.histo.buckets: 3 } + - match: { aggregations.histo.buckets.0.key_as_string: "2022-03-23T00:00:00.000Z" } + - match: { aggregations.histo.buckets.0.m_terms.buckets.0.key: ["a", "127.0.0.1"] } + - match: { aggregations.histo.buckets.0.m_terms.buckets.1.key: ["b", "127.0.0.1"] } + - match: { aggregations.histo.buckets.1.key_as_string: "2022-03-24T00:00:00.000Z" } + - length: { aggregations.histo.buckets.1.m_terms.buckets: 0 } + - match: { aggregations.histo.buckets.2.key_as_string: "2022-03-25T00:00:00.000Z" } + - match: { aggregations.histo.buckets.2.m_terms.buckets.0.key: [ "a", "127.0.0.1" ] } + - match: { aggregations.histo.buckets.2.m_terms.buckets.1.key: [ "b", "127.0.0.1" ] } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/380_doc_count_field.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/380_doc_count_field.yml new file mode 100644 index 0000000000000..a7a796eceeb39 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/380_doc_count_field.yml @@ -0,0 +1,146 @@ +setup: + - do: + indices.create: + index: test_1 + body: + settings: + number_of_replicas: 0 + mappings: + properties: + str: + type: keyword + number: + type: integer + + - do: + bulk: + index: test_1 + refresh: true + body: + - '{"index": {}}' + - '{"_doc_count": 10, "str": "abc", "number" : 500, "unmapped": "abc" }' + - '{"index": {}}' + - '{"_doc_count": 5, "str": "xyz", "number" : 100, "unmapped": "xyz" }' + - '{"index": {}}' + - '{"_doc_count": 7, "str": "foo", "number" : 100, "unmapped": "foo" }' + - '{"index": {}}' + - '{"_doc_count": 1, "str": "foo", "number" : 200, "unmapped": "foo" }' + - '{"index": {}}' + - '{"str": "abc", "number" : 500, "unmapped": "abc" }' + +--- +"Test numeric terms agg with doc_count": + - skip: + version: "1.0.0 - " + reason: "until this is released and we know exact version" + + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "num_terms" : { "terms" : { "field" : "number" } } } } + + - match: { hits.total: 5 } + - length: { aggregations.num_terms.buckets: 3 } + - match: { aggregations.num_terms.buckets.0.key: 100 } + - match: { aggregations.num_terms.buckets.0.doc_count: 12 } + - match: { aggregations.num_terms.buckets.1.key: 500 } + - match: { aggregations.num_terms.buckets.1.doc_count: 11 } + - match: { aggregations.num_terms.buckets.2.key: 200 } + - match: { aggregations.num_terms.buckets.2.doc_count: 1 } + +--- +"Test keyword terms agg with doc_count": + - skip: + version: "1.0.0 - " + reason: "until this is released and we know exact version" + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "str_terms" : { "terms" : { "field" : "str" } } } } + + - match: { hits.total: 5 } + - length: { aggregations.str_terms.buckets: 3 } + - match: { aggregations.str_terms.buckets.0.key: "abc" } + - match: { aggregations.str_terms.buckets.0.doc_count: 11 } + - match: { aggregations.str_terms.buckets.1.key: "foo" } + - match: { aggregations.str_terms.buckets.1.doc_count: 8 } + - match: { aggregations.str_terms.buckets.2.key: "xyz" } + - match: { aggregations.str_terms.buckets.2.doc_count: 5 } + +--- +"Test unmapped string terms agg with doc_count": + - skip: + version: "1.0.0 - " + reason: "until this is released and we know exact version" + - do: + bulk: + index: test_2 + refresh: true + body: + - '{"index": {}}' + - '{"_doc_count": 10, "str": "abc" }' + - '{"index": {}}' + - '{"str": "abc" }' + - do: + search: + index: test_2 + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "str_terms" : { "terms" : { "field" : "str.keyword" } } } } + + - match: { hits.total: 2 } + - length: { aggregations.str_terms.buckets: 1 } + - match: { aggregations.str_terms.buckets.0.key: "abc" } + - match: { aggregations.str_terms.buckets.0.doc_count: 11 } + +--- +"Test composite str_terms agg with doc_count": + - skip: + version: "1.0.0 - " + reason: "until this is released and we know exact version" + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : + { "composite_agg" : { "composite" : + { + "sources": ["str_terms": { "terms": { "field": "str" } }] + } + } + } + } + + - match: { hits.total: 5 } + - length: { aggregations.composite_agg.buckets: 3 } + - match: { aggregations.composite_agg.buckets.0.key.str_terms: "abc" } + - match: { aggregations.composite_agg.buckets.0.doc_count: 11 } + - match: { aggregations.composite_agg.buckets.1.key.str_terms: "foo" } + - match: { aggregations.composite_agg.buckets.1.doc_count: 8 } + - match: { aggregations.composite_agg.buckets.2.key.str_terms: "xyz" } + - match: { aggregations.composite_agg.buckets.2.doc_count: 5 } + +--- +"Test composite num_terms agg with doc_count": + - skip: + version: "1.0.0 - " + reason: "until this is released and we know exact version" + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : + { "composite_agg" : + { "composite" : + { + "sources": ["num_terms" : { "terms" : { "field" : "number" } }] + } + } + } + } + + - match: { hits.total: 5 } + - length: { aggregations.composite_agg.buckets: 3 } + - match: { aggregations.composite_agg.buckets.0.key.num_terms: 100 } + - match: { aggregations.composite_agg.buckets.0.doc_count: 12 } + - match: { aggregations.composite_agg.buckets.1.key.num_terms: 200 } + - match: { aggregations.composite_agg.buckets.1.doc_count: 1 } + - match: { aggregations.composite_agg.buckets.2.key.num_terms: 500 } + - match: { aggregations.composite_agg.buckets.2.doc_count: 11 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yml index a050b4c5bbf11..7d887d56ae8fe 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/40_range.yml @@ -427,3 +427,104 @@ setup: - match: { aggregations.date_range.buckets.0.key: "2020-01-01T00:00:00.000Z-*" } - is_false: aggregations.date_range.buckets.0.to - match: { aggregations.date_range.buckets.0.sounds.value: 0 } + +--- +"Unsigned long range": + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.put_mapping: + index: test + body: + properties: + unsigned: + type: unsigned_long + + - do: + index: + index: test + id: 1 + body: { "unsigned" : 42 } + + - do: + index: + index: test + id: 2 + body: { "unsigned" : 100 } + + - do: + index: + index: test + id: 3 + body: { "unsigned" : 50 } + + - do: + indices.refresh: {} + + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "unsigned_long_range" : { "range" : { "field" : "unsigned", "ranges": [ { "to": 50 }, { "from": 50, "to": 150 }, { "from": 150 } ] } } } } + + - match: { hits.total: 3 } + + - length: { aggregations.unsigned_long_range.buckets: 3 } + + - match: { aggregations.unsigned_long_range.buckets.0.key: "*-50.0" } + + - is_false: aggregations.unsigned_long_range.buckets.0.from + + - match: { aggregations.unsigned_long_range.buckets.0.to: 50.0 } + + - match: { aggregations.unsigned_long_range.buckets.0.doc_count: 1 } + + - match: { aggregations.unsigned_long_range.buckets.1.key: "50.0-150.0" } + + - match: { aggregations.unsigned_long_range.buckets.1.from: 50.0 } + + - match: { aggregations.unsigned_long_range.buckets.1.to: 150.0 } + + - match: { aggregations.unsigned_long_range.buckets.1.doc_count: 2 } + + - match: { aggregations.unsigned_long_range.buckets.2.key: "150.0-*" } + + - match: { aggregations.unsigned_long_range.buckets.2.from: 150.0 } + + - is_false: aggregations.unsigned_long_range.buckets.2.to + + - match: { aggregations.unsigned_long_range.buckets.2.doc_count: 0 } + + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { "unsigned_long_range" : { "range" : { "field" : "unsigned", "ranges": [ { "from": null, "to": 50 }, { "from": 50, "to": 150 }, { "from": 150, "to": null } ] } } } } + + - match: { hits.total: 3 } + + - length: { aggregations.unsigned_long_range.buckets: 3 } + + - match: { aggregations.unsigned_long_range.buckets.0.key: "*-50.0" } + + - is_false: aggregations.unsigned_long_range.buckets.0.from + + - match: { aggregations.unsigned_long_range.buckets.0.to: 50.0 } + + - match: { aggregations.unsigned_long_range.buckets.0.doc_count: 1 } + + - match: { aggregations.unsigned_long_range.buckets.1.key: "50.0-150.0" } + + - match: { aggregations.unsigned_long_range.buckets.1.from: 50.0 } + + - match: { aggregations.unsigned_long_range.buckets.1.to: 150.0 } + + - match: { aggregations.unsigned_long_range.buckets.1.doc_count: 2 } + + - match: { aggregations.unsigned_long_range.buckets.2.key: "150.0-*" } + + - match: { aggregations.unsigned_long_range.buckets.2.from: 150.0 } + + - is_false: aggregations.unsigned_long_range.buckets.2.to + + - match: { aggregations.unsigned_long_range.buckets.2.doc_count: 0 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/60_empty.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/60_empty.yml new file mode 100644 index 0000000000000..54f61d46b6f6c --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/60_empty.yml @@ -0,0 +1,32 @@ +--- +"Empty aggs Body": + - skip: + version: "- 2.8.99" + reason: "the fix was introduced in 2.9.0" + - do: + index: + index: test + id: 1 + body: { "double" : 42 } + + - do: + index: + index: test + id: 2 + body: { "double" : 100 } + + - do: + index: + index: test + id: 3 + body: { "double" : 50 } + + - do: + indices.refresh: {} + + - do: + search: + rest_total_hits_as_int: true + body: { "size" : 0, "aggs" : { } } + + - match: { hits.total: 3 } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/80_typed_keys_unsigned.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/80_typed_keys_unsigned.yml new file mode 100644 index 0000000000000..5ae912a92d8c8 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.aggregation/80_typed_keys_unsigned.yml @@ -0,0 +1,237 @@ +setup: + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test + body: + settings: + number_of_replicas: 0 + mappings: + properties: + name: + type: keyword + num: + type: unsigned_long + created: + type: date + + - do: + bulk: + refresh: true + index: test + body: + - '{"index": {}}' + - '{"name": "one", "num": 1, "created": "2010-03-12T01:07:45"}' + - '{"index": {}}' + - '{"name": "two", "num": 2, "created": "2010-03-12T04:11:00"}' + - '{"index": {}}' + - '{"name": "three", "num": 3, "created": "2010-04-27T03:43:34"}' + +--- +"Test typed keys parameter for avg aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_avg: + avg: + field: num + - is_true: aggregations.avg#test_avg + +--- +"Test typed keys parameter for cardinality aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_cardinality: + cardinality: + field: name + - is_true: aggregations.cardinality#test_cardinality + +--- +"Test typed keys parameter for extended_stats aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_extended_stats: + extended_stats: + field: num + - is_true: aggregations.extended_stats#test_extended_stats + +--- +"Test typed keys parameter for max aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_max: + max: + field: num + - is_true: aggregations.max#test_max + +--- +"Test typed keys parameter for min aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_min: + min: + field: num + - is_true: aggregations.min#test_min + +--- +"Test typed keys parameter for percentiles aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_percentiles: + percentiles: + field: num + - is_true: aggregations.tdigest_percentiles#test_percentiles + +--- +"Test typed keys parameter for percentile_ranks aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_percentile_ranks: + percentile_ranks: + field: num + values: [0,10] + - is_true: aggregations.tdigest_percentile_ranks#test_percentile_ranks + +--- +"Test typed keys parameter for stats aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_stats: + stats: + field: num + - is_true: aggregations.stats#test_stats + +--- +"Test typed keys parameter for sum aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_sum: + sum: + field: num + - is_true: aggregations.sum#test_sum + +--- +"Test typed keys parameter for terms and top_hits aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_terms: + terms: + field: name + aggs: + test_top_hits: + top_hits: + sort: num + - is_true: aggregations.sterms#test_terms + - is_true: aggregations.sterms#test_terms.buckets.0.top_hits#test_top_hits + - is_true: aggregations.sterms#test_terms.buckets.1.top_hits#test_top_hits + - is_true: aggregations.sterms#test_terms.buckets.2.top_hits#test_top_hits + +--- +"Test typed keys parameter for terms aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_terms: + terms: + field: num + - is_true: aggregations.ulterms#test_terms + +--- +"Test typed keys parameter for value_count aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_value_count: + value_count: + field: num + - is_true: aggregations.value_count#test_value_count + +--- +"Test typed keys parameter for date_histogram aggregation and max_bucket pipeline aggregation": + - do: + search: + rest_total_hits_as_int: true + typed_keys: true + body: + size: 0 + aggregations: + test_created_histogram: + date_histogram: + field: created + calendar_interval: month + aggregations: + test_sum: + sum: + field: num + test_deriv: + derivative: + buckets_path: "test_sum" + test_max_bucket: + max_bucket: + buckets_path: "test_created_histogram>test_sum" + + - is_true: aggregations.date_histogram#test_created_histogram + - is_true: aggregations.date_histogram#test_created_histogram.buckets.0.sum#test_sum + - is_true: aggregations.date_histogram#test_created_histogram.buckets.1.sum#test_sum + - is_true: aggregations.date_histogram#test_created_histogram.buckets.1.derivative#test_deriv + - is_true: aggregations.bucket_metric_value#test_max_bucket diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/30_max_analyzed_offset.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/30_max_analyzed_offset.yml index 462f4f5d25e0b..a18ac45e62175 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/30_max_analyzed_offset.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/30_max_analyzed_offset.yml @@ -36,6 +36,17 @@ setup: body: {"query" : {"match" : {"field1" : "fox"}}, "highlight" : {"type" : "unified", "fields" : {"field1" : {}}}} - match: { error.root_cause.0.type: "illegal_argument_exception" } +--- +"Unified highlighter on a field WITHOUT OFFSETS using max_analyzer_offset should SUCCEED": + - skip: + version: " - 2.1.99" + reason: only starting supporting the parameter max_analyzer_offset on version 2.2 + - do: + search: + rest_total_hits_as_int: true + index: test1 + body: {"query" : {"match" : {"field1" : "quick"}}, "highlight" : {"type" : "unified", "fields" : {"field1" : {"max_analyzer_offset": 10}}}} + - match: {hits.hits.0.highlight.field1.0: "The quick brown fox went to the forest and saw another fox."} --- "Plain highlighter on a field WITHOUT OFFSETS exceeding index.highlight.max_analyzed_offset should FAIL": diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yml index 091638d6a07fb..4de2e8142f6ec 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/10_source_filtering.yml @@ -196,4 +196,3 @@ setup: # When this test is run during runtime-field's tests we *don't* get floating point errors. Thus the funny assertion here that matches both. - lt: { hits.hits.0.fields.d.0: 3.141 } - gte: { hits.hits.0.fields.d.0: 3.14 } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml index 149bc90f31ea0..78134336ea45e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/110_field_collapsing.yml @@ -254,6 +254,7 @@ setup: - do: catch: /cannot use \`collapse\` in conjunction with \`search_after\`/ search: + allow_partial_search_results: false rest_total_hits_as_int: true index: test body: @@ -267,6 +268,7 @@ setup: - do: catch: /cannot use \`collapse\` in conjunction with \`rescore\`/ search: + allow_partial_search_results: false rest_total_hits_as_int: true index: test body: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/140_pre_filter_search_shards.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/140_pre_filter_search_shards.yml index 31f6f35003e2d..40e1fbcf7a2ab 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/140_pre_filter_search_shards.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/140_pre_filter_search_shards.yml @@ -154,4 +154,3 @@ setup: - match: { _shards.failed: 0 } - match: { hits.total: 2 } - length: { aggregations.idx_terms.buckets: 2 } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/160_exists_query.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/160_exists_query.yml index 201e456be2cdd..be97930d41eb9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/160_exists_query.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/160_exists_query.yml @@ -561,19 +561,6 @@ setup: - match: {hits.total: 4} ---- -"Test exists query on _type field": - - do: - search: - rest_total_hits_as_int: true - index: test - body: - query: - exists: - field: _type - - - match: {hits.total: 4} - --- "Test exists query on _routing field": - do: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/190_index_prefix_search.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/190_index_prefix_search.yml index 6f276f669f815..4532a26451c05 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/190_index_prefix_search.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/190_index_prefix_search.yml @@ -17,6 +17,12 @@ setup: id: 1 body: { text: some short words with a stupendously long one } + - do: + index: + index: test + id: 2 + body: { text: sentence with UPPERCASE WORDS } + - do: indices.refresh: index: [test] @@ -76,6 +82,25 @@ setup: - match: {hits.max_score: 1} - match: {hits.hits.0._score: 1} +--- +"search with uppercase regex": + - skip: + version: " - 2.3.99" + reason: uppercase regex not supported before 2.4.0 + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + query_string: + default_field: text + query: /UPPERCASE/ + + - match: {hits.total: 1} + - match: {hits.max_score: 1} + - match: {hits.hits.0._score: 1} + --- "search index prefixes with span_multi": - do: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/230_interval_query.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/230_interval_query.yml index 0286d3caf66b8..0b9172e0740ea 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/230_interval_query.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/230_interval_query.yml @@ -750,5 +750,3 @@ setup: - prefix: prefix: out - match: { hits.total.value: 3 } - - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/240_date_nanos.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/240_date_nanos.yml index feb875e81a785..1ddba45c97c72 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/240_date_nanos.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/240_date_nanos.yml @@ -164,4 +164,3 @@ setup: - match: { aggregations.date.buckets.1.key: 1540857600000 } - match: { aggregations.date.buckets.1.key_as_string: "2018-10-30T00:00:00.000Z" } - match: { aggregations.date.buckets.1.doc_count: 2 } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/260_sort_mixed.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/260_sort_mixed.yml new file mode 100644 index 0000000000000..ba2b18eb3b6d0 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/260_sort_mixed.yml @@ -0,0 +1,121 @@ +"search across indices with mixed long and double numeric types": + - skip: + version: " - 2.6.99" + reason: relies on numeric sort optimization that landed in 2.7.0 only + + - do: + indices.create: + index: test_1 + body: + mappings: + properties: + counter: + type: long + + - do: + indices.create: + index: test_2 + body: + mappings: + properties: + counter: + type: double + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + - counter: 223372036854775800 + - index: + _index: test_2 + - counter: 1223372036854775800.23 + - index: + _index: test_2 + - counter: 184.4 + + - do: + search: + index: test_* + rest_total_hits_as_int: true + body: + sort: [{ counter: desc }] + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { hits.hits.0._index: test_2 } + - match: { hits.hits.0._source.counter: 1223372036854775800.23 } + - match: { hits.hits.0.sort.0: 1223372036854775800.23 } + - match: { hits.hits.1._index: test_1 } + - match: { hits.hits.1._source.counter: 223372036854775800 } + - match: { hits.hits.1.sort.0: 223372036854775800 } + + - do: + search: + index: test_* + rest_total_hits_as_int: true + body: + sort: [{ counter: asc }] + - match: { hits.total: 3 } + - length: { hits.hits: 3 } + - match: { hits.hits.0._index: test_2 } + - match: { hits.hits.0._source.counter: 184.4 } + - match: { hits.hits.0.sort.0: 184.4 } + - match: { hits.hits.1._index: test_1 } + - match: { hits.hits.1._source.counter: 223372036854775800 } + - match: { hits.hits.1.sort.0: 223372036854775800 } + +--- +"search across indices with mixed long, double and unsigned_long numeric types": + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test_1 + body: + mappings: + properties: + counter: + type: unsigned_long + - do: + indices.create: + index: test_2 + body: + mappings: + properties: + counter: + type: long + - do: + indices.create: + index: test_3 + body: + mappings: + properties: + counter: + type: double + - do: + bulk: + refresh: true + body: + - index: + _index: test_1 + - counter: 10223372036854775800 + - index: + _index: test_2 + - counter: 223372036854775800 + - index: + _index: test_3 + - counter: 184.0 + + - do: + catch: bad_request + search: + index: test_* + rest_total_hits_as_int: true + body: + sort: [{ counter: asc }] + # The search across fields with mixed types (where one of those is unsigned_long) is not supported + - match: { status: 400 } + - match: { error.type: search_phase_execution_exception } + - match: { error.caused_by.reason: "Can't do sort across indices, as a field has [unsigned_long] type in one index, and different type in another index!" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yml index 5f5d88dba7687..55e1566656faf 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/90_search_after.yml @@ -160,6 +160,28 @@ - match: {hits.hits.0._source.timestamp: "2019-10-21 00:30:04.828" } - match: {hits.hits.0.sort: [1571617804828] } + # search_after with the sort with missing + - do: + bulk: + refresh: true + index: test + body: | + {"index":{}} + {"timestamp": null} + - do: + search: + index: test + rest_total_hits_as_int: true + body: + "size": 5 + "sort": [ { "timestamp": { "order": "asc", "missing": "_last" } } ] + search_after: [ "2021-10-21 08:30:04.828" ] # making it out of min/max so only missing value hit is qualified + + - match: { hits.total: 3 } + - length: { hits.hits: 1 } + - match: { hits.hits.0._index: test } + - match: { hits.hits.0._source.timestamp: null } + --- "date_nanos": - skip: @@ -225,3 +247,76 @@ - match: {hits.hits.0._source.timestamp: "2019-10-21 00:30:04.828740" } - match: {hits.hits.0.sort: [1571617804828740000] } +--- +"unsigned long": + - skip: + version: " - 2.7.99" + reason: unsigned_long is not supported before 2.8.0 + + - do: + indices.create: + index: test + body: + mappings: + properties: + population: + type: unsigned_long + - do: + bulk: + refresh: true + index: test + body: | + {"index":{}} + {"population": 10223372036854775800} + {"index":{}} + {"population": 15223372036854775800} + + - do: + search: + index: test + rest_total_hits_as_int: true + body: + size: 1 + sort: [{ population: asc }] + - match: {hits.total: 2 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._index: test } + - match: {hits.hits.0._source.population: 10223372036854775800 } + - match: {hits.hits.0.sort: [10223372036854775800] } + + # search_after with the sort + - do: + search: + index: test + rest_total_hits_as_int: true + body: + size: 1 + sort: [{ population: asc }] + search_after: [10223372036854775800] + - match: {hits.total: 2 } + - length: {hits.hits: 1 } + - match: {hits.hits.0._index: test } + - match: {hits.hits.0._source.population: 15223372036854775800 } + - match: {hits.hits.0.sort: [15223372036854775800] } + + # search_after with the sort with missing + - do: + bulk: + refresh: true + index: test + body: | + {"index":{}} + {"population": null} + - do: + search: + index: test + rest_total_hits_as_int: true + body: + "size": 5 + "sort": [ { "population": { "order": "asc", "missing": "_last" } } ] + search_after: [15223372036854775801] # making it out of min/max so only missing value hit is qualified + + - match: { hits.total: 3 } + - length: { hits.hits: 1 } + - match: { hits.hits.0._index: test } + - match: { hits.hits.0._source.population: null } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search_pipeline/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search_pipeline/10_basic.yml new file mode 100644 index 0000000000000..b7e6addff7ad4 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search_pipeline/10_basic.yml @@ -0,0 +1,170 @@ +--- +# Only send sanity request to nodes in >=2.9 versions because search pipeline is GA in 2.9.0. +# Nodes from earlier versions (before 2.9.0) will submit the request to nodes from 2.9.0 +# or later during the "mixedClusterTest," but this will fail because 2.9.0 removed the feature flag setting. + +"Test basic pipeline crud": + - skip: + version: " - 2.8.99" + reason: "Only send sanity request to nodes in >=2.9 versions" + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "description": "_description", + "request_processors": [ + ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.get: + id: "my_pipeline" + - match: { my_pipeline.description: "_description" } + + - do: + search_pipeline.delete: + id: "my_pipeline" + - match: { acknowledged: true } + + - do: + catch: missing + search_pipeline.get: + id: "my_pipeline" + +--- +"Test Put Versioned Pipeline": + - skip: + version: " - 2.8.99" + reason: "Only send sanity request to nodes in >=2.9 versions" + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "version": 10, + "request_processors": [ ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.get: + id: "my_pipeline" + - match: { my_pipeline.version: 10 } + + # Lower version + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "version": 9, + "request_processors": [ ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.get: + id: "my_pipeline" + - match: { my_pipeline.version: 9 } + + # Higher version + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "version": 6789, + "request_processors": [ ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.get: + id: "my_pipeline" + - match: { my_pipeline.version: 6789 } + + # No version + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "request_processors": [ ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.get: + id: "my_pipeline" + - is_false: my_pipeline.version + + # Coming back with a version + - do: + search_pipeline.put: + id: "my_pipeline" + body: > + { + "version": 5385, + "request_processors": [ ] + } + - match: { acknowledged: true } + + - do: + search_pipeline.get: + id: "my_pipeline" + - match: { my_pipeline.version: 5385 } + + # Able to delete the versioned pipeline + - do: + search_pipeline.delete: + id: "my_pipeline" + - match: { acknowledged: true } + + - do: + catch: missing + search_pipeline.get: + id: "my_pipeline" +--- +"Test Get All Pipelines": + - skip: + version: " - 2.8.99" + reason: "Only send sanity request to nodes in >=2.9 versions" + - do: + search_pipeline.put: + id: "first_pipeline" + body: > + { + "description": "first", + "request_processors": [] + } + - do: + search_pipeline.put: + id: "second_pipeline" + body: > + { + "description": "second", + "request_processors": [] + } + + - do: + search_pipeline.get: {} + - match: { first_pipeline.description: "first" } + - match: { second_pipeline.description: "second" } + +--- +"Test invalid config": + - skip: + version: " - 2.8.99" + reason: "Only send sanity request to nodes in >=2.9 versions" + - do: + catch: /parse_exception/ + search_pipeline.put: + id: "my_pipeline" + body: > + { + "description": "_description", + "request_processors": [], + "invalid_field" : {} + } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml index d0385ac0125f4..aa0ebf46362d5 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml @@ -20,6 +20,35 @@ - is_true: tasks +--- +"tasks_list test detailed": + - skip: + features: [arbitrary_key] + version: " - 2.0.99" + reason: resource_stats were introduced in 2.1.0 + + - do: + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id + + - do: + tasks.list: {} + + - is_true: nodes + - is_true: nodes.$node_id.roles + + - do: + tasks.list: + group_by: parents + detailed: true + - set: + tasks._arbitrary_key_: task_id + + - is_true: tasks + - is_true: tasks.$task_id.resource_stats + - is_true: tasks.$task_id.resource_stats.total + --- "tasks_list headers": - skip: @@ -33,3 +62,21 @@ - is_true: tasks - match: { tasks.0.headers.X-Opaque-Id: "That is me" } +--- +"tasks_list detailed (with thread info)": + - skip: + version: " - 2.8.99" + reason: thread_info was introduced in 2.9.0 + features: [arbitrary_key] + + - do: + tasks.list: + group_by: parents + detailed: true + - set: + tasks._arbitrary_key_: task_id + + - is_true: tasks + - is_true: tasks.$task_id.resource_stats + - is_true: tasks.$task_id.resource_stats.thread_info + - is_true: tasks.$task_id.resource_stats.total diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/20_doc_upsert.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/20_doc_upsert.yml index 4d03971aba252..cfdd38b9ffd1c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/update/20_doc_upsert.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/update/20_doc_upsert.yml @@ -32,5 +32,3 @@ - match: { _source.foo: bar } - match: { _source.count: 1 } - - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/22_doc_as_upsert.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/22_doc_as_upsert.yml index c65fc5af27fcc..7ee5c01089ff1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/update/22_doc_as_upsert.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/update/22_doc_as_upsert.yml @@ -32,5 +32,3 @@ - match: { _source.foo: bar } - match: { _source.count: 2 } - - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/35_if_seq_no.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/35_if_seq_no.yml index f982adf693ad0..c93be37be49f5 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/update/35_if_seq_no.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/update/35_if_seq_no.yml @@ -61,4 +61,3 @@ - match: { errors: true } - match: { items.0.update.status: 409 } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/40_routing.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/40_routing.yml index 6f43d381e0537..28e42f9dafea9 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/update/40_routing.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/update/40_routing.yml @@ -55,4 +55,3 @@ doc: { foo: baz } - match: { get._source.foo: baz } - diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/update/85_fields_meta.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/update/85_fields_meta.yml index fe76ab5299cda..2d4fda22f4442 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/update/85_fields_meta.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/update/85_fields_meta.yml @@ -27,5 +27,3 @@ id: 1 parent: 5 stored_fields: [ _routing ] - - diff --git a/rest-api-spec/src/yamlRestTest/java/org/opensearch/test/rest/ClientYamlTestSuiteIT.java b/rest-api-spec/src/yamlRestTest/java/org/opensearch/test/rest/ClientYamlTestSuiteIT.java index 9a1973e9d5aeb..a7f196190c350 100644 --- a/rest-api-spec/src/yamlRestTest/java/org/opensearch/test/rest/ClientYamlTestSuiteIT.java +++ b/rest-api-spec/src/yamlRestTest/java/org/opensearch/test/rest/ClientYamlTestSuiteIT.java @@ -33,8 +33,8 @@ package org.opensearch.test.rest; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; - import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite; + import org.apache.lucene.tests.util.TimeUnits; import org.opensearch.test.rest.yaml.ClientYamlTestCandidate; import org.opensearch.test.rest.yaml.OpenSearchClientYamlSuiteTestCase; diff --git a/sandbox/plugins/concurrent-search/src/test/java/org/opensearch/search/profile/query/QueryProfilerTests.java b/sandbox/plugins/concurrent-search/src/test/java/org/opensearch/search/profile/query/QueryProfilerTests.java new file mode 100644 index 0000000000000..0b88abb314fcd --- /dev/null +++ b/sandbox/plugins/concurrent-search/src/test/java/org/opensearch/search/profile/query/QueryProfilerTests.java @@ -0,0 +1,316 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.profile.query; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field.Store; +import org.apache.lucene.document.StringField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LRUQueryCache; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryCachingPolicy; +import org.apache.lucene.search.QueryVisitor; +import org.apache.lucene.search.ScoreMode; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.search.ScorerSupplier; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TotalHitCountCollector; +import org.apache.lucene.search.Weight; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.apache.lucene.tests.search.RandomApproximationQuery; +import org.apache.lucene.tests.util.TestUtil; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.search.internal.ContextIndexSearcher; +import org.opensearch.search.profile.ProfileResult; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.threadpool.ThreadPool; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; + +public class QueryProfilerTests extends OpenSearchTestCase { + + private Directory dir; + private IndexReader reader; + private ContextIndexSearcher searcher; + private ExecutorService executor; + + @ParametersFactory + public static Collection concurrency() { + return Arrays.asList(new Integer[] { 0 }, new Integer[] { 5 }); + } + + public QueryProfilerTests(int concurrency) { + this.executor = (concurrency > 0) ? Executors.newFixedThreadPool(concurrency) : null; + } + + @Before + public void setUp() throws Exception { + super.setUp(); + + dir = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), dir); + final int numDocs = TestUtil.nextInt(random(), 1, 20); + for (int i = 0; i < numDocs; ++i) { + final int numHoles = random().nextInt(5); + for (int j = 0; j < numHoles; ++j) { + w.addDocument(new Document()); + } + Document doc = new Document(); + doc.add(new StringField("foo", "bar", Store.NO)); + w.addDocument(doc); + } + reader = w.getReader(); + w.close(); + searcher = new ContextIndexSearcher( + reader, + IndexSearcher.getDefaultSimilarity(), + IndexSearcher.getDefaultQueryCache(), + ALWAYS_CACHE_POLICY, + true, + executor + ); + } + + @After + public void tearDown() throws Exception { + super.tearDown(); + + LRUQueryCache cache = (LRUQueryCache) searcher.getQueryCache(); + assertThat(cache.getHitCount(), equalTo(0L)); + assertThat(cache.getCacheCount(), equalTo(0L)); + assertThat(cache.getTotalCount(), equalTo(cache.getMissCount())); + assertThat(cache.getCacheSize(), equalTo(0L)); + + if (executor != null) { + ThreadPool.terminate(executor, 10, TimeUnit.SECONDS); + } + + IOUtils.close(reader, dir); + dir = null; + reader = null; + searcher = null; + } + + public void testBasic() throws IOException { + QueryProfiler profiler = new QueryProfiler(executor != null); + searcher.setProfiler(profiler); + Query query = new TermQuery(new Term("foo", "bar")); + searcher.search(query, 1); + List results = profiler.getTree(); + assertEquals(1, results.size()); + Map breakdown = results.get(0).getTimeBreakdown(); + assertThat(breakdown.get(QueryTimingType.CREATE_WEIGHT.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.BUILD_SCORER.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.NEXT_DOC.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.ADVANCE.toString()), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.SCORE.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.MATCH.toString()), equalTo(0L)); + + assertThat(breakdown.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.BUILD_SCORER.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.NEXT_DOC.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.ADVANCE.toString() + "_count"), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.SCORE.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.MATCH.toString() + "_count"), equalTo(0L)); + + long rewriteTime = profiler.getRewriteTime(); + assertThat(rewriteTime, greaterThan(0L)); + } + + public void testNoScoring() throws IOException { + QueryProfiler profiler = new QueryProfiler(executor != null); + searcher.setProfiler(profiler); + Query query = new TermQuery(new Term("foo", "bar")); + searcher.search(query, 1, Sort.INDEXORDER); // scores are not needed + List results = profiler.getTree(); + assertEquals(1, results.size()); + Map breakdown = results.get(0).getTimeBreakdown(); + assertThat(breakdown.get(QueryTimingType.CREATE_WEIGHT.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.BUILD_SCORER.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.NEXT_DOC.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.ADVANCE.toString()), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.SCORE.toString()), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.MATCH.toString()), equalTo(0L)); + + assertThat(breakdown.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.BUILD_SCORER.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.NEXT_DOC.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.ADVANCE.toString() + "_count"), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.SCORE.toString() + "_count"), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.MATCH.toString() + "_count"), equalTo(0L)); + + long rewriteTime = profiler.getRewriteTime(); + assertThat(rewriteTime, greaterThan(0L)); + } + + public void testUseIndexStats() throws IOException { + QueryProfiler profiler = new QueryProfiler(executor != null); + searcher.setProfiler(profiler); + Query query = new TermQuery(new Term("foo", "bar")); + searcher.count(query); // will use index stats + List results = profiler.getTree(); + assertEquals(1, results.size()); + ProfileResult result = results.get(0); + assertEquals(0, (long) result.getTimeBreakdown().get("build_scorer_count")); + + long rewriteTime = profiler.getRewriteTime(); + assertThat(rewriteTime, greaterThan(0L)); + } + + public void testApproximations() throws IOException { + QueryProfiler profiler = new QueryProfiler(executor != null); + searcher.setProfiler(profiler); + Query query = new RandomApproximationQuery(new TermQuery(new Term("foo", "bar")), random()); + searcher.count(query); + List results = profiler.getTree(); + assertEquals(1, results.size()); + Map breakdown = results.get(0).getTimeBreakdown(); + assertThat(breakdown.get(QueryTimingType.CREATE_WEIGHT.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.BUILD_SCORER.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.NEXT_DOC.toString()), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.ADVANCE.toString()), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.SCORE.toString()), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.MATCH.toString()), greaterThan(0L)); + + assertThat(breakdown.get(QueryTimingType.CREATE_WEIGHT.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.BUILD_SCORER.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.NEXT_DOC.toString() + "_count"), greaterThan(0L)); + assertThat(breakdown.get(QueryTimingType.ADVANCE.toString() + "_count"), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.SCORE.toString() + "_count"), equalTo(0L)); + assertThat(breakdown.get(QueryTimingType.MATCH.toString() + "_count"), greaterThan(0L)); + + long rewriteTime = profiler.getRewriteTime(); + assertThat(rewriteTime, greaterThan(0L)); + } + + public void testCollector() throws IOException { + TotalHitCountCollector collector = new TotalHitCountCollector(); + ProfileCollector profileCollector = new ProfileCollector(collector); + assertEquals(0, profileCollector.getTime()); + final LeafCollector leafCollector = profileCollector.getLeafCollector(reader.leaves().get(0)); + assertThat(profileCollector.getTime(), greaterThan(0L)); + long time = profileCollector.getTime(); + leafCollector.setScorer(null); + assertThat(profileCollector.getTime(), greaterThan(time)); + time = profileCollector.getTime(); + leafCollector.collect(0); + assertThat(profileCollector.getTime(), greaterThan(time)); + } + + private static class DummyQuery extends Query { + + @Override + public String toString(String field) { + return getClass().getSimpleName(); + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { + return new Weight(this) { + @Override + public Explanation explain(LeafReaderContext context, int doc) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Scorer scorer(LeafReaderContext context) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOException { + return new ScorerSupplier() { + + @Override + public Scorer get(long loadCost) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public long cost() { + return 42; + } + }; + } + + @Override + public boolean isCacheable(LeafReaderContext ctx) { + return true; + } + }; + } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + } + + public void testScorerSupplier() throws IOException { + Directory dir = newDirectory(); + IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()); + w.addDocument(new Document()); + DirectoryReader reader = DirectoryReader.open(w); + w.close(); + IndexSearcher s = newSearcher(reader); + s.setQueryCache(null); + Weight weight = s.createWeight(s.rewrite(new DummyQuery()), randomFrom(ScoreMode.values()), 1f); + // exception when getting the scorer + expectThrows(UnsupportedOperationException.class, () -> weight.scorer(s.getIndexReader().leaves().get(0))); + // no exception, means scorerSupplier is delegated + weight.scorerSupplier(s.getIndexReader().leaves().get(0)); + reader.close(); + dir.close(); + } + + private static final QueryCachingPolicy ALWAYS_CACHE_POLICY = new QueryCachingPolicy() { + + @Override + public void onUse(Query query) {} + + @Override + public boolean shouldCache(Query query) throws IOException { + return true; + } + + }; +} diff --git a/sandbox/plugins/concurrent-search/src/test/java/org/opensearch/search/query/QueryPhaseTests.java b/sandbox/plugins/concurrent-search/src/test/java/org/opensearch/search/query/QueryPhaseTests.java new file mode 100644 index 0000000000000..a39a0cee9dbab --- /dev/null +++ b/sandbox/plugins/concurrent-search/src/test/java/org/opensearch/search/query/QueryPhaseTests.java @@ -0,0 +1,1333 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.search.query; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field.Store; +import org.apache.lucene.document.LatLonDocValuesField; +import org.apache.lucene.document.LatLonPoint; +import org.apache.lucene.document.LongPoint; +import org.apache.lucene.document.NumericDocValuesField; +import org.apache.lucene.document.SortedDocValuesField; +import org.apache.lucene.document.SortedSetDocValuesField; +import org.apache.lucene.document.StringField; +import org.apache.lucene.document.TextField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.NoMergePolicy; +import org.apache.lucene.index.Term; +import org.apache.lucene.queries.spans.SpanNearQuery; +import org.apache.lucene.queries.spans.SpanTermQuery; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanClause.Occur; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.CollectorManager; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.DocValuesFieldExistsQuery; +import org.apache.lucene.search.FieldComparator; +import org.apache.lucene.search.FieldDoc; +import org.apache.lucene.search.FilterCollector; +import org.apache.lucene.search.FilterLeafCollector; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.MatchNoDocsQuery; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.PrefixQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.TopFieldDocs; +import org.apache.lucene.search.TotalHitCountCollector; +import org.apache.lucene.search.TotalHits; +import org.apache.lucene.search.Weight; +import org.apache.lucene.search.grouping.CollapseTopFieldDocs; +import org.apache.lucene.search.join.BitSetProducer; +import org.apache.lucene.search.join.ScoreMode; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.FixedBitSet; +import org.opensearch.action.search.SearchShardTask; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.mapper.DateFieldMapper; +import org.opensearch.index.mapper.MappedFieldType; +import org.opensearch.index.mapper.MapperService; +import org.opensearch.index.mapper.NumberFieldMapper; +import org.opensearch.index.mapper.NumberFieldMapper.NumberFieldType; +import org.opensearch.index.mapper.NumberFieldMapper.NumberType; +import org.opensearch.index.query.ParsedQuery; +import org.opensearch.index.query.QueryShardContext; +import org.opensearch.index.search.OpenSearchToParentBlockJoinQuery; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.index.shard.IndexShardTestCase; +import org.opensearch.lucene.queries.MinDocQuery; +import org.opensearch.search.DocValueFormat; +import org.opensearch.search.collapse.CollapseBuilder; +import org.opensearch.search.internal.ContextIndexSearcher; +import org.opensearch.search.internal.ScrollContext; +import org.opensearch.search.internal.SearchContext; +import org.opensearch.search.sort.SortAndFormats; +import org.opensearch.tasks.TaskCancelledException; +import org.opensearch.test.TestSearchContext; +import org.opensearch.threadpool.ThreadPool; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.opensearch.search.query.TopDocsCollectorContext.hasInfMaxScore; + +public class QueryPhaseTests extends IndexShardTestCase { + + private IndexShard indexShard; + private final ExecutorService executor; + private final QueryPhaseSearcher queryPhaseSearcher; + + @ParametersFactory + public static Collection concurrency() { + return Arrays.asList( + new Object[] { 0, QueryPhase.DEFAULT_QUERY_PHASE_SEARCHER }, + new Object[] { 5, new ConcurrentQueryPhaseSearcher() } + ); + } + + public QueryPhaseTests(int concurrency, QueryPhaseSearcher queryPhaseSearcher) { + this.executor = (concurrency > 0) ? Executors.newFixedThreadPool(concurrency) : null; + this.queryPhaseSearcher = queryPhaseSearcher; + } + + @Override + public Settings threadPoolSettings() { + return Settings.builder().put(super.threadPoolSettings()).put("thread_pool.search.min_queue_size", 10).build(); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + indexShard = newShard(true); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + closeShards(indexShard); + + if (executor != null) { + ThreadPool.terminate(executor, 10, TimeUnit.SECONDS); + } + } + + private void countTestCase(Query query, IndexReader reader, boolean shouldCollectSearch, boolean shouldCollectCount) throws Exception { + ContextIndexSearcher searcher = shouldCollectSearch + ? newContextSearcher(reader, executor) + : newEarlyTerminationContextSearcher(reader, 0, executor); + TestSearchContext context = new TestSearchContext(null, indexShard, searcher); + context.parsedQuery(new ParsedQuery(query)); + context.setSize(0); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + final boolean rescore = QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertFalse(rescore); + + ContextIndexSearcher countSearcher = shouldCollectCount + ? newContextSearcher(reader, executor) + : newEarlyTerminationContextSearcher(reader, 0, executor); + assertEquals(countSearcher.count(query), context.queryResult().topDocs().topDocs.totalHits.value); + } + + private void countTestCase(boolean withDeletions) throws Exception { + Directory dir = newDirectory(); + IndexWriterConfig iwc = newIndexWriterConfig().setMergePolicy(NoMergePolicy.INSTANCE); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + final int numDocs = scaledRandomIntBetween(600, 900); + for (int i = 0; i < numDocs; ++i) { + Document doc = new Document(); + if (randomBoolean()) { + doc.add(new StringField("foo", "bar", Store.NO)); + doc.add(new SortedSetDocValuesField("foo", new BytesRef("bar"))); + doc.add(new SortedSetDocValuesField("docValuesOnlyField", new BytesRef("bar"))); + doc.add(new LatLonDocValuesField("latLonDVField", 1.0, 1.0)); + doc.add(new LatLonPoint("latLonDVField", 1.0, 1.0)); + } + if (randomBoolean()) { + doc.add(new StringField("foo", "baz", Store.NO)); + doc.add(new SortedSetDocValuesField("foo", new BytesRef("baz"))); + } + if (withDeletions && (rarely() || i == 0)) { + doc.add(new StringField("delete", "yes", Store.NO)); + } + w.addDocument(doc); + } + if (withDeletions) { + w.deleteDocuments(new Term("delete", "yes")); + } + final IndexReader reader = w.getReader(); + Query matchAll = new MatchAllDocsQuery(); + Query matchAllCsq = new ConstantScoreQuery(matchAll); + Query tq = new TermQuery(new Term("foo", "bar")); + Query tCsq = new ConstantScoreQuery(tq); + Query dvfeq = new DocValuesFieldExistsQuery("foo"); + Query dvfeq_points = new DocValuesFieldExistsQuery("latLonDVField"); + Query dvfeqCsq = new ConstantScoreQuery(dvfeq); + // field with doc-values but not indexed will need to collect + Query dvOnlyfeq = new DocValuesFieldExistsQuery("docValuesOnlyField"); + BooleanQuery bq = new BooleanQuery.Builder().add(matchAll, Occur.SHOULD).add(tq, Occur.MUST).build(); + + countTestCase(matchAll, reader, false, false); + countTestCase(matchAllCsq, reader, false, false); + countTestCase(tq, reader, withDeletions, withDeletions); + countTestCase(tCsq, reader, withDeletions, withDeletions); + countTestCase(dvfeq, reader, withDeletions, true); + countTestCase(dvfeq_points, reader, withDeletions, true); + countTestCase(dvfeqCsq, reader, withDeletions, true); + countTestCase(dvOnlyfeq, reader, true, true); + countTestCase(bq, reader, true, true); + reader.close(); + w.close(); + dir.close(); + } + + public void testCountWithoutDeletions() throws Exception { + countTestCase(false); + } + + public void testCountWithDeletions() throws Exception { + countTestCase(true); + } + + public void testPostFilterDisablesCountOptimization() throws Exception { + Directory dir = newDirectory(); + final Sort sort = new Sort(new SortField("rank", SortField.Type.INT)); + IndexWriterConfig iwc = newIndexWriterConfig().setIndexSort(sort); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + Document doc = new Document(); + w.addDocument(doc); + w.close(); + + IndexReader reader = DirectoryReader.open(dir); + + TestSearchContext context = new TestSearchContext(null, indexShard, newEarlyTerminationContextSearcher(reader, 0, executor)); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertEquals(1, context.queryResult().topDocs().topDocs.totalHits.value); + + context.setSearcher(newContextSearcher(reader, executor)); + context.parsedPostFilter(new ParsedQuery(new MatchNoDocsQuery())); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertEquals(0, context.queryResult().topDocs().topDocs.totalHits.value); + reader.close(); + dir.close(); + } + + public void testTerminateAfterWithFilter() throws Exception { + Directory dir = newDirectory(); + final Sort sort = new Sort(new SortField("rank", SortField.Type.INT)); + IndexWriterConfig iwc = newIndexWriterConfig().setIndexSort(sort); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + Document doc = new Document(); + for (int i = 0; i < 10; i++) { + doc.add(new StringField("foo", Integer.toString(i), Store.NO)); + } + w.addDocument(doc); + w.close(); + + IndexReader reader = DirectoryReader.open(dir); + + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor)); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + + context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); + context.terminateAfter(1); + context.setSize(10); + for (int i = 0; i < 10; i++) { + context.parsedPostFilter(new ParsedQuery(new TermQuery(new Term("foo", Integer.toString(i))))); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertEquals(1, context.queryResult().topDocs().topDocs.totalHits.value); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + } + reader.close(); + dir.close(); + } + + public void testMinScoreDisablesCountOptimization() throws Exception { + Directory dir = newDirectory(); + final Sort sort = new Sort(new SortField("rank", SortField.Type.INT)); + IndexWriterConfig iwc = newIndexWriterConfig().setIndexSort(sort); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + Document doc = new Document(); + w.addDocument(doc); + w.close(); + + IndexReader reader = DirectoryReader.open(dir); + TestSearchContext context = new TestSearchContext(null, indexShard, newEarlyTerminationContextSearcher(reader, 0, executor)); + context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); + context.setSize(0); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertEquals(1, context.queryResult().topDocs().topDocs.totalHits.value); + + context.minimumScore(100); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertEquals(0, context.queryResult().topDocs().topDocs.totalHits.value); + assertEquals(TotalHits.Relation.EQUAL_TO, context.queryResult().topDocs().topDocs.totalHits.relation); + reader.close(); + dir.close(); + } + + public void testQueryCapturesThreadPoolStats() throws Exception { + Directory dir = newDirectory(); + IndexWriterConfig iwc = newIndexWriterConfig(); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + final int numDocs = scaledRandomIntBetween(600, 900); + for (int i = 0; i < numDocs; ++i) { + w.addDocument(new Document()); + } + w.close(); + IndexReader reader = DirectoryReader.open(dir); + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor)); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + QuerySearchResult results = context.queryResult(); + assertThat(results.serviceTimeEWMA(), greaterThanOrEqualTo(0L)); + assertThat(results.nodeQueueSize(), greaterThanOrEqualTo(0)); + reader.close(); + dir.close(); + } + + public void testInOrderScrollOptimization() throws Exception { + Directory dir = newDirectory(); + final Sort sort = new Sort(new SortField("rank", SortField.Type.INT)); + IndexWriterConfig iwc = newIndexWriterConfig().setIndexSort(sort); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + final int numDocs = scaledRandomIntBetween(600, 900); + for (int i = 0; i < numDocs; ++i) { + w.addDocument(new Document()); + } + w.close(); + IndexReader reader = DirectoryReader.open(dir); + ScrollContext scrollContext = new ScrollContext(); + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor), scrollContext); + context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); + context.sort(new SortAndFormats(sort, new DocValueFormat[] { DocValueFormat.RAW })); + scrollContext.lastEmittedDoc = null; + scrollContext.maxScore = Float.NaN; + scrollContext.totalHits = null; + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + int size = randomIntBetween(2, 5); + context.setSize(size); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertNull(context.queryResult().terminatedEarly()); + assertThat(context.terminateAfter(), equalTo(0)); + assertThat(context.queryResult().getTotalHits().value, equalTo((long) numDocs)); + + context.setSearcher(newEarlyTerminationContextSearcher(reader, size, executor)); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertThat(context.queryResult().getTotalHits().value, equalTo((long) numDocs)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs[0].doc, greaterThanOrEqualTo(size)); + reader.close(); + dir.close(); + } + + public void testTerminateAfterEarlyTermination() throws Exception { + Directory dir = newDirectory(); + IndexWriterConfig iwc = newIndexWriterConfig(); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + final int numDocs = scaledRandomIntBetween(600, 900); + for (int i = 0; i < numDocs; ++i) { + Document doc = new Document(); + if (randomBoolean()) { + doc.add(new StringField("foo", "bar", Store.NO)); + } + if (randomBoolean()) { + doc.add(new StringField("foo", "baz", Store.NO)); + } + doc.add(new NumericDocValuesField("rank", numDocs - i)); + w.addDocument(doc); + } + w.close(); + final IndexReader reader = DirectoryReader.open(dir); + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor)); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); + + context.terminateAfter(numDocs); + { + context.setSize(10); + final TestTotalHitCountCollectorManager manager = TestTotalHitCountCollectorManager.create(executor); + context.queryCollectorManagers().put(TotalHitCountCollector.class, manager); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertFalse(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(10)); + assertThat(manager.getTotalHits(), equalTo(numDocs)); + } + + context.terminateAfter(1); + { + context.setSize(1); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(1L)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + + context.setSize(0); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(1L)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(0)); + } + + { + context.setSize(1); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(1L)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + } + { + context.setSize(1); + BooleanQuery bq = new BooleanQuery.Builder().add(new TermQuery(new Term("foo", "bar")), Occur.SHOULD) + .add(new TermQuery(new Term("foo", "baz")), Occur.SHOULD) + .build(); + context.parsedQuery(new ParsedQuery(bq)); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(1L)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + + context.setSize(0); + context.parsedQuery(new ParsedQuery(bq)); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(1L)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(0)); + } + { + context.setSize(1); + final TestTotalHitCountCollectorManager manager = TestTotalHitCountCollectorManager.create(executor, 1); + context.queryCollectorManagers().put(TotalHitCountCollector.class, manager); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(1L)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + assertThat(manager.getTotalHits(), equalTo(1)); + context.queryCollectorManagers().clear(); + } + { + context.setSize(0); + final TestTotalHitCountCollectorManager manager = TestTotalHitCountCollectorManager.create(executor, 1); + context.queryCollectorManagers().put(TotalHitCountCollector.class, manager); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(1L)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(0)); + assertThat(manager.getTotalHits(), equalTo(1)); + } + + // tests with trackTotalHits and terminateAfter + context.terminateAfter(10); + context.setSize(0); + for (int trackTotalHits : new int[] { -1, 3, 76, 100 }) { + context.trackTotalHitsUpTo(trackTotalHits); + final TestTotalHitCountCollectorManager manager = TestTotalHitCountCollectorManager.create(executor); + context.queryCollectorManagers().put(TotalHitCountCollector.class, manager); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + if (trackTotalHits == -1) { + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(0L)); + } else { + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) Math.min(trackTotalHits, 10))); + } + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(0)); + // The concurrent search terminates the collection when the number of hits is reached by each + // concurrent collector. In this case, in general, the number of results are multiplied by the number of + // slices (as the unit of concurrency). To address that, we have to use the shared global state, + // much as HitsThresholdChecker does. + if (executor == null) { + assertThat(manager.getTotalHits(), equalTo(10)); + } + } + + context.terminateAfter(7); + context.setSize(10); + for (int trackTotalHits : new int[] { -1, 3, 75, 100 }) { + context.trackTotalHitsUpTo(trackTotalHits); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(context.queryResult().terminatedEarly()); + if (trackTotalHits == -1) { + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(0L)); + } else { + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(7L)); + } + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(7)); + } + reader.close(); + dir.close(); + } + + public void testIndexSortingEarlyTermination() throws Exception { + Directory dir = newDirectory(); + final Sort sort = new Sort(new SortField("rank", SortField.Type.INT)); + IndexWriterConfig iwc = newIndexWriterConfig().setIndexSort(sort); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + final int numDocs = scaledRandomIntBetween(600, 900); + for (int i = 0; i < numDocs; ++i) { + Document doc = new Document(); + if (randomBoolean()) { + doc.add(new StringField("foo", "bar", Store.NO)); + } + if (randomBoolean()) { + doc.add(new StringField("foo", "baz", Store.NO)); + } + doc.add(new NumericDocValuesField("rank", numDocs - i)); + w.addDocument(doc); + } + w.close(); + + final IndexReader reader = DirectoryReader.open(dir); + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor)); + context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); + context.setSize(1); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + context.sort(new SortAndFormats(sort, new DocValueFormat[] { DocValueFormat.RAW })); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs[0], instanceOf(FieldDoc.class)); + FieldDoc fieldDoc = (FieldDoc) context.queryResult().topDocs().topDocs.scoreDocs[0]; + assertThat(fieldDoc.fields[0], equalTo(1)); + + { + context.parsedPostFilter(new ParsedQuery(new MinDocQuery(1))); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertNull(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo(numDocs - 1L)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs[0], instanceOf(FieldDoc.class)); + assertThat(fieldDoc.fields[0], anyOf(equalTo(1), equalTo(2))); + context.parsedPostFilter(null); + + final TestTotalHitCountCollectorManager manager = TestTotalHitCountCollectorManager.create(executor, sort); + context.queryCollectorManagers().put(TotalHitCountCollector.class, manager); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertNull(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs[0], instanceOf(FieldDoc.class)); + assertThat(fieldDoc.fields[0], anyOf(equalTo(1), equalTo(2))); + // When searching concurrently, each executors short-circuits when "size" is reached, + // including total hits collector + assertThat(manager.getTotalHits(), lessThanOrEqualTo(numDocs)); + + context.queryCollectorManagers().clear(); + } + + { + context.setSearcher(newEarlyTerminationContextSearcher(reader, 1, executor)); + context.trackTotalHitsUpTo(SearchContext.TRACK_TOTAL_HITS_DISABLED); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertNull(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs[0], instanceOf(FieldDoc.class)); + assertThat(fieldDoc.fields[0], anyOf(equalTo(1), equalTo(2))); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertNull(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(1)); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs[0], instanceOf(FieldDoc.class)); + assertThat(fieldDoc.fields[0], anyOf(equalTo(1), equalTo(2))); + } + reader.close(); + dir.close(); + } + + public void testIndexSortScrollOptimization() throws Exception { + Directory dir = newDirectory(); + final Sort indexSort = new Sort(new SortField("rank", SortField.Type.INT), new SortField("tiebreaker", SortField.Type.INT)); + IndexWriterConfig iwc = newIndexWriterConfig().setIndexSort(indexSort); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + final int numDocs = scaledRandomIntBetween(600, 900); + for (int i = 0; i < numDocs; ++i) { + Document doc = new Document(); + doc.add(new NumericDocValuesField("rank", random().nextInt())); + doc.add(new NumericDocValuesField("tiebreaker", i)); + w.addDocument(doc); + } + if (randomBoolean()) { + w.forceMerge(randomIntBetween(1, 10)); + } + w.close(); + + final IndexReader reader = DirectoryReader.open(dir); + List searchSortAndFormats = new ArrayList<>(); + searchSortAndFormats.add(new SortAndFormats(indexSort, new DocValueFormat[] { DocValueFormat.RAW, DocValueFormat.RAW })); + // search sort is a prefix of the index sort + searchSortAndFormats.add(new SortAndFormats(new Sort(indexSort.getSort()[0]), new DocValueFormat[] { DocValueFormat.RAW })); + for (SortAndFormats searchSortAndFormat : searchSortAndFormats) { + ScrollContext scrollContext = new ScrollContext(); + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor), scrollContext); + context.parsedQuery(new ParsedQuery(new MatchAllDocsQuery())); + scrollContext.lastEmittedDoc = null; + scrollContext.maxScore = Float.NaN; + scrollContext.totalHits = null; + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + context.setSize(10); + context.sort(searchSortAndFormat); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertNull(context.queryResult().terminatedEarly()); + assertThat(context.terminateAfter(), equalTo(0)); + assertThat(context.queryResult().getTotalHits().value, equalTo((long) numDocs)); + int sizeMinus1 = context.queryResult().topDocs().topDocs.scoreDocs.length - 1; + FieldDoc lastDoc = (FieldDoc) context.queryResult().topDocs().topDocs.scoreDocs[sizeMinus1]; + + context.setSearcher(newEarlyTerminationContextSearcher(reader, 10, executor)); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertNull(context.queryResult().terminatedEarly()); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertThat(context.terminateAfter(), equalTo(0)); + assertThat(context.queryResult().getTotalHits().value, equalTo((long) numDocs)); + FieldDoc firstDoc = (FieldDoc) context.queryResult().topDocs().topDocs.scoreDocs[0]; + for (int i = 0; i < searchSortAndFormat.sort.getSort().length; i++) { + @SuppressWarnings("unchecked") + FieldComparator comparator = (FieldComparator) searchSortAndFormat.sort.getSort()[i].getComparator( + i, + false + ); + int cmp = comparator.compareValues(firstDoc.fields[i], lastDoc.fields[i]); + if (cmp == 0) { + continue; + } + assertThat(cmp, equalTo(1)); + break; + } + } + reader.close(); + dir.close(); + } + + public void testDisableTopScoreCollection() throws Exception { + Directory dir = newDirectory(); + IndexWriterConfig iwc = newIndexWriterConfig(new StandardAnalyzer()); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + Document doc = new Document(); + final int numDocs = 2 * scaledRandomIntBetween(50, 450); + for (int i = 0; i < numDocs; i++) { + doc.clear(); + if (i % 2 == 0) { + doc.add(new TextField("title", "foo bar", Store.NO)); + } else { + doc.add(new TextField("title", "foo", Store.NO)); + } + w.addDocument(doc); + } + w.close(); + + IndexReader reader = DirectoryReader.open(dir); + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor)); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + Query q = new SpanNearQuery.Builder("title", true).addClause(new SpanTermQuery(new Term("title", "foo"))) + .addClause(new SpanTermQuery(new Term("title", "bar"))) + .build(); + + context.parsedQuery(new ParsedQuery(q)); + context.setSize(3); + context.trackTotalHitsUpTo(3); + TopDocsCollectorContext topDocsContext = TopDocsCollectorContext.createTopDocsCollectorContext(context, false); + assertEquals(topDocsContext.create(null).scoreMode(), org.apache.lucene.search.ScoreMode.COMPLETE); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertEquals(numDocs / 2, context.queryResult().topDocs().topDocs.totalHits.value); + assertEquals(context.queryResult().topDocs().topDocs.totalHits.relation, TotalHits.Relation.EQUAL_TO); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(3)); + + context.sort(new SortAndFormats(new Sort(new SortField("other", SortField.Type.INT)), new DocValueFormat[] { DocValueFormat.RAW })); + topDocsContext = TopDocsCollectorContext.createTopDocsCollectorContext(context, false); + assertEquals(topDocsContext.create(null).scoreMode(), org.apache.lucene.search.ScoreMode.TOP_DOCS); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertEquals(numDocs / 2, context.queryResult().topDocs().topDocs.totalHits.value); + assertThat(context.queryResult().topDocs().topDocs.scoreDocs.length, equalTo(3)); + assertEquals(context.queryResult().topDocs().topDocs.totalHits.relation, TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO); + + reader.close(); + dir.close(); + } + + public void testEnhanceSortOnNumeric() throws Exception { + final String fieldNameLong = "long-field"; + final String fieldNameDate = "date-field"; + MappedFieldType fieldTypeLong = new NumberFieldMapper.NumberFieldType(fieldNameLong, NumberFieldMapper.NumberType.LONG); + MappedFieldType fieldTypeDate = new DateFieldMapper.DateFieldType(fieldNameDate); + MapperService mapperService = mock(MapperService.class); + when(mapperService.fieldType(fieldNameLong)).thenReturn(fieldTypeLong); + when(mapperService.fieldType(fieldNameDate)).thenReturn(fieldTypeDate); + // enough docs to have a tree with several leaf nodes + final int numDocs = 3500 * 5; + Directory dir = newDirectory(); + IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(null)); + long firstValue = randomLongBetween(-10000000L, 10000000L); + long longValue = firstValue; + long dateValue = randomLongBetween(0, 3000000000000L); + for (int i = 1; i <= numDocs; ++i) { + Document doc = new Document(); + + doc.add(new LongPoint(fieldNameLong, longValue)); + doc.add(new NumericDocValuesField(fieldNameLong, longValue)); + + doc.add(new LongPoint(fieldNameDate, dateValue)); + doc.add(new NumericDocValuesField(fieldNameDate, dateValue)); + writer.addDocument(doc); + longValue++; + dateValue++; + if (i % 3500 == 0) writer.commit(); + } + writer.close(); + final IndexReader reader = DirectoryReader.open(dir); + final SortField sortFieldLong = new SortField(fieldNameLong, SortField.Type.LONG); + sortFieldLong.setMissingValue(Long.MAX_VALUE); + final SortField sortFieldDate = new SortField(fieldNameDate, SortField.Type.LONG); + sortFieldDate.setMissingValue(Long.MAX_VALUE); + DocValueFormat dateFormat = fieldTypeDate.docValueFormat(null, null); + final Sort longSort = new Sort(sortFieldLong); + final Sort longDateSort = new Sort(sortFieldLong, sortFieldDate); + final Sort dateSort = new Sort(sortFieldDate); + final Sort dateLongSort = new Sort(sortFieldDate, sortFieldLong); + SortAndFormats longSortAndFormats = new SortAndFormats(longSort, new DocValueFormat[] { DocValueFormat.RAW }); + SortAndFormats longDateSortAndFormats = new SortAndFormats(longDateSort, new DocValueFormat[] { DocValueFormat.RAW, dateFormat }); + SortAndFormats dateSortAndFormats = new SortAndFormats(dateSort, new DocValueFormat[] { dateFormat }); + SortAndFormats dateLongSortAndFormats = new SortAndFormats(dateLongSort, new DocValueFormat[] { dateFormat, DocValueFormat.RAW }); + ParsedQuery query = new ParsedQuery(new MatchAllDocsQuery()); + SearchShardTask task = new SearchShardTask(123L, "", "", "", null, Collections.emptyMap()); + + // 1. Test a sort on long field + { + TestSearchContext searchContext = spy(new TestSearchContext(null, indexShard, newContextSearcher(reader, executor))); + when(searchContext.mapperService()).thenReturn(mapperService); + searchContext.sort(longSortAndFormats); + searchContext.parsedQuery(query); + searchContext.setTask(task); + searchContext.setSize(10); + QueryPhase.executeInternal(searchContext.withCleanQueryResult(), queryPhaseSearcher); + assertSortResults(searchContext.queryResult().topDocs().topDocs, (long) numDocs, false); + } + + // 2. Test a sort on long field + date field + { + TestSearchContext searchContext = spy(new TestSearchContext(null, indexShard, newContextSearcher(reader, executor))); + when(searchContext.mapperService()).thenReturn(mapperService); + searchContext.sort(longDateSortAndFormats); + searchContext.parsedQuery(query); + searchContext.setTask(task); + searchContext.setSize(10); + QueryPhase.executeInternal(searchContext.withCleanQueryResult(), queryPhaseSearcher); + assertSortResults(searchContext.queryResult().topDocs().topDocs, (long) numDocs, true); + } + + // 3. Test a sort on date field + { + TestSearchContext searchContext = spy(new TestSearchContext(null, indexShard, newContextSearcher(reader, executor))); + when(searchContext.mapperService()).thenReturn(mapperService); + searchContext.sort(dateSortAndFormats); + searchContext.parsedQuery(query); + searchContext.setTask(task); + searchContext.setSize(10); + QueryPhase.executeInternal(searchContext.withCleanQueryResult(), queryPhaseSearcher); + assertSortResults(searchContext.queryResult().topDocs().topDocs, (long) numDocs, false); + } + + // 4. Test a sort on date field + long field + { + TestSearchContext searchContext = spy(new TestSearchContext(null, indexShard, newContextSearcher(reader, executor))); + when(searchContext.mapperService()).thenReturn(mapperService); + searchContext.sort(dateLongSortAndFormats); + searchContext.parsedQuery(query); + searchContext.setTask(task); + searchContext.setSize(10); + QueryPhase.executeInternal(searchContext.withCleanQueryResult(), queryPhaseSearcher); + assertSortResults(searchContext.queryResult().topDocs().topDocs, (long) numDocs, true); + } + + // 5. Test that sort optimization is run when from > 0 and size = 0 + { + TestSearchContext searchContext = spy(new TestSearchContext(null, indexShard, newContextSearcher(reader, executor))); + when(searchContext.mapperService()).thenReturn(mapperService); + searchContext.sort(longSortAndFormats); + searchContext.parsedQuery(query); + searchContext.setTask(task); + searchContext.from(5); + searchContext.setSize(0); + QueryPhase.executeInternal(searchContext.withCleanQueryResult(), queryPhaseSearcher); + assertSortResults(searchContext.queryResult().topDocs().topDocs, (long) numDocs, false); + } + + // 6. Test that sort optimization works with from = 0 and size= 0 + { + TestSearchContext searchContext = spy(new TestSearchContext(null, indexShard, newContextSearcher(reader, executor))); + when(searchContext.mapperService()).thenReturn(mapperService); + searchContext.sort(longSortAndFormats); + searchContext.parsedQuery(query); + searchContext.setTask(task); + searchContext.setSize(0); + QueryPhase.executeInternal(searchContext.withCleanQueryResult(), queryPhaseSearcher); + } + + // 7. Test that sort optimization works with search after + { + TestSearchContext searchContext = spy(new TestSearchContext(null, indexShard, newContextSearcher(reader, executor))); + when(searchContext.mapperService()).thenReturn(mapperService); + int afterDocument = (int) randomLongBetween(0, 50); + long afterValue = firstValue + afterDocument; + FieldDoc after = new FieldDoc(afterDocument, Float.NaN, new Long[] { afterValue }); + searchContext.searchAfter(after); + searchContext.sort(longSortAndFormats); + searchContext.parsedQuery(query); + searchContext.setTask(task); + searchContext.setSize(10); + QueryPhase.executeInternal(searchContext.withCleanQueryResult(), queryPhaseSearcher); + final TopDocs topDocs = searchContext.queryResult().topDocs().topDocs; + long topValue = (long) ((FieldDoc) topDocs.scoreDocs[0]).fields[0]; + assertThat(topValue, greaterThan(afterValue)); + assertSortResults(topDocs, (long) numDocs, false); + + final TotalHits totalHits = topDocs.totalHits; + assertEquals(TotalHits.Relation.EQUAL_TO, totalHits.relation); + assertEquals(numDocs, totalHits.value); + } + + reader.close(); + dir.close(); + } + + public void testMaxScoreQueryVisitor() { + BitSetProducer producer = context -> new FixedBitSet(1); + Query query = new OpenSearchToParentBlockJoinQuery(new MatchAllDocsQuery(), producer, ScoreMode.Avg, "nested"); + assertTrue(hasInfMaxScore(query)); + + query = new OpenSearchToParentBlockJoinQuery(new MatchAllDocsQuery(), producer, ScoreMode.None, "nested"); + assertFalse(hasInfMaxScore(query)); + + for (Occur occur : Occur.values()) { + query = new BooleanQuery.Builder().add( + new OpenSearchToParentBlockJoinQuery(new MatchAllDocsQuery(), producer, ScoreMode.Avg, "nested"), + occur + ).build(); + if (occur == Occur.MUST) { + assertTrue(hasInfMaxScore(query)); + } else { + assertFalse(hasInfMaxScore(query)); + } + + query = new BooleanQuery.Builder().add( + new BooleanQuery.Builder().add( + new OpenSearchToParentBlockJoinQuery(new MatchAllDocsQuery(), producer, ScoreMode.Avg, "nested"), + occur + ).build(), + occur + ).build(); + if (occur == Occur.MUST) { + assertTrue(hasInfMaxScore(query)); + } else { + assertFalse(hasInfMaxScore(query)); + } + + query = new BooleanQuery.Builder().add( + new BooleanQuery.Builder().add( + new OpenSearchToParentBlockJoinQuery(new MatchAllDocsQuery(), producer, ScoreMode.Avg, "nested"), + occur + ).build(), + Occur.FILTER + ).build(); + assertFalse(hasInfMaxScore(query)); + + query = new BooleanQuery.Builder().add( + new BooleanQuery.Builder().add(new SpanTermQuery(new Term("field", "foo")), occur) + .add(new OpenSearchToParentBlockJoinQuery(new MatchAllDocsQuery(), producer, ScoreMode.Avg, "nested"), occur) + .build(), + occur + ).build(); + if (occur == Occur.MUST) { + assertTrue(hasInfMaxScore(query)); + } else { + assertFalse(hasInfMaxScore(query)); + } + } + } + + // assert score docs are in order and their number is as expected + private void assertSortResults(TopDocs topDocs, long expectedNumDocs, boolean isDoubleSort) { + if (topDocs.totalHits.relation == TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO) { + assertThat(topDocs.totalHits.value, lessThanOrEqualTo(expectedNumDocs)); + } else { + assertEquals(topDocs.totalHits.value, expectedNumDocs); + } + long cur1, cur2; + long prev1 = Long.MIN_VALUE; + long prev2 = Long.MIN_VALUE; + for (ScoreDoc scoreDoc : topDocs.scoreDocs) { + cur1 = (long) ((FieldDoc) scoreDoc).fields[0]; + assertThat(cur1, greaterThanOrEqualTo(prev1)); // test that docs are properly sorted on the first sort + if (isDoubleSort) { + cur2 = (long) ((FieldDoc) scoreDoc).fields[1]; + if (cur1 == prev1) { + assertThat(cur2, greaterThanOrEqualTo(prev2)); // test that docs are properly sorted on the secondary sort + } + prev2 = cur2; + } + prev1 = cur1; + } + } + + public void testMinScore() throws Exception { + Directory dir = newDirectory(); + IndexWriterConfig iwc = newIndexWriterConfig(); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + for (int i = 0; i < 10; i++) { + Document doc = new Document(); + doc.add(new StringField("foo", "bar", Store.NO)); + doc.add(new StringField("filter", "f1", Store.NO)); + w.addDocument(doc); + } + w.close(); + + IndexReader reader = DirectoryReader.open(dir); + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor)); + context.parsedQuery( + new ParsedQuery( + new BooleanQuery.Builder().add(new TermQuery(new Term("foo", "bar")), Occur.MUST) + .add(new TermQuery(new Term("filter", "f1")), Occur.SHOULD) + .build() + ) + ); + context.minimumScore(0.01f); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + context.setSize(1); + context.trackTotalHitsUpTo(5); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertEquals(10, context.queryResult().topDocs().topDocs.totalHits.value); + + reader.close(); + dir.close(); + } + + public void testMaxScore() throws Exception { + Directory dir = newDirectory(); + final Sort sort = new Sort(new SortField("filter", SortField.Type.STRING)); + IndexWriterConfig iwc = newIndexWriterConfig().setIndexSort(sort); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + + final int numDocs = scaledRandomIntBetween(600, 900); + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + doc.add(new StringField("foo", "bar", Store.NO)); + doc.add(new StringField("filter", "f1" + ((i > 0) ? " " + Integer.toString(i) : ""), Store.NO)); + doc.add(new SortedDocValuesField("filter", newBytesRef("f1" + ((i > 0) ? " " + Integer.toString(i) : "")))); + w.addDocument(doc); + } + w.close(); + + IndexReader reader = DirectoryReader.open(dir); + TestSearchContext context = new TestSearchContext(null, indexShard, newContextSearcher(reader, executor)); + context.trackScores(true); + context.parsedQuery( + new ParsedQuery( + new BooleanQuery.Builder().add(new TermQuery(new Term("foo", "bar")), Occur.MUST) + .add(new TermQuery(new Term("filter", "f1")), Occur.SHOULD) + .build() + ) + ); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + context.setSize(1); + context.trackTotalHitsUpTo(5); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertFalse(Float.isNaN(context.queryResult().getMaxScore())); + assertEquals(1, context.queryResult().topDocs().topDocs.scoreDocs.length); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, greaterThanOrEqualTo(6L)); + + context.sort(new SortAndFormats(sort, new DocValueFormat[] { DocValueFormat.RAW })); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertFalse(Float.isNaN(context.queryResult().getMaxScore())); + assertEquals(1, context.queryResult().topDocs().topDocs.scoreDocs.length); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, greaterThanOrEqualTo(6L)); + + context.trackScores(false); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(Float.isNaN(context.queryResult().getMaxScore())); + assertEquals(1, context.queryResult().topDocs().topDocs.scoreDocs.length); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, greaterThanOrEqualTo(6L)); + + reader.close(); + dir.close(); + } + + public void testCollapseQuerySearchResults() throws Exception { + Directory dir = newDirectory(); + final Sort sort = new Sort(new SortField("user", SortField.Type.INT)); + IndexWriterConfig iwc = newIndexWriterConfig().setIndexSort(sort); + RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc); + + // Always end up with uneven buckets so collapsing is predictable + final int numDocs = 2 * scaledRandomIntBetween(600, 900) - 1; + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + doc.add(new StringField("foo", "bar", Store.NO)); + doc.add(new NumericDocValuesField("user", i & 1)); + w.addDocument(doc); + } + w.close(); + + IndexReader reader = DirectoryReader.open(dir); + QueryShardContext queryShardContext = mock(QueryShardContext.class); + when(queryShardContext.fieldMapper("user")).thenReturn( + new NumberFieldType("user", NumberType.INTEGER, true, false, true, false, null, Collections.emptyMap()) + ); + + TestSearchContext context = new TestSearchContext(queryShardContext, indexShard, newContextSearcher(reader, executor)); + context.collapse(new CollapseBuilder("user").build(context.getQueryShardContext())); + context.trackScores(true); + context.parsedQuery(new ParsedQuery(new TermQuery(new Term("foo", "bar")))); + context.setTask(new SearchShardTask(123L, "", "", "", null, Collections.emptyMap())); + context.setSize(2); + context.trackTotalHitsUpTo(5); + + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertFalse(Float.isNaN(context.queryResult().getMaxScore())); + assertEquals(2, context.queryResult().topDocs().topDocs.scoreDocs.length); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertThat(context.queryResult().topDocs().topDocs, instanceOf(CollapseTopFieldDocs.class)); + + CollapseTopFieldDocs topDocs = (CollapseTopFieldDocs) context.queryResult().topDocs().topDocs; + assertThat(topDocs.collapseValues.length, equalTo(2)); + assertThat(topDocs.collapseValues[0], equalTo(0L)); // user == 0 + assertThat(topDocs.collapseValues[1], equalTo(1L)); // user == 1 + + context.sort(new SortAndFormats(sort, new DocValueFormat[] { DocValueFormat.RAW })); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertFalse(Float.isNaN(context.queryResult().getMaxScore())); + assertEquals(2, context.queryResult().topDocs().topDocs.scoreDocs.length); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertThat(context.queryResult().topDocs().topDocs, instanceOf(CollapseTopFieldDocs.class)); + + topDocs = (CollapseTopFieldDocs) context.queryResult().topDocs().topDocs; + assertThat(topDocs.collapseValues.length, equalTo(2)); + assertThat(topDocs.collapseValues[0], equalTo(0L)); // user == 0 + assertThat(topDocs.collapseValues[1], equalTo(1L)); // user == 1 + + context.trackScores(false); + QueryPhase.executeInternal(context.withCleanQueryResult(), queryPhaseSearcher); + assertTrue(Float.isNaN(context.queryResult().getMaxScore())); + assertEquals(2, context.queryResult().topDocs().topDocs.scoreDocs.length); + assertThat(context.queryResult().topDocs().topDocs.totalHits.value, equalTo((long) numDocs)); + assertThat(context.queryResult().topDocs().topDocs, instanceOf(CollapseTopFieldDocs.class)); + + topDocs = (CollapseTopFieldDocs) context.queryResult().topDocs().topDocs; + assertThat(topDocs.collapseValues.length, equalTo(2)); + assertThat(topDocs.collapseValues[0], equalTo(0L)); // user == 0 + assertThat(topDocs.collapseValues[1], equalTo(1L)); // user == 1 + + reader.close(); + dir.close(); + } + + public void testCancellationDuringPreprocess() throws IOException { + try (Directory dir = newDirectory(); RandomIndexWriter w = new RandomIndexWriter(random(), dir, newIndexWriterConfig())) { + + for (int i = 0; i < 10; i++) { + Document doc = new Document(); + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < i; j++) { + sb.append('a'); + } + doc.add(new StringField("foo", sb.toString(), Store.NO)); + w.addDocument(doc); + } + w.flush(); + w.close(); + + try (IndexReader reader = DirectoryReader.open(dir)) { + TestSearchContext context = new TestSearchContextWithRewriteAndCancellation( + null, + indexShard, + newContextSearcher(reader, executor) + ); + PrefixQuery prefixQuery = new PrefixQuery(new Term("foo", "a"), MultiTermQuery.SCORING_BOOLEAN_REWRITE); + context.parsedQuery(new ParsedQuery(prefixQuery)); + SearchShardTask task = mock(SearchShardTask.class); + when(task.isCancelled()).thenReturn(true); + context.setTask(task); + expectThrows(TaskCancelledException.class, () -> new QueryPhase().preProcess(context)); + } + } + } + + private static class TestSearchContextWithRewriteAndCancellation extends TestSearchContext { + + private TestSearchContextWithRewriteAndCancellation( + QueryShardContext queryShardContext, + IndexShard indexShard, + ContextIndexSearcher searcher + ) { + super(queryShardContext, indexShard, searcher); + } + + @Override + public void preProcess(boolean rewrite) { + try { + searcher().rewrite(query()); + } catch (IOException e) { + fail("IOException shouldn't be thrown"); + } + } + + @Override + public boolean lowLevelCancellation() { + return true; + } + } + + private static ContextIndexSearcher newContextSearcher(IndexReader reader, ExecutorService executor) throws IOException { + return new ContextIndexSearcher( + reader, + IndexSearcher.getDefaultSimilarity(), + IndexSearcher.getDefaultQueryCache(), + IndexSearcher.getDefaultQueryCachingPolicy(), + true, + executor + ); + } + + private static ContextIndexSearcher newEarlyTerminationContextSearcher(IndexReader reader, int size, ExecutorService executor) + throws IOException { + return new ContextIndexSearcher( + reader, + IndexSearcher.getDefaultSimilarity(), + IndexSearcher.getDefaultQueryCache(), + IndexSearcher.getDefaultQueryCachingPolicy(), + true, + executor + ) { + + @Override + public void search(List leaves, Weight weight, Collector collector) throws IOException { + final Collector in = new AssertingEarlyTerminationFilterCollector(collector, size); + super.search(leaves, weight, in); + } + }; + } + + // used to check that numeric long or date sort optimization was run + private static ContextIndexSearcher newOptimizedContextSearcher(IndexReader reader, int queryType, ExecutorService executor) + throws IOException { + return new ContextIndexSearcher( + reader, + IndexSearcher.getDefaultSimilarity(), + IndexSearcher.getDefaultQueryCache(), + IndexSearcher.getDefaultQueryCachingPolicy(), + true, + executor + ) { + + @Override + public void search( + Query query, + CollectorManager manager, + QuerySearchResult result, + DocValueFormat[] formats, + TotalHits totalHits + ) throws IOException { + assertTrue(query instanceof BooleanQuery); + List clauses = ((BooleanQuery) query).clauses(); + assertTrue(clauses.size() == 2); + assertTrue(clauses.get(0).getOccur() == Occur.FILTER); + assertTrue(clauses.get(1).getOccur() == Occur.SHOULD); + if (queryType == 0) { + assertTrue( + clauses.get(1).getQuery().getClass() == LongPoint.newDistanceFeatureQuery("random_field", 1, 1, 1).getClass() + ); + } + if (queryType == 1) assertTrue(clauses.get(1).getQuery() instanceof DocValuesFieldExistsQuery); + super.search(query, manager, result, formats, totalHits); + } + + @Override + public void search( + List leaves, + Weight weight, + @SuppressWarnings("rawtypes") CollectorManager manager, + QuerySearchResult result, + DocValueFormat[] formats, + TotalHits totalHits + ) throws IOException { + final Query query = weight.getQuery(); + assertTrue(query instanceof BooleanQuery); + List clauses = ((BooleanQuery) query).clauses(); + assertTrue(clauses.size() == 2); + assertTrue(clauses.get(0).getOccur() == Occur.FILTER); + assertTrue(clauses.get(1).getOccur() == Occur.SHOULD); + if (queryType == 0) { + assertTrue( + clauses.get(1).getQuery().getClass() == LongPoint.newDistanceFeatureQuery("random_field", 1, 1, 1).getClass() + ); + } + if (queryType == 1) assertTrue(clauses.get(1).getQuery() instanceof DocValuesFieldExistsQuery); + super.search(leaves, weight, manager, result, formats, totalHits); + } + + @Override + public void search(List leaves, Weight weight, Collector collector) throws IOException { + if (getExecutor() == null) { + assert (false); // should not be there, expected to search with CollectorManager + } else { + super.search(leaves, weight, collector); + } + } + }; + } + + private static class TestTotalHitCountCollectorManager extends TotalHitCountCollectorManager { + private int totalHits; + private final TotalHitCountCollector collector; + private final Integer teminateAfter; + + static TestTotalHitCountCollectorManager create(final ExecutorService executor) { + return create(executor, null, null); + } + + static TestTotalHitCountCollectorManager create(final ExecutorService executor, final Integer teminateAfter) { + return create(executor, null, teminateAfter); + } + + static TestTotalHitCountCollectorManager create(final ExecutorService executor, final Sort sort) { + return create(executor, sort, null); + } + + static TestTotalHitCountCollectorManager create(final ExecutorService executor, final Sort sort, final Integer teminateAfter) { + if (executor == null) { + return new TestTotalHitCountCollectorManager(new TotalHitCountCollector(), sort); + } else { + return new TestTotalHitCountCollectorManager(sort, teminateAfter); + } + } + + private TestTotalHitCountCollectorManager(final TotalHitCountCollector collector, final Sort sort) { + super(sort); + this.collector = collector; + this.teminateAfter = null; + } + + private TestTotalHitCountCollectorManager(final Sort sort, final Integer teminateAfter) { + super(sort); + this.collector = null; + this.teminateAfter = teminateAfter; + } + + @Override + public TotalHitCountCollector newCollector() throws IOException { + return (collector == null) ? super.newCollector() : collector; + } + + @Override + public ReduceableSearchResult reduce(Collection collectors) throws IOException { + final ReduceableSearchResult result = super.reduce(collectors); + totalHits = collectors.stream().mapToInt(TotalHitCountCollector::getTotalHits).sum(); + + if (teminateAfter != null) { + assertThat(totalHits, greaterThanOrEqualTo(teminateAfter)); + totalHits = Math.min(totalHits, teminateAfter); + } + + return result; + } + + public int getTotalHits() { + return (collector == null) ? totalHits : collector.getTotalHits(); + } + } + + private static class AssertingEarlyTerminationFilterCollector extends FilterCollector { + private final int size; + + AssertingEarlyTerminationFilterCollector(Collector in, int size) { + super(in); + this.size = size; + } + + @Override + public LeafCollector getLeafCollector(LeafReaderContext context) throws IOException { + final LeafCollector in = super.getLeafCollector(context); + return new FilterLeafCollector(in) { + int collected; + + @Override + public void collect(int doc) throws IOException { + assert collected <= size : "should not collect more than " + size + " doc per segment, got " + collected; + ++collected; + super.collect(doc); + } + }; + } + } +} diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000000000..a0917776507be --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,161 @@ +#!/bin/bash + +# Copyright OpenSearch Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. + +set -ex + +function usage() { + echo "Usage: $0 [args]" + echo "" + echo "Arguments:" + echo -e "-v VERSION\t[Required] OpenSearch version." + echo -e "-q QUALIFIER\t[Optional] Version qualifier." + echo -e "-s SNAPSHOT\t[Optional] Build a snapshot, default is 'false'." + echo -e "-p PLATFORM\t[Optional] Platform, default is 'uname -s'." + echo -e "-a ARCHITECTURE\t[Optional] Build architecture, default is 'uname -m'." + echo -e "-d DISTRIBUTION\t[Optional] Distribution, default is 'tar'." + echo -e "-o OUTPUT\t[Optional] Output path, default is 'artifacts'." + echo -e "-h help" +} + +while getopts ":h:v:q:s:o:p:a:d:" arg; do + case $arg in + h) + usage + exit 1 + ;; + v) + VERSION=$OPTARG + ;; + q) + QUALIFIER=$OPTARG + ;; + s) + SNAPSHOT=$OPTARG + ;; + o) + OUTPUT=$OPTARG + ;; + p) + PLATFORM=$OPTARG + ;; + a) + ARCHITECTURE=$OPTARG + ;; + d) + DISTRIBUTION=$OPTARG + ;; + :) + echo "Error: -${OPTARG} requires an argument" + usage + exit 1 + ;; + ?) + echo "Invalid option: -${arg}" + exit 1 + ;; + esac +done + +if [ -z "$VERSION" ]; then + echo "Error: You must specify the OpenSearch version" + usage + exit 1 +fi + +[ -z "$OUTPUT" ] && OUTPUT=artifacts + +mkdir -p $OUTPUT/maven/org/opensearch + +# Build project and publish to maven local. +./gradlew publishToMavenLocal -Dbuild.snapshot=$SNAPSHOT -Dbuild.version_qualifier=$QUALIFIER + +# Publish to existing test repo, using this to stage release versions of the artifacts that can be released from the same build. +./gradlew publishNebulaPublicationToTestRepository -Dbuild.snapshot=$SNAPSHOT -Dbuild.version_qualifier=$QUALIFIER + +# Copy maven publications to be promoted +cp -r ./build/local-test-repo/org/opensearch "${OUTPUT}"/maven/org + +# Assemble distribution artifact +# see https://github.com/opensearch-project/OpenSearch/blob/main/settings.gradle#L34 for other distribution targets + +[ -z "$PLATFORM" ] && PLATFORM=$(uname -s | awk '{print tolower($0)}') +[ -z "$ARCHITECTURE" ] && ARCHITECTURE=`uname -m` +[ -z "$DISTRIBUTION" ] && DISTRIBUTION="tar" + +case $PLATFORM-$DISTRIBUTION-$ARCHITECTURE in + linux-tar-x64|darwin-tar-x64) + PACKAGE="tar" + EXT="tar.gz" + TYPE="archives" + TARGET="$PLATFORM-$PACKAGE" + SUFFIX="$PLATFORM-x64" + ;; + linux-tar-arm64|darwin-tar-arm64) + PACKAGE="tar" + EXT="tar.gz" + TYPE="archives" + TARGET="$PLATFORM-arm64-$PACKAGE" + SUFFIX="$PLATFORM-arm64" + ;; + linux-rpm-x64) + PACKAGE="rpm" + EXT="rpm" + TYPE="packages" + TARGET="rpm" + SUFFIX="x86_64" + ;; + linux-rpm-arm64) + PACKAGE="rpm" + EXT="rpm" + TYPE="packages" + TARGET="arm64-rpm" + SUFFIX="aarch64" + ;; + windows-zip-x64) + PACKAGE="zip" + EXT="zip" + TYPE="archives" + TARGET="$PLATFORM-$PACKAGE" + SUFFIX="$PLATFORM-x64" + ;; + windows-zip-arm64) + PACKAGE="zip" + EXT="zip" + TYPE="archives" + TARGET="$PLATFORM-arm64-$PACKAGE" + SUFFIX="$PLATFORM-arm64" + ;; + *) + echo "Unsupported platform-distribution-architecture combination: $PLATFORM-$DISTRIBUTION-$ARCHITECTURE" + exit 1 + ;; +esac + +echo "Building OpenSearch for $PLATFORM-$DISTRIBUTION-$ARCHITECTURE" + +./gradlew :distribution:$TYPE:$TARGET:assemble -Dbuild.snapshot=$SNAPSHOT -Dbuild.version_qualifier=$QUALIFIER + +# Copy artifact to dist folder in bundle build output +[[ "$SNAPSHOT" == "true" ]] && IDENTIFIER="-SNAPSHOT" +ARTIFACT_BUILD_NAME=`ls distribution/$TYPE/$TARGET/build/distributions/ | grep "opensearch-min.*$SUFFIX.$EXT"` +mkdir -p "${OUTPUT}/dist" +cp distribution/$TYPE/$TARGET/build/distributions/$ARTIFACT_BUILD_NAME "${OUTPUT}"/dist/$ARTIFACT_BUILD_NAME + +echo "Building core plugins..." +mkdir -p "${OUTPUT}/core-plugins" +cd plugins +../gradlew assemble -Dbuild.snapshot="$SNAPSHOT" -Dbuild.version_qualifier=$QUALIFIER +cd .. +for plugin in plugins/*; do + PLUGIN_NAME=$(basename "$plugin") + if [ -d "$plugin" ] && [ "examples" != "$PLUGIN_NAME" ]; then + PLUGIN_ARTIFACT_BUILD_NAME=`ls "$plugin"/build/distributions/ | grep "$PLUGIN_NAME.*$IDENTIFIER.zip"` + cp "$plugin"/build/distributions/"$PLUGIN_ARTIFACT_BUILD_NAME" "${OUTPUT}"/core-plugins/"$PLUGIN_ARTIFACT_BUILD_NAME" + fi +done diff --git a/server/build.gradle b/server/build.gradle index 45ec2300008f2..9c409d77363cb 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -30,21 +30,33 @@ import org.opensearch.gradle.info.BuildParams -apply plugin: 'opensearch.build' -apply plugin: 'nebula.optional-base' -apply plugin: 'opensearch.publish' -apply plugin: 'opensearch.internal-cluster-test' +plugins { + id('com.google.protobuf') version 'latest.release' + id('opensearch.build') + id('opensearch.publish') + id('opensearch.internal-cluster-test') + id('opensearch.optional-dependencies') +} publishing { publications { - nebula { + nebula(MavenPublication) { artifactId 'opensearch' } } } -archivesBaseName = 'opensearch' +base { + archivesBaseName = 'opensearch' +} +sourceSets { + main { + java { + srcDir "${buildDir}/generated/source/proto/main/java" + } + } +} // we want to keep the JDKs in our IDEs set to JDK 8 until minimum JDK is bumped to 11 so we do not include this source set in our IDEs if (!isEclipse) { sourceSets { @@ -87,10 +99,14 @@ if (!isEclipse) { dependencies { + api project(':libs:opensearch-common') api project(':libs:opensearch-core') + api project(":libs:opensearch-compress") api project(':libs:opensearch-secure-sm') api project(':libs:opensearch-x-content') api project(":libs:opensearch-geo") + api project(":libs:opensearch-telemetry") + compileOnly project(':libs:opensearch-plugin-classloader') testRuntimeOnly project(':libs:opensearch-plugin-classloader') @@ -113,7 +129,6 @@ dependencies { // utilities api project(":libs:opensearch-cli") - api 'com.carrotsearch:hppc:0.8.1' // time handling, remove with java 8 time api "joda-time:joda-time:${versions.joda}" @@ -121,7 +136,7 @@ dependencies { // percentiles aggregation api 'com.tdunning:t-digest:3.2' // precentil ranks aggregation - api 'org.hdrhistogram:HdrHistogram:2.1.9' + api 'org.hdrhistogram:HdrHistogram:2.1.12' // lucene spatial api "org.locationtech.spatial4j:spatial4j:${versions.spatial4j}", optional @@ -129,11 +144,20 @@ dependencies { // logging api "org.apache.logging.log4j:log4j-api:${versions.log4j}" + api "org.apache.logging.log4j:log4j-jul:${versions.log4j}" api "org.apache.logging.log4j:log4j-core:${versions.log4j}", optional + annotationProcessor "org.apache.logging.log4j:log4j-core:${versions.log4j}" // jna api "net.java.dev.jna:jna:${versions.jna}" + // jcraft + api "com.jcraft:jzlib:${versions.jzlib}" + + // protobuf + api "com.google.protobuf:protobuf-java:${versions.protobuf}" + api "jakarta.annotation:jakarta.annotation-api:${versions.jakarta_annotation}" + testImplementation(project(":test:framework")) { // tests use the locally compiled version of server exclude group: 'org.opensearch', module: 'server' @@ -149,33 +173,23 @@ tasks.withType(JavaCompile).configureEach { options.compilerArgs -= '-Xlint:unchecked' } +compileJava { + options.compilerArgs += ['-processor', 'org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor'] +} + tasks.named("internalClusterTest").configure { // TODO: these run faster with C2 only because they run for so, so long jvmArgs -= '-XX:TieredStopAtLevel=1' } -// Until this project is always being formatted with spotless, we need to -// guard against `spotless()` not existing. -try { - spotless { - java { - // Contains large data tables that do not format well. - targetExclude 'src/main/java/org/opensearch/search/aggregations/metrics/HyperLogLogPlusPlus.java' - } - } -} -catch (Exception e) { - if (e.getMessage().contains("Could not find method spotless") == false) { - throw e; - } -} - tasks.named("forbiddenPatterns").configure { + dependsOn("generateProto") exclude '**/*.json' exclude '**/*.jmx' exclude '**/*.dic' exclude '**/*.binary' exclude '**/*.st' + exclude '**/*.meta' } tasks.named("testingConventions").configure { @@ -217,6 +231,22 @@ def generatePluginsList = tasks.register("generatePluginsList") { } } +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:${versions.protobuf}" + } + + generateProtoTasks { + all().each { task -> + task.builtins { + java { + option "annotate_code" + } + } + } + } +} + tasks.named("processResources").configure { dependsOn generateModulesList, generatePluginsList } @@ -258,7 +288,9 @@ tasks.named("thirdPartyAudit").configure { 'com.lmax.disruptor.LifecycleAware', 'com.lmax.disruptor.RingBuffer', 'com.lmax.disruptor.Sequence', + 'com.lmax.disruptor.SequenceBarrier', 'com.lmax.disruptor.SequenceReportingEventHandler', + 'com.lmax.disruptor.TimeoutException', 'com.lmax.disruptor.WaitStrategy', 'com.lmax.disruptor.dsl.Disruptor', 'com.lmax.disruptor.dsl.ProducerType', @@ -287,6 +319,7 @@ tasks.named("thirdPartyAudit").configure { 'org.apache.commons.csv.QuoteMode', 'org.apache.kafka.clients.producer.Producer', 'org.apache.kafka.clients.producer.RecordMetadata', + 'org.apache.kafka.common.serialization.ByteArraySerializer', 'org.codehaus.stax2.XMLStreamWriter2', 'org.jctools.queues.MpscArrayQueue', 'org.osgi.framework.Bundle', @@ -295,6 +328,7 @@ tasks.named("thirdPartyAudit").configure { 'org.osgi.framework.BundleEvent', 'org.osgi.framework.BundleReference', 'org.osgi.framework.FrameworkUtil', + 'org.osgi.framework.ServiceReference', 'org.osgi.framework.ServiceRegistration', 'org.osgi.framework.SynchronousBundleListener', 'org.osgi.framework.wiring.BundleWire', @@ -317,10 +351,15 @@ tasks.named("thirdPartyAudit").configure { 'com.google.common.geometry.S2$Metric', 'com.google.common.geometry.S2LatLng' ) - - if (BuildParams.runtimeJavaVersion > JavaVersion.VERSION_1_8) { - ignoreMissingClasses 'javax.xml.bind.DatatypeConverter' - } + ignoreViolations( + 'com.google.protobuf.MessageSchema', + 'com.google.protobuf.UnsafeUtil', + 'com.google.protobuf.UnsafeUtil$1', + 'com.google.protobuf.UnsafeUtil$Android32MemoryAccessor', + 'com.google.protobuf.UnsafeUtil$Android64MemoryAccessor', + 'com.google.protobuf.UnsafeUtil$JvmMemoryAccessor', + 'com.google.protobuf.UnsafeUtil$MemoryAccessor' + ) } tasks.named("dependencyLicenses").configure { @@ -333,13 +372,20 @@ tasks.named("dependencyLicenses").configure { } } +tasks.named("filepermissions").configure { + mustRunAfter("generateProto") +} + tasks.named("licenseHeaders").configure { + dependsOn("generateProto") // Ignore our vendored version of Google Guice excludes << 'org/opensearch/common/inject/**/*' // Ignore temporary copies of impending 8.7 Lucene classes excludes << 'org/apache/lucene/search/RegExp87*' excludes << 'org/apache/lucene/search/RegexpQuery87*' excludes << 'org/opensearch/client/documentation/placeholder.txt' + // Ignore for protobuf generated code + excludes << 'org/opensearch/extensions/proto/*' } tasks.test { @@ -347,3 +393,10 @@ tasks.test { jvmArgs += ["--add-opens", "java.base/java.nio.file=ALL-UNNAMED"] } } + +tasks.named("sourcesJar").configure { + // Ignore duplicates for protobuf generated code (main and generatedSources). + filesMatching("**/proto/*") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } +} diff --git a/server/licenses/HdrHistogram-2.1.12.jar.sha1 b/server/licenses/HdrHistogram-2.1.12.jar.sha1 new file mode 100644 index 0000000000000..9d20fa0e5f22d --- /dev/null +++ b/server/licenses/HdrHistogram-2.1.12.jar.sha1 @@ -0,0 +1 @@ +6eb7552156e0d517ae80cc2247be1427c8d90452 \ No newline at end of file diff --git a/server/licenses/HdrHistogram-2.1.9.jar.sha1 b/server/licenses/HdrHistogram-2.1.9.jar.sha1 deleted file mode 100644 index 2378df07b2c0c..0000000000000 --- a/server/licenses/HdrHistogram-2.1.9.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e4631ce165eb400edecfa32e03d3f1be53dee754 \ No newline at end of file diff --git a/server/licenses/hppc-0.8.1.jar.sha1 b/server/licenses/hppc-0.8.1.jar.sha1 deleted file mode 100644 index 47684ed023210..0000000000000 --- a/server/licenses/hppc-0.8.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -ffc7ba8f289428b9508ab484b8001dea944ae603 \ No newline at end of file diff --git a/server/licenses/hppc-LICENSE.txt b/server/licenses/hppc-LICENSE.txt deleted file mode 100644 index 31467575cdbfe..0000000000000 --- a/server/licenses/hppc-LICENSE.txt +++ /dev/null @@ -1,203 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2010-2013, Carrot Search s.c., Boznicza 11/56, Poznan, Poland - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/server/licenses/hppc-NOTICE.txt b/server/licenses/hppc-NOTICE.txt deleted file mode 100644 index 1d8842c0bc69d..0000000000000 --- a/server/licenses/hppc-NOTICE.txt +++ /dev/null @@ -1,11 +0,0 @@ -ACKNOWLEDGEMENT -=============== - -HPPC borrowed code, ideas or both from: - - * Apache Lucene, http://lucene.apache.org/ - (Apache license) - * Fastutil, http://fastutil.di.unimi.it/ - (Apache license) - * Koloboke, https://github.com/OpenHFT/Koloboke - (Apache license) diff --git a/server/licenses/jakarta.annotation-api-1.3.5.jar.sha1 b/server/licenses/jakarta.annotation-api-1.3.5.jar.sha1 new file mode 100644 index 0000000000000..da07243fcc79f --- /dev/null +++ b/server/licenses/jakarta.annotation-api-1.3.5.jar.sha1 @@ -0,0 +1 @@ +59eb84ee0d616332ff44aba065f3888cf002cd2d \ No newline at end of file diff --git a/server/licenses/jakarta.annotation-api-LICENSE.txt b/server/licenses/jakarta.annotation-api-LICENSE.txt new file mode 100644 index 0000000000000..b021839cdf874 --- /dev/null +++ b/server/licenses/jakarta.annotation-api-LICENSE.txt @@ -0,0 +1,637 @@ +# Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + + 1. DEFINITIONS + + "Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + + "Contributor" means any person or entity that Distributes the Program. + + "Licensed Patents" mean patent claims licensable by a Contributor which + are necessarily infringed by the use or sale of its Contribution alone + or when combined with the Program. + + "Program" means the Contributions Distributed in accordance with this + Agreement. + + "Recipient" means anyone who receives the Program under this Agreement + or any Secondary License (as applicable), including Contributors. + + "Derivative Works" shall mean any work, whether in Source Code or other + form, that is based on (or derived from) the Program and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. + + "Modified Works" shall mean any work in Source Code or other form that + results from an addition to, deletion from, or modification of the + contents of the Program, including, for purposes of clarity any new file + in Source Code form that contains any contents of the Program. Modified + Works shall not include works that contain only declarations, + interfaces, types, classes, structures, or files of the Program solely + in each case in order to link to, bind by name, or subclass the Program + or Modified Works thereof. + + "Distribute" means the acts of a) distributing or b) making available + in any manner that enables the transfer of a copy. + + "Source Code" means the form of a Program preferred for making + modifications, including but not limited to software source code, + documentation source, and configuration files. + + "Secondary License" means either the GNU General Public License, + Version 2.0, or any later versions of that license, including any + exceptions or additional permissions as identified by the initial + Contributor. + + 2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + + 3. REQUIREMENTS + + 3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + + 3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + + 3.3 Contributors may not remove or alter any copyright, patent, + trademark, attribution notices, disclaimers of warranty, or limitations + of liability ("notices") contained within the Program from any copy of + the Program which they Distribute, provided that Contributors may add + their own appropriate notices. + + 4. COMMERCIAL DISTRIBUTION + + Commercial distributors of software may accept certain responsibilities + with respect to end users, business partners and the like. While this + license is intended to facilitate the commercial use of the Program, + the Contributor who includes the Program in a commercial product + offering should do so in a manner which does not create potential + liability for other Contributors. Therefore, if a Contributor includes + the Program in a commercial product offering, such Contributor + ("Commercial Contributor") hereby agrees to defend and indemnify every + other Contributor ("Indemnified Contributor") against any losses, + damages and costs (collectively "Losses") arising from claims, lawsuits + and other legal actions brought by a third party against the Indemnified + Contributor to the extent caused by the acts or omissions of such + Commercial Contributor in connection with its distribution of the Program + in a commercial product offering. The obligations in this section do not + apply to any claims or Losses relating to any actual or alleged + intellectual property infringement. In order to qualify, an Indemnified + Contributor must: a) promptly notify the Commercial Contributor in + writing of such claim, and b) allow the Commercial Contributor to control, + and cooperate with the Commercial Contributor in, the defense and any + related settlement negotiations. The Indemnified Contributor may + participate in any such claim at its own expense. + + For example, a Contributor might include the Program in a commercial + product offering, Product X. That Contributor is then a Commercial + Contributor. If that Commercial Contributor then makes performance + claims, or offers warranties related to Product X, those performance + claims and warranties are such Commercial Contributor's responsibility + alone. Under this section, the Commercial Contributor would have to + defend claims against the other Contributors related to those performance + claims and warranties, and if a court requires any other Contributor to + pay any damages as a result, the Commercial Contributor must pay + those damages. + + 5. NO WARRANTY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT + PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR + IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF + TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR + PURPOSE. Each Recipient is solely responsible for determining the + appropriateness of using and distributing the Program and assumes all + risks associated with its exercise of rights under this Agreement, + including but not limited to the risks and costs of program errors, + compliance with applicable laws, damage to or loss of data, programs + or equipment, and unavailability or interruption of operations. + + 6. DISCLAIMER OF LIABILITY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT + PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS + SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST + PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE + EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + + 7. GENERAL + + If any provision of this Agreement is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this Agreement, and without further + action by the parties hereto, such provision shall be reformed to the + minimum extent necessary to make such provision valid and enforceable. + + If Recipient institutes patent litigation against any entity + (including a cross-claim or counterclaim in a lawsuit) alleging that the + Program itself (excluding combinations of the Program with other software + or hardware) infringes such Recipient's patent(s), then such Recipient's + rights granted under Section 2(b) shall terminate as of the date such + litigation is filed. + + All Recipient's rights under this Agreement shall terminate if it + fails to comply with any of the material terms or conditions of this + Agreement and does not cure such failure in a reasonable period of + time after becoming aware of such noncompliance. If all Recipient's + rights under this Agreement terminate, Recipient agrees to cease use + and distribution of the Program as soon as reasonably practicable. + However, Recipient's obligations under this Agreement and any licenses + granted by Recipient relating to the Program shall continue and survive. + + Everyone is permitted to copy and distribute copies of this Agreement, + but in order to avoid inconsistency the Agreement is copyrighted and + may only be modified in the following manner. The Agreement Steward + reserves the right to publish new versions (including revisions) of + this Agreement from time to time. No one other than the Agreement + Steward has the right to modify this Agreement. The Eclipse Foundation + is the initial Agreement Steward. The Eclipse Foundation may assign the + responsibility to serve as the Agreement Steward to a suitable separate + entity. Each new version of the Agreement will be given a distinguishing + version number. The Program (including Contributions) may always be + Distributed subject to the version of the Agreement under which it was + received. In addition, after a new version of the Agreement is published, + Contributor may elect to Distribute the Program (including its + Contributions) under the new version. + + Except as expressly stated in Sections 2(a) and 2(b) above, Recipient + receives no rights or licenses to the intellectual property of any + Contributor under this Agreement, whether expressly, by implication, + estoppel or otherwise. All rights in the Program not expressly granted + under this Agreement are reserved. Nothing in this Agreement is intended + to be enforceable by any entity that is not a Contributor or Recipient. + No third-party beneficiary rights are created under this Agreement. + + Exhibit A - Form of Secondary Licenses Notice + + "This Source Code may also be made available under the following + Secondary Licenses when the conditions for such availability set forth + in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), + version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. + +--- + +## The GNU General Public License (GPL) Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1335 + USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your freedom to + share and change it. By contrast, the GNU General Public License is + intended to guarantee your freedom to share and change free software--to + make sure the software is free for all its users. This General Public + License applies to most of the Free Software Foundation's software and + to any other program whose authors commit to using it. (Some other Free + Software Foundation software is covered by the GNU Library General + Public License instead.) You can apply it to your programs, too. + + When we speak of free software, we are referring to freedom, not price. + Our General Public Licenses are designed to make sure that you have the + freedom to distribute copies of free software (and charge for this + service if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid anyone + to deny you these rights or to ask you to surrender the rights. These + restrictions translate to certain responsibilities for you if you + distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether gratis + or for a fee, you must give the recipients all the rights that you have. + You must make sure that they, too, receive or can get the source code. + And you must show them these terms so they know their rights. + + We protect your rights with two steps: (1) copyright the software, and + (2) offer you this license which gives you legal permission to copy, + distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain + that everyone understands that there is no warranty for this free + software. If the software is modified by someone else and passed on, we + want its recipients to know that what they have is not the original, so + that any problems introduced by others will not reflect on the original + authors' reputations. + + Finally, any free program is threatened constantly by software patents. + We wish to avoid the danger that redistributors of a free program will + individually obtain patent licenses, in effect making the program + proprietary. To prevent this, we have made it clear that any patent must + be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed under + the terms of this General Public License. The "Program", below, refers + to any such program or work, and a "work based on the Program" means + either the Program or any derivative work under copyright law: that is + to say, a work containing the Program or a portion of it, either + verbatim or with modifications and/or translated into another language. + (Hereinafter, translation is included without limitation in the term + "modification".) Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of running + the Program is not restricted, and the output from the Program is + covered only if its contents constitute a work based on the Program + (independent of having been made by running the Program). Whether that + is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and + you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion of + it, thus forming a work based on the Program, and copy and distribute + such modifications or work under the terms of Section 1 above, provided + that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not + normally print such an announcement, your work based on the Program + is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Program, and + can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based on + the Program, the distribution of the whole must be on the terms of this + License, whose permissions for other licensees extend to the entire + whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of a + storage or distribution medium does not bring the other work under the + scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source code + means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to control + compilation and installation of the executable. However, as a special + exception, the source code distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on + which the executable runs, unless that component itself accompanies the + executable. + + If distribution of executable or object code is made by offering access + to copy from a designated place, then offering equivalent access to copy + the source code from the same place counts as distribution of the source + code, even though third parties are not compelled to copy the source + along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt otherwise + to copy, modify, sublicense or distribute the Program is void, and will + automatically terminate your rights under this License. However, parties + who have received copies, or rights, from you under this License will + not have their licenses terminated so long as such parties remain in + full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Program or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Program (or any work based on the + Program), you indicate your acceptance of this License to do so, and all + its terms and conditions for copying, distributing or modifying the + Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further restrictions + on the recipients' exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute + so as to satisfy simultaneously your obligations under this License and + any other pertinent obligations, then as a consequence you may not + distribute the Program at all. For example, if a patent license would + not permit royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only way you + could satisfy both it and this License would be to refrain entirely from + distribution of the Program. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is implemented + by public license practices. Many people have made generous + contributions to the wide range of software distributed through that + system in reliance on consistent application of that system; it is up to + the author/donor to decide if he or she is willing to distribute + software through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be + a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Program under this License may + add an explicit geographical distribution limitation excluding those + countries, so that distribution is permitted only in or among countries + not thus excluded. In such case, this License incorporates the + limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a version + number of this License, you may choose any version ever published by the + Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted by the + Free Software Foundation, write to the Free Software Foundation; we + sometimes make exceptions for this. Our decision will be guided by the + two goals of preserving the free status of all derivatives of our free + software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, + EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH + YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY + AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest to + attach them to the start of each source file to most effectively convey + the exclusion of warranty; and each file should have at least the + "copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + + Also add information on how to contact you by electronic and paper mail. + + If the program is interactive, make it output a short notice like this + when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. This is free software, and you are welcome to redistribute + it under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the + appropriate parts of the General Public License. Of course, the commands + you use may be called something other than `show w' and `show c'; they + could even be mouse-clicks or menu items--whatever suits your program. + + You should also get your employer (if you work as a programmer) or your + school, if any, to sign a "copyright disclaimer" for the program, if + necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (which makes passes at compilers) written by + James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + + This General Public License does not permit incorporating your program + into proprietary programs. If your program is a subroutine library, you + may consider it more useful to permit linking proprietary applications + with the library. If this is what you want to do, use the GNU Library + General Public License instead of this License. + +--- + +## CLASSPATH EXCEPTION + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License version 2 cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from or + based on this library. If you modify this library, you may extend this + exception to your version of the library, but you are not obligated to + do so. If you do not wish to do so, delete this exception statement + from your version. \ No newline at end of file diff --git a/server/licenses/jakarta.annotation-api-NOTICE.txt b/server/licenses/jakarta.annotation-api-NOTICE.txt new file mode 100644 index 0000000000000..8e60c4b1b80c5 --- /dev/null +++ b/server/licenses/jakarta.annotation-api-NOTICE.txt @@ -0,0 +1,38 @@ +# Notices for Jakarta Annotations + +This content is produced and maintained by the Jakarta Annotations project. + + * Project home: https://projects.eclipse.org/projects/ee4j.ca + +## Trademarks + +Jakarta Annotations is a trademark of the Eclipse Foundation. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code + +The project maintains the following source code repositories: + + * https://github.com/eclipse-ee4j/common-annotations-api + +## Third-party Content + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. \ No newline at end of file diff --git a/server/licenses/jna-5.13.0.jar.sha1 b/server/licenses/jna-5.13.0.jar.sha1 new file mode 100644 index 0000000000000..faf2012f0b5c0 --- /dev/null +++ b/server/licenses/jna-5.13.0.jar.sha1 @@ -0,0 +1 @@ +1200e7ebeedbe0d10062093f32925a912020e747 \ No newline at end of file diff --git a/server/licenses/jna-5.5.0.jar.sha1 b/server/licenses/jna-5.5.0.jar.sha1 deleted file mode 100644 index 5621dfc743dd0..0000000000000 --- a/server/licenses/jna-5.5.0.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -0e0845217c4907822403912ad6828d8e0b256208 diff --git a/server/licenses/joda-time-2.10.12.jar.sha1 b/server/licenses/joda-time-2.10.12.jar.sha1 deleted file mode 100644 index 538f23152f69d..0000000000000 --- a/server/licenses/joda-time-2.10.12.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -95b3f193ad0493d94dcd7daa9ea575c30e6be5f5 \ No newline at end of file diff --git a/server/licenses/joda-time-2.12.2.jar.sha1 b/server/licenses/joda-time-2.12.2.jar.sha1 new file mode 100644 index 0000000000000..6e9b28eb35597 --- /dev/null +++ b/server/licenses/joda-time-2.12.2.jar.sha1 @@ -0,0 +1 @@ +78e18a7b4180e911dafba0a412adfa82c1e3d14b \ No newline at end of file diff --git a/server/licenses/jzlib-1.1.3.jar.sha1 b/server/licenses/jzlib-1.1.3.jar.sha1 new file mode 100644 index 0000000000000..2affa9b8cd51b --- /dev/null +++ b/server/licenses/jzlib-1.1.3.jar.sha1 @@ -0,0 +1 @@ +c01428efa717624f7aabf4df319939dda9646b2d \ No newline at end of file diff --git a/server/licenses/jzlib-LICENSE.txt b/server/licenses/jzlib-LICENSE.txt new file mode 100644 index 0000000000000..245abb23411e0 --- /dev/null +++ b/server/licenses/jzlib-LICENSE.txt @@ -0,0 +1,29 @@ +JZlib 0.0.* were released under the GNU LGPL license. Later, we have switched +over to a BSD-style license. + +------------------------------------------------------------------------------ +Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + The name of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/server/licenses/jzlib-NOTICE.txt b/server/licenses/jzlib-NOTICE.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/server/licenses/log4j-api-2.17.1.jar.sha1 b/server/licenses/log4j-api-2.17.1.jar.sha1 deleted file mode 100644 index 9d0e5dc631ed5..0000000000000 --- a/server/licenses/log4j-api-2.17.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d771af8e336e372fb5399c99edabe0919aeaf5b2 \ No newline at end of file diff --git a/server/licenses/log4j-api-2.20.0.jar.sha1 b/server/licenses/log4j-api-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..37154d9861ac0 --- /dev/null +++ b/server/licenses/log4j-api-2.20.0.jar.sha1 @@ -0,0 +1 @@ +1fe6082e660daf07c689a89c94dc0f49c26b44bb \ No newline at end of file diff --git a/server/licenses/log4j-core-2.17.1.jar.sha1 b/server/licenses/log4j-core-2.17.1.jar.sha1 deleted file mode 100644 index 7d4634f3d4e18..0000000000000 --- a/server/licenses/log4j-core-2.17.1.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -779f60f3844dadc3ef597976fcb1e5127b1f343d \ No newline at end of file diff --git a/server/licenses/log4j-core-2.20.0.jar.sha1 b/server/licenses/log4j-core-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..49c972626563b --- /dev/null +++ b/server/licenses/log4j-core-2.20.0.jar.sha1 @@ -0,0 +1 @@ +eb2a9a47b1396e00b5eee1264296729a70565cc0 \ No newline at end of file diff --git a/server/licenses/log4j-jul-2.20.0.jar.sha1 b/server/licenses/log4j-jul-2.20.0.jar.sha1 new file mode 100644 index 0000000000000..a456651e4569e --- /dev/null +++ b/server/licenses/log4j-jul-2.20.0.jar.sha1 @@ -0,0 +1 @@ +8170e6118eac1ab332046c179718a0f107f688e1 \ No newline at end of file diff --git a/server/licenses/log4j-jul-LICENSE.txt b/server/licenses/log4j-jul-LICENSE.txt new file mode 100644 index 0000000000000..d645695673349 --- /dev/null +++ b/server/licenses/log4j-jul-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/server/licenses/log4j-jul-NOTICE.txt b/server/licenses/log4j-jul-NOTICE.txt new file mode 100644 index 0000000000000..243a0391fb574 --- /dev/null +++ b/server/licenses/log4j-jul-NOTICE.txt @@ -0,0 +1,20 @@ +Apache Log4j +Copyright 1999-2021 Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +ResolverUtil.java +Copyright 2005-2006 Tim Fennell + +Dumbster SMTP test server +Copyright 2004 Jason Paul Kitchen + +TypeUtil.java +Copyright 2002-2012 Ramnivas Laddad, Juergen Hoeller, Chris Beams + +picocli (http://picocli.info) +Copyright 2017 Remko Popma + +TimeoutBlockingWaitStrategy.java and parts of Util.java +Copyright 2011 LMAX Ltd. diff --git a/server/licenses/lucene-analysis-common-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-analysis-common-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 6ef0f1eafc345..0000000000000 --- a/server/licenses/lucene-analysis-common-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -bafd720282a371efe7b0e7238f9dee7e2ad3a586 \ No newline at end of file diff --git a/server/licenses/lucene-analysis-common-9.7.0.jar.sha1 b/server/licenses/lucene-analysis-common-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..45d8f459573b1 --- /dev/null +++ b/server/licenses/lucene-analysis-common-9.7.0.jar.sha1 @@ -0,0 +1 @@ +27ba6caaa4587a982cd451f7217b5a982bcfc44a \ No newline at end of file diff --git a/server/licenses/lucene-backward-codecs-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-backward-codecs-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 017333945a866..0000000000000 --- a/server/licenses/lucene-backward-codecs-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -597fe288a252a14c0876451c97afee2b4529f85a \ No newline at end of file diff --git a/server/licenses/lucene-backward-codecs-9.7.0.jar.sha1 b/server/licenses/lucene-backward-codecs-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..3981ea4fa226e --- /dev/null +++ b/server/licenses/lucene-backward-codecs-9.7.0.jar.sha1 @@ -0,0 +1 @@ +6389463bfbfcf902c8d31d12e9513a6818ac9d5e \ No newline at end of file diff --git a/server/licenses/lucene-core-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-core-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index a2ba0f0ffa43c..0000000000000 --- a/server/licenses/lucene-core-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -80cd2fff33ced89924771c7079d42bf82f1266f6 \ No newline at end of file diff --git a/server/licenses/lucene-core-9.7.0.jar.sha1 b/server/licenses/lucene-core-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..2b0f77275c0ab --- /dev/null +++ b/server/licenses/lucene-core-9.7.0.jar.sha1 @@ -0,0 +1 @@ +ad391210ffd806931334be9670a35af00c56f959 \ No newline at end of file diff --git a/server/licenses/lucene-grouping-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-grouping-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index ac0c1be0f952b..0000000000000 --- a/server/licenses/lucene-grouping-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -7059f47668a2942c60ad03b1d58eca8dcb010e4e \ No newline at end of file diff --git a/server/licenses/lucene-grouping-9.7.0.jar.sha1 b/server/licenses/lucene-grouping-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..90acbf6dcee8d --- /dev/null +++ b/server/licenses/lucene-grouping-9.7.0.jar.sha1 @@ -0,0 +1 @@ +8e6f0c229f4861be641047c33b05067176e4279c \ No newline at end of file diff --git a/server/licenses/lucene-highlighter-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-highlighter-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index fa08ed63f7c44..0000000000000 --- a/server/licenses/lucene-highlighter-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3c841ca23eb08a939fa49ba4af249c3b6d849c42 \ No newline at end of file diff --git a/server/licenses/lucene-highlighter-9.7.0.jar.sha1 b/server/licenses/lucene-highlighter-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..bfcca0bc6cb5b --- /dev/null +++ b/server/licenses/lucene-highlighter-9.7.0.jar.sha1 @@ -0,0 +1 @@ +facb7c7ee0f75ed457a2d98f10d6430e25a53691 \ No newline at end of file diff --git a/server/licenses/lucene-join-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-join-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 2a3e2a9107a60..0000000000000 --- a/server/licenses/lucene-join-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -4984e041ae68f5939c01e41b2c9648ae2c021340 \ No newline at end of file diff --git a/server/licenses/lucene-join-9.7.0.jar.sha1 b/server/licenses/lucene-join-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..0dab3a7ddc41a --- /dev/null +++ b/server/licenses/lucene-join-9.7.0.jar.sha1 @@ -0,0 +1 @@ +d041bdc0947a14223cf68357407ee18b21027587 \ No newline at end of file diff --git a/server/licenses/lucene-memory-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-memory-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index eefd08d222ef8..0000000000000 --- a/server/licenses/lucene-memory-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -fead9467ce65469579168eb0f47e014fdb3c63d9 \ No newline at end of file diff --git a/server/licenses/lucene-memory-9.7.0.jar.sha1 b/server/licenses/lucene-memory-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..357a9c4b2ea26 --- /dev/null +++ b/server/licenses/lucene-memory-9.7.0.jar.sha1 @@ -0,0 +1 @@ +0fade51ee353e15ddbbc45262aafe6f99ed020f1 \ No newline at end of file diff --git a/server/licenses/lucene-misc-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-misc-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 226f97cf6f3bc..0000000000000 --- a/server/licenses/lucene-misc-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d98ab1966b8ca53b70fe071281bcea27d602ec30 \ No newline at end of file diff --git a/server/licenses/lucene-misc-9.7.0.jar.sha1 b/server/licenses/lucene-misc-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..da5e1921626b2 --- /dev/null +++ b/server/licenses/lucene-misc-9.7.0.jar.sha1 @@ -0,0 +1 @@ +7fcf451e2376526c3a027958812866cc5b0ff13f \ No newline at end of file diff --git a/server/licenses/lucene-queries-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-queries-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index c151e6b76e21a..0000000000000 --- a/server/licenses/lucene-queries-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -895e27127ae55031e35e152da8be941bd55f7f6a \ No newline at end of file diff --git a/server/licenses/lucene-queries-9.7.0.jar.sha1 b/server/licenses/lucene-queries-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..fa82e95a7e19f --- /dev/null +++ b/server/licenses/lucene-queries-9.7.0.jar.sha1 @@ -0,0 +1 @@ +126989d4622419aa06fcbf3a342e859cab8c8799 \ No newline at end of file diff --git a/server/licenses/lucene-queryparser-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-queryparser-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index b73b7152aed05..0000000000000 --- a/server/licenses/lucene-queryparser-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -1433392237ea01ef35f4e2ffc52f496b0669624c \ No newline at end of file diff --git a/server/licenses/lucene-queryparser-9.7.0.jar.sha1 b/server/licenses/lucene-queryparser-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..438db0aea66e1 --- /dev/null +++ b/server/licenses/lucene-queryparser-9.7.0.jar.sha1 @@ -0,0 +1 @@ +6e77bde908ff698354e4a2149e6dd4658b56d7b0 \ No newline at end of file diff --git a/server/licenses/lucene-sandbox-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-sandbox-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index d441dd2f8cb31..0000000000000 --- a/server/licenses/lucene-sandbox-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -b0688963ca8288f5a3e47ca6e4b38bc2fde780e7 \ No newline at end of file diff --git a/server/licenses/lucene-sandbox-9.7.0.jar.sha1 b/server/licenses/lucene-sandbox-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..38b0b1cccbc29 --- /dev/null +++ b/server/licenses/lucene-sandbox-9.7.0.jar.sha1 @@ -0,0 +1 @@ +9f3e8e1947f2f1c5784132444af51a060ff0b4bf \ No newline at end of file diff --git a/server/licenses/lucene-spatial-extras-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-spatial-extras-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 5ffa78a6e7d87..0000000000000 --- a/server/licenses/lucene-spatial-extras-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -206e8918a726710c8a6fb927e59adf26c6ad5bed \ No newline at end of file diff --git a/server/licenses/lucene-spatial-extras-9.7.0.jar.sha1 b/server/licenses/lucene-spatial-extras-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..48679df469fd1 --- /dev/null +++ b/server/licenses/lucene-spatial-extras-9.7.0.jar.sha1 @@ -0,0 +1 @@ +01b0bc7a407d8c35a70a1adf7966bb3e7caae928 \ No newline at end of file diff --git a/server/licenses/lucene-spatial3d-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-spatial3d-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 8c4bb08303c34..0000000000000 --- a/server/licenses/lucene-spatial3d-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3d1e26c37b45bdf2ef598d16468220ab33983a8f \ No newline at end of file diff --git a/server/licenses/lucene-spatial3d-9.7.0.jar.sha1 b/server/licenses/lucene-spatial3d-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..55d4d217fa6b9 --- /dev/null +++ b/server/licenses/lucene-spatial3d-9.7.0.jar.sha1 @@ -0,0 +1 @@ +7c6b1b6e0a70c9cd177371e648648c2f896742a2 \ No newline at end of file diff --git a/server/licenses/lucene-suggest-9.1.0-snapshot-ea989fe8f30.jar.sha1 b/server/licenses/lucene-suggest-9.1.0-snapshot-ea989fe8f30.jar.sha1 deleted file mode 100644 index 3c8d9b87da0e5..0000000000000 --- a/server/licenses/lucene-suggest-9.1.0-snapshot-ea989fe8f30.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -69ab05339614766c732fef7c037cc5b676bd40dc \ No newline at end of file diff --git a/server/licenses/lucene-suggest-9.7.0.jar.sha1 b/server/licenses/lucene-suggest-9.7.0.jar.sha1 new file mode 100644 index 0000000000000..d4d7e6cd6bed9 --- /dev/null +++ b/server/licenses/lucene-suggest-9.7.0.jar.sha1 @@ -0,0 +1 @@ +5c37fd9a5d71dc87fe1cd4c18ff295ec8cfac170 \ No newline at end of file diff --git a/server/licenses/protobuf-java-3.22.3.jar.sha1 b/server/licenses/protobuf-java-3.22.3.jar.sha1 new file mode 100644 index 0000000000000..80feeec023e7b --- /dev/null +++ b/server/licenses/protobuf-java-3.22.3.jar.sha1 @@ -0,0 +1 @@ +fdee98b8f6abab73f146a4edb4c09e56f8278d03 \ No newline at end of file diff --git a/plugins/repository-gcs/licenses/protobuf-LICENSE.txt b/server/licenses/protobuf-java-LICENSE.txt similarity index 100% rename from plugins/repository-gcs/licenses/protobuf-LICENSE.txt rename to server/licenses/protobuf-java-LICENSE.txt diff --git a/plugins/repository-gcs/licenses/protobuf-NOTICE.txt b/server/licenses/protobuf-java-NOTICE.txt similarity index 100% rename from plugins/repository-gcs/licenses/protobuf-NOTICE.txt rename to server/licenses/protobuf-java-NOTICE.txt diff --git a/server/src/internalClusterTest/java/org/opensearch/action/IndicesRequestIT.java b/server/src/internalClusterTest/java/org/opensearch/action/IndicesRequestIT.java index 17366cf0d08fc..6e4d66b74d7c1 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/IndicesRequestIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/IndicesRequestIT.java @@ -56,6 +56,8 @@ import org.opensearch.action.admin.indices.recovery.RecoveryRequest; import org.opensearch.action.admin.indices.refresh.RefreshRequest; import org.opensearch.action.admin.indices.refresh.TransportShardRefreshAction; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsAction; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsRequest; import org.opensearch.action.admin.indices.segments.IndicesSegmentsAction; import org.opensearch.action.admin.indices.segments.IndicesSegmentsRequest; import org.opensearch.action.admin.indices.settings.get.GetSettingsAction; @@ -92,9 +94,9 @@ import org.opensearch.action.update.UpdateResponse; import org.opensearch.client.Requests; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.NetworkPlugin; import org.opensearch.plugins.Plugin; @@ -110,7 +112,6 @@ import org.opensearch.transport.TransportInterceptor; import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestHandler; - import org.junit.After; import org.junit.Before; @@ -468,6 +469,17 @@ public void testRecovery() { assertSameIndices(recoveryRequest, recoveryAction); } + public void testSegmentReplicationStats() { + String segmentReplicationAction = SegmentReplicationStatsAction.NAME + "[n]"; + interceptTransportActions(segmentReplicationAction); + + SegmentReplicationStatsRequest segmentReplicationStatsRequest = new SegmentReplicationStatsRequest(randomIndicesOrAliases()); + internalCluster().coordOnlyNodeClient().admin().indices().segmentReplicationStats(segmentReplicationStatsRequest).actionGet(); + + clearInterceptedActions(); + assertSameIndices(segmentReplicationStatsRequest, segmentReplicationAction); + } + public void testSegments() { String segmentsAction = IndicesSegmentsAction.NAME + "[n]"; interceptTransportActions(segmentsAction); diff --git a/server/src/internalClusterTest/java/org/opensearch/action/ListenerActionIT.java b/server/src/internalClusterTest/java/org/opensearch/action/ListenerActionIT.java index 1512fa4934ca1..c82ca0a06f4a1 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/ListenerActionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/ListenerActionIT.java @@ -36,6 +36,7 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.client.Client; import org.opensearch.client.Requests; +import org.opensearch.core.action.ActionListener; import org.opensearch.test.OpenSearchIntegTestCase; import java.util.concurrent.CountDownLatch; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/RejectionActionIT.java b/server/src/internalClusterTest/java/org/opensearch/action/RejectionActionIT.java index f930b9e9cfda0..e52df4476241b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/RejectionActionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/RejectionActionIT.java @@ -38,7 +38,8 @@ import org.opensearch.action.search.SearchType; import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; import org.opensearch.index.query.QueryBuilders; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/ClientTimeoutIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/ClientTimeoutIT.java index d83e8112569ee..340caa75c61d9 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/ClientTimeoutIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/ClientTimeoutIT.java @@ -8,9 +8,9 @@ package org.opensearch.action.admin; -import org.opensearch.action.admin.cluster.node.info.NodesInfoResponse; -import org.opensearch.action.admin.cluster.node.info.NodesInfoAction; import org.opensearch.action.admin.cluster.node.info.NodeInfo; +import org.opensearch.action.admin.cluster.node.info.NodesInfoAction; +import org.opensearch.action.admin.cluster.node.info.NodesInfoResponse; import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsAction; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; @@ -33,10 +33,10 @@ import java.util.Collections; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.lessThan; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) public class ClientTimeoutIT extends OpenSearchIntegTestCase { @@ -47,7 +47,7 @@ protected Collection> nodePlugins() { } public void testNodesInfoTimeout() { - String masterNode = internalCluster().startMasterOnlyNode(); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); String dataNode = internalCluster().startDataOnlyNode(); String anotherDataNode = internalCluster().startDataOnlyNode(); @@ -65,11 +65,11 @@ public void testNodesInfoTimeout() { nodes.add(node.getNode().getName()); } assertThat(response.getNodes().size(), equalTo(2)); - assertThat(nodes.contains(masterNode), is(true)); + assertThat(nodes.contains(clusterManagerNode), is(true)); } public void testNodesStatsTimeout() { - String masterNode = internalCluster().startMasterOnlyNode(); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); String dataNode = internalCluster().startDataOnlyNode(); String anotherDataNode = internalCluster().startDataOnlyNode(); TimeValue timeout = TimeValue.timeValueMillis(1000); @@ -87,11 +87,11 @@ public void testNodesStatsTimeout() { nodes.add(node.getNode().getName()); } assertThat(response.getNodes().size(), equalTo(2)); - assertThat(nodes.contains(masterNode), is(true)); + assertThat(nodes.contains(clusterManagerNode), is(true)); } public void testListTasksTimeout() { - String masterNode = internalCluster().startMasterOnlyNode(); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); String dataNode = internalCluster().startDataOnlyNode(); String anotherDataNode = internalCluster().startDataOnlyNode(); TimeValue timeout = TimeValue.timeValueMillis(1000); @@ -108,7 +108,7 @@ public void testListTasksTimeout() { } public void testRecoveriesWithTimeout() { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); String dataNode = internalCluster().startDataOnlyNode(); String anotherDataNode = internalCluster().startDataOnlyNode(); @@ -148,7 +148,7 @@ public void testRecoveriesWithTimeout() { } public void testStatsWithTimeout() { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); String dataNode = internalCluster().startDataOnlyNode(); String anotherDataNode = internalCluster().startDataOnlyNode(); diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/HotThreadsIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/HotThreadsIT.java index ab44c95b4f5a6..6343bd127c458 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/HotThreadsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/HotThreadsIT.java @@ -32,11 +32,11 @@ package org.opensearch.action.admin; import org.apache.lucene.util.Constants; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.hotthreads.NodeHotThreads; import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsRequestBuilder; import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsResponse; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; import org.opensearch.test.OpenSearchIntegTestCase; import org.hamcrest.Matcher; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/ReloadSecureSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/ReloadSecureSettingsIT.java index 7e6dad47121a9..8deb7557966a8 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/ReloadSecureSettingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/ReloadSecureSettingsIT.java @@ -33,13 +33,13 @@ package org.opensearch.action.admin; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.reload.NodesReloadSecureSettingsResponse; -import org.opensearch.common.Strings; import org.opensearch.common.settings.KeyStoreWrapper; import org.opensearch.common.settings.SecureSettings; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.PluginsService; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainIT.java index 8c7e5378516bf..a9a6993ff8d64 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainIT.java @@ -49,18 +49,17 @@ import org.opensearch.cluster.routing.allocation.NodeAllocationResult; import org.opensearch.cluster.routing.allocation.decider.Decision; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.index.shard.ShardId; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; import java.util.Collections; @@ -1110,7 +1109,7 @@ public void testAssignedReplicaOnSpecificNode() throws Exception { public void testCannotAllocateStaleReplicaExplanation() throws Exception { logger.info("--> starting 3 nodes"); - final String masterNode = internalCluster().startNode(); + final String clusterManagerNode = internalCluster().startNode(); // start replica node first, so it's path will be used first when we start a node after // stopping all of them at end of test. final String replicaNode = internalCluster().startNode(); @@ -1123,7 +1122,7 @@ public void testCannotAllocateStaleReplicaExplanation() throws Exception { 1, Settings.builder() .put("index.routing.allocation.include._name", primaryNode) - .put("index.routing.allocation.exclude._name", masterNode) + .put("index.routing.allocation.exclude._name", clusterManagerNode) .build(), ActiveShardCount.ONE ); @@ -1165,7 +1164,7 @@ public void testCannotAllocateStaleReplicaExplanation() throws Exception { logger.info("--> restart the node with the stale replica"); String restartedNode = internalCluster().startDataOnlyNode(replicaDataPathSettings); - ensureClusterSizeConsistency(); // wait for the master to finish processing join. + ensureClusterSizeConsistency(); // wait for the cluster-manager to finish processing join. // wait until the system has fetched shard data and we know there is no valid shard copy assertBusy(() -> { @@ -1275,7 +1274,7 @@ private ClusterAllocationExplanation runExplain(boolean primary, String nodeId, XContentBuilder builder = JsonXContent.contentBuilder(); builder.prettyPrint(); builder.humanReadable(true); - logger.debug("--> explain json output: \n{}", Strings.toString(explanation.toXContent(builder, ToXContent.EMPTY_PARAMS))); + logger.debug("--> explain json output: \n{}", explanation.toXContent(builder, ToXContent.EMPTY_PARAMS).toString()); } return explanation; } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/AbstractTasksIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/AbstractTasksIT.java new file mode 100644 index 0000000000000..0197ccf059737 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/AbstractTasksIT.java @@ -0,0 +1,190 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.node.tasks; + +import org.opensearch.ExceptionsHelper; +import org.opensearch.ResourceNotFoundException; +import org.opensearch.action.admin.cluster.node.tasks.get.GetTaskResponse; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.collect.Tuple; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.tasks.resourcetracker.ThreadResourceInfo; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.plugins.Plugin; +import org.opensearch.tasks.TaskInfo; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.tasks.MockTaskManager; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * Base IT test class for Tasks ITs + */ +abstract class AbstractTasksIT extends OpenSearchIntegTestCase { + + protected Map, RecordingTaskManagerListener> listeners = new HashMap<>(); + + @Override + protected Collection> getMockPlugins() { + Collection> mockPlugins = new ArrayList<>(super.getMockPlugins()); + mockPlugins.remove(MockTransportService.TestPlugin.class); + return mockPlugins; + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class, TestTaskPlugin.class); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(MockTaskManager.USE_MOCK_TASK_MANAGER_SETTING.getKey(), true) + .build(); + } + + @Override + public void tearDown() throws Exception { + for (Map.Entry, RecordingTaskManagerListener> entry : listeners.entrySet()) { + ((MockTaskManager) internalCluster().getInstance(TransportService.class, entry.getKey().v1()).getTaskManager()).removeListener( + entry.getValue() + ); + } + listeners.clear(); + super.tearDown(); + } + + /** + * Registers recording task event listeners with the given action mask on all nodes + */ + protected void registerTaskManagerListeners(String actionMasks) { + for (String nodeName : internalCluster().getNodeNames()) { + DiscoveryNode node = internalCluster().getInstance(ClusterService.class, nodeName).localNode(); + RecordingTaskManagerListener listener = new RecordingTaskManagerListener(node.getId(), actionMasks.split(",")); + ((MockTaskManager) internalCluster().getInstance(TransportService.class, nodeName).getTaskManager()).addListener(listener); + RecordingTaskManagerListener oldListener = listeners.put(new Tuple<>(node.getName(), actionMasks), listener); + assertNull(oldListener); + } + } + + /** + * Resets all recording task event listeners with the given action mask on all nodes + */ + protected void resetTaskManagerListeners(String actionMasks) { + for (Map.Entry, RecordingTaskManagerListener> entry : listeners.entrySet()) { + if (actionMasks == null || entry.getKey().v2().equals(actionMasks)) { + entry.getValue().reset(); + } + } + } + + /** + * Returns the number of events that satisfy the criteria across all nodes + * + * @param actionMasks action masks to match + * @return number of events that satisfy the criteria + */ + protected int numberOfEvents(String actionMasks, Function, Boolean> criteria) { + return findEvents(actionMasks, criteria).size(); + } + + /** + * Returns all events that satisfy the criteria across all nodes + * + * @param actionMasks action masks to match + * @return number of events that satisfy the criteria + */ + protected List findEvents(String actionMasks, Function, Boolean> criteria) { + List events = new ArrayList<>(); + for (Map.Entry, RecordingTaskManagerListener> entry : listeners.entrySet()) { + if (actionMasks == null || entry.getKey().v2().equals(actionMasks)) { + for (Tuple taskEvent : entry.getValue().getEvents()) { + if (criteria.apply(taskEvent)) { + events.add(taskEvent.v2()); + } + } + } + } + return events; + } + + protected Map> getThreadStats(String actionMasks, TaskId taskId) { + for (Map.Entry, RecordingTaskManagerListener> entry : listeners.entrySet()) { + if (actionMasks == null || entry.getKey().v2().equals(actionMasks)) { + for (Tuple>> threadStats : entry.getValue().getThreadStats()) { + if (taskId.equals(threadStats.v1())) { + return threadStats.v2(); + } + } + } + } + return new HashMap<>(); + } + + /** + * Asserts that all tasks in the tasks list have the same parentTask + */ + protected void assertParentTask(List tasks, TaskInfo parentTask) { + for (TaskInfo task : tasks) { + assertParentTask(task, parentTask); + } + } + + protected void assertParentTask(TaskInfo task, TaskInfo parentTask) { + assertTrue(task.getParentTaskId().isSet()); + assertEquals(parentTask.getTaskId().getNodeId(), task.getParentTaskId().getNodeId()); + assertTrue(Strings.hasLength(task.getParentTaskId().getNodeId())); + assertEquals(parentTask.getId(), task.getParentTaskId().getId()); + } + + protected void expectNotFound(ThrowingRunnable r) { + Exception e = expectThrows(Exception.class, r); + ResourceNotFoundException notFound = (ResourceNotFoundException) ExceptionsHelper.unwrap(e, ResourceNotFoundException.class); + if (notFound == null) { + throw new AssertionError("Expected " + ResourceNotFoundException.class.getSimpleName(), e); + } + } + + /** + * Fetch the task status from the list tasks API using it's "fallback to get from the task index" behavior. Asserts some obvious stuff + * about the fetched task and returns a map of it's status. + */ + protected GetTaskResponse expectFinishedTask(TaskId taskId) throws IOException { + GetTaskResponse response = client().admin().cluster().prepareGetTask(taskId).get(); + assertTrue("the task should have been completed before fetching", response.getTask().isCompleted()); + TaskInfo info = response.getTask().getTask(); + assertEquals(taskId, info.getTaskId()); + assertNull(info.getStatus()); // The test task doesn't have any status + return response; + } + + protected void indexDocumentsWithRefresh(String indexName, int numDocs) { + for (int i = 0; i < numDocs; i++) { + client().prepareIndex(indexName) + .setId("test_id_" + String.valueOf(i)) + .setSource("{\"foo_" + String.valueOf(i) + "\": \"bar_" + String.valueOf(i) + "\"}", MediaTypeRegistry.JSON) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .get(); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/CancellableTasksIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/CancellableTasksIT.java index 6c507171091e7..c4dcedcc722cf 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/CancellableTasksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/CancellableTasksIT.java @@ -31,14 +31,12 @@ package org.opensearch.action.admin.cluster.node.tasks; -import org.apache.lucene.util.SetOnce; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ExceptionsHelper; import org.opensearch.ResourceNotFoundException; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionRunnable; import org.opensearch.action.ActionType; import org.opensearch.action.LatchedActionListener; @@ -50,31 +48,37 @@ import org.opensearch.action.support.PlainActionFuture; import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.SetOnce; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.util.concurrent.AbstractRunnable; import org.opensearch.common.util.concurrent.ConcurrentCollections; import org.opensearch.common.util.set.Sets; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskCancelledException; +import org.opensearch.core.tasks.TaskId; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.tasks.CancellableTask; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskCancelledException; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.tasks.TaskManager; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; - import org.junit.Before; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -87,6 +91,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; @@ -94,7 +99,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; -public class CancellableTasksIT extends OpenSearchIntegTestCase { +public class CancellableTasksIT extends ParameterizedOpenSearchIntegTestCase { static int idGenerator = 0; static final Map beforeSendLatches = ConcurrentCollections.newConcurrentMap(); @@ -102,6 +107,23 @@ public class CancellableTasksIT extends OpenSearchIntegTestCase { static final Map beforeExecuteLatches = ConcurrentCollections.newConcurrentMap(); static final Map completedLatches = ConcurrentCollections.newConcurrentMap(); + public CancellableTasksIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Before public void resetTestStates() { idGenerator = 0; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/ConcurrentSearchTasksIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/ConcurrentSearchTasksIT.java new file mode 100644 index 0000000000000..ceacb028698de --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/ConcurrentSearchTasksIT.java @@ -0,0 +1,118 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.node.tasks; + +import org.opensearch.action.admin.indices.segments.IndicesSegmentsRequest; +import org.opensearch.action.search.SearchAction; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.collect.Tuple; +import org.opensearch.common.settings.FeatureFlagSettings; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.tasks.resourcetracker.ThreadResourceInfo; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.tasks.TaskInfo; +import org.hamcrest.MatcherAssert; + +import java.util.List; +import java.util.Map; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.notNullValue; + +/** + * Integration tests for task management API with Concurrent Segment Search + * + * The way the test framework bootstraps the test cluster makes it difficult to parameterize the feature flag. + * Once concurrent search is moved behind a cluster setting we can parameterize these tests behind the setting. + */ +public class ConcurrentSearchTasksIT extends AbstractTasksIT { + + private static final int INDEX_SEARCHER_THREADS = 10; + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put("thread_pool.index_searcher.size", INDEX_SEARCHER_THREADS) + .put("thread_pool.index_searcher.queue_size", INDEX_SEARCHER_THREADS) + .build(); + } + + private int getSegmentCount(String indexName) { + return client().admin() + .indices() + .segments(new IndicesSegmentsRequest(indexName)) + .actionGet() + .getIndices() + .get(indexName) + .getShards() + .get(0) + .getShards()[0].getSegments() + .size(); + } + + @Override + protected Settings featureFlagSettings() { + Settings.Builder featureSettings = Settings.builder(); + for (Setting builtInFlag : FeatureFlagSettings.BUILT_IN_FEATURE_FLAGS) { + featureSettings.put(builtInFlag.getKey(), builtInFlag.getDefaultRaw(Settings.EMPTY)); + } + featureSettings.put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, true); + return featureSettings.build(); + } + + /** + * Tests the number of threads that worked on a search task. + * + * Currently, we try to control concurrency by creating an index with 7 segments and rely on + * the way concurrent search creates leaf slices from segments. Once more concurrency controls are introduced + * we should improve this test to use those methods. + */ + public void testConcurrentSearchTaskTracking() { + final String INDEX_NAME = "test"; + final int NUM_SHARDS = 1; + final int NUM_DOCS = 7; + + registerTaskManagerListeners(SearchAction.NAME); // coordinator task + registerTaskManagerListeners(SearchAction.NAME + "[*]"); // shard task + createIndex( + INDEX_NAME, + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, NUM_SHARDS) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + ensureGreen(INDEX_NAME); // Make sure all shards are allocated to catch replication tasks + indexDocumentsWithRefresh(INDEX_NAME, NUM_DOCS); // Concurrent search requires >5 segments or >250,000 docs to have concurrency, so + // we index 7 docs flushing between each to create new segments + assertSearchResponse(client().prepareSearch(INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).get()); + + // the search operation should produce one coordinator task + List mainTask = findEvents(SearchAction.NAME, Tuple::v1); + assertEquals(1, mainTask.size()); + TaskInfo mainTaskInfo = mainTask.get(0); + + List shardTasks = findEvents(SearchAction.NAME + "[*]", Tuple::v1); + assertEquals(NUM_SHARDS, shardTasks.size()); // We should only have 1 shard search task per shard + for (TaskInfo taskInfo : shardTasks) { + MatcherAssert.assertThat(taskInfo.getParentTaskId(), notNullValue()); + assertEquals(mainTaskInfo.getTaskId(), taskInfo.getParentTaskId()); + + Map> threadStats = getThreadStats(SearchAction.NAME + "[*]", taskInfo.getTaskId()); + // Concurrent search forks each slice of 5 segments to different thread + assertEquals((int) Math.ceil(getSegmentCount(INDEX_NAME) / 5.0), threadStats.size()); + + // assert that all task descriptions have non-zero length + MatcherAssert.assertThat(taskInfo.getDescription().length(), greaterThan(0)); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/TaskStorageRetryIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/TaskStorageRetryIT.java index e710d3a8438d8..455be343de2c5 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/TaskStorageRetryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/TaskStorageRetryIT.java @@ -36,9 +36,9 @@ import org.opensearch.action.support.PlainListenableActionFuture; import org.opensearch.client.node.NodeClient; import org.opensearch.common.settings.Settings; +import org.opensearch.core.tasks.TaskId; import org.opensearch.plugins.Plugin; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import org.opensearch.test.OpenSearchSingleNodeTestCase; import org.opensearch.threadpool.ThreadPool; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/TasksIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/TasksIT.java index fbac2f7dbff6e..c7d75108883dd 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/TasksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/node/tasks/TasksIT.java @@ -32,12 +32,9 @@ package org.opensearch.action.admin.cluster.node.tasks; +import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; import org.opensearch.OpenSearchTimeoutException; -import org.opensearch.ExceptionsHelper; -import org.opensearch.ResourceNotFoundException; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; import org.opensearch.action.TaskOperationFailure; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.node.tasks.cancel.CancelTasksResponse; @@ -57,32 +54,25 @@ import org.opensearch.action.support.WriteRequest; import org.opensearch.action.support.replication.ReplicationResponse; import org.opensearch.action.support.replication.TransportReplicationActionTests; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.collect.Tuple; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.plugins.Plugin; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.tasks.TaskResult; import org.opensearch.tasks.TaskResultsService; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.tasks.MockTaskManager; import org.opensearch.test.tasks.MockTaskManagerListener; -import org.opensearch.test.transport.MockTransportService; import org.opensearch.transport.ReceiveTimeoutTransportException; import org.opensearch.transport.TransportService; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -117,32 +107,10 @@ /** * Integration tests for task management API *

    - * We need at least 2 nodes so we have a master node a non-master node + * We need at least 2 nodes so we have a cluster-manager node a non-cluster-manager node */ @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, minNumDataNodes = 2) -public class TasksIT extends OpenSearchIntegTestCase { - - private Map, RecordingTaskManagerListener> listeners = new HashMap<>(); - - @Override - protected Collection> getMockPlugins() { - Collection> mockPlugins = new ArrayList<>(super.getMockPlugins()); - mockPlugins.remove(MockTransportService.TestPlugin.class); - return mockPlugins; - } - - @Override - protected Collection> nodePlugins() { - return Arrays.asList(MockTransportService.TestPlugin.class, TestTaskPlugin.class); - } - - @Override - protected Settings nodeSettings(int nodeOrdinal) { - return Settings.builder() - .put(super.nodeSettings(nodeOrdinal)) - .put(MockTaskManager.USE_MOCK_TASK_MANAGER_SETTING.getKey(), true) - .build(); - } +public class TasksIT extends AbstractTasksIT { public void testTaskCounts() { // Run only on data nodes @@ -154,18 +122,18 @@ public void testTaskCounts() { assertThat(response.getTasks().size(), greaterThanOrEqualTo(cluster().numDataNodes())); } - public void testMasterNodeOperationTasks() { + public void testClusterManagerNodeOperationTasks() { registerTaskManagerListeners(ClusterHealthAction.NAME); - // First run the health on the master node - should produce only one task on the master node - internalCluster().masterClient().admin().cluster().prepareHealth().get(); + // First run the health on the cluster-manager node - should produce only one task on the cluster-manager node + internalCluster().clusterManagerClient().admin().cluster().prepareHealth().get(); assertEquals(1, numberOfEvents(ClusterHealthAction.NAME, Tuple::v1)); // counting only registration events assertEquals(1, numberOfEvents(ClusterHealthAction.NAME, event -> event.v1() == false)); // counting only unregistration events resetTaskManagerListeners(ClusterHealthAction.NAME); - // Now run the health on a non-master node - should produce one task on master and one task on another node - internalCluster().nonMasterClient().admin().cluster().prepareHealth().get(); + // Now run the health on a non-cluster-manager node - should produce one task on cluster-manager and one task on another node + internalCluster().nonClusterManagerClient().admin().cluster().prepareHealth().get(); assertEquals(2, numberOfEvents(ClusterHealthAction.NAME, Tuple::v1)); // counting only registration events assertEquals(2, numberOfEvents(ClusterHealthAction.NAME, event -> event.v1() == false)); // counting only unregistration events List tasks = findEvents(ClusterHealthAction.NAME, Tuple::v1); @@ -319,7 +287,9 @@ public void testTransportBulkTasks() { ensureGreen("test"); // Make sure all shards are allocated to catch replication tasks // ensures the mapping is available on all nodes so we won't retry the request (in case replicas don't have the right mapping). client().admin().indices().preparePutMapping("test").setSource("foo", "type=keyword").get(); - client().prepareBulk().add(client().prepareIndex("test").setId("test_id").setSource("{\"foo\": \"bar\"}", XContentType.JSON)).get(); + client().prepareBulk() + .add(client().prepareIndex("test").setId("test_id").setSource("{\"foo\": \"bar\"}", MediaTypeRegistry.JSON)) + .get(); // the bulk operation should produce one main task List topTask = findEvents(BulkAction.NAME, Tuple::v1); @@ -370,7 +340,7 @@ public void testSearchTaskDescriptions() { ensureGreen("test"); // Make sure all shards are allocated to catch replication tasks client().prepareIndex("test") .setId("test_id") - .setSource("{\"foo\": \"bar\"}", XContentType.JSON) + .setSource("{\"foo\": \"bar\"}", MediaTypeRegistry.JSON) .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) .get(); @@ -470,6 +440,9 @@ public void onTaskUnregistered(Task task) {} @Override public void waitForTaskCompletion(Task task) {} + + @Override + public void taskExecutionStarted(Task task, Boolean closeableInvoked) {} }); } // Need to run the task in a separate thread because node client's .execute() is blocked by our task listener @@ -507,14 +480,12 @@ public void waitForTaskCompletion(Task task) {} if (index != null) { index.join(); } - assertBusy( - () -> { - assertEquals( - emptyList(), - client().admin().cluster().prepareListTasks().setActions("indices:data/write/index*").get().getTasks() - ); - } - ); + assertBusy(() -> { + assertEquals( + emptyList(), + client().admin().cluster().prepareListTasks().setActions("indices:data/write/index*").get().getTasks() + ); + }); } } @@ -542,6 +513,17 @@ public void testTasksCancellation() throws Exception { .get(); assertEquals(1, cancelTasksResponse.getTasks().size()); + // Tasks are marked as cancelled at this point but not yet completed. + List taskInfoList = client().admin() + .cluster() + .prepareListTasks() + .setActions(TestTaskPlugin.TestTaskAction.NAME + "*") + .get() + .getTasks(); + for (TaskInfo taskInfo : taskInfoList) { + assertTrue(taskInfo.isCancelled()); + assertNotNull(taskInfo.getCancellationStartTime()); + } future.get(); logger.info("--> checking that test tasks are not running"); @@ -651,6 +633,9 @@ public void waitForTaskCompletion(Task task) { waitForWaitingToStart.countDown(); } + @Override + public void taskExecutionStarted(Task task, Boolean closeableInvoked) {} + @Override public void onTaskRegistered(Task task) {} @@ -907,7 +892,8 @@ public void testNodeNotFoundButTaskFound() throws Exception { false, false, TaskId.EMPTY_TASK_ID, - Collections.emptyMap() + Collections.emptyMap(), + null ), new RuntimeException("test") ), @@ -935,106 +921,4 @@ public void onFailure(Exception e) { assertNotNull(response.getTask().getError()); assertNull(response.getTask().getResponse()); } - - @Override - public void tearDown() throws Exception { - for (Map.Entry, RecordingTaskManagerListener> entry : listeners.entrySet()) { - ((MockTaskManager) internalCluster().getInstance(TransportService.class, entry.getKey().v1()).getTaskManager()).removeListener( - entry.getValue() - ); - } - listeners.clear(); - super.tearDown(); - } - - /** - * Registers recording task event listeners with the given action mask on all nodes - */ - private void registerTaskManagerListeners(String actionMasks) { - for (String nodeName : internalCluster().getNodeNames()) { - DiscoveryNode node = internalCluster().getInstance(ClusterService.class, nodeName).localNode(); - RecordingTaskManagerListener listener = new RecordingTaskManagerListener(node.getId(), actionMasks.split(",")); - ((MockTaskManager) internalCluster().getInstance(TransportService.class, nodeName).getTaskManager()).addListener(listener); - RecordingTaskManagerListener oldListener = listeners.put(new Tuple<>(node.getName(), actionMasks), listener); - assertNull(oldListener); - } - } - - /** - * Resets all recording task event listeners with the given action mask on all nodes - */ - private void resetTaskManagerListeners(String actionMasks) { - for (Map.Entry, RecordingTaskManagerListener> entry : listeners.entrySet()) { - if (actionMasks == null || entry.getKey().v2().equals(actionMasks)) { - entry.getValue().reset(); - } - } - } - - /** - * Returns the number of events that satisfy the criteria across all nodes - * - * @param actionMasks action masks to match - * @return number of events that satisfy the criteria - */ - private int numberOfEvents(String actionMasks, Function, Boolean> criteria) { - return findEvents(actionMasks, criteria).size(); - } - - /** - * Returns all events that satisfy the criteria across all nodes - * - * @param actionMasks action masks to match - * @return number of events that satisfy the criteria - */ - private List findEvents(String actionMasks, Function, Boolean> criteria) { - List events = new ArrayList<>(); - for (Map.Entry, RecordingTaskManagerListener> entry : listeners.entrySet()) { - if (actionMasks == null || entry.getKey().v2().equals(actionMasks)) { - for (Tuple taskEvent : entry.getValue().getEvents()) { - if (criteria.apply(taskEvent)) { - events.add(taskEvent.v2()); - } - } - } - } - return events; - } - - /** - * Asserts that all tasks in the tasks list have the same parentTask - */ - private void assertParentTask(List tasks, TaskInfo parentTask) { - for (TaskInfo task : tasks) { - assertParentTask(task, parentTask); - } - } - - private void assertParentTask(TaskInfo task, TaskInfo parentTask) { - assertTrue(task.getParentTaskId().isSet()); - assertEquals(parentTask.getTaskId().getNodeId(), task.getParentTaskId().getNodeId()); - assertTrue(Strings.hasLength(task.getParentTaskId().getNodeId())); - assertEquals(parentTask.getId(), task.getParentTaskId().getId()); - } - - private void expectNotFound(ThrowingRunnable r) { - Exception e = expectThrows(Exception.class, r); - ResourceNotFoundException notFound = (ResourceNotFoundException) ExceptionsHelper.unwrap(e, ResourceNotFoundException.class); - if (notFound == null) { - throw new AssertionError("Expected " + ResourceNotFoundException.class.getSimpleName(), e); - } - } - - /** - * Fetch the task status from the list tasks API using it's "fallback to get from the task index" behavior. Asserts some obvious stuff - * about the fetched task and returns a map of it's status. - */ - private GetTaskResponse expectFinishedTask(TaskId taskId) throws IOException { - GetTaskResponse response = client().admin().cluster().prepareGetTask(taskId).get(); - assertTrue("the task should have been completed before fetching", response.getTask().isCompleted()); - TaskInfo info = response.getTask().getTask(); - assertEquals(taskId, info.getTaskId()); - assertNull(info.getStatus()); // The test task doesn't have any status - return response; - } } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/repositories/RepositoryBlocksIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/repositories/RepositoryBlocksIT.java index 4d3b60da0987f..aff7c5d9876ac 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/repositories/RepositoryBlocksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/repositories/RepositoryBlocksIT.java @@ -97,7 +97,7 @@ public void testVerifyRepositoryWithBlocks() { .prepareVerifyRepository("test-repo-blocks") .execute() .actionGet(); - assertThat(response.getNodes().size(), equalTo(cluster().numDataAndMasterNodes())); + assertThat(response.getNodes().size(), equalTo(cluster().numDataAndClusterManagerNodes())); } finally { setClusterReadOnly(false); } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/snapshots/SnapshotBlocksIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/snapshots/SnapshotBlocksIT.java index 1731c607a066d..347011721c728 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/snapshots/SnapshotBlocksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/snapshots/SnapshotBlocksIT.java @@ -37,13 +37,11 @@ import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse; - import org.opensearch.cluster.metadata.Metadata; import org.opensearch.common.settings.Settings; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; - import org.junit.Before; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_BLOCKS_READ; @@ -92,7 +90,7 @@ protected void setUpRepository() throws Exception { logger.info("--> verify the repository"); VerifyRepositoryResponse verifyResponse = client().admin().cluster().prepareVerifyRepository(REPOSITORY_NAME).get(); - assertThat(verifyResponse.getNodes().size(), equalTo(cluster().numDataAndMasterNodes())); + assertThat(verifyResponse.getNodes().size(), equalTo(cluster().numDataAndClusterManagerNodes())); logger.info("--> create a snapshot"); CreateSnapshotResponse snapshotResponse = client().admin() diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/state/TransportClusterStateActionDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/state/TransportClusterStateActionDisruptionIT.java index c6f47a01ed2d2..1a0cad36c8f04 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/state/TransportClusterStateActionDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/state/TransportClusterStateActionDisruptionIT.java @@ -40,7 +40,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.discovery.MasterNotDiscoveredException; +import org.opensearch.discovery.ClusterManagerNotDiscoveredException; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; @@ -68,26 +68,29 @@ protected Collection> nodePlugins() { return Collections.singletonList(MockTransportService.TestPlugin.class); } - public void testNonLocalRequestAlwaysFindsMaster() throws Exception { - runRepeatedlyWhileChangingMaster(() -> { + public void testNonLocalRequestAlwaysFindsClusterManager() throws Exception { + runRepeatedlyWhileChangingClusterManager(() -> { final ClusterStateRequestBuilder clusterStateRequestBuilder = client().admin() .cluster() .prepareState() .clear() .setNodes(true) - .setMasterNodeTimeout("100ms"); + .setClusterManagerNodeTimeout("100ms"); final ClusterStateResponse clusterStateResponse; try { clusterStateResponse = clusterStateRequestBuilder.get(); - } catch (MasterNotDiscoveredException e) { + } catch (ClusterManagerNotDiscoveredException e) { return; // ok, we hit the disconnected node } - assertNotNull("should always contain a master node", clusterStateResponse.getState().nodes().getMasterNodeId()); + assertNotNull( + "should always contain a cluster-manager node", + clusterStateResponse.getState().nodes().getClusterManagerNodeId() + ); }); } public void testLocalRequestAlwaysSucceeds() throws Exception { - runRepeatedlyWhileChangingMaster(() -> { + runRepeatedlyWhileChangingClusterManager(() -> { final String node = randomFrom(internalCluster().getNodeNames()); final DiscoveryNodes discoveryNodes = client(node).admin() .cluster() @@ -95,7 +98,7 @@ public void testLocalRequestAlwaysSucceeds() throws Exception { .clear() .setLocal(true) .setNodes(true) - .setMasterNodeTimeout("100ms") + .setClusterManagerNodeTimeout("100ms") .get() .getState() .nodes(); @@ -108,8 +111,8 @@ public void testLocalRequestAlwaysSucceeds() throws Exception { }); } - public void testNonLocalRequestAlwaysFindsMasterAndWaitsForMetadata() throws Exception { - runRepeatedlyWhileChangingMaster(() -> { + public void testNonLocalRequestAlwaysFindsClusterManagerAndWaitsForMetadata() throws Exception { + runRepeatedlyWhileChangingClusterManager(() -> { final String node = randomFrom(internalCluster().getNodeNames()); final long metadataVersion = internalCluster().getInstance(ClusterService.class, node) .getClusterApplierService() @@ -123,25 +126,25 @@ public void testNonLocalRequestAlwaysFindsMasterAndWaitsForMetadata() throws Exc .clear() .setNodes(true) .setMetadata(true) - .setMasterNodeTimeout(TimeValue.timeValueMillis(100)) + .setClusterManagerNodeTimeout(TimeValue.timeValueMillis(100)) .setWaitForTimeOut(TimeValue.timeValueMillis(100)) .setWaitForMetadataVersion(waitForMetadataVersion); final ClusterStateResponse clusterStateResponse; try { clusterStateResponse = clusterStateRequestBuilder.get(); - } catch (MasterNotDiscoveredException e) { + } catch (ClusterManagerNotDiscoveredException e) { return; // ok, we hit the disconnected node } if (clusterStateResponse.isWaitForTimedOut() == false) { final ClusterState state = clusterStateResponse.getState(); - assertNotNull("should always contain a master node", state.nodes().getMasterNodeId()); + assertNotNull("should always contain a cluster-manager node", state.nodes().getClusterManagerNodeId()); assertThat("waited for metadata version", state.metadata().version(), greaterThanOrEqualTo(waitForMetadataVersion)); } }); } public void testLocalRequestWaitsForMetadata() throws Exception { - runRepeatedlyWhileChangingMaster(() -> { + runRepeatedlyWhileChangingClusterManager(() -> { final String node = randomFrom(internalCluster().getNodeNames()); final long metadataVersion = internalCluster().getInstance(ClusterService.class, node) .getClusterApplierService() @@ -156,7 +159,7 @@ public void testLocalRequestWaitsForMetadata() throws Exception { .setLocal(true) .setMetadata(true) .setWaitForMetadataVersion(waitForMetadataVersion) - .setMasterNodeTimeout(TimeValue.timeValueMillis(100)) + .setClusterManagerNodeTimeout(TimeValue.timeValueMillis(100)) .setWaitForTimeOut(TimeValue.timeValueMillis(100)) .get(); if (clusterStateResponse.isWaitForTimedOut() == false) { @@ -170,7 +173,7 @@ public void testLocalRequestWaitsForMetadata() throws Exception { }); } - public void runRepeatedlyWhileChangingMaster(Runnable runnable) throws Exception { + public void runRepeatedlyWhileChangingClusterManager(Runnable runnable) throws Exception { internalCluster().startNodes(3); assertBusy( @@ -191,7 +194,7 @@ public void runRepeatedlyWhileChangingMaster(Runnable runnable) throws Exception ) ); - final String masterName = internalCluster().getMasterName(); + final String clusterManagerName = internalCluster().getClusterManagerName(); final AtomicBoolean shutdown = new AtomicBoolean(); final Thread assertingThread = new Thread(() -> { @@ -204,9 +207,12 @@ public void runRepeatedlyWhileChangingMaster(Runnable runnable) throws Exception String value = "none"; while (shutdown.get() == false) { value = "none".equals(value) ? "all" : "none"; - final String nonMasterNode = randomValueOtherThan(masterName, () -> randomFrom(internalCluster().getNodeNames())); + final String nonClusterManagerNode = randomValueOtherThan( + clusterManagerName, + () -> randomFrom(internalCluster().getNodeNames()) + ); assertAcked( - client(nonMasterNode).admin() + client(nonClusterManagerNode).admin() .cluster() .prepareUpdateSettings() .setPersistentSettings(Settings.builder().put(CLUSTER_ROUTING_REBALANCE_ENABLE_SETTING.getKey(), value)) @@ -222,22 +228,25 @@ public void runRepeatedlyWhileChangingMaster(Runnable runnable) throws Exception assertingThread.start(); updatingThread.start(); - final MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( + final MockTransportService clusterManagerTransportService = (MockTransportService) internalCluster().getInstance( TransportService.class, - masterName + clusterManagerName ); for (MockTransportService mockTransportService : mockTransportServices) { - if (masterTransportService != mockTransportService) { - masterTransportService.addFailToSendNoConnectRule(mockTransportService); - mockTransportService.addFailToSendNoConnectRule(masterTransportService); + if (clusterManagerTransportService != mockTransportService) { + clusterManagerTransportService.addFailToSendNoConnectRule(mockTransportService); + mockTransportService.addFailToSendNoConnectRule(clusterManagerTransportService); } } assertBusy(() -> { - final String nonMasterNode = randomValueOtherThan(masterName, () -> randomFrom(internalCluster().getNodeNames())); - final String claimedMasterName = internalCluster().getMasterName(nonMasterNode); - assertThat(claimedMasterName, not(equalTo(masterName))); + final String nonClusterManagerNode = randomValueOtherThan( + clusterManagerName, + () -> randomFrom(internalCluster().getNodeNames()) + ); + final String claimedClusterManagerName = internalCluster().getClusterManagerName(nonClusterManagerNode); + assertThat(claimedClusterManagerName, not(equalTo(clusterManagerName))); }); shutdown.set(true); diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/stats/ClusterStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/stats/ClusterStatsIT.java index 5b48268dfa89f..085a32593063a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/stats/ClusterStatsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/stats/ClusterStatsIT.java @@ -35,7 +35,9 @@ import org.opensearch.Version; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.cluster.node.stats.NodeStats; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; +import org.opensearch.client.Client; import org.opensearch.client.Requests; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.node.DiscoveryNodeRole; @@ -47,12 +49,13 @@ import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; - import org.hamcrest.Matchers; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -82,13 +85,7 @@ private void waitForNodes(int numNodes) { public void testNodeCounts() { int total = 1; internalCluster().startNode(); - Map expectedCounts = new HashMap<>(); - expectedCounts.put(DiscoveryNodeRole.DATA_ROLE.roleName(), 1); - expectedCounts.put(DiscoveryNodeRole.MASTER_ROLE.roleName(), 1); - expectedCounts.put(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE.roleName(), 1); - expectedCounts.put(DiscoveryNodeRole.INGEST_ROLE.roleName(), 1); - expectedCounts.put(DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE.roleName(), 1); - expectedCounts.put(ClusterStatsNodes.Counts.COORDINATING_ONLY, 0); + Map expectedCounts = getExpectedCounts(1, 1, 1, 1, 1, 0, 0); int numNodes = randomIntBetween(1, 5); ClusterStatsResponse response = client().admin().cluster().prepareClusterStats().get(); @@ -97,7 +94,7 @@ public void testNodeCounts() { for (int i = 0; i < numNodes; i++) { boolean isDataNode = randomBoolean(); boolean isIngestNode = randomBoolean(); - boolean isMasterNode = randomBoolean(); + boolean isClusterManagerNode = randomBoolean(); boolean isRemoteClusterClientNode = false; final Set roles = new HashSet<>(); if (isDataNode) { @@ -106,7 +103,7 @@ public void testNodeCounts() { if (isIngestNode) { roles.add(DiscoveryNodeRole.INGEST_ROLE); } - if (isMasterNode) { + if (isClusterManagerNode) { roles.add(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE); } if (isRemoteClusterClientNode) { @@ -128,14 +125,14 @@ public void testNodeCounts() { if (isIngestNode) { incrementCountForRole(DiscoveryNodeRole.INGEST_ROLE.roleName(), expectedCounts); } - if (isMasterNode) { + if (isClusterManagerNode) { incrementCountForRole(DiscoveryNodeRole.MASTER_ROLE.roleName(), expectedCounts); incrementCountForRole(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE.roleName(), expectedCounts); } if (isRemoteClusterClientNode) { incrementCountForRole(DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE.roleName(), expectedCounts); } - if (!isDataNode && !isMasterNode && !isIngestNode && !isRemoteClusterClientNode) { + if (!isDataNode && !isClusterManagerNode && !isIngestNode && !isRemoteClusterClientNode) { incrementCountForRole(ClusterStatsNodes.Counts.COORDINATING_ONLY, expectedCounts); } @@ -144,6 +141,25 @@ public void testNodeCounts() { } } + // Validate assigning value "master" to setting "node.roles" can get correct count in Node Stats response after MASTER_ROLE deprecated. + public void testNodeCountsWithDeprecatedMasterRole() throws ExecutionException, InterruptedException { + int total = 1; + Settings settings = Settings.builder() + .putList(NodeRoleSettings.NODE_ROLES_SETTING.getKey(), Collections.singletonList(DiscoveryNodeRole.MASTER_ROLE.roleName())) + .build(); + internalCluster().startNode(settings); + waitForNodes(total); + + Map expectedCounts = getExpectedCounts(0, 1, 1, 0, 0, 0, 0); + + Client client = client(); + ClusterStatsResponse response = client.admin().cluster().prepareClusterStats().get(); + assertCounts(response.getNodesStats().getCounts(), total, expectedCounts); + + Set expectedRoles = Set.of(DiscoveryNodeRole.MASTER_ROLE.roleName()); + assertEquals(expectedRoles, getNodeRoles(client, 0)); + } + private static void incrementCountForRole(String role, Map counts) { Integer count = counts.get(role); if (count == null) { @@ -254,12 +270,12 @@ public void testAllocatedProcessors() throws Exception { } public void testClusterStatusWhenStateNotRecovered() throws Exception { - internalCluster().startMasterOnlyNode(Settings.builder().put("gateway.recover_after_nodes", 2).build()); + internalCluster().startClusterManagerOnlyNode(Settings.builder().put("gateway.recover_after_nodes", 2).build()); ClusterStatsResponse response = client().admin().cluster().prepareClusterStats().get(); assertThat(response.getStatus(), equalTo(ClusterHealthStatus.RED)); if (randomBoolean()) { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); } else { internalCluster().startDataOnlyNode(); } @@ -298,4 +314,140 @@ public void testFieldTypes() { } } } + + public void testNodeRolesWithMasterLegacySettings() throws ExecutionException, InterruptedException { + int total = 1; + Settings legacyMasterSettings = Settings.builder() + .put("node.master", true) + .put("node.data", false) + .put("node.ingest", false) + .build(); + + internalCluster().startNodes(legacyMasterSettings); + waitForNodes(total); + + Map expectedCounts = getExpectedCounts(0, 1, 1, 0, 1, 0, 0); + + Client client = client(); + ClusterStatsResponse clusterStatsResponse = client.admin().cluster().prepareClusterStats().get(); + assertCounts(clusterStatsResponse.getNodesStats().getCounts(), total, expectedCounts); + + Set expectedRoles = Set.of( + DiscoveryNodeRole.MASTER_ROLE.roleName(), + DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE.roleName() + ); + assertEquals(expectedRoles, getNodeRoles(client, 0)); + } + + public void testNodeRolesWithClusterManagerRole() throws ExecutionException, InterruptedException { + int total = 1; + Settings clusterManagerNodeRoleSettings = Settings.builder() + .put( + "node.roles", + String.format( + Locale.ROOT, + "%s, %s", + DiscoveryNodeRole.CLUSTER_MANAGER_ROLE.roleName(), + DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE.roleName() + ) + ) + .build(); + + internalCluster().startNodes(clusterManagerNodeRoleSettings); + waitForNodes(total); + + Map expectedCounts = getExpectedCounts(0, 1, 1, 0, 1, 0, 0); + + Client client = client(); + ClusterStatsResponse clusterStatsResponse = client.admin().cluster().prepareClusterStats().get(); + assertCounts(clusterStatsResponse.getNodesStats().getCounts(), total, expectedCounts); + + Set expectedRoles = Set.of( + DiscoveryNodeRole.CLUSTER_MANAGER_ROLE.roleName(), + DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE.roleName() + ); + assertEquals(expectedRoles, getNodeRoles(client, 0)); + } + + public void testNodeRolesWithSeedDataNodeLegacySettings() throws ExecutionException, InterruptedException { + int total = 1; + Settings legacySeedDataNodeSettings = Settings.builder() + .put("node.master", true) + .put("node.data", true) + .put("node.ingest", false) + .build(); + + internalCluster().startNodes(legacySeedDataNodeSettings); + waitForNodes(total); + + Map expectedRoleCounts = getExpectedCounts(1, 1, 1, 0, 1, 0, 0); + + Client client = client(); + ClusterStatsResponse clusterStatsResponse = client.admin().cluster().prepareClusterStats().get(); + assertCounts(clusterStatsResponse.getNodesStats().getCounts(), total, expectedRoleCounts); + + Set expectedRoles = Set.of( + DiscoveryNodeRole.MASTER_ROLE.roleName(), + DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE.roleName(), + DiscoveryNodeRole.DATA_ROLE.roleName() + ); + assertEquals(expectedRoles, getNodeRoles(client, 0)); + } + + public void testNodeRolesWithDataNodeLegacySettings() throws ExecutionException, InterruptedException { + int total = 2; + Settings legacyDataNodeSettings = Settings.builder() + .put("node.master", false) + .put("node.data", true) + .put("node.ingest", false) + .build(); + + // can't start data-only node without assigning cluster-manager + internalCluster().startClusterManagerOnlyNodes(1); + internalCluster().startNodes(legacyDataNodeSettings); + waitForNodes(total); + + Map expectedRoleCounts = getExpectedCounts(1, 1, 1, 0, 1, 0, 0); + + Client client = client(); + ClusterStatsResponse clusterStatsResponse = client.admin().cluster().prepareClusterStats().get(); + assertCounts(clusterStatsResponse.getNodesStats().getCounts(), total, expectedRoleCounts); + + Set> expectedNodesRoles = Set.of( + Set.of(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE.roleName()), + Set.of(DiscoveryNodeRole.DATA_ROLE.roleName(), DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE.roleName()) + ); + assertEquals(expectedNodesRoles, Set.of(getNodeRoles(client, 0), getNodeRoles(client, 1))); + } + + private Map getExpectedCounts( + int dataRoleCount, + int masterRoleCount, + int clusterManagerRoleCount, + int ingestRoleCount, + int remoteClusterClientRoleCount, + int searchRoleCount, + int coordinatingOnlyCount + ) { + Map expectedCounts = new HashMap<>(); + expectedCounts.put(DiscoveryNodeRole.DATA_ROLE.roleName(), dataRoleCount); + expectedCounts.put(DiscoveryNodeRole.MASTER_ROLE.roleName(), masterRoleCount); + expectedCounts.put(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE.roleName(), clusterManagerRoleCount); + expectedCounts.put(DiscoveryNodeRole.INGEST_ROLE.roleName(), ingestRoleCount); + expectedCounts.put(DiscoveryNodeRole.REMOTE_CLUSTER_CLIENT_ROLE.roleName(), remoteClusterClientRoleCount); + expectedCounts.put(DiscoveryNodeRole.SEARCH_ROLE.roleName(), searchRoleCount); + expectedCounts.put(ClusterStatsNodes.Counts.COORDINATING_ONLY, coordinatingOnlyCount); + return expectedCounts; + } + + private Set getNodeRoles(Client client, int nodeNumber) throws ExecutionException, InterruptedException { + NodesStatsResponse nodesStatsResponse = client.admin().cluster().nodesStats(new NodesStatsRequest()).get(); + return nodesStatsResponse.getNodes() + .get(nodeNumber) + .getNode() + .getRoles() + .stream() + .map(DiscoveryNodeRole::roleName) + .collect(Collectors.toSet()); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java index 8484cce1045d2..83aa744a80599 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/cluster/tasks/PendingTasksBlocksIT.java @@ -34,8 +34,8 @@ import org.opensearch.common.settings.Settings; import org.opensearch.gateway.GatewayService; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Arrays; @@ -91,7 +91,7 @@ public void testPendingTasksWithClusterNotRecoveredBlock() throws Exception { } // restart the cluster but prevent it from performing state recovery - final int nodeCount = client().admin().cluster().prepareNodesInfo("data:true", "master:true").get().getNodes().size(); + final int nodeCount = client().admin().cluster().prepareNodesInfo("data:true", "cluster_manager:true").get().getNodes().size(); internalCluster().fullRestart(new InternalTestCluster.RestartCallback() { @Override public Settings onNodeStopped(String nodeName) { @@ -107,7 +107,7 @@ public boolean validateClusterForming() { assertNotNull(client().admin().cluster().preparePendingClusterTasks().get().getPendingTasks()); // starting one more node allows the cluster to recover - internalCluster().startDataOnlyNode(); // cannot update minimum_master_nodes before the cluster has formed + internalCluster().startDataOnlyNode(); // cannot update minimum_cluster_manager_nodes before the cluster has formed ensureGreen(); } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/alias/ValidateIndicesAliasesRequestIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/alias/ValidateIndicesAliasesRequestIT.java index 60243bd52ded3..910a9d351d83a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/alias/ValidateIndicesAliasesRequestIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/alias/ValidateIndicesAliasesRequestIT.java @@ -33,16 +33,16 @@ package org.opensearch.action.admin.indices.alias; import org.opensearch.action.RequestValidators; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistResponse; import org.opensearch.action.admin.indices.alias.get.GetAliasesRequest; import org.opensearch.action.admin.indices.alias.get.GetAliasesResponse; import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchSingleNodeTestCase; + import java.util.Collection; import java.util.Collections; import java.util.List; @@ -108,8 +108,8 @@ public void testAllowed() { request.addAliasAction(IndicesAliasesRequest.AliasActions.add().index("index").alias("alias")); assertAcked(client().admin().indices().aliases(request).actionGet()); final GetAliasesResponse response = client().admin().indices().getAliases(new GetAliasesRequest("alias")).actionGet(); - assertThat(response.getAliases().keys().size(), equalTo(1)); - assertThat(response.getAliases().keys().iterator().next().value, equalTo("index")); + assertThat(response.getAliases().keySet().size(), equalTo(1)); + assertThat(response.getAliases().keySet().iterator().next(), equalTo("index")); final List aliasMetadata = response.getAliases().get("index"); assertThat(aliasMetadata, hasSize(1)); assertThat(aliasMetadata.get(0).alias(), equalTo("alias")); @@ -143,8 +143,6 @@ public void testSomeAllowed() { final Exception e = expectThrows(IllegalStateException.class, () -> client().admin().indices().aliases(request).actionGet()); final String index = "foo_allowed".equals(origin) ? "bar" : "foo"; assertThat(e, hasToString(containsString("origin [" + origin + "] not allowed for index [" + index + "]"))); - final AliasesExistResponse response = client().admin().indices().aliasesExist(new GetAliasesRequest("alias")).actionGet(); - assertFalse(response.exists()); + assertTrue(client().admin().indices().getAliases(new GetAliasesRequest("alias")).actionGet().getAliases().isEmpty()); } - } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheBlocksIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheBlocksIT.java index 126bb57501abb..31c1aca53ae4a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheBlocksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheBlocksIT.java @@ -36,6 +36,7 @@ import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import java.util.Arrays; +import java.util.Collections; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_BLOCKS_METADATA; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_BLOCKS_READ; @@ -55,7 +56,12 @@ public void testClearIndicesCacheWithBlocks() { NumShards numShards = getNumShards("test"); // Request is not blocked - for (String blockSetting : Arrays.asList(SETTING_BLOCKS_READ, SETTING_BLOCKS_WRITE)) { + for (String blockSetting : Arrays.asList( + SETTING_BLOCKS_READ, + SETTING_BLOCKS_WRITE, + SETTING_READ_ONLY, + SETTING_READ_ONLY_ALLOW_DELETE + )) { try { enableIndexBlock("test", blockSetting); ClearIndicesCacheResponse clearIndicesCacheResponse = client().admin() @@ -73,7 +79,7 @@ public void testClearIndicesCacheWithBlocks() { } } // Request is blocked - for (String blockSetting : Arrays.asList(SETTING_READ_ONLY, SETTING_BLOCKS_METADATA, SETTING_READ_ONLY_ALLOW_DELETE)) { + for (String blockSetting : Arrays.asList(SETTING_BLOCKS_METADATA)) { try { enableIndexBlock("test", blockSetting); assertBlocked( @@ -84,4 +90,42 @@ public void testClearIndicesCacheWithBlocks() { } } } + + public void testClearIndicesFileCacheWithBlocks() { + createIndex("test"); + ensureGreen("test"); + + NumShards numShards = getNumShards("test"); + + // Request is not blocked + for (String blockSetting : Arrays.asList( + SETTING_BLOCKS_READ, + SETTING_BLOCKS_WRITE, + SETTING_READ_ONLY, + SETTING_READ_ONLY_ALLOW_DELETE + )) { + try { + enableIndexBlock("test", blockSetting); + ClearIndicesCacheResponse clearIndicesCacheResponse = client().admin() + .indices() + .prepareClearCache("test") + .setFileCache(true) + .execute() + .actionGet(); + assertNoFailures(clearIndicesCacheResponse); + assertThat(clearIndicesCacheResponse.getSuccessfulShards(), equalTo(numShards.totalNumShards)); + } finally { + disableIndexBlock("test", blockSetting); + } + } + + for (String blockSetting : Collections.singletonList(SETTING_BLOCKS_METADATA)) { + try { + enableIndexBlock("test", blockSetting); + assertBlocked(client().admin().indices().prepareClearCache("test").setQueryCache(true).setFileCache(true)); + } finally { + disableIndexBlock("test", blockSetting); + } + } + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/CloneIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/CloneIndexIT.java index 98fc6483703c4..0551d19b02b8f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/CloneIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/CloneIndexIT.java @@ -37,7 +37,7 @@ import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.query.TermsQueryBuilder; import org.opensearch.index.seqno.SeqNoStats; import org.opensearch.test.OpenSearchIntegTestCase; @@ -62,7 +62,7 @@ public void testCreateCloneIndex() { ).get(); final int docs = randomIntBetween(0, 128); for (int i = 0; i < docs; i++) { - client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } internalCluster().ensureAtLeastNumDataNodes(2); // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node @@ -122,7 +122,7 @@ public void testCreateCloneIndex() { } for (int i = docs; i < 2 * docs; i++) { - client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } flushAndRefresh(); assertHitCount( diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/CreateIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/CreateIndexIT.java index 3ef2a63c7d0ac..d0f4c98444b2e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/CreateIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/CreateIndexIT.java @@ -32,7 +32,6 @@ package org.opensearch.action.admin.indices.create; -import org.opensearch.action.ActionListener; import org.opensearch.action.UnavailableShardsException; import org.opensearch.action.admin.cluster.state.ClusterStateResponse; import org.opensearch.action.admin.indices.alias.Alias; @@ -45,10 +44,10 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.cluster.metadata.Metadata; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.action.ActionListener; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.MapperParsingException; @@ -59,6 +58,7 @@ import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; @@ -100,7 +100,7 @@ public void testCreationDateGenerated() { assertThat(state, notNullValue()); Metadata metadata = state.getMetadata(); assertThat(metadata, notNullValue()); - ImmutableOpenMap indices = metadata.getIndices(); + final Map indices = metadata.getIndices(); assertThat(indices, notNullValue()); assertThat(indices.size(), equalTo(1)); IndexMetadata index = indices.get("test"); @@ -340,7 +340,7 @@ public void testFailureToCreateIndexCleansUpIndicesService() { IllegalStateException.class ); - IndicesService indicesService = internalCluster().getInstance(IndicesService.class, internalCluster().getMasterName()); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, internalCluster().getClusterManagerName()); for (IndexService indexService : indicesService) { assertThat(indexService.index().getName(), not("test-idx-2")); } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/ShrinkIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/ShrinkIndexIT.java index e8a6c68a41076..cafcb73b699fc 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/ShrinkIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/ShrinkIndexIT.java @@ -37,7 +37,6 @@ import org.apache.lucene.search.SortedSetSelector; import org.apache.lucene.search.SortedSetSortField; import org.apache.lucene.util.Constants; - import org.opensearch.Version; import org.opensearch.action.admin.cluster.reroute.ClusterRerouteResponse; import org.opensearch.action.admin.cluster.state.ClusterStateRequest; @@ -64,19 +63,19 @@ import org.opensearch.cluster.routing.UnassignedInfo; import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.common.Priority; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.index.engine.SegmentsStats; import org.opensearch.index.query.TermsQueryBuilder; import org.opensearch.index.seqno.SeqNoStats; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; import java.util.Arrays; @@ -109,18 +108,12 @@ public void testCreateShrinkIndexToN() { for (int i = 0; i < 20; i++) { client().prepareIndex("source") .setId(Integer.toString(i)) - .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON) + .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON) .get(); } - ImmutableOpenMap dataNodes = client().admin() - .cluster() - .prepareState() - .get() - .getState() - .nodes() - .getDataNodes(); + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); assertTrue("at least 2 nodes but was: " + dataNodes.size(), dataNodes.size() >= 2); - DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(DiscoveryNode.class); + DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); String mergeNode = discoveryNodes[0].getName(); // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node // if we change the setting too quickly we will end up with one replica unassigned which can't be assigned anymore due @@ -153,7 +146,7 @@ public void testCreateShrinkIndexToN() { for (int i = 0; i < 20; i++) { // now update client().prepareIndex("first_shrink") .setId(Integer.toString(i)) - .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON) + .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON) .get(); } flushAndRefresh(); @@ -196,7 +189,7 @@ public void testCreateShrinkIndexToN() { for (int i = 0; i < 20; i++) { // now update client().prepareIndex("second_shrink") .setId(Integer.toString(i)) - .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON) + .setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON) .get(); } flushAndRefresh(); @@ -211,15 +204,9 @@ public void testShrinkIndexPrimaryTerm() throws Exception { internalCluster().ensureAtLeastNumDataNodes(2); prepareCreate("source").setSettings(Settings.builder().put(indexSettings()).put("number_of_shards", numberOfShards)).get(); - final ImmutableOpenMap dataNodes = client().admin() - .cluster() - .prepareState() - .get() - .getState() - .nodes() - .getDataNodes(); + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); assertThat(dataNodes.size(), greaterThanOrEqualTo(2)); - final DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(DiscoveryNode.class); + final DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); final String mergeNode = discoveryNodes[0].getName(); // This needs more than the default timeout if a large number of shards were created. ensureGreen(TimeValue.timeValueSeconds(120)); @@ -244,7 +231,7 @@ public void testShrinkIndexPrimaryTerm() throws Exception { final int hash = Math.floorMod(Murmur3HashFunction.hash(s), numberOfShards); if (hash == shardId) { final IndexRequest request = new IndexRequest("source").id(s) - .source("{ \"f\": \"" + s + "\"}", XContentType.JSON); + .source("{ \"f\": \"" + s + "\"}", MediaTypeRegistry.JSON); client().index(request).get(); break; } else { @@ -295,17 +282,11 @@ public void testCreateShrinkIndex() { ).get(); final int docs = randomIntBetween(0, 128); for (int i = 0; i < docs; i++) { - client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } - ImmutableOpenMap dataNodes = client().admin() - .cluster() - .prepareState() - .get() - .getState() - .nodes() - .getDataNodes(); + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); assertTrue("at least 2 nodes but was: " + dataNodes.size(), dataNodes.size() >= 2); - DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(DiscoveryNode.class); + DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node // if we change the setting too quickly we will end up with one replica unassigned which can't be assigned anymore due // to the require._name below. @@ -396,7 +377,7 @@ public void testCreateShrinkIndex() { } for (int i = docs; i < 2 * docs; i++) { - client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } flushAndRefresh(); assertHitCount(client().prepareSearch("target").setSize(2 * size).setQuery(new TermsQueryBuilder("foo", "bar")).get(), 2 * docs); @@ -423,17 +404,11 @@ public void testCreateShrinkIndexFails() throws Exception { Settings.builder().put(indexSettings()).put("number_of_shards", randomIntBetween(2, 7)).put("number_of_replicas", 0) ).get(); for (int i = 0; i < 20; i++) { - client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } - ImmutableOpenMap dataNodes = client().admin() - .cluster() - .prepareState() - .get() - .getState() - .nodes() - .getDataNodes(); + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); assertTrue("at least 2 nodes but was: " + dataNodes.size(), dataNodes.size() >= 2); - DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(DiscoveryNode.class); + DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); String spareNode = discoveryNodes[0].getName(); String mergeNode = discoveryNodes[1].getName(); // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node @@ -496,7 +471,7 @@ public void testCreateShrinkIndexFails() throws Exception { final InternalClusterInfoService infoService = (InternalClusterInfoService) internalCluster().getInstance( ClusterInfoService.class, - internalCluster().getMasterName() + internalCluster().getClusterManagerName() ); infoService.refresh(); // kick off a retry and wait until it's done! @@ -530,18 +505,12 @@ public void testCreateShrinkWithIndexSort() throws Exception { for (int i = 0; i < 20; i++) { client().prepareIndex("source") .setId(Integer.toString(i)) - .setSource("{\"foo\" : \"bar\", \"id\" : " + i + "}", XContentType.JSON) + .setSource("{\"foo\" : \"bar\", \"id\" : " + i + "}", MediaTypeRegistry.JSON) .get(); } - ImmutableOpenMap dataNodes = client().admin() - .cluster() - .prepareState() - .get() - .getState() - .nodes() - .getDataNodes(); + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); assertTrue("at least 2 nodes but was: " + dataNodes.size(), dataNodes.size() >= 2); - DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(DiscoveryNode.class); + DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); String mergeNode = discoveryNodes[0].getName(); // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node // if we change the setting too quickly we will end up with one replica unassigned which can't be assigned anymore due @@ -599,7 +568,7 @@ public void testCreateShrinkWithIndexSort() throws Exception { // ... and that the index sort is also applied to updates for (int i = 20; i < 40; i++) { - client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } flushAndRefresh(); assertSortedSegments("target", expectedIndexSort); @@ -610,17 +579,11 @@ public void testShrinkCommitsMergeOnIdle() throws Exception { Settings.builder().put(indexSettings()).put("index.number_of_replicas", 0).put("number_of_shards", 5) ).get(); for (int i = 0; i < 30; i++) { - client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } client().admin().indices().prepareFlush("source").get(); - ImmutableOpenMap dataNodes = client().admin() - .cluster() - .prepareState() - .get() - .getState() - .nodes() - .getDataNodes(); - DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(DiscoveryNode.class); + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); + DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node // if we change the setting too quickly we will end up with one replica unassigned which can't be assigned anymore due // to the require._name below. @@ -760,4 +723,72 @@ public void testShrinkThenSplitWithFailedNode() throws Exception { ); ensureGreen("splitagain"); } + + public void testCreateShrinkIndexWithMaxShardSize() { + internalCluster().ensureAtLeastNumDataNodes(2); + final String shrinkNode = internalCluster().startDataOnlyNode(); + + final int shardCount = between(2, 5); + prepareCreate("source").setSettings( + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, shardCount) + ).get(); + for (int i = 0; i < 20; i++) { + client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); + } + client().admin().indices().prepareFlush("source").get(); + ensureGreen(); + + client().admin() + .indices() + .prepareUpdateSettings("source") + .setSettings( + Settings.builder() + .put(IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_SETTING.getConcreteSettingForNamespace("_name").getKey(), shrinkNode) + .put(IndexMetadata.SETTING_BLOCKS_WRITE, true) + ) + .get(); + ensureGreen(); + + // Cannot set max_shard_size and index.number_of_shards at the same time + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .indices() + .prepareResizeIndex("source", "target") + .setSettings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ) + .setMaxShardSize(new ByteSizeValue(1)) + .setResizeType(ResizeType.SHRINK) + .get() + ); + assertTrue(exc.getMessage().contains("Cannot set max_shard_size and index.number_of_shards at the same time!")); + + // use max_shard_size to calculate the target index's shards number + // set max_shard_size to 1 then the target index's shards number will be same with the source index's + assertAcked( + client().admin() + .indices() + .prepareResizeIndex("source", "target") + .setSettings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .putNull(IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_SETTING.getConcreteSettingForNamespace("_name").getKey()) + .build() + ) + .setMaxShardSize(new ByteSizeValue(1)) + .setResizeType(ResizeType.SHRINK) + .get() + ); + ensureGreen(); + + GetSettingsResponse target = client().admin().indices().prepareGetSettings("target").get(); + assertEquals(String.valueOf(shardCount), target.getIndexToSettings().get("target").get("index.number_of_shards")); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/SplitIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/SplitIndexIT.java index 89e11984621da..c8b151e24ce98 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/SplitIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/create/SplitIndexIT.java @@ -38,7 +38,6 @@ import org.apache.lucene.search.SortedSetSortField; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.util.Constants; - import org.opensearch.Version; import org.opensearch.action.admin.cluster.state.ClusterStateRequest; import org.opensearch.action.admin.cluster.state.ClusterStateResponse; @@ -61,8 +60,8 @@ import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.index.engine.SegmentsStats; import org.opensearch.index.query.TermsQueryBuilder; @@ -347,7 +346,7 @@ public void testSplitIndexPrimaryTerm() throws Exception { final int hash = Math.floorMod(Murmur3HashFunction.hash(s), numberOfShards); if (hash == shardId) { final IndexRequest request = new IndexRequest("source").id(s) - .source("{ \"f\": \"" + s + "\"}", XContentType.JSON); + .source("{ \"f\": \"" + s + "\"}", MediaTypeRegistry.JSON); client().index(request).get(); break; } else { @@ -403,7 +402,7 @@ public void testCreateSplitIndex() throws Exception { ).get(); final int docs = randomIntBetween(0, 128); for (int i = 0; i < docs; i++) { - client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("source").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node // if we change the setting too quickly we will end up with one replica unassigned which can't be assigned anymore due @@ -487,7 +486,7 @@ public void testCreateSplitIndex() throws Exception { } for (int i = docs; i < 2 * docs; i++) { - client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } flushAndRefresh(); assertHitCount( @@ -526,7 +525,7 @@ public void testCreateSplitWithIndexSort() throws Exception { for (int i = 0; i < 20; i++) { client().prepareIndex("source") .setId(Integer.toString(i)) - .setSource("{\"foo\" : \"bar\", \"id\" : " + i + "}", XContentType.JSON) + .setSource("{\"foo\" : \"bar\", \"id\" : " + i + "}", MediaTypeRegistry.JSON) .get(); } // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node @@ -582,7 +581,7 @@ public void testCreateSplitWithIndexSort() throws Exception { // ... and that the index sort is also applied to updates for (int i = 20; i < 40; i++) { - client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", XContentType.JSON).get(); + client().prepareIndex("target").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); } flushAndRefresh(); assertSortedSegments("target", expectedIndexSort); diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamIndexTemplateIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamIndexTemplateIT.java index ed500d72c3787..08f7fb17e5164 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamIndexTemplateIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamIndexTemplateIT.java @@ -8,8 +8,7 @@ package org.opensearch.action.admin.indices.datastream; -import org.opensearch.common.collect.List; - +import java.util.List; import java.util.concurrent.ExecutionException; import static org.hamcrest.Matchers.containsString; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamRolloverIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamRolloverIT.java index bac16dd4b7cc0..6afa48da0918e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamRolloverIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamRolloverIT.java @@ -10,7 +10,7 @@ import org.opensearch.action.admin.indices.rollover.RolloverResponse; import org.opensearch.cluster.metadata.DataStream; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import java.util.Collections; import java.util.stream.Collectors; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamTestCase.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamTestCase.java index 7b0d917504a2f..50ff76c6b62f3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamTestCase.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/datastream/DataStreamTestCase.java @@ -16,19 +16,19 @@ import org.opensearch.cluster.metadata.ComposableIndexTemplate; import org.opensearch.cluster.metadata.DataStream; import org.opensearch.cluster.metadata.Template; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.test.OpenSearchIntegTestCase; import java.util.List; import java.util.stream.Collectors; -import static org.hamcrest.Matchers.is; import static org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import static org.opensearch.test.OpenSearchIntegTestCase.Scope; +import static org.hamcrest.Matchers.is; @ClusterScope(scope = Scope.TEST, numDataNodes = 2) public class DataStreamTestCase extends OpenSearchIntegTestCase { @@ -90,7 +90,7 @@ public AcknowledgedResponse createDataStreamIndexTemplate(String name, List index(new IndexRequest("logs-demo").id("doc-1").source("{}", XContentType.JSON))); + exception = expectThrows( + Exception.class, + () -> index(new IndexRequest("logs-demo").id("doc-1").source("{}", MediaTypeRegistry.JSON)) + ); assertThat(exception.getMessage(), containsString("only write ops with an op_type of create are allowed in data streams")); // Documents must contain a valid timestamp field. exception = expectThrows( Exception.class, - () -> index(new IndexRequest("logs-demo").id("doc-1").source("{}", XContentType.JSON).opType(DocWriteRequest.OpType.CREATE)) + () -> index( + new IndexRequest("logs-demo").id("doc-1").source("{}", MediaTypeRegistry.JSON).opType(DocWriteRequest.OpType.CREATE) + ) ); assertThat( exception.getMessage(), diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/exists/IndicesExistsIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/exists/IndicesExistsIT.java index f8be80948f8cf..b5ab4b5290171 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/exists/IndicesExistsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/exists/IndicesExistsIT.java @@ -34,11 +34,11 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.discovery.MasterNotDiscoveredException; +import org.opensearch.discovery.ClusterManagerNotDiscoveredException; import org.opensearch.gateway.GatewayService; +import org.opensearch.test.InternalTestCluster; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; -import org.opensearch.test.InternalTestCluster; import java.io.IOException; @@ -48,13 +48,13 @@ public class IndicesExistsIT extends OpenSearchIntegTestCase { public void testIndexExistsWithBlocksInPlace() throws IOException { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); Settings settings = Settings.builder().put(GatewayService.RECOVER_AFTER_NODES_SETTING.getKey(), 99).build(); String node = internalCluster().startNode(settings); assertRequestBuilderThrows( - client(node).admin().indices().prepareExists("test").setMasterNodeTimeout(TimeValue.timeValueSeconds(0)), - MasterNotDiscoveredException.class + client(node).admin().indices().prepareExists("test").setClusterManagerNodeTimeout(TimeValue.timeValueSeconds(0)), + ClusterManagerNotDiscoveredException.class ); internalCluster().stopRandomNode(InternalTestCluster.nameFilter(node)); // shut down node so that test properly cleans up diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeIT.java index 195817bf04cc9..09af533292e9a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeIT.java @@ -40,7 +40,7 @@ import org.opensearch.cluster.routing.IndexShardRoutingTable; import org.opensearch.common.concurrent.GatedCloseable; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.engine.Engine; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/get/GetIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/get/GetIndexIT.java index ffc738ac98de5..e5db895e7dfa9 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/get/GetIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/get/GetIndexIT.java @@ -32,13 +32,11 @@ package org.opensearch.action.admin.indices.get; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.get.GetIndexRequest.Feature; import org.opensearch.action.support.IndicesOptions; import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; import org.opensearch.index.IndexNotFoundException; import org.opensearch.test.OpenSearchIntegTestCase; @@ -46,6 +44,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_METADATA_BLOCK; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_BLOCKS_METADATA; @@ -254,7 +253,7 @@ private GetIndexResponse runWithRandomFeatureMethod(GetIndexRequestBuilder reque } private void assertSettings(GetIndexResponse response, String indexName) { - ImmutableOpenMap settings = response.settings(); + final Map settings = response.settings(); assertThat(settings, notNullValue()); assertThat(settings.size(), equalTo(1)); Settings indexSettings = settings.get(indexName); @@ -263,7 +262,7 @@ private void assertSettings(GetIndexResponse response, String indexName) { } private void assertNonEmptySettings(GetIndexResponse response, String indexName) { - ImmutableOpenMap settings = response.settings(); + final Map settings = response.settings(); assertThat(settings, notNullValue()); assertThat(settings.size(), equalTo(1)); Settings indexSettings = settings.get(indexName); @@ -271,7 +270,7 @@ private void assertNonEmptySettings(GetIndexResponse response, String indexName) } private void assertMappings(GetIndexResponse response, String indexName) { - ImmutableOpenMap mappings = response.mappings(); + final Map mappings = response.mappings(); assertThat(mappings, notNullValue()); assertThat(mappings.size(), equalTo(1)); MappingMetadata indexMappings = mappings.get(indexName); @@ -279,7 +278,7 @@ private void assertMappings(GetIndexResponse response, String indexName) { } private void assertEmptyOrOnlyDefaultMappings(GetIndexResponse response, String indexName) { - ImmutableOpenMap mappings = response.mappings(); + final Map mappings = response.mappings(); assertThat(mappings, notNullValue()); assertThat(mappings.size(), equalTo(1)); MappingMetadata indexMappings = mappings.get(indexName); @@ -287,7 +286,7 @@ private void assertEmptyOrOnlyDefaultMappings(GetIndexResponse response, String } private void assertAliases(GetIndexResponse response, String indexName) { - ImmutableOpenMap> aliases = response.aliases(); + final Map> aliases = response.aliases(); assertThat(aliases, notNullValue()); assertThat(aliases.size(), equalTo(1)); List indexAliases = aliases.get(indexName); @@ -310,8 +309,8 @@ private void assertEmptyMappings(GetIndexResponse response) { private void assertEmptyAliases(GetIndexResponse response) { assertThat(response.aliases(), notNullValue()); - for (final ObjectObjectCursor> entry : response.getAliases()) { - assertTrue(entry.value.isEmpty()); + for (final List entry : response.getAliases().values()) { + assertTrue(entry.isEmpty()); } } } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/mapping/put/ValidateMappingRequestPluginIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/mapping/put/ValidateMappingRequestPluginIT.java index fe1bc05dc5f20..ee516a53fadef 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/mapping/put/ValidateMappingRequestPluginIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/mapping/put/ValidateMappingRequestPluginIT.java @@ -34,7 +34,7 @@ import org.opensearch.action.RequestValidators; import org.opensearch.common.util.concurrent.ConcurrentCollections; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchSingleNodeTestCase; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/rollover/RolloverIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/rollover/RolloverIT.java index 178dc2c6ffa87..d4e07aa4251c3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/rollover/RolloverIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/rollover/RolloverIT.java @@ -35,10 +35,10 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.ResourceAlreadyExistsException; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.opensearch.action.admin.indices.template.delete.DeleteIndexTemplateRequestBuilder; import org.opensearch.action.admin.indices.template.put.PutIndexTemplateRequestBuilder; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.AutoExpandReplicas; @@ -46,13 +46,13 @@ import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.MockLogAppender; +import org.opensearch.test.OpenSearchIntegTestCase; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -64,6 +64,7 @@ import static org.opensearch.index.mapper.MapperService.SINGLE_MAPPING_NAME; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.is; @@ -172,6 +173,7 @@ public void testRolloverWithNoWriteIndex() { } public void testRolloverWithIndexSettings() throws Exception { + Alias testAlias = new Alias("test_alias"); boolean explicitWriteIndex = randomBoolean(); if (explicitWriteIndex) { @@ -210,6 +212,118 @@ public void testRolloverWithIndexSettings() throws Exception { } } + public void testRolloverWithIndexSettingsBalancedReplica() throws Exception { + Alias testAlias = new Alias("test_alias"); + boolean explicitWriteIndex = randomBoolean(); + if (explicitWriteIndex) { + testAlias.writeIndex(true); + } + assertAcked(prepareCreate("test_index-2").addAlias(testAlias).get()); + manageReplicaBalanceSetting(true); + index("test_index-2", "type1", "1", "field", "value"); + flush("test_index-2"); + final Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build(); + + final IllegalArgumentException restoreError = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareRolloverIndex("test_alias").settings(settings).alias(new Alias("extra_alias")).get() + ); + + assertThat( + restoreError.getMessage(), + containsString("expected total copies needs to be a multiple of total awareness attributes [2]") + ); + + client().admin() + .indices() + .prepareRolloverIndex("test_alias") + .settings( + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1).build() + ) + .alias(new Alias("extra_alias")) + .waitForActiveShards(0) + .get(); + + client().admin() + .indices() + .prepareRolloverIndex("test_alias") + .settings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) + .put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-1") + .build() + ) + .alias(new Alias("extra_alias")) + .waitForActiveShards(0) + .get(); + + client().admin() + .indices() + .prepareRolloverIndex("test_alias") + .settings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) + .put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-all") + .build() + ) + .alias(new Alias("extra_alias")) + .waitForActiveShards(0) + .get(); + + final IllegalArgumentException restoreError2 = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .indices() + .prepareRolloverIndex("test_alias") + .settings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) + .put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-0") + .build() + ) + .alias(new Alias("extra_alias")) + .get() + ); + + assertThat( + restoreError2.getMessage(), + containsString("expected max cap on auto expand to be a multiple of total awareness attributes [2]") + ); + + manageReplicaBalanceSetting(false); + } + + public void testRolloverWithIndexSettingsBalancedWithUseZoneForReplicaDefaultCount() throws Exception { + DeleteIndexTemplateRequestBuilder deleteTemplate = client().admin().indices().prepareDeleteTemplate("random_index_template"); + assertAcked(deleteTemplate.execute().actionGet()); + + Alias testAlias = new Alias("test_alias"); + boolean explicitWriteIndex = randomBoolean(); + if (explicitWriteIndex) { + testAlias.writeIndex(true); + } + assertAcked(prepareCreate("test_index-2").addAlias(testAlias).get()); + manageReplicaSettingForDefaultReplica(true); + index("test_index-2", "type1", "1", "field", "value"); + flush("test_index-2"); + + final Settings settings = Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3).build(); + client().admin().indices().prepareRolloverIndex("test_alias").settings(settings).alias(new Alias("extra_alias")).get(); + + final ClusterState state = client().admin().cluster().prepareState().get().getState(); + final IndexMetadata newIndex = state.metadata().index("test_index-000003"); + assertThat(newIndex.getNumberOfShards(), equalTo(3)); + assertThat(newIndex.getNumberOfReplicas(), equalTo(2)); + manageReplicaSettingForDefaultReplica(false); + randomIndexTemplate(); + } + public void testRolloverWithIndexSettingsWithoutPrefix() throws Exception { Alias testAlias = new Alias("test_alias"); boolean explicitWriteIndex = randomBoolean(); diff --git a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/shards/IndicesShardStoreRequestIT.java b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/shards/IndicesShardStoreRequestIT.java index ea9f7e0a7232d..fe5ec9227e844 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/shards/IndicesShardStoreRequestIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/admin/indices/shards/IndicesShardStoreRequestIT.java @@ -32,8 +32,6 @@ package org.opensearch.action.admin.indices.shards; -import com.carrotsearch.hppc.cursors.IntObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectCursor; import org.apache.lucene.index.CorruptIndexException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.client.Requests; @@ -42,10 +40,8 @@ import org.opensearch.cluster.routing.IndexRoutingTable; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.ShardRoutingState; -import org.opensearch.common.collect.ImmutableOpenIntMap; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexService; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; @@ -102,10 +98,10 @@ public void testBasic() throws Exception { // all shards response = client().admin().indices().shardStores(Requests.indicesShardStoresRequest(index).shardStatuses("all")).get(); assertThat(response.getStoreStatuses().containsKey(index), equalTo(true)); - ImmutableOpenIntMap> shardStores = response.getStoreStatuses().get(index); + final Map> shardStores = response.getStoreStatuses().get(index); assertThat(shardStores.values().size(), equalTo(2)); - for (ObjectCursor> shardStoreStatuses : shardStores.values()) { - for (IndicesShardStoresResponse.StoreStatus storeStatus : shardStoreStatuses.value) { + for (var shardStoreStatuses : shardStores.values()) { + for (IndicesShardStoresResponse.StoreStatus storeStatus : shardStoreStatuses) { assertThat(storeStatus.getAllocationId(), notNullValue()); assertThat(storeStatus.getNode(), notNullValue()); assertThat(storeStatus.getStoreException(), nullValue()); @@ -124,13 +120,13 @@ public void testBasic() throws Exception { List unassignedShards = clusterState.routingTable().index(index).shardsWithState(ShardRoutingState.UNASSIGNED); response = client().admin().indices().shardStores(Requests.indicesShardStoresRequest(index)).get(); assertThat(response.getStoreStatuses().containsKey(index), equalTo(true)); - ImmutableOpenIntMap> shardStoresStatuses = response.getStoreStatuses().get(index); + final Map> shardStoresStatuses = response.getStoreStatuses().get(index); assertThat(shardStoresStatuses.size(), equalTo(unassignedShards.size())); - for (IntObjectCursor> storesStatus : shardStoresStatuses) { - assertThat("must report for one store", storesStatus.value.size(), equalTo(1)); + for (var storesStatus : shardStoresStatuses.values()) { + assertThat("must report for one store", storesStatus.size(), equalTo(1)); assertThat( "reported store should be primary", - storesStatus.value.get(0).getAllocationStatus(), + storesStatus.get(0).getAllocationStatus(), equalTo(IndicesShardStoresResponse.StoreStatus.AllocationStatus.PRIMARY) ); } @@ -151,8 +147,7 @@ public void testIndices() throws Exception { .indices() .shardStores(Requests.indicesShardStoresRequest().shardStatuses("all")) .get(); - ImmutableOpenMap>> shardStatuses = response - .getStoreStatuses(); + Map>> shardStatuses = response.getStoreStatuses(); assertThat(shardStatuses.containsKey(index1), equalTo(true)); assertThat(shardStatuses.containsKey(index2), equalTo(true)); assertThat(shardStatuses.get(index1).size(), equalTo(2)); @@ -207,21 +202,21 @@ public void testCorruptedShards() throws Exception { assertBusy(() -> { // IndicesClusterStateService#failAndRemoveShard() called asynchronously but we need it to have completed here. IndicesShardStoresResponse rsp = client().admin().indices().prepareShardStores(index).setShardStatuses("all").get(); - ImmutableOpenIntMap> shardStatuses = rsp.getStoreStatuses().get(index); + final Map> shardStatuses = rsp.getStoreStatuses().get(index); assertNotNull(shardStatuses); assertThat(shardStatuses.size(), greaterThan(0)); - for (IntObjectCursor> shardStatus : shardStatuses) { - for (IndicesShardStoresResponse.StoreStatus status : shardStatus.value) { - if (corruptedShardIDMap.containsKey(shardStatus.key) - && corruptedShardIDMap.get(shardStatus.key).contains(status.getNode().getName())) { + for (var shardStatus : shardStatuses.entrySet()) { + for (IndicesShardStoresResponse.StoreStatus status : shardStatus.getValue()) { + if (corruptedShardIDMap.containsKey(shardStatus.getKey()) + && corruptedShardIDMap.get(shardStatus.getKey()).contains(status.getNode().getName())) { assertThat( - "shard [" + shardStatus.key + "] is failed on node [" + status.getNode().getName() + "]", + "shard [" + shardStatus.getKey() + "] is failed on node [" + status.getNode().getName() + "]", status.getStoreException(), notNullValue() ); } else { assertNull( - "shard [" + shardStatus.key + "] is not failed on node [" + status.getNode().getName() + "]", + "shard [" + shardStatus.getKey() + "] is not failed on node [" + status.getNode().getName() + "]", status.getStoreException() ); } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkIntegrationIT.java index e2a1363f163da..cf83f20244a4b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkIntegrationIT.java @@ -37,19 +37,19 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse; - import org.opensearch.action.index.IndexRequest; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.ingest.PutPipelineRequest; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.action.support.replication.ReplicationRequest; +import org.opensearch.action.update.UpdateRequest; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.ingest.IngestTestPlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; @@ -83,7 +83,7 @@ protected Collection> nodePlugins() { public void testBulkIndexCreatesMapping() throws Exception { String bulkAction = copyToStringFromClasspath("/org/opensearch/action/bulk/bulk-log.json"); BulkRequestBuilder bulkBuilder = client().prepareBulk(); - bulkBuilder.add(bulkAction.getBytes(StandardCharsets.UTF_8), 0, bulkAction.length(), null, XContentType.JSON); + bulkBuilder.add(bulkAction.getBytes(StandardCharsets.UTF_8), 0, bulkAction.length(), null, MediaTypeRegistry.JSON); bulkBuilder.get(); assertBusy(() -> { GetMappingsResponse mappingsResponse = client().admin().indices().prepareGetMappings().get(); @@ -154,7 +154,7 @@ public void testBulkWithGlobalDefaults() throws Exception { String bulkAction = copyToStringFromClasspath("/org/opensearch/action/bulk/simple-bulk-missing-index-type.json"); { BulkRequestBuilder bulkBuilder = client().prepareBulk(); - bulkBuilder.add(bulkAction.getBytes(StandardCharsets.UTF_8), 0, bulkAction.length(), null, XContentType.JSON); + bulkBuilder.add(bulkAction.getBytes(StandardCharsets.UTF_8), 0, bulkAction.length(), null, MediaTypeRegistry.JSON); ActionRequestValidationException ex = expectThrows(ActionRequestValidationException.class, bulkBuilder::get); assertThat(ex.validationErrors(), containsInAnyOrder("index is missing", "index is missing", "index is missing")); @@ -164,7 +164,7 @@ public void testBulkWithGlobalDefaults() throws Exception { createSamplePipeline("pipeline"); BulkRequestBuilder bulkBuilder = client().prepareBulk("test").routing("routing").pipeline("pipeline"); - bulkBuilder.add(bulkAction.getBytes(StandardCharsets.UTF_8), 0, bulkAction.length(), null, XContentType.JSON); + bulkBuilder.add(bulkAction.getBytes(StandardCharsets.UTF_8), 0, bulkAction.length(), null, MediaTypeRegistry.JSON); BulkResponse bulkItemResponses = bulkBuilder.get(); assertFalse(bulkItemResponses.hasFailures()); } @@ -182,7 +182,7 @@ private void createSamplePipeline(String pipelineId) throws IOException, Executi AcknowledgedResponse acknowledgedResponse = client().admin() .cluster() - .putPipeline(new PutPipelineRequest(pipelineId, BytesReference.bytes(pipeline), XContentType.JSON)) + .putPipeline(new PutPipelineRequest(pipelineId, BytesReference.bytes(pipeline), MediaTypeRegistry.JSON)) .get(); assertTrue(acknowledgedResponse.isAcknowledged()); @@ -193,34 +193,63 @@ public void testDeleteIndexWhileIndexing() throws Exception { String index = "deleted_while_indexing"; createIndex(index); AtomicBoolean stopped = new AtomicBoolean(); - Thread[] threads = new Thread[between(1, 4)]; AtomicInteger docID = new AtomicInteger(); - for (int i = 0; i < threads.length; i++) { - threads[i] = new Thread(() -> { - while (stopped.get() == false && docID.get() < 5000) { - String id = Integer.toString(docID.incrementAndGet()); - try { - IndexResponse response = client().prepareIndex(index) - .setId(id) - .setSource(Collections.singletonMap("f" + randomIntBetween(1, 10), randomNonNegativeLong()), XContentType.JSON) - .get(); - assertThat(response.getResult(), is(oneOf(CREATED, UPDATED))); - logger.info("--> index id={} seq_no={}", response.getId(), response.getSeqNo()); - } catch (OpenSearchException ignore) { - logger.info("--> fail to index id={}", id); - } + Thread thread = new Thread(() -> { + while (stopped.get() == false && docID.get() < 5000) { + String id = Integer.toString(docID.incrementAndGet()); + try { + IndexResponse response = client().prepareIndex(index) + .setId(id) + .setSource(Collections.singletonMap("f" + randomIntBetween(1, 10), randomNonNegativeLong()), MediaTypeRegistry.JSON) + .get(); + assertThat(response.getResult(), is(oneOf(CREATED, UPDATED))); + logger.info("--> index id={} seq_no={}", response.getId(), response.getSeqNo()); + } catch (OpenSearchException ignore) { + logger.info("--> fail to index id={}", id); } - }); - threads[i].start(); - } + } + }); + thread.start(); ensureGreen(index); assertBusy(() -> assertThat(docID.get(), greaterThanOrEqualTo(1))); assertAcked(client().admin().indices().prepareDelete(index)); stopped.set(true); - for (Thread thread : threads) { - thread.join(ReplicationRequest.DEFAULT_TIMEOUT.millis() / 2); - assertFalse(thread.isAlive()); - } + thread.join(ReplicationRequest.DEFAULT_TIMEOUT.millis() / 2); + assertFalse(thread.isAlive()); } + public void testDocIdTooLong() { + String index = "testing"; + createIndex(index); + String validId = String.join("", Collections.nCopies(512, "a")); + String invalidId = String.join("", Collections.nCopies(513, "a")); + + // Index Request + IndexRequest indexRequest = new IndexRequest(index).source(Collections.singletonMap("foo", "baz")); + // Valid id shouldn't throw any exception + assertFalse(client().prepareBulk().add(indexRequest.id(validId)).get().hasFailures()); + // Invalid id should throw the ActionRequestValidationException + validateDocIdLimit(() -> client().prepareBulk().add(indexRequest.id(invalidId)).get()); + + // Update Request + UpdateRequest updateRequest = new UpdateRequest(index, validId).doc("reason", "no source"); + // Valid id shouldn't throw any exception + assertFalse(client().prepareBulk().add(updateRequest).get().hasFailures()); + // Invalid id should throw the ActionRequestValidationException + validateDocIdLimit(() -> client().prepareBulk().add(updateRequest.id(invalidId)).get()); + } + + private void validateDocIdLimit(Runnable runner) { + try { + runner.run(); + fail("Request validation for docId didn't fail"); + } catch (ActionRequestValidationException e) { + assertEquals( + 1, + e.validationErrors().stream().filter(msg -> msg.contains("is too long, must be no longer than 512 bytes but was")).count() + ); + } catch (Exception e) { + fail("Request validation for docId failed with different exception: " + e); + } + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorClusterSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorClusterSettingsIT.java index 14531787e9903..ea7af48266905 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorClusterSettingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorClusterSettingsIT.java @@ -33,7 +33,7 @@ package org.opensearch.action.bulk; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; @@ -50,9 +50,9 @@ public void testBulkProcessorAutoCreateRestrictions() throws Exception { client().admin().cluster().prepareHealth("willwork").setWaitForGreenStatus().execute().actionGet(); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); - bulkRequestBuilder.add(client().prepareIndex("willwork").setId("1").setSource("{\"foo\":1}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("wontwork").setId("2").setSource("{\"foo\":2}", XContentType.JSON)); - bulkRequestBuilder.add(client().prepareIndex("willwork").setId("3").setSource("{\"foo\":3}", XContentType.JSON)); + bulkRequestBuilder.add(client().prepareIndex("willwork").setId("1").setSource("{\"foo\":1}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("wontwork").setId("2").setSource("{\"foo\":2}", MediaTypeRegistry.JSON)); + bulkRequestBuilder.add(client().prepareIndex("willwork").setId("3").setSource("{\"foo\":3}", MediaTypeRegistry.JSON)); BulkResponse br = bulkRequestBuilder.get(); BulkItemResponse[] responses = br.getItems(); assertEquals(3, responses.length); diff --git a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorIT.java b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorIT.java index 850034bc631b1..94202c208ba3d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorIT.java @@ -33,6 +33,7 @@ package org.opensearch.action.bulk; import com.carrotsearch.randomizedtesting.generators.RandomPicks; + import org.opensearch.action.get.MultiGetItemResponse; import org.opensearch.action.get.MultiGetRequestBuilder; import org.opensearch.action.get.MultiGetResponse; @@ -41,9 +42,9 @@ import org.opensearch.client.Requests; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Arrays; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorRetryIT.java b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorRetryIT.java index 687a4e9b733fd..737c0acc309fd 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorRetryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkProcessorRetryIT.java @@ -32,12 +32,11 @@ package org.opensearch.action.bulk; import org.opensearch.action.admin.indices.refresh.RefreshRequest; - import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.rest.RestStatus; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.transport.RemoteTransportException; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkRejectionIT.java b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkRejectionIT.java index aa2f2a214de27..a41664fe71c24 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkRejectionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkRejectionIT.java @@ -31,15 +31,15 @@ package org.opensearch.action.bulk; -import org.opensearch.action.ActionFuture; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.WriteRequest; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; import org.opensearch.index.IndexService; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Arrays; import java.util.Collection; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkWithUpdatesIT.java b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkWithUpdatesIT.java index 6311ac6876192..d7fb632c847d1 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkWithUpdatesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/bulk/BulkWithUpdatesIT.java @@ -46,9 +46,9 @@ import org.opensearch.action.update.UpdateResponse; import org.opensearch.client.Requests; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.VersionType; import org.opensearch.indices.IndexClosedException; import org.opensearch.plugins.Plugin; @@ -56,8 +56,8 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptException; import org.opensearch.script.ScriptType; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.ArrayList; import java.util.Arrays; @@ -105,18 +105,9 @@ protected Map, Object>> pluginScripts() { scripts.put("ctx._source.field2 = 'value2'", vars -> srcScript(vars, source -> source.replace("field2", "value2"))); - scripts.put( - "throw script exception on unknown var", - vars -> { - throw new ScriptException( - "message", - null, - Collections.emptyList(), - "exception on unknown var", - CustomScriptPlugin.NAME - ); - } - ); + scripts.put("throw script exception on unknown var", vars -> { + throw new ScriptException("message", null, Collections.emptyList(), "exception on unknown var", CustomScriptPlugin.NAME); + }); scripts.put("ctx.op = \"none\"", vars -> ((Map) vars.get("ctx")).put("op", "none")); scripts.put("ctx.op = \"delete\"", vars -> ((Map) vars.get("ctx")).put("op", "delete")); @@ -627,19 +618,19 @@ public void testThatInvalidIndexNamesShouldNotBreakCompleteBulkRequest() { // issue 6630 public void testThatFailedUpdateRequestReturnsCorrectType() throws Exception { BulkResponse indexBulkItemResponse = client().prepareBulk() - .add(new IndexRequest("test").id("3").source("{ \"title\" : \"Great Title of doc 3\" }", XContentType.JSON)) - .add(new IndexRequest("test").id("4").source("{ \"title\" : \"Great Title of doc 4\" }", XContentType.JSON)) - .add(new IndexRequest("test").id("5").source("{ \"title\" : \"Great Title of doc 5\" }", XContentType.JSON)) - .add(new IndexRequest("test").id("6").source("{ \"title\" : \"Great Title of doc 6\" }", XContentType.JSON)) + .add(new IndexRequest("test").id("3").source("{ \"title\" : \"Great Title of doc 3\" }", MediaTypeRegistry.JSON)) + .add(new IndexRequest("test").id("4").source("{ \"title\" : \"Great Title of doc 4\" }", MediaTypeRegistry.JSON)) + .add(new IndexRequest("test").id("5").source("{ \"title\" : \"Great Title of doc 5\" }", MediaTypeRegistry.JSON)) + .add(new IndexRequest("test").id("6").source("{ \"title\" : \"Great Title of doc 6\" }", MediaTypeRegistry.JSON)) .setRefreshPolicy(RefreshPolicy.IMMEDIATE) .get(); assertNoFailures(indexBulkItemResponse); BulkResponse bulkItemResponse = client().prepareBulk() - .add(new IndexRequest("test").id("1").source("{ \"title\" : \"Great Title of doc 1\" }", XContentType.JSON)) - .add(new IndexRequest("test").id("2").source("{ \"title\" : \"Great Title of doc 2\" }", XContentType.JSON)) - .add(new UpdateRequest("test", "3").doc("{ \"date\" : \"2014-01-30T23:59:57\"}", XContentType.JSON)) - .add(new UpdateRequest("test", "4").doc("{ \"date\" : \"2014-13-30T23:59:57\"}", XContentType.JSON)) + .add(new IndexRequest("test").id("1").source("{ \"title\" : \"Great Title of doc 1\" }", MediaTypeRegistry.JSON)) + .add(new IndexRequest("test").id("2").source("{ \"title\" : \"Great Title of doc 2\" }", MediaTypeRegistry.JSON)) + .add(new UpdateRequest("test", "3").doc("{ \"date\" : \"2014-01-30T23:59:57\"}", MediaTypeRegistry.JSON)) + .add(new UpdateRequest("test", "4").doc("{ \"date\" : \"2014-13-30T23:59:57\"}", MediaTypeRegistry.JSON)) .add(new DeleteRequest("test", "5")) .add(new DeleteRequest("test", "6")) .get(); @@ -741,13 +732,21 @@ public void testNoopUpdate() { final BulkItemResponse noopUpdate = bulkResponse.getItems()[0]; assertThat(noopUpdate.getResponse().getResult(), equalTo(DocWriteResponse.Result.NOOP)); - assertThat(Strings.toString(noopUpdate), noopUpdate.getResponse().getShardInfo().getSuccessful(), equalTo(2)); + assertThat( + Strings.toString(MediaTypeRegistry.JSON, noopUpdate), + noopUpdate.getResponse().getShardInfo().getSuccessful(), + equalTo(2) + ); final BulkItemResponse notFoundUpdate = bulkResponse.getItems()[1]; assertNotNull(notFoundUpdate.getFailure()); final BulkItemResponse notFoundDelete = bulkResponse.getItems()[2]; assertThat(notFoundDelete.getResponse().getResult(), equalTo(DocWriteResponse.Result.NOT_FOUND)); - assertThat(Strings.toString(notFoundDelete), notFoundDelete.getResponse().getShardInfo().getSuccessful(), equalTo(2)); + assertThat( + Strings.toString(MediaTypeRegistry.JSON, notFoundDelete), + notFoundDelete.getResponse().getShardInfo().getSuccessful(), + equalTo(2) + ); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/ingest/AsyncIngestProcessorIT.java b/server/src/internalClusterTest/java/org/opensearch/action/ingest/AsyncIngestProcessorIT.java index a4111e14f06a1..c62c61d5919d6 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/ingest/AsyncIngestProcessorIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/ingest/AsyncIngestProcessorIT.java @@ -39,11 +39,11 @@ import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.ingest.AbstractProcessor; @@ -84,12 +84,12 @@ protected Collection> getPlugins() { public void testAsyncProcessorImplementation() { // A pipeline with 2 processors: the test async processor and sync test processor. BytesReference pipelineBody = new BytesArray("{\"processors\": [{\"test-async\": {}, \"test\": {}}]}"); - client().admin().cluster().putPipeline(new PutPipelineRequest("_id", pipelineBody, XContentType.JSON)).actionGet(); + client().admin().cluster().putPipeline(new PutPipelineRequest("_id", pipelineBody, MediaTypeRegistry.JSON)).actionGet(); BulkRequest bulkRequest = new BulkRequest(); int numDocs = randomIntBetween(8, 256); for (int i = 0; i < numDocs; i++) { - bulkRequest.add(new IndexRequest("foobar").id(Integer.toString(i)).source("{}", XContentType.JSON).setPipeline("_id")); + bulkRequest.add(new IndexRequest("foobar").id(Integer.toString(i)).source("{}", MediaTypeRegistry.JSON).setPipeline("_id")); } BulkResponse bulkResponse = client().bulk(bulkRequest).actionGet(); assertThat(bulkResponse.getItems().length, equalTo(numDocs)); diff --git a/server/src/internalClusterTest/java/org/opensearch/action/search/SearchProgressActionListenerIT.java b/server/src/internalClusterTest/java/org/opensearch/action/search/SearchProgressActionListenerIT.java index eb69eaaa9c2e1..4475ee837da4e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/search/SearchProgressActionListenerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/search/SearchProgressActionListenerIT.java @@ -34,9 +34,9 @@ import org.apache.lucene.search.TotalHits; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsResponse; - import org.opensearch.client.Client; import org.opensearch.client.node.NodeClient; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.aggregations.AggregationBuilders; @@ -44,7 +44,6 @@ import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.sort.FieldSortBuilder; import org.opensearch.search.sort.SortOrder; -import org.opensearch.tasks.TaskId; import org.opensearch.test.OpenSearchSingleNodeTestCase; import java.util.ArrayList; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/search/TransportSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/action/search/TransportSearchIT.java index a356d273f7060..f0a3b5a5901ce 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/search/TransportSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/search/TransportSearchIT.java @@ -35,9 +35,7 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.CollectionTerminatedException; import org.apache.lucene.search.ScoreMode; - import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; @@ -47,23 +45,25 @@ import org.opensearch.action.support.WriteRequest; import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Strings; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AtomicArray; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.QueryShardContext; import org.opensearch.index.query.RangeQueryBuilder; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; +import org.opensearch.indices.replication.common.ReplicationType; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.SearchPlugin; -import org.opensearch.rest.RestStatus; import org.opensearch.search.DocValueFormat; import org.opensearch.search.SearchHit; import org.opensearch.search.aggregations.AbstractAggregationBuilder; @@ -370,6 +370,48 @@ public void testSearchIdle() throws Exception { }); } + public void testSearchIdleWithSegmentReplication() { + int numOfReplicas = 1; + internalCluster().ensureAtLeastNumDataNodes(numOfReplicas + 1); + final Settings.Builder settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, randomIntBetween(1, 5)) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numOfReplicas) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT); + assertAcked(prepareCreate("test").setSettings(settings).setMapping("created_date", "type=date,format=yyyy-MM-dd")); + ensureGreen("test"); + assertAcked( + client().admin() + .indices() + .prepareUpdateSettings("test") + .setSettings( + Settings.builder() + .put(IndexSettings.INDEX_SEARCH_IDLE_AFTER.getKey(), TimeValue.timeValueMillis(randomIntBetween(50, 500))) + ) + ); + + for (String node : internalCluster().nodesInclude("test")) { + final IndicesService indicesService = internalCluster().getInstance(IndicesService.class, node); + for (IndexShard indexShard : indicesService.indexServiceSafe(resolveIndex("test"))) { + assertFalse(indexShard.isSearchIdleSupported()); + } + } + + assertAcked( + client().admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)) + ); + + for (String node : internalCluster().nodesInclude("test")) { + final IndicesService indicesService = internalCluster().getInstance(IndicesService.class, node); + for (IndexShard indexShard : indicesService.indexServiceSafe(resolveIndex("test"))) { + assertTrue(indexShard.isSearchIdleSupported()); + } + } + ; + } + public void testCircuitBreakerReduceFail() throws Exception { int numShards = randomIntBetween(1, 10); indexSomeDocs("test", numShards, numShards * 3); @@ -552,6 +594,11 @@ protected Aggregator createInternal( ) throws IOException { return new TestAggregator(name, parent, searchContext); } + + @Override + protected boolean supportsConcurrentSegmentSearch() { + return true; + } }; } diff --git a/server/src/internalClusterTest/java/org/opensearch/action/support/ActiveShardsObserverIT.java b/server/src/internalClusterTest/java/org/opensearch/action/support/ActiveShardsObserverIT.java index 30f5c21ba6cd7..e19a7483370c2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/support/ActiveShardsObserverIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/support/ActiveShardsObserverIT.java @@ -32,9 +32,9 @@ package org.opensearch.action.support; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.indices.create.CreateIndexResponse; import org.opensearch.common.Priority; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.test.OpenSearchIntegTestCase; diff --git a/server/src/internalClusterTest/java/org/opensearch/action/support/WaitActiveShardCountIT.java b/server/src/internalClusterTest/java/org/opensearch/action/support/WaitActiveShardCountIT.java index e919b2b85e079..08cffac8aac5d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/support/WaitActiveShardCountIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/support/WaitActiveShardCountIT.java @@ -35,12 +35,11 @@ import org.opensearch.action.UnavailableShardsException; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.indices.create.CreateIndexResponse; - import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Priority; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.test.OpenSearchIntegTestCase; import static org.opensearch.common.unit.TimeValue.timeValueMillis; @@ -63,11 +62,11 @@ public void testReplicationWaitsForActiveShardCount() throws Exception { assertAcked(createIndexResponse); // indexing, by default, will work (waiting for one shard copy only) - client().prepareIndex("test").setId("1").setSource(source("1", "test"), XContentType.JSON).execute().actionGet(); + client().prepareIndex("test").setId("1").setSource(source("1", "test"), MediaTypeRegistry.JSON).execute().actionGet(); try { client().prepareIndex("test") .setId("1") - .setSource(source("1", "test"), XContentType.JSON) + .setSource(source("1", "test"), MediaTypeRegistry.JSON) .setWaitForActiveShards(2) // wait for 2 active shard copies .setTimeout(timeValueMillis(100)) .execute() @@ -99,7 +98,7 @@ public void testReplicationWaitsForActiveShardCount() throws Exception { // this should work, since we now have two client().prepareIndex("test") .setId("1") - .setSource(source("1", "test"), XContentType.JSON) + .setSource(source("1", "test"), MediaTypeRegistry.JSON) .setWaitForActiveShards(2) .setTimeout(timeValueSeconds(1)) .execute() @@ -108,7 +107,7 @@ public void testReplicationWaitsForActiveShardCount() throws Exception { try { client().prepareIndex("test") .setId("1") - .setSource(source("1", "test"), XContentType.JSON) + .setSource(source("1", "test"), MediaTypeRegistry.JSON) .setWaitForActiveShards(ActiveShardCount.ALL) .setTimeout(timeValueMillis(100)) .execute() @@ -143,7 +142,7 @@ public void testReplicationWaitsForActiveShardCount() throws Exception { // this should work, since we now have all shards started client().prepareIndex("test") .setId("1") - .setSource(source("1", "test"), XContentType.JSON) + .setSource(source("1", "test"), MediaTypeRegistry.JSON) .setWaitForActiveShards(ActiveShardCount.ALL) .setTimeout(timeValueSeconds(1)) .execute() diff --git a/server/src/internalClusterTest/java/org/opensearch/action/support/clustermanager/IndexingClusterManagerFailoverIT.java b/server/src/internalClusterTest/java/org/opensearch/action/support/clustermanager/IndexingClusterManagerFailoverIT.java new file mode 100644 index 0000000000000..959b16d4c4694 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/action/support/clustermanager/IndexingClusterManagerFailoverIT.java @@ -0,0 +1,132 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager; + +import org.opensearch.action.DocWriteResponse; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.transport.MockTransportService; + +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +import static org.hamcrest.Matchers.equalTo; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) +public class IndexingClusterManagerFailoverIT extends OpenSearchIntegTestCase { + + @Override + protected Collection> nodePlugins() { + final HashSet> classes = new HashSet<>(super.nodePlugins()); + classes.add(MockTransportService.TestPlugin.class); + return classes; + } + + /** + * Indexing operations which entail mapping changes require a blocking request to the cluster-manager node to update the mapping. + * If the cluster-manager node is being disrupted or if it cannot commit cluster state changes, it needs to retry within timeout limits. + * This retry logic is implemented in TransportMasterNodeAction and tested by the following cluster-manager failover scenario. + */ + public void testClusterManagerFailoverDuringIndexingWithMappingChanges() throws Throwable { + logger.info("--> start 4 nodes, 3 cluster-manager, 1 data"); + + internalCluster().setBootstrapClusterManagerNodeIndex(2); + + internalCluster().startClusterManagerOnlyNodes(3, Settings.EMPTY); + + String dataNode = internalCluster().startDataOnlyNode(Settings.EMPTY); + + logger.info("--> wait for all nodes to join the cluster"); + ensureStableCluster(4); + + // We index data with mapping changes into cluster and have cluster-manager failover at same time + client().admin() + .indices() + .prepareCreate("myindex") + .setSettings(Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0)) + .get(); + ensureGreen("myindex"); + + final CyclicBarrier barrier = new CyclicBarrier(2); + + Thread indexingThread = new Thread(new Runnable() { + @Override + public void run() { + try { + barrier.await(); + } catch (InterruptedException e) { + logger.warn("Barrier interrupted", e); + return; + } catch (BrokenBarrierException e) { + logger.warn("Broken barrier", e); + return; + } + for (int i = 0; i < 10; i++) { + // index data + IndexResponse response = client(dataNode).prepareIndex("myindex").setSource("field_" + i, "val").get(); + assertEquals(DocWriteResponse.Result.CREATED, response.getResult()); + } + } + }); + indexingThread.setName("indexingThread"); + indexingThread.start(); + + barrier.await(); + + // interrupt communication between cluster-manager and other nodes in cluster + NetworkDisruption partition = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); + internalCluster().setDisruptionScheme(partition); + + logger.info("--> disrupting network"); + partition.startDisrupting(); + + logger.info("--> waiting for new cluster-manager to be elected"); + ensureStableCluster(3, dataNode); + + partition.stopDisrupting(); + logger.info("--> waiting to heal"); + ensureStableCluster(4); + + indexingThread.join(); + + ensureGreen("myindex"); + refresh(); + assertThat(client().prepareSearch("myindex").get().getHits().getTotalHits().value, equalTo(10L)); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/action/support/master/IndexingMasterFailoverIT.java b/server/src/internalClusterTest/java/org/opensearch/action/support/master/IndexingMasterFailoverIT.java deleted file mode 100644 index f8db63bc8b61d..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/action/support/master/IndexingMasterFailoverIT.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.support.master; - -import org.opensearch.action.DocWriteResponse; -import org.opensearch.action.index.IndexResponse; -import org.opensearch.common.settings.Settings; -import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.test.disruption.NetworkDisruption; -import org.opensearch.test.transport.MockTransportService; - -import java.util.Collection; -import java.util.HashSet; -import java.util.concurrent.BrokenBarrierException; -import java.util.concurrent.CyclicBarrier; - -import static org.hamcrest.Matchers.equalTo; - -@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) -public class IndexingMasterFailoverIT extends OpenSearchIntegTestCase { - - @Override - protected Collection> nodePlugins() { - final HashSet> classes = new HashSet<>(super.nodePlugins()); - classes.add(MockTransportService.TestPlugin.class); - return classes; - } - - /** - * Indexing operations which entail mapping changes require a blocking request to the master node to update the mapping. - * If the master node is being disrupted or if it cannot commit cluster state changes, it needs to retry within timeout limits. - * This retry logic is implemented in TransportMasterNodeAction and tested by the following master failover scenario. - */ - public void testMasterFailoverDuringIndexingWithMappingChanges() throws Throwable { - logger.info("--> start 4 nodes, 3 master, 1 data"); - - internalCluster().setBootstrapMasterNodeIndex(2); - - internalCluster().startMasterOnlyNodes(3, Settings.EMPTY); - - String dataNode = internalCluster().startDataOnlyNode(Settings.EMPTY); - - logger.info("--> wait for all nodes to join the cluster"); - ensureStableCluster(4); - - // We index data with mapping changes into cluster and have master failover at same time - client().admin() - .indices() - .prepareCreate("myindex") - .setSettings(Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0)) - .get(); - ensureGreen("myindex"); - - final CyclicBarrier barrier = new CyclicBarrier(2); - - Thread indexingThread = new Thread(new Runnable() { - @Override - public void run() { - try { - barrier.await(); - } catch (InterruptedException e) { - logger.warn("Barrier interrupted", e); - return; - } catch (BrokenBarrierException e) { - logger.warn("Broken barrier", e); - return; - } - for (int i = 0; i < 10; i++) { - // index data - IndexResponse response = client(dataNode).prepareIndex("myindex").setSource("field_" + i, "val").get(); - assertEquals(DocWriteResponse.Result.CREATED, response.getResult()); - } - } - }); - indexingThread.setName("indexingThread"); - indexingThread.start(); - - barrier.await(); - - // interrupt communication between master and other nodes in cluster - NetworkDisruption partition = isolateMasterDisruption(NetworkDisruption.DISCONNECT); - internalCluster().setDisruptionScheme(partition); - - logger.info("--> disrupting network"); - partition.startDisrupting(); - - logger.info("--> waiting for new master to be elected"); - ensureStableCluster(3, dataNode); - - partition.stopDisrupting(); - logger.info("--> waiting to heal"); - ensureStableCluster(4); - - indexingThread.join(); - - ensureGreen("myindex"); - refresh(); - assertThat(client().prepareSearch("myindex").get().getHits().getTotalHits().value, equalTo(10L)); - } - -} diff --git a/server/src/internalClusterTest/java/org/opensearch/action/support/replication/TransportReplicationActionRetryOnClosedNodeIT.java b/server/src/internalClusterTest/java/org/opensearch/action/support/replication/TransportReplicationActionRetryOnClosedNodeIT.java index 8a9f3eb5f2e63..569e64d795b06 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/support/replication/TransportReplicationActionRetryOnClosedNodeIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/support/replication/TransportReplicationActionRetryOnClosedNodeIT.java @@ -32,38 +32,37 @@ package org.opensearch.action.support.replication; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.support.ActionFilters; import org.opensearch.cluster.action.shard.ShardStateAction; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.NetworkPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.PluginsService; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.Transport; import org.opensearch.transport.TransportInterceptor; import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestOptions; -import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; - import org.hamcrest.Matchers; import java.io.IOException; @@ -203,7 +202,7 @@ public void sendRequest( } public void testRetryOnStoppedTransportService() throws Exception { - internalCluster().startMasterOnlyNodes(2); + internalCluster().startClusterManagerOnlyNodes(2); String primary = internalCluster().startDataOnlyNode(); assertAcked( prepareCreate("test").setSettings( diff --git a/server/src/internalClusterTest/java/org/opensearch/action/termvectors/GetTermVectorsIT.java b/server/src/internalClusterTest/java/org/opensearch/action/termvectors/GetTermVectorsIT.java index b5e60f44983f5..7bd1467933e00 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/termvectors/GetTermVectorsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/termvectors/GetTermVectorsIT.java @@ -39,15 +39,15 @@ import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.util.BytesRef; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsResponse; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.index.IndexRequestBuilder; -import org.opensearch.common.Strings; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.engine.VersionConflictEngineException; import org.opensearch.plugins.Plugin; import org.opensearch.test.MockKeywordPlugin; @@ -74,6 +74,10 @@ public class GetTermVectorsIT extends AbstractTermVectorsTestCase { + public GetTermVectorsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(MockKeywordPlugin.class); diff --git a/server/src/internalClusterTest/java/org/opensearch/action/termvectors/MultiTermVectorsIT.java b/server/src/internalClusterTest/java/org/opensearch/action/termvectors/MultiTermVectorsIT.java index 91d280a9c4771..7c6c47c682281 100644 --- a/server/src/internalClusterTest/java/org/opensearch/action/termvectors/MultiTermVectorsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/action/termvectors/MultiTermVectorsIT.java @@ -52,6 +52,10 @@ import static org.hamcrest.Matchers.nullValue; public class MultiTermVectorsIT extends AbstractTermVectorsTestCase { + public MultiTermVectorsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + public void testDuelESLucene() throws Exception { AbstractTermVectorsTestCase.TestFieldSetting[] testFieldSettings = getFieldSettings(); createIndexBasedOnFieldSettings("test", "alias", testFieldSettings); diff --git a/server/src/internalClusterTest/java/org/opensearch/aliases/IndexAliasesIT.java b/server/src/internalClusterTest/java/org/opensearch/aliases/IndexAliasesIT.java index ff64a2cd90cb8..f91df19232971 100644 --- a/server/src/internalClusterTest/java/org/opensearch/aliases/IndexAliasesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/aliases/IndexAliasesIT.java @@ -32,10 +32,8 @@ package org.opensearch.aliases; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistResponse; import org.opensearch.action.admin.indices.alias.get.GetAliasesResponse; import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.opensearch.action.delete.DeleteResponse; @@ -52,7 +50,7 @@ import org.opensearch.common.StopWatch; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.TermQueryBuilder; @@ -88,7 +86,6 @@ import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY; import static org.opensearch.index.query.QueryBuilders.rangeQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; -import static org.opensearch.test.hamcrest.CollectionAssertions.hasKey; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertBlocked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -97,6 +94,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -117,7 +115,7 @@ public void testAliases() throws Exception { logger.info("--> indexing against [alias1], should fail now"); IllegalArgumentException exception = expectThrows( IllegalArgumentException.class, - () -> client().index(indexRequest("alias1").id("1").source(source("2", "test"), XContentType.JSON)).actionGet() + () -> client().index(indexRequest("alias1").id("1").source(source("2", "test"), MediaTypeRegistry.JSON)).actionGet() ); assertThat( exception.getMessage(), @@ -134,7 +132,7 @@ public void testAliases() throws Exception { }); logger.info("--> indexing against [alias1], should work now"); - IndexResponse indexResponse = client().index(indexRequest("alias1").id("1").source(source("1", "test"), XContentType.JSON)) + IndexResponse indexResponse = client().index(indexRequest("alias1").id("1").source(source("1", "test"), MediaTypeRegistry.JSON)) .actionGet(); assertThat(indexResponse.getIndex(), equalTo("test")); @@ -151,7 +149,7 @@ public void testAliases() throws Exception { logger.info("--> indexing against [alias1], should fail now"); exception = expectThrows( IllegalArgumentException.class, - () -> client().index(indexRequest("alias1").id("1").source(source("2", "test"), XContentType.JSON)).actionGet() + () -> client().index(indexRequest("alias1").id("1").source(source("2", "test"), MediaTypeRegistry.JSON)).actionGet() ); assertThat( exception.getMessage(), @@ -179,7 +177,7 @@ public void testAliases() throws Exception { }); logger.info("--> indexing against [alias1], should work now"); - indexResponse = client().index(indexRequest("alias1").id("1").source(source("1", "test"), XContentType.JSON)).actionGet(); + indexResponse = client().index(indexRequest("alias1").id("1").source(source("1", "test"), MediaTypeRegistry.JSON)).actionGet(); assertThat(indexResponse.getIndex(), equalTo("test")); assertAliasesVersionIncreases("test_x", () -> { @@ -188,7 +186,7 @@ public void testAliases() throws Exception { }); logger.info("--> indexing against [alias1], should work now"); - indexResponse = client().index(indexRequest("alias1").id("1").source(source("1", "test"), XContentType.JSON)).actionGet(); + indexResponse = client().index(indexRequest("alias1").id("1").source(source("1", "test"), MediaTypeRegistry.JSON)).actionGet(); assertThat(indexResponse.getIndex(), equalTo("test_x")); logger.info("--> deleting against [alias1], should fail now"); @@ -201,7 +199,7 @@ public void testAliases() throws Exception { }); logger.info("--> indexing against [alias1], should work against [test_x]"); - indexResponse = client().index(indexRequest("alias1").id("1").source(source("1", "test"), XContentType.JSON)).actionGet(); + indexResponse = client().index(indexRequest("alias1").id("1").source(source("1", "test"), MediaTypeRegistry.JSON)).actionGet(); assertThat(indexResponse.getIndex(), equalTo("test_x")); } @@ -283,16 +281,18 @@ public void testSearchingFilteringAliasesSingleIndex() throws Exception { logger.info("--> indexing against [test]"); client().index( - indexRequest("test").id("1").source(source("1", "foo test"), XContentType.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE) + indexRequest("test").id("1").source(source("1", "foo test"), MediaTypeRegistry.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE) ).actionGet(); client().index( - indexRequest("test").id("2").source(source("2", "bar test"), XContentType.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE) + indexRequest("test").id("2").source(source("2", "bar test"), MediaTypeRegistry.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE) ).actionGet(); client().index( - indexRequest("test").id("3").source(source("3", "baz test"), XContentType.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE) + indexRequest("test").id("3").source(source("3", "baz test"), MediaTypeRegistry.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE) ).actionGet(); client().index( - indexRequest("test").id("4").source(source("4", "something else"), XContentType.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE) + indexRequest("test").id("4") + .source(source("4", "something else"), MediaTypeRegistry.JSON) + .setRefreshPolicy(RefreshPolicy.IMMEDIATE) ).actionGet(); logger.info("--> checking single filtering alias search"); @@ -389,16 +389,16 @@ public void testSearchingFilteringAliasesTwoIndices() throws Exception { ); logger.info("--> indexing against [test1]"); - client().index(indexRequest("test1").id("1").source(source("1", "foo test"), XContentType.JSON)).get(); - client().index(indexRequest("test1").id("2").source(source("2", "bar test"), XContentType.JSON)).get(); - client().index(indexRequest("test1").id("3").source(source("3", "baz test"), XContentType.JSON)).get(); - client().index(indexRequest("test1").id("4").source(source("4", "something else"), XContentType.JSON)).get(); + client().index(indexRequest("test1").id("1").source(source("1", "foo test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test1").id("2").source(source("2", "bar test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test1").id("3").source(source("3", "baz test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test1").id("4").source(source("4", "something else"), MediaTypeRegistry.JSON)).get(); logger.info("--> indexing against [test2]"); - client().index(indexRequest("test2").id("5").source(source("5", "foo test"), XContentType.JSON)).get(); - client().index(indexRequest("test2").id("6").source(source("6", "bar test"), XContentType.JSON)).get(); - client().index(indexRequest("test2").id("7").source(source("7", "baz test"), XContentType.JSON)).get(); - client().index(indexRequest("test2").id("8").source(source("8", "something else"), XContentType.JSON)).get(); + client().index(indexRequest("test2").id("5").source(source("5", "foo test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test2").id("6").source(source("6", "bar test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test2").id("7").source(source("7", "baz test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test2").id("8").source(source("8", "something else"), MediaTypeRegistry.JSON)).get(); refresh(); @@ -503,17 +503,17 @@ public void testSearchingFilteringAliasesMultipleIndices() throws Exception { ); logger.info("--> indexing against [test1]"); - client().index(indexRequest("test1").id("11").source(source("11", "foo test1"), XContentType.JSON)).get(); - client().index(indexRequest("test1").id("12").source(source("12", "bar test1"), XContentType.JSON)).get(); - client().index(indexRequest("test1").id("13").source(source("13", "baz test1"), XContentType.JSON)).get(); + client().index(indexRequest("test1").id("11").source(source("11", "foo test1"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test1").id("12").source(source("12", "bar test1"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test1").id("13").source(source("13", "baz test1"), MediaTypeRegistry.JSON)).get(); - client().index(indexRequest("test2").id("21").source(source("21", "foo test2"), XContentType.JSON)).get(); - client().index(indexRequest("test2").id("22").source(source("22", "bar test2"), XContentType.JSON)).get(); - client().index(indexRequest("test2").id("23").source(source("23", "baz test2"), XContentType.JSON)).get(); + client().index(indexRequest("test2").id("21").source(source("21", "foo test2"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test2").id("22").source(source("22", "bar test2"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test2").id("23").source(source("23", "baz test2"), MediaTypeRegistry.JSON)).get(); - client().index(indexRequest("test3").id("31").source(source("31", "foo test3"), XContentType.JSON)).get(); - client().index(indexRequest("test3").id("32").source(source("32", "bar test3"), XContentType.JSON)).get(); - client().index(indexRequest("test3").id("33").source(source("33", "baz test3"), XContentType.JSON)).get(); + client().index(indexRequest("test3").id("31").source(source("31", "foo test3"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test3").id("32").source(source("32", "bar test3"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test3").id("33").source(source("33", "baz test3"), MediaTypeRegistry.JSON)).get(); refresh(); @@ -626,16 +626,16 @@ public void testDeletingByQueryFilteringAliases() throws Exception { ); logger.info("--> indexing against [test1]"); - client().index(indexRequest("test1").id("1").source(source("1", "foo test"), XContentType.JSON)).get(); - client().index(indexRequest("test1").id("2").source(source("2", "bar test"), XContentType.JSON)).get(); - client().index(indexRequest("test1").id("3").source(source("3", "baz test"), XContentType.JSON)).get(); - client().index(indexRequest("test1").id("4").source(source("4", "something else"), XContentType.JSON)).get(); + client().index(indexRequest("test1").id("1").source(source("1", "foo test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test1").id("2").source(source("2", "bar test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test1").id("3").source(source("3", "baz test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test1").id("4").source(source("4", "something else"), MediaTypeRegistry.JSON)).get(); logger.info("--> indexing against [test2]"); - client().index(indexRequest("test2").id("5").source(source("5", "foo test"), XContentType.JSON)).get(); - client().index(indexRequest("test2").id("6").source(source("6", "bar test"), XContentType.JSON)).get(); - client().index(indexRequest("test2").id("7").source(source("7", "baz test"), XContentType.JSON)).get(); - client().index(indexRequest("test2").id("8").source(source("8", "something else"), XContentType.JSON)).get(); + client().index(indexRequest("test2").id("5").source(source("5", "foo test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test2").id("6").source(source("6", "bar test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test2").id("7").source(source("7", "baz test"), MediaTypeRegistry.JSON)).get(); + client().index(indexRequest("test2").id("8").source(source("8", "something else"), MediaTypeRegistry.JSON)).get(); refresh(); @@ -684,8 +684,9 @@ public void testDeleteAliases() throws Exception { assertAliasesVersionIncreases(indices, () -> admin().indices().prepareAliases().removeAlias(indices, aliases).get()); - AliasesExistResponse response = admin().indices().prepareAliasesExist(aliases).get(); - assertThat(response.exists(), equalTo(false)); + for (String alias : aliases) { + assertTrue(admin().indices().prepareGetAliases(alias).get().getAliases().isEmpty()); + } logger.info("--> creating index [foo_foo] and [bar_bar]"); assertAcked(prepareCreate("foo_foo")); @@ -701,9 +702,9 @@ public void testDeleteAliases() throws Exception { () -> assertAcked(admin().indices().prepareAliases().addAliasAction(AliasActions.remove().index("foo*").alias("foo"))) ); - assertTrue(admin().indices().prepareAliasesExist("foo").get().exists()); - assertFalse(admin().indices().prepareAliasesExist("foo").setIndices("foo_foo").get().exists()); - assertTrue(admin().indices().prepareAliasesExist("foo").setIndices("bar_bar").get().exists()); + assertFalse(admin().indices().prepareGetAliases("foo").get().getAliases().isEmpty()); + assertTrue(admin().indices().prepareGetAliases("foo").setIndices("foo_foo").get().getAliases().isEmpty()); + assertFalse(admin().indices().prepareGetAliases("foo").setIndices("bar_bar").get().getAliases().isEmpty()); IllegalArgumentException iae = expectThrows( IllegalArgumentException.class, () -> admin().indices().prepareAliases().addAliasAction(AliasActions.remove().index("foo").alias("foo")).execute().actionGet() @@ -723,7 +724,7 @@ public void testWaitForAliasCreationMultipleShards() throws Exception { for (int i = 0; i < 10; i++) { final String aliasName = "alias" + i; assertAliasesVersionIncreases("test", () -> assertAcked(admin().indices().prepareAliases().addAlias("test", aliasName))); - client().index(indexRequest(aliasName).id("1").source(source("1", "test"), XContentType.JSON)).get(); + client().index(indexRequest(aliasName).id("1").source(source("1", "test"), MediaTypeRegistry.JSON)).get(); } } @@ -744,7 +745,7 @@ public void testWaitForAliasCreationSingleShard() throws Exception { for (int i = 0; i < 10; i++) { final String aliasName = "alias" + i; assertAliasesVersionIncreases("test", () -> assertAcked(admin().indices().prepareAliases().addAlias("test", aliasName))); - client().index(indexRequest(aliasName).id("1").source(source("1", "test"), XContentType.JSON)).get(); + client().index(indexRequest(aliasName).id("1").source(source("1", "test"), MediaTypeRegistry.JSON)).get(); } } @@ -766,7 +767,7 @@ public void run() { "test", () -> assertAcked(admin().indices().prepareAliases().addAlias("test", aliasName)) ); - client().index(indexRequest(aliasName).id("1").source(source("1", "test"), XContentType.JSON)).actionGet(); + client().index(indexRequest(aliasName).id("1").source(source("1", "test"), MediaTypeRegistry.JSON)).actionGet(); } }); } @@ -880,8 +881,7 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().get("foobar").get(0).getFilter(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getIndexRouting(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getSearchRouting(), nullValue()); - AliasesExistResponse existsResponse = admin().indices().prepareAliasesExist("alias1").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("alias1").get().getAliases().isEmpty()); logger.info("--> getting all aliases that start with alias*"); getResponse = admin().indices().prepareGetAliases("alias*").get(); @@ -898,8 +898,7 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().get("foobar").get(1).getFilter(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(1).getIndexRouting(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(1).getSearchRouting(), nullValue()); - existsResponse = admin().indices().prepareAliasesExist("alias*").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("alias*").get().getAliases().isEmpty()); logger.info("--> creating aliases [bar, baz, foo]"); assertAliasesVersionIncreases( @@ -937,8 +936,10 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().get("bazbar").get(1).getFilter(), nullValue()); assertThat(getResponse.getAliases().get("bazbar").get(1).getIndexRouting(), nullValue()); assertThat(getResponse.getAliases().get("bazbar").get(1).getSearchRouting(), nullValue()); - existsResponse = admin().indices().prepareAliasesExist("bar", "bac").addIndices("bazbar").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("bar").get().getAliases().isEmpty()); + assertFalse(admin().indices().prepareGetAliases("bac").get().getAliases().isEmpty()); + assertFalse(admin().indices().prepareGetAliases("bar").addIndices("bazbar").get().getAliases().isEmpty()); + assertFalse(admin().indices().prepareGetAliases("bac").addIndices("bazbar").get().getAliases().isEmpty()); logger.info("--> getting *b* for index baz*"); getResponse = admin().indices().prepareGetAliases("*b*").addIndices("baz*").get(); @@ -957,8 +958,7 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().get("bazbar").get(1).getFilter(), nullValue()); assertThat(getResponse.getAliases().get("bazbar").get(1).getIndexRouting(), nullValue()); assertThat(getResponse.getAliases().get("bazbar").get(1).getSearchRouting(), nullValue()); - existsResponse = admin().indices().prepareAliasesExist("*b*").addIndices("baz*").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("*b*").addIndices("baz*").get().getAliases().isEmpty()); logger.info("--> getting *b* for index *bar"); getResponse = admin().indices().prepareGetAliases("b*").addIndices("*bar").get(); @@ -982,8 +982,7 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().get("foobar").get(0).getFilter(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getIndexRouting(), equalTo("bla")); assertThat(getResponse.getAliases().get("foobar").get(0).getSearchRouting(), equalTo("bla")); - existsResponse = admin().indices().prepareAliasesExist("b*").addIndices("*bar").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("b*").addIndices("*bar").get().getAliases().isEmpty()); logger.info("--> getting f* for index *bar"); getResponse = admin().indices().prepareGetAliases("f*").addIndices("*bar").get(); @@ -994,8 +993,7 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().get("foobar").get(0).getFilter(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getIndexRouting(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getSearchRouting(), nullValue()); - existsResponse = admin().indices().prepareAliasesExist("f*").addIndices("*bar").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("f*").addIndices("*bar").get().getAliases().isEmpty()); // alias at work logger.info("--> getting f* for index *bac"); @@ -1008,8 +1006,7 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().get("foobar").get(0).getFilter(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getIndexRouting(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getSearchRouting(), nullValue()); - existsResponse = admin().indices().prepareAliasesExist("foo").addIndices("*bac").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("foo").addIndices("*bac").get().getAliases().isEmpty()); logger.info("--> getting foo for index foobar"); getResponse = admin().indices().prepareGetAliases("foo").addIndices("foobar").get(); @@ -1020,8 +1017,7 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().get("foobar").get(0).getFilter(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getIndexRouting(), nullValue()); assertThat(getResponse.getAliases().get("foobar").get(0).getSearchRouting(), nullValue()); - existsResponse = admin().indices().prepareAliasesExist("foo").addIndices("foobar").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("foo").addIndices("foobar").get().getAliases().isEmpty()); for (String aliasName : new String[] { null, "_all", "*" }) { logger.info("--> getting {} alias for index foobar", aliasName); @@ -1044,17 +1040,15 @@ public void testIndicesGetAliases() throws Exception { assertThat(getResponse.getAliases().size(), equalTo(2)); assertThat(getResponse.getAliases().get("foobar").size(), equalTo(4)); assertThat(getResponse.getAliases().get("bazbar").size(), equalTo(2)); - existsResponse = admin().indices().prepareAliasesExist("*").addIndices("*bac").get(); - assertThat(existsResponse.exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("*").addIndices("*bac").get().getAliases().isEmpty()); assertAcked(admin().indices().prepareAliases().removeAlias("foobar", "foo")); getResponse = admin().indices().prepareGetAliases("foo").addIndices("foobar").get(); - for (final ObjectObjectCursor> entry : getResponse.getAliases()) { - assertTrue(entry.value.isEmpty()); + for (final List entry : getResponse.getAliases().values()) { + assertTrue(entry.isEmpty()); } - existsResponse = admin().indices().prepareAliasesExist("foo").addIndices("foobar").get(); - assertThat(existsResponse.exists(), equalTo(false)); + assertTrue(admin().indices().prepareGetAliases("foo").addIndices("foobar").get().getAliases().isEmpty()); } public void testGetAllAliasesWorks() { @@ -1094,7 +1088,7 @@ public void testCreateIndexWithAliasesInSource() throws Exception { + " \"alias4\" : {\"is_hidden\": true}\n" + " }\n" + "}", - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -1226,7 +1220,7 @@ public void testAliasesWithBlocks() { ); assertAliasesVersionIncreases("test", () -> assertAcked(admin().indices().prepareAliases().removeAlias("test", "alias1"))); assertThat(admin().indices().prepareGetAliases("alias2").execute().actionGet().getAliases().get("test").size(), equalTo(1)); - assertThat(admin().indices().prepareAliasesExist("alias2").get().exists(), equalTo(true)); + assertFalse(admin().indices().prepareGetAliases("alias2").get().getAliases().isEmpty()); } finally { disableIndexBlock("test", block); } @@ -1244,8 +1238,7 @@ public void testAliasesWithBlocks() { () -> assertBlocked(admin().indices().prepareAliases().removeAlias("test", "alias2"), INDEX_READ_ONLY_BLOCK) ); assertThat(admin().indices().prepareGetAliases("alias2").execute().actionGet().getAliases().get("test").size(), equalTo(1)); - assertThat(admin().indices().prepareAliasesExist("alias2").get().exists(), equalTo(true)); - + assertFalse(admin().indices().prepareGetAliases("alias2").get().getAliases().isEmpty()); } finally { disableIndexBlock("test", SETTING_READ_ONLY); } @@ -1262,8 +1255,7 @@ public void testAliasesWithBlocks() { () -> assertBlocked(admin().indices().prepareAliases().removeAlias("test", "alias2"), INDEX_METADATA_BLOCK) ); assertBlocked(admin().indices().prepareGetAliases("alias2"), INDEX_METADATA_BLOCK); - assertBlocked(admin().indices().prepareAliasesExist("alias2"), INDEX_METADATA_BLOCK); - + assertBlocked(admin().indices().prepareGetAliases("alias2"), INDEX_METADATA_BLOCK); } finally { disableIndexBlock("test", SETTING_BLOCKS_METADATA); } @@ -1288,12 +1280,12 @@ public void testAliasActionRemoveIndex() throws InterruptedException, ExecutionE assertAcked(client().admin().indices().prepareAliases().removeIndex("foo*")); assertFalse(client().admin().indices().prepareExists("foo_foo").execute().actionGet().isExists()); - assertTrue(admin().indices().prepareAliasesExist("foo").get().exists()); + assertFalse(admin().indices().prepareGetAliases("foo").get().getAliases().isEmpty()); assertTrue(client().admin().indices().prepareExists("bar_bar").execute().actionGet().isExists()); - assertTrue(admin().indices().prepareAliasesExist("foo").setIndices("bar_bar").get().exists()); + assertFalse(admin().indices().prepareGetAliases("foo").setIndices("bar_bar").get().getAliases().isEmpty()); assertAcked(client().admin().indices().prepareAliases().removeIndex("bar_bar")); - assertFalse(admin().indices().prepareAliasesExist("foo").get().exists()); + assertTrue(admin().indices().prepareGetAliases("foo").get().getAliases().isEmpty()); assertFalse(client().admin().indices().prepareExists("bar_bar").execute().actionGet().isExists()); } @@ -1389,12 +1381,13 @@ public void testIndexingAndQueryingHiddenAliases() throws Exception { ensureGreen(); // Put a couple docs in each index directly - IndexResponse res = client().index(indexRequest(nonWriteIndex).id("1").source(source("1", "nonwrite"), XContentType.JSON)).get(); + IndexResponse res = client().index(indexRequest(nonWriteIndex).id("1").source(source("1", "nonwrite"), MediaTypeRegistry.JSON)) + .get(); assertThat(res.status().getStatus(), equalTo(201)); - res = client().index(indexRequest(writeIndex).id("2").source(source("2", "writeindex"), XContentType.JSON)).get(); + res = client().index(indexRequest(writeIndex).id("2").source(source("2", "writeindex"), MediaTypeRegistry.JSON)).get(); assertThat(res.status().getStatus(), equalTo(201)); // And through the alias - res = client().index(indexRequest(alias).id("3").source(source("3", "through alias"), XContentType.JSON)).get(); + res = client().index(indexRequest(alias).id("3").source(source("3", "through alias"), MediaTypeRegistry.JSON)).get(); assertThat(res.status().getStatus(), equalTo(201)); refresh(writeIndex, nonWriteIndex); diff --git a/server/src/internalClusterTest/java/org/opensearch/blocks/CreateIndexBlockIT.java b/server/src/internalClusterTest/java/org/opensearch/blocks/CreateIndexBlockIT.java new file mode 100644 index 0000000000000..3c1f1e83f7481 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/blocks/CreateIndexBlockIT.java @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.blocks; + +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.After; + +import static org.opensearch.test.OpenSearchIntegTestCase.client; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertBlocked; + +public class CreateIndexBlockIT extends OpenSearchIntegTestCase { + + public void testBlockCreateIndex() { + setCreateIndexBlock("true"); + assertBlocked(client().admin().indices().prepareCreate("uncreated-idx"), Metadata.CLUSTER_CREATE_INDEX_BLOCK); + setCreateIndexBlock("false"); + assertAcked(client().admin().indices().prepareCreate("created-idx").execute().actionGet()); + } + + @After + public void cleanup() throws Exception { + Settings settings = Settings.builder().putNull(Metadata.SETTING_CREATE_INDEX_BLOCK_SETTING.getKey()).build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings).get()); + } + + private void setCreateIndexBlock(String value) { + Settings settings = Settings.builder().put(Metadata.SETTING_CREATE_INDEX_BLOCK_SETTING.getKey(), value).build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings).get()); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/blocks/SimpleBlocksIT.java b/server/src/internalClusterTest/java/org/opensearch/blocks/SimpleBlocksIT.java index 8ede3e25b2e1a..a7354dddfd16d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/blocks/SimpleBlocksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/blocks/SimpleBlocksIT.java @@ -38,7 +38,6 @@ import org.opensearch.action.admin.indices.exists.indices.IndicesExistsResponse; import org.opensearch.action.admin.indices.readonly.AddIndexBlockRequestBuilder; import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequestBuilder; - import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.support.ActiveShardCount; diff --git a/server/src/internalClusterTest/java/org/opensearch/broadcast/BroadcastActionsIT.java b/server/src/internalClusterTest/java/org/opensearch/broadcast/BroadcastActionsIT.java index f9f99eb2662b0..96b8c63f6c2ba 100644 --- a/server/src/internalClusterTest/java/org/opensearch/broadcast/BroadcastActionsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/broadcast/BroadcastActionsIT.java @@ -33,8 +33,8 @@ package org.opensearch.broadcast; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/bwcompat/RecoveryWithUnsupportedIndicesIT.java b/server/src/internalClusterTest/java/org/opensearch/bwcompat/RecoveryWithUnsupportedIndicesIT.java index 047584c1f13cb..158ed107b79c3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/bwcompat/RecoveryWithUnsupportedIndicesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/bwcompat/RecoveryWithUnsupportedIndicesIT.java @@ -31,14 +31,6 @@ package org.opensearch.bwcompat; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - import org.apache.lucene.tests.util.LuceneTestCase; import org.apache.lucene.tests.util.TestUtil; import org.opensearch.ExceptionsHelper; @@ -48,6 +40,14 @@ import org.opensearch.gateway.CorruptStateException; import org.opensearch.test.OpenSearchIntegTestCase; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + import static org.hamcrest.Matchers.containsString; @LuceneTestCase.SuppressCodecs("*") diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterAwarenessHealthIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterAwarenessHealthIT.java new file mode 100644 index 0000000000000..9e6d2a6d385c7 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterAwarenessHealthIT.java @@ -0,0 +1,244 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster; + +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.cluster.awarenesshealth.ClusterAwarenessAttributesHealth; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.common.settings.Settings; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.List; +import java.util.Map; + +import static org.opensearch.test.NodeRoles.onlyRole; +import static org.hamcrest.Matchers.equalTo; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class ClusterAwarenessHealthIT extends OpenSearchIntegTestCase { + + public void testAwarenessAttributeHealthSucceeded() { + createIndex("test"); + ensureGreen(); + + for (final String node : internalCluster().getNodeNames()) { + // a very high time out, which should never fire due to the local flag + logger.info("--> getting cluster health on [{}]", node); + final ClusterHealthResponse health = client(node).admin() + .cluster() + .prepareHealth() + .setTimeout("30s") + .setLevel("awareness_attributes") + .setAwarenessAttribute("zone") + .get("10s"); + + assertFalse("timed out on " + node, health.isTimedOut()); + assertThat( + "health status on " + node, + health.getClusterAwarenessHealth().getClusterAwarenessAttributesHealthMap().size(), + equalTo(1) + ); + } + } + + public void testAwarenessAttributeHealthValidationFailed() { + createIndex("test"); + ensureGreen(); + for (final String node : internalCluster().getNodeNames()) { + // a very high time out, which should never fire due to the local flag + logger.info("--> getting cluster health on [{}]", node); + try { + final ClusterHealthResponse health = client(node).admin() + .cluster() + .prepareHealth() + .setTimeout("30s") + .setAwarenessAttribute("zone") + .get("10s"); + } catch (Exception exception) { + assertThat( + exception.getMessage(), + equalTo("Validation Failed: 1: level=awareness_attributes is required with awareness_attribute parameter;") + ); + } + } + } + + public void testAwarenessAttributeHealthValidationFailedOnIndexHealth() { + createIndex("test"); + ensureGreen(); + for (final String node : internalCluster().getNodeNames()) { + // a very high time out, which should never fire due to the local flag + logger.info("--> getting cluster health on [{}]", node); + try { + final ClusterHealthResponse health = client(node).admin() + .cluster() + .prepareHealth("test") + .setTimeout("30s") + .setLevel("awareness_attributes") + .setAwarenessAttribute("zone") + .get("10s"); + } catch (Exception exception) { + assertThat( + exception.getMessage(), + equalTo("Validation Failed: 1: awareness_attribute is not a supported parameter with index health;") + ); + } + } + } + + public void testAwarenessAttributeHealth() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'd' & 'e' & 'f'"); + List clusterManagerNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "d") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "e") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "f") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + + logger.info("--> start 3 data nodes on zones 'a' & 'b' & 'c'"); + List dataNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build() + ); + + final ClusterHealthResponse health = client(dataNodes.get(0)).admin() + .cluster() + .prepareHealth() + .setTimeout("30s") + .setLevel("awareness_attributes") + .setAwarenessAttribute("zone") + .get("10s"); + + ensureStableCluster(6); + assertThat(health.getClusterAwarenessHealth().getClusterAwarenessAttributesHealthMap().size(), equalTo(1)); + Map attributes = health.getClusterAwarenessHealth() + .getClusterAwarenessAttributesHealthMap(); + for (String attribute : attributes.keySet()) { + String attributeName = attributes.get(attribute).getAwarenessAttributeName(); + assertThat(attributeName, equalTo("zone")); + assertThat(attributes.get(attribute).getAwarenessAttributeHealthMap().size(), equalTo(3)); + } + } + + public void testAwarenessAttributeHealthAttributeDoesNotExists() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'd' & 'e' & 'f'"); + List clusterManagerNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "d") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "e") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "f") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + + logger.info("--> start 3 data nodes on zones 'a' & 'b' & 'c'"); + List dataNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build() + ); + + final ClusterHealthResponse health = client(dataNodes.get(0)).admin() + .cluster() + .prepareHealth() + .setTimeout("30s") + .setLevel("awareness_attributes") + .setAwarenessAttribute("rack") + .get("10s"); + + ensureStableCluster(6); + assertThat(health.getClusterAwarenessHealth().getClusterAwarenessAttributesHealthMap().size(), equalTo(1)); + Map attributes = health.getClusterAwarenessHealth() + .getClusterAwarenessAttributesHealthMap(); + for (String attribute : attributes.keySet()) { + String attributeName = attributes.get(attribute).getAwarenessAttributeName(); + assertThat(attributeName, equalTo("rack")); + assertThat(attributes.get(attribute).getAwarenessAttributeHealthMap().size(), equalTo(0)); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterHealthIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterHealthIT.java index 393ab8e5a7544..d63b87cbee6f7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterHealthIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterHealthIT.java @@ -32,7 +32,6 @@ package org.opensearch.cluster; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest; import org.opensearch.action.support.IndicesOptions; @@ -42,10 +41,11 @@ import org.opensearch.cluster.routing.UnassignedInfo; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.ArrayList; import java.util.List; @@ -60,7 +60,7 @@ public class ClusterHealthIT extends OpenSearchIntegTestCase { public void testSimpleLocalHealth() { createIndex("test"); - ensureGreen(); // master should think it's green now. + ensureGreen(); // cluster-manager should think it's green now. for (final String node : internalCluster().getNodeNames()) { // a very high time out, which should never fire due to the local flag @@ -307,7 +307,10 @@ public void testWaitForEventsRetriesIfOtherConditionsNotMet() { .execute(); final AtomicBoolean keepSubmittingTasks = new AtomicBoolean(true); - final ClusterService clusterService = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()); + final ClusterService clusterService = internalCluster().getInstance( + ClusterService.class, + internalCluster().getClusterManagerName() + ); final PlainActionFuture completionFuture = new PlainActionFuture<>(); clusterService.submitStateUpdateTask("looping task", new ClusterStateUpdateTask(Priority.LOW) { @Override @@ -336,7 +339,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS assertFalse(client().admin().cluster().prepareHealth("index").setWaitForGreenStatus().get().isTimedOut()); // at this point the original health response should not have returned: there was never a point where the index was green AND - // the master had processed all pending tasks above LANGUID priority. + // the cluster-manager had processed all pending tasks above LANGUID priority. assertFalse(healthResponseFuture.isDone()); keepSubmittingTasks.set(false); assertFalse(healthResponseFuture.actionGet(TimeValue.timeValueSeconds(30)).isTimedOut()); @@ -346,14 +349,14 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } } - public void testHealthOnMasterFailover() throws Exception { + public void testHealthOnClusterManagerFailover() throws Exception { final String node = internalCluster().startDataOnlyNode(); final boolean withIndex = randomBoolean(); if (withIndex) { - // Create index with many shards to provoke the health request to wait (for green) while master is being shut down. - // Notice that this is set to 0 after the test completed starting a number of health requests and master restarts. + // Create index with many shards to provoke the health request to wait (for green) while cluster-manager is being shut down. + // Notice that this is set to 0 after the test completed starting a number of health requests and cluster-manager restarts. // This ensures that the cluster is yellow when the health request is made, making the health request wait on the observer, - // triggering a call to observer.onClusterServiceClose when master is shutdown. + // triggering a call to observer.onClusterServiceClose when cluster-manager is shutdown. createIndex( "test", Settings.builder() @@ -364,8 +367,8 @@ public void testHealthOnMasterFailover() throws Exception { ); } final List> responseFutures = new ArrayList<>(); - // Run a few health requests concurrent to master fail-overs against a data-node to make sure master failover is handled - // without exceptions + // Run a few health requests concurrent to cluster-manager fail-overs against a data-node + // to make sure cluster-manager failover is handled without exceptions final int iterations = withIndex ? 10 : 20; for (int i = 0; i < iterations; ++i) { responseFutures.add( @@ -374,10 +377,10 @@ public void testHealthOnMasterFailover() throws Exception { .prepareHealth() .setWaitForEvents(Priority.LANGUID) .setWaitForGreenStatus() - .setMasterNodeTimeout(TimeValue.timeValueMinutes(2)) + .setClusterManagerNodeTimeout(TimeValue.timeValueMinutes(2)) .execute() ); - internalCluster().restartNode(internalCluster().getMasterName(), InternalTestCluster.EMPTY_CALLBACK); + internalCluster().restartNode(internalCluster().getClusterManagerName(), InternalTestCluster.EMPTY_CALLBACK); } if (withIndex) { assertAcked( @@ -394,9 +397,12 @@ public void testHealthOnMasterFailover() throws Exception { } } - public void testWaitForEventsTimesOutIfMasterBusy() { + public void testWaitForEventsTimesOutIfClusterManagerBusy() { final AtomicBoolean keepSubmittingTasks = new AtomicBoolean(true); - final ClusterService clusterService = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()); + final ClusterService clusterService = internalCluster().getInstance( + ClusterService.class, + internalCluster().getClusterManagerName() + ); final PlainActionFuture completionFuture = new PlainActionFuture<>(); clusterService.submitStateUpdateTask("looping task", new ClusterStateUpdateTask(Priority.LOW) { @Override diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterInfoServiceIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterInfoServiceIT.java index e8ea0bb933a3e..35b8bdf3dafe5 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterInfoServiceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterInfoServiceIT.java @@ -32,9 +32,7 @@ package org.opensearch.cluster; -import com.carrotsearch.hppc.cursors.ObjectCursor; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; import org.opensearch.action.admin.cluster.node.stats.NodesStatsAction; import org.opensearch.action.admin.indices.stats.IndicesStatsAction; @@ -46,23 +44,23 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; import org.opensearch.index.IndexService; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.store.Store; +import org.opensearch.index.store.remote.filecache.FileCacheStats; import org.opensearch.indices.IndicesService; import org.opensearch.indices.SystemIndexDescriptor; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.SystemIndexPlugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.transport.TransportService; - import org.hamcrest.Matchers; import java.util.Arrays; @@ -70,6 +68,7 @@ import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -166,34 +165,39 @@ public void testClusterInfoServiceCollectsInformation() { } ensureGreen(indexName); InternalTestCluster internalTestCluster = internalCluster(); - // Get the cluster info service on the master node + // Get the cluster info service on the cluster-manager node final InternalClusterInfoService infoService = (InternalClusterInfoService) internalTestCluster.getInstance( ClusterInfoService.class, - internalTestCluster.getMasterName() + internalTestCluster.getClusterManagerName() ); infoService.setUpdateFrequency(TimeValue.timeValueMillis(200)); ClusterInfo info = infoService.refresh(); assertNotNull("info should not be null", info); - ImmutableOpenMap leastUsages = info.getNodeLeastAvailableDiskUsages(); - ImmutableOpenMap mostUsages = info.getNodeMostAvailableDiskUsages(); - ImmutableOpenMap shardSizes = info.shardSizes; + final Map leastUsages = info.getNodeLeastAvailableDiskUsages(); + final Map mostUsages = info.getNodeMostAvailableDiskUsages(); + final Map shardSizes = info.shardSizes; assertNotNull(leastUsages); assertNotNull(shardSizes); assertThat("some usages are populated", leastUsages.values().size(), Matchers.equalTo(2)); assertThat("some shard sizes are populated", shardSizes.values().size(), greaterThan(0)); - for (ObjectCursor usage : leastUsages.values()) { - logger.info("--> usage: {}", usage.value); - assertThat("usage has be retrieved", usage.value.getFreeBytes(), greaterThan(0L)); + for (Map.Entry usage : leastUsages.entrySet()) { + logger.info("--> usage: {}", usage.getValue()); + assertThat("usage has be retrieved", usage.getValue().getFreeBytes(), greaterThan(0L)); } - for (ObjectCursor usage : mostUsages.values()) { - logger.info("--> usage: {}", usage.value); - assertThat("usage has be retrieved", usage.value.getFreeBytes(), greaterThan(0L)); + for (DiskUsage usage : mostUsages.values()) { + logger.info("--> usage: {}", usage); + assertThat("usage has be retrieved", usage.getFreeBytes(), greaterThan(0L)); } - for (ObjectCursor size : shardSizes.values()) { - logger.info("--> shard size: {}", size.value); - assertThat("shard size is greater than 0", size.value, greaterThanOrEqualTo(0L)); + for (Long size : shardSizes.values()) { + logger.info("--> shard size: {}", size); + assertThat("shard size is greater than 0", size, greaterThanOrEqualTo(0L)); } - ClusterService clusterService = internalTestCluster.getInstance(ClusterService.class, internalTestCluster.getMasterName()); + + final Map nodeFileCacheStats = info.nodeFileCacheStats; + assertNotNull(nodeFileCacheStats); + assertThat("file cache is empty on non search nodes", nodeFileCacheStats.size(), Matchers.equalTo(0)); + + ClusterService clusterService = internalTestCluster.getInstance(ClusterService.class, internalTestCluster.getClusterManagerName()); ClusterState state = clusterService.state(); for (ShardRouting shard : state.routingTable().allShards()) { String dataPath = info.getDataPath(shard); @@ -210,6 +214,28 @@ public void testClusterInfoServiceCollectsInformation() { } } + public void testClusterInfoServiceCollectsFileCacheInformation() { + internalCluster().startNodes(1); + internalCluster().ensureAtLeastNumSearchAndDataNodes(2); + + InternalTestCluster internalTestCluster = internalCluster(); + // Get the cluster info service on the cluster-manager node + final InternalClusterInfoService infoService = (InternalClusterInfoService) internalTestCluster.getInstance( + ClusterInfoService.class, + internalTestCluster.getClusterManagerName() + ); + infoService.setUpdateFrequency(TimeValue.timeValueMillis(200)); + ClusterInfo info = infoService.refresh(); + assertNotNull("info should not be null", info); + final Map nodeFileCacheStats = info.nodeFileCacheStats; + assertNotNull(nodeFileCacheStats); + assertThat("file cache is enabled on both search nodes", nodeFileCacheStats.size(), Matchers.equalTo(2)); + + for (FileCacheStats fileCacheStats : nodeFileCacheStats.values()) { + assertThat("file cache is non empty", fileCacheStats.getTotal().getBytes(), greaterThan(0L)); + } + } + public void testClusterInfoServiceInformationClearOnError() { internalCluster().startNodes( 2, @@ -221,7 +247,7 @@ public void testClusterInfoServiceInformationClearOnError() { InternalTestCluster internalTestCluster = internalCluster(); InternalClusterInfoService infoService = (InternalClusterInfoService) internalTestCluster.getInstance( ClusterInfoService.class, - internalTestCluster.getMasterName() + internalTestCluster.getClusterManagerName() ); // get one healthy sample ClusterInfo info = infoService.refresh(); @@ -231,7 +257,7 @@ public void testClusterInfoServiceInformationClearOnError() { MockTransportService mockTransportService = (MockTransportService) internalCluster().getInstance( TransportService.class, - internalTestCluster.getMasterName() + internalTestCluster.getClusterManagerName() ); final AtomicBoolean timeout = new AtomicBoolean(false); @@ -272,7 +298,7 @@ public void testClusterInfoServiceInformationClearOnError() { // now we cause an exception timeout.set(false); - ActionFilters actionFilters = internalTestCluster.getInstance(ActionFilters.class, internalTestCluster.getMasterName()); + ActionFilters actionFilters = internalTestCluster.getInstance(ActionFilters.class, internalTestCluster.getClusterManagerName()); BlockingActionFilter blockingActionFilter = null; for (ActionFilter filter : actionFilters.filters()) { if (filter instanceof BlockingActionFilter) { diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterStateDiffIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterStateDiffIT.java index ea1daffaf770d..44ab41fb5a8d3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterStateDiffIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/ClusterStateDiffIT.java @@ -32,12 +32,11 @@ package org.opensearch.cluster; -import com.carrotsearch.hppc.cursors.ObjectCursor; import org.opensearch.cluster.block.ClusterBlock; import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; -import org.opensearch.cluster.coordination.NoMasterBlockService; +import org.opensearch.cluster.coordination.NoClusterManagerBlockService; import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.cluster.metadata.IndexGraveyard; import org.opensearch.cluster.metadata.IndexGraveyardTests; @@ -55,18 +54,17 @@ import org.opensearch.cluster.routing.TestShardRouting; import org.opensearch.cluster.routing.UnassignedInfo; import org.opensearch.common.UUIDs; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.set.Sets; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.gateway.GatewayService; -import org.opensearch.index.Index; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.index.shard.ShardId; import org.opensearch.snapshots.Snapshot; import org.opensearch.snapshots.SnapshotId; import org.opensearch.snapshots.SnapshotInfoTests; @@ -75,6 +73,7 @@ import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import static java.util.Collections.emptyList; @@ -93,9 +92,13 @@ public class ClusterStateDiffIT extends OpenSearchIntegTestCase { public void testClusterStateDiffSerialization() throws Exception { NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry(ClusterModule.getNamedWriteables()); - DiscoveryNode masterNode = randomNode("master"); + DiscoveryNode clusterManagerNode = randomNode("cluster-manager"); DiscoveryNode otherNode = randomNode("other"); - DiscoveryNodes discoveryNodes = DiscoveryNodes.builder().add(masterNode).add(otherNode).localNodeId(masterNode.getId()).build(); + DiscoveryNodes discoveryNodes = DiscoveryNodes.builder() + .add(clusterManagerNode) + .add(otherNode) + .localNodeId(clusterManagerNode.getId()) + .build(); ClusterState clusterState = ClusterState.builder(new ClusterName("test")).nodes(discoveryNodes).build(); ClusterState clusterStateFromDiffs = ClusterState.Builder.fromBytes( ClusterState.Builder.toBytes(clusterState), @@ -168,9 +171,9 @@ public void testClusterStateDiffSerialization() throws Exception { assertThat(clusterStateFromDiffs.nodes().getNodes(), equalTo(clusterState.nodes().getNodes())); assertThat(clusterStateFromDiffs.nodes().getLocalNodeId(), equalTo(previousClusterStateFromDiffs.nodes().getLocalNodeId())); assertThat(clusterStateFromDiffs.nodes().getNodes(), equalTo(clusterState.nodes().getNodes())); - for (ObjectCursor node : clusterStateFromDiffs.nodes().getNodes().keys()) { - DiscoveryNode node1 = clusterState.nodes().get(node.value); - DiscoveryNode node2 = clusterStateFromDiffs.nodes().get(node.value); + for (final String node : clusterStateFromDiffs.nodes().getNodes().keySet()) { + DiscoveryNode node1 = clusterState.nodes().get(node); + DiscoveryNode node2 = clusterStateFromDiffs.nodes().get(node); assertThat(node1.getVersion(), equalTo(node2.getVersion())); assertThat(node1.getAddress(), equalTo(node2.getAddress())); assertThat(node1.getAttributes(), equalTo(node2.getAttributes())); @@ -252,7 +255,7 @@ private ClusterState.Builder randomNodes(ClusterState clusterState) { DiscoveryNodes.Builder nodes = DiscoveryNodes.builder(clusterState.nodes()); List nodeIds = randomSubsetOf( randomInt(clusterState.nodes().getNodes().size() - 1), - clusterState.nodes().getNodes().keys().toArray(String.class) + clusterState.nodes().getNodes().keySet().toArray(new String[0]) ); for (String nodeId : nodeIds) { if (nodeId.startsWith("node-")) { @@ -278,7 +281,7 @@ private ClusterState.Builder randomRoutingTable(ClusterState clusterState) { if (numberOfIndices > 0) { List randomIndices = randomSubsetOf( randomInt(numberOfIndices - 1), - clusterState.routingTable().indicesRouting().keys().toArray(String.class) + clusterState.routingTable().indicesRouting().keySet().toArray(new String[0]) ); for (String index : randomIndices) { if (randomBoolean()) { @@ -287,7 +290,7 @@ private ClusterState.Builder randomRoutingTable(ClusterState clusterState) { builder.add( randomChangeToIndexRoutingTable( clusterState.routingTable().indicesRouting().get(index), - clusterState.nodes().getNodes().keys().toArray(String.class) + clusterState.nodes().getNodes().keySet().toArray(new String[0]) ) ); } @@ -295,7 +298,7 @@ private ClusterState.Builder randomRoutingTable(ClusterState clusterState) { } int additionalIndexCount = randomIntBetween(1, 20); for (int i = 0; i < additionalIndexCount; i++) { - builder.add(randomIndexRoutingTable("index-" + randomInt(), clusterState.nodes().getNodes().keys().toArray(String.class))); + builder.add(randomIndexRoutingTable("index-" + randomInt(), clusterState.nodes().getNodes().keySet().toArray(new String[0]))); } return ClusterState.builder(clusterState).routingTable(builder.build()); } @@ -343,16 +346,16 @@ private IndexRoutingTable randomIndexRoutingTable(String index, String[] nodeIds */ private IndexRoutingTable randomChangeToIndexRoutingTable(IndexRoutingTable original, String[] nodes) { IndexRoutingTable.Builder builder = IndexRoutingTable.builder(original.getIndex()); - for (ObjectCursor indexShardRoutingTable : original.shards().values()) { + for (var indexShardRoutingTable : original.shards().values()) { Set availableNodes = Sets.newHashSet(nodes); - for (ShardRouting shardRouting : indexShardRoutingTable.value.shards()) { + for (ShardRouting shardRouting : indexShardRoutingTable.shards()) { availableNodes.remove(shardRouting.currentNodeId()); if (shardRouting.relocating()) { availableNodes.remove(shardRouting.relocatingNodeId()); } } - for (ShardRouting shardRouting : indexShardRoutingTable.value.shards()) { + for (ShardRouting shardRouting : indexShardRoutingTable.shards()) { final ShardRouting updatedShardRouting = randomChange(shardRouting, availableNodes); availableNodes.remove(updatedShardRouting.currentNodeId()); if (shardRouting.relocating()) { @@ -392,9 +395,9 @@ private ClusterState.Builder randomBlocks(ClusterState clusterState) { private ClusterBlock randomGlobalBlock() { switch (randomInt(2)) { case 0: - return NoMasterBlockService.NO_MASTER_BLOCK_ALL; + return NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ALL; case 1: - return NoMasterBlockService.NO_MASTER_BLOCK_WRITES; + return NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_WRITES; default: return GatewayService.STATE_NOT_RECOVERED_BLOCK; } @@ -408,7 +411,7 @@ private interface RandomClusterPart { /** * Returns list of parts from metadata */ - ImmutableOpenMap parts(ClusterState clusterState); + Map parts(ClusterState clusterState); /** * Puts the part back into metadata @@ -438,12 +441,12 @@ private interface RandomClusterPart { */ private ClusterState randomClusterStateParts(ClusterState clusterState, String prefix, RandomClusterPart randomPart) { ClusterState.Builder builder = ClusterState.builder(clusterState); - ImmutableOpenMap parts = randomPart.parts(clusterState); + final Map parts = randomPart.parts(clusterState); int partCount = parts.size(); if (partCount > 0) { List randomParts = randomSubsetOf( randomInt(partCount - 1), - randomPart.parts(clusterState).keys().toArray(String.class) + randomPart.parts(clusterState).keySet().toArray(new String[0]) ); for (String part : randomParts) { if (randomBoolean()) { @@ -522,7 +525,7 @@ private interface RandomPart { /** * Returns list of parts from metadata */ - ImmutableOpenMap parts(Metadata metadata); + Map parts(Metadata metadata); /** * Puts the part back into metadata @@ -552,10 +555,10 @@ private interface RandomPart { */ private Metadata randomParts(Metadata metadata, String prefix, RandomPart randomPart) { Metadata.Builder builder = Metadata.builder(metadata); - ImmutableOpenMap parts = randomPart.parts(metadata); + final Map parts = randomPart.parts(metadata); int partCount = parts.size(); if (partCount > 0) { - List randomParts = randomSubsetOf(randomInt(partCount - 1), randomPart.parts(metadata).keys().toArray(String.class)); + List randomParts = randomSubsetOf(randomInt(partCount - 1), randomPart.parts(metadata).keySet().toArray(new String[0])); for (String part : randomParts) { if (randomBoolean()) { randomPart.remove(builder, part); @@ -579,7 +582,7 @@ private Metadata randomIndices(Metadata metadata) { return randomParts(metadata, "index", new RandomPart() { @Override - public ImmutableOpenMap parts(Metadata metadata) { + public Map parts(Metadata metadata) { return metadata.indices(); } @@ -617,7 +620,7 @@ public IndexMetadata randomChange(IndexMetadata part) { break; case 1: if (randomBoolean() && part.getAliases().isEmpty() == false) { - builder.removeAlias(randomFrom(part.getAliases().keys().toArray(String.class))); + builder.removeAlias(randomFrom(part.getAliases().keySet().toArray(new String[0]))); } else { builder.putAlias(AliasMetadata.builder(randomAlphaOfLength(10))); } @@ -641,7 +644,7 @@ public IndexMetadata randomChange(IndexMetadata part) { private Metadata randomTemplates(Metadata metadata) { return randomParts(metadata, "template", new RandomPart() { @Override - public ImmutableOpenMap parts(Metadata metadata) { + public Map parts(Metadata metadata) { return metadata.templates(); } @@ -698,7 +701,7 @@ private Metadata randomMetadataCustoms(final Metadata metadata) { return randomParts(metadata, "custom", new RandomPart() { @Override - public ImmutableOpenMap parts(Metadata metadata) { + public Map parts(Metadata metadata) { return metadata.customs(); } @@ -740,7 +743,7 @@ private ClusterState.Builder randomClusterStateCustoms(final ClusterState cluste return ClusterState.builder(randomClusterStateParts(clusterState, "custom", new RandomClusterPart() { @Override - public ImmutableOpenMap parts(ClusterState clusterState) { + public Map parts(ClusterState clusterState) { return clusterState.customs(); } @@ -764,15 +767,16 @@ public ClusterState.Custom randomCreate(String name) { new Snapshot(randomName("repo"), new SnapshotId(randomName("snap"), UUIDs.randomBase64UUID())), randomBoolean(), randomBoolean(), - SnapshotsInProgressSerializationTests.randomState(ImmutableOpenMap.of()), + SnapshotsInProgressSerializationTests.randomState(Map.of()), Collections.emptyList(), Collections.emptyList(), Math.abs(randomLong()), randomIntBetween(0, 1000), - ImmutableOpenMap.of(), + Map.of(), null, SnapshotInfoTests.randomUserMetadata(), - randomVersion(random()) + randomVersion(random()), + false ) ) ); @@ -783,7 +787,7 @@ public ClusterState.Custom randomCreate(String name) { new Snapshot(randomName("repo"), new SnapshotId(randomName("snap"), UUIDs.randomBase64UUID())), RestoreInProgress.State.fromValue((byte) randomIntBetween(0, 3)), emptyList(), - ImmutableOpenMap.of() + Map.of() ) ).build(); default: diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/MinimumClusterManagerNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/MinimumClusterManagerNodesIT.java new file mode 100644 index 0000000000000..4c8bf24b1655a --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/MinimumClusterManagerNodesIT.java @@ -0,0 +1,427 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster; + +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.client.Client; +import org.opensearch.cluster.coordination.FailedToCommitClusterStateException; +import org.opensearch.cluster.coordination.NoClusterManagerBlockService; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.Priority; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.set.Sets; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; +import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.transport.MockTransportService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoTimeout; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +@ClusterScope(scope = Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) +public class MinimumClusterManagerNodesIT extends OpenSearchIntegTestCase { + + @Override + protected Collection> nodePlugins() { + final HashSet> classes = new HashSet<>(super.nodePlugins()); + classes.add(MockTransportService.TestPlugin.class); + return classes; + } + + public void testTwoNodesNoClusterManagerBlock() throws Exception { + internalCluster().setBootstrapClusterManagerNodeIndex(1); + + Settings settings = Settings.builder().put("discovery.initial_state_timeout", "500ms").build(); + + logger.info("--> start first node"); + String node1Name = internalCluster().startNode(settings); + + logger.info("--> should be blocked, no cluster-manager..."); + ClusterState state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(true)); + assertThat(state.nodes().getSize(), equalTo(1)); // verify that we still see the local node in the cluster state + + logger.info("--> start second node, cluster should be formed"); + String node2Name = internalCluster().startNode(settings); + + ClusterHealthResponse clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("2") + .execute() + .actionGet(); + assertThat(clusterHealthResponse.isTimedOut(), equalTo(false)); + + state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(false)); + state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(false)); + + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + assertThat(state.nodes().getSize(), equalTo(2)); + assertThat(state.metadata().indices().containsKey("test"), equalTo(false)); + + createIndex("test"); + NumShards numShards = getNumShards("test"); + logger.info("--> indexing some data"); + for (int i = 0; i < 100; i++) { + client().prepareIndex("test").setId(Integer.toString(i)).setSource("field", "value").execute().actionGet(); + } + // make sure that all shards recovered before trying to flush + assertThat( + client().admin() + .cluster() + .prepareHealth("test") + .setWaitForActiveShards(numShards.totalNumShards) + .execute() + .actionGet() + .getActiveShards(), + equalTo(numShards.totalNumShards) + ); + // flush for simpler debugging + flushAndRefresh(); + + logger.info("--> verify we get the data back"); + for (int i = 0; i < 10; i++) { + assertThat( + client().prepareSearch() + .setSize(0) + .setQuery(QueryBuilders.matchAllQuery()) + .execute() + .actionGet() + .getHits() + .getTotalHits().value, + equalTo(100L) + ); + } + + String clusterManagerNode = internalCluster().getClusterManagerName(); + String otherNode = node1Name.equals(clusterManagerNode) ? node2Name : node1Name; + logger.info("--> add voting config exclusion for non-cluster-manager node, to be sure it's not elected"); + client().execute(AddVotingConfigExclusionsAction.INSTANCE, new AddVotingConfigExclusionsRequest(otherNode)).get(); + logger.info("--> stop cluster-manager node, no cluster-manager block should appear"); + Settings clusterManagerDataPathSettings = internalCluster().dataPathSettings(clusterManagerNode); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(clusterManagerNode)); + + assertBusy(() -> { + ClusterState clusterState = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertTrue(clusterState.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID)); + }); + + state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(true)); + // verify that both nodes are still in the cluster state but there is no cluster-manager + assertThat(state.nodes().getSize(), equalTo(2)); + assertThat(state.nodes().getClusterManagerNode(), equalTo(null)); + + logger.info("--> starting the previous cluster-manager node again..."); + node2Name = internalCluster().startNode(Settings.builder().put(settings).put(clusterManagerDataPathSettings).build()); + + clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForYellowStatus() + .setWaitForNodes("2") + .execute() + .actionGet(); + assertThat(clusterHealthResponse.isTimedOut(), equalTo(false)); + + state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(false)); + state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(false)); + + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + assertThat(state.nodes().getSize(), equalTo(2)); + assertThat(state.metadata().indices().containsKey("test"), equalTo(true)); + + ensureGreen(); + + logger.info("--> verify we get the data back after cluster reform"); + for (int i = 0; i < 10; i++) { + assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(), 100); + } + + logger.info("--> clearing voting config exclusions"); + ClearVotingConfigExclusionsRequest clearRequest = new ClearVotingConfigExclusionsRequest(); + clearRequest.setWaitForRemoval(false); + client().execute(ClearVotingConfigExclusionsAction.INSTANCE, clearRequest).get(); + + clusterManagerNode = internalCluster().getClusterManagerName(); + otherNode = node1Name.equals(clusterManagerNode) ? node2Name : node1Name; + logger.info("--> add voting config exclusion for cluster-manager node, to be sure it's not elected"); + client().execute(AddVotingConfigExclusionsAction.INSTANCE, new AddVotingConfigExclusionsRequest(clusterManagerNode)).get(); + logger.info("--> stop non-cluster-manager node, no cluster-manager block should appear"); + Settings otherNodeDataPathSettings = internalCluster().dataPathSettings(otherNode); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(otherNode)); + + assertBusy(() -> { + ClusterState state1 = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state1.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(true)); + }); + + logger.info("--> starting the previous cluster-manager node again..."); + internalCluster().startNode(Settings.builder().put(settings).put(otherNodeDataPathSettings).build()); + + ensureGreen(); + clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("2") + .setWaitForGreenStatus() + .execute() + .actionGet(); + assertThat(clusterHealthResponse.isTimedOut(), equalTo(false)); + + state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(false)); + state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(false)); + + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + assertThat(state.nodes().getSize(), equalTo(2)); + assertThat(state.metadata().indices().containsKey("test"), equalTo(true)); + + logger.info("Running Cluster Health"); + ensureGreen(); + + logger.info("--> verify we the data back"); + for (int i = 0; i < 10; i++) { + assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(), 100); + } + } + + public void testThreeNodesNoClusterManagerBlock() throws Exception { + internalCluster().setBootstrapClusterManagerNodeIndex(2); + + Settings settings = Settings.builder().put("discovery.initial_state_timeout", "500ms").build(); + + logger.info("--> start first 2 nodes"); + internalCluster().startNodes(2, settings); + + ClusterState state; + + assertBusy(() -> { + for (Client client : clients()) { + ClusterState state1 = client.admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(state1.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(true)); + } + }); + + logger.info("--> start one more node"); + internalCluster().startNode(settings); + + ensureGreen(); + ClusterHealthResponse clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("3") + .execute() + .actionGet(); + assertThat(clusterHealthResponse.isTimedOut(), equalTo(false)); + + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + assertThat(state.nodes().getSize(), equalTo(3)); + + createIndex("test"); + NumShards numShards = getNumShards("test"); + logger.info("--> indexing some data"); + for (int i = 0; i < 100; i++) { + client().prepareIndex("test").setId(Integer.toString(i)).setSource("field", "value").execute().actionGet(); + } + ensureGreen(); + // make sure that all shards recovered before trying to flush + assertThat( + client().admin() + .cluster() + .prepareHealth("test") + .setWaitForActiveShards(numShards.totalNumShards) + .execute() + .actionGet() + .isTimedOut(), + equalTo(false) + ); + // flush for simpler debugging + client().admin().indices().prepareFlush().execute().actionGet(); + + refresh(); + logger.info("--> verify we get the data back"); + for (int i = 0; i < 10; i++) { + assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(), 100); + } + + List nonClusterManagerNodes = new ArrayList<>( + Sets.difference( + Sets.newHashSet(internalCluster().getNodeNames()), + Collections.singleton(internalCluster().getClusterManagerName()) + ) + ); + Settings nonClusterManagerDataPathSettings1 = internalCluster().dataPathSettings(nonClusterManagerNodes.get(0)); + Settings nonClusterManagerDataPathSettings2 = internalCluster().dataPathSettings(nonClusterManagerNodes.get(1)); + internalCluster().stopRandomNonClusterManagerNode(); + internalCluster().stopRandomNonClusterManagerNode(); + + logger.info("--> verify that there is no cluster-manager anymore on remaining node"); + // spin here to wait till the state is set + assertBusy(() -> { + ClusterState st = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); + assertThat(st.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID), equalTo(true)); + }); + + logger.info("--> start back the 2 nodes "); + internalCluster().startNodes(nonClusterManagerDataPathSettings1, nonClusterManagerDataPathSettings2); + + internalCluster().validateClusterFormed(); + ensureGreen(); + + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + assertThat(state.nodes().getSize(), equalTo(3)); + + logger.info("--> verify we the data back"); + for (int i = 0; i < 10; i++) { + assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(), 100); + } + } + + public void testCannotCommitStateThreeNodes() throws Exception { + internalCluster().setBootstrapClusterManagerNodeIndex(2); + + Settings settings = Settings.builder().put("discovery.initial_state_timeout", "500ms").build(); + + internalCluster().startNodes(3, settings); + ensureStableCluster(3); + + final String clusterManager = internalCluster().getClusterManagerName(); + Set otherNodes = new HashSet<>(Arrays.asList(internalCluster().getNodeNames())); + otherNodes.remove(clusterManager); + NetworkDisruption partition = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); + internalCluster().setDisruptionScheme(partition); + + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference failure = new AtomicReference<>(); + logger.debug("--> submitting for cluster state to be rejected"); + final ClusterService clusterManagerClusterService = internalCluster().clusterService(clusterManager); + clusterManagerClusterService.submitStateUpdateTask("test", new ClusterStateUpdateTask() { + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + latch.countDown(); + } + + @Override + public ClusterState execute(ClusterState currentState) throws Exception { + logger.debug("--> starting the disruption, preventing cluster state publishing"); + partition.startDisrupting(); + Metadata.Builder metadata = Metadata.builder(currentState.metadata()) + .persistentSettings( + Settings.builder().put(currentState.metadata().persistentSettings()).put("_SHOULD_NOT_BE_THERE_", true).build() + ); + return ClusterState.builder(currentState).metadata(metadata).build(); + } + + @Override + public void onFailure(String source, Exception e) { + failure.set(e); + latch.countDown(); + } + }); + + logger.debug("--> waiting for cluster state to be processed/rejected"); + latch.await(); + + assertThat(failure.get(), instanceOf(FailedToCommitClusterStateException.class)); + + logger.debug("--> check that there is no cluster-manager in minor partition"); + assertBusy(() -> assertThat(clusterManagerClusterService.state().nodes().getClusterManagerNode(), nullValue())); + + // let major partition to elect new cluster-manager, to ensure that old cluster-manager is not elected once partition is restored, + // otherwise persistent setting (which is a part of accepted state on old cluster-manager) will be propagated to other nodes + logger.debug("--> wait for cluster-manager to be elected in major partition"); + assertBusy(() -> { + DiscoveryNode clusterManagerNode = internalCluster().client(randomFrom(otherNodes)) + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode(); + assertThat(clusterManagerNode, notNullValue()); + assertThat(clusterManagerNode.getName(), not(equalTo(clusterManager))); + }); + + partition.stopDisrupting(); + + logger.debug("--> waiting for cluster to heal"); + assertNoTimeout(client().admin().cluster().prepareHealth().setWaitForNodes("3").setWaitForEvents(Priority.LANGUID)); + + for (String node : internalCluster().getNodeNames()) { + Settings nodeSetting = internalCluster().clusterService(node).state().metadata().settings(); + assertThat( + node + " processed the cluster state despite of a min cluster-manager node violation", + nodeSetting.get("_SHOULD_NOT_BE_THERE_"), + nullValue() + ); + } + + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/MinimumMasterNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/MinimumMasterNodesIT.java deleted file mode 100644 index c3dc686921eb6..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/MinimumMasterNodesIT.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.cluster; - -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; -import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsRequest; -import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; -import org.opensearch.client.Client; -import org.opensearch.cluster.coordination.FailedToCommitClusterStateException; -import org.opensearch.cluster.coordination.NoMasterBlockService; -import org.opensearch.cluster.metadata.Metadata; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Priority; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.set.Sets; -import org.opensearch.index.query.QueryBuilders; -import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; -import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalTestCluster; -import org.opensearch.test.disruption.NetworkDisruption; -import org.opensearch.test.transport.MockTransportService; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoTimeout; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; - -@ClusterScope(scope = Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) -public class MinimumMasterNodesIT extends OpenSearchIntegTestCase { - - @Override - protected Collection> nodePlugins() { - final HashSet> classes = new HashSet<>(super.nodePlugins()); - classes.add(MockTransportService.TestPlugin.class); - return classes; - } - - public void testTwoNodesNoMasterBlock() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(1); - - Settings settings = Settings.builder().put("discovery.initial_state_timeout", "500ms").build(); - - logger.info("--> start first node"); - String node1Name = internalCluster().startNode(settings); - - logger.info("--> should be blocked, no master..."); - ClusterState state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(true)); - assertThat(state.nodes().getSize(), equalTo(1)); // verify that we still see the local node in the cluster state - - logger.info("--> start second node, cluster should be formed"); - String node2Name = internalCluster().startNode(settings); - - ClusterHealthResponse clusterHealthResponse = client().admin() - .cluster() - .prepareHealth() - .setWaitForEvents(Priority.LANGUID) - .setWaitForNodes("2") - .execute() - .actionGet(); - assertThat(clusterHealthResponse.isTimedOut(), equalTo(false)); - - state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(false)); - state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(false)); - - state = client().admin().cluster().prepareState().execute().actionGet().getState(); - assertThat(state.nodes().getSize(), equalTo(2)); - assertThat(state.metadata().indices().containsKey("test"), equalTo(false)); - - createIndex("test"); - NumShards numShards = getNumShards("test"); - logger.info("--> indexing some data"); - for (int i = 0; i < 100; i++) { - client().prepareIndex("test").setId(Integer.toString(i)).setSource("field", "value").execute().actionGet(); - } - // make sure that all shards recovered before trying to flush - assertThat( - client().admin() - .cluster() - .prepareHealth("test") - .setWaitForActiveShards(numShards.totalNumShards) - .execute() - .actionGet() - .getActiveShards(), - equalTo(numShards.totalNumShards) - ); - // flush for simpler debugging - flushAndRefresh(); - - logger.info("--> verify we get the data back"); - for (int i = 0; i < 10; i++) { - assertThat( - client().prepareSearch() - .setSize(0) - .setQuery(QueryBuilders.matchAllQuery()) - .execute() - .actionGet() - .getHits() - .getTotalHits().value, - equalTo(100L) - ); - } - - String masterNode = internalCluster().getMasterName(); - String otherNode = node1Name.equals(masterNode) ? node2Name : node1Name; - logger.info("--> add voting config exclusion for non-master node, to be sure it's not elected"); - client().execute(AddVotingConfigExclusionsAction.INSTANCE, new AddVotingConfigExclusionsRequest(otherNode)).get(); - logger.info("--> stop master node, no master block should appear"); - Settings masterDataPathSettings = internalCluster().dataPathSettings(masterNode); - internalCluster().stopRandomNode(InternalTestCluster.nameFilter(masterNode)); - - assertBusy(() -> { - ClusterState clusterState = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertTrue(clusterState.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID)); - }); - - state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(true)); - // verify that both nodes are still in the cluster state but there is no master - assertThat(state.nodes().getSize(), equalTo(2)); - assertThat(state.nodes().getMasterNode(), equalTo(null)); - - logger.info("--> starting the previous master node again..."); - node2Name = internalCluster().startNode(Settings.builder().put(settings).put(masterDataPathSettings).build()); - - clusterHealthResponse = client().admin() - .cluster() - .prepareHealth() - .setWaitForEvents(Priority.LANGUID) - .setWaitForYellowStatus() - .setWaitForNodes("2") - .execute() - .actionGet(); - assertThat(clusterHealthResponse.isTimedOut(), equalTo(false)); - - state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(false)); - state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(false)); - - state = client().admin().cluster().prepareState().execute().actionGet().getState(); - assertThat(state.nodes().getSize(), equalTo(2)); - assertThat(state.metadata().indices().containsKey("test"), equalTo(true)); - - ensureGreen(); - - logger.info("--> verify we get the data back after cluster reform"); - for (int i = 0; i < 10; i++) { - assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(), 100); - } - - logger.info("--> clearing voting config exclusions"); - ClearVotingConfigExclusionsRequest clearRequest = new ClearVotingConfigExclusionsRequest(); - clearRequest.setWaitForRemoval(false); - client().execute(ClearVotingConfigExclusionsAction.INSTANCE, clearRequest).get(); - - masterNode = internalCluster().getMasterName(); - otherNode = node1Name.equals(masterNode) ? node2Name : node1Name; - logger.info("--> add voting config exclusion for master node, to be sure it's not elected"); - client().execute(AddVotingConfigExclusionsAction.INSTANCE, new AddVotingConfigExclusionsRequest(masterNode)).get(); - logger.info("--> stop non-master node, no master block should appear"); - Settings otherNodeDataPathSettings = internalCluster().dataPathSettings(otherNode); - internalCluster().stopRandomNode(InternalTestCluster.nameFilter(otherNode)); - - assertBusy(() -> { - ClusterState state1 = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state1.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(true)); - }); - - logger.info("--> starting the previous master node again..."); - internalCluster().startNode(Settings.builder().put(settings).put(otherNodeDataPathSettings).build()); - - ensureGreen(); - clusterHealthResponse = client().admin() - .cluster() - .prepareHealth() - .setWaitForEvents(Priority.LANGUID) - .setWaitForNodes("2") - .setWaitForGreenStatus() - .execute() - .actionGet(); - assertThat(clusterHealthResponse.isTimedOut(), equalTo(false)); - - state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(false)); - state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(false)); - - state = client().admin().cluster().prepareState().execute().actionGet().getState(); - assertThat(state.nodes().getSize(), equalTo(2)); - assertThat(state.metadata().indices().containsKey("test"), equalTo(true)); - - logger.info("Running Cluster Health"); - ensureGreen(); - - logger.info("--> verify we the data back"); - for (int i = 0; i < 10; i++) { - assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(), 100); - } - } - - public void testThreeNodesNoMasterBlock() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(2); - - Settings settings = Settings.builder().put("discovery.initial_state_timeout", "500ms").build(); - - logger.info("--> start first 2 nodes"); - internalCluster().startNodes(2, settings); - - ClusterState state; - - assertBusy(() -> { - for (Client client : clients()) { - ClusterState state1 = client.admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(state1.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(true)); - } - }); - - logger.info("--> start one more node"); - internalCluster().startNode(settings); - - ensureGreen(); - ClusterHealthResponse clusterHealthResponse = client().admin() - .cluster() - .prepareHealth() - .setWaitForEvents(Priority.LANGUID) - .setWaitForNodes("3") - .execute() - .actionGet(); - assertThat(clusterHealthResponse.isTimedOut(), equalTo(false)); - - state = client().admin().cluster().prepareState().execute().actionGet().getState(); - assertThat(state.nodes().getSize(), equalTo(3)); - - createIndex("test"); - NumShards numShards = getNumShards("test"); - logger.info("--> indexing some data"); - for (int i = 0; i < 100; i++) { - client().prepareIndex("test").setId(Integer.toString(i)).setSource("field", "value").execute().actionGet(); - } - ensureGreen(); - // make sure that all shards recovered before trying to flush - assertThat( - client().admin() - .cluster() - .prepareHealth("test") - .setWaitForActiveShards(numShards.totalNumShards) - .execute() - .actionGet() - .isTimedOut(), - equalTo(false) - ); - // flush for simpler debugging - client().admin().indices().prepareFlush().execute().actionGet(); - - refresh(); - logger.info("--> verify we get the data back"); - for (int i = 0; i < 10; i++) { - assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(), 100); - } - - List nonMasterNodes = new ArrayList<>( - Sets.difference(Sets.newHashSet(internalCluster().getNodeNames()), Collections.singleton(internalCluster().getMasterName())) - ); - Settings nonMasterDataPathSettings1 = internalCluster().dataPathSettings(nonMasterNodes.get(0)); - Settings nonMasterDataPathSettings2 = internalCluster().dataPathSettings(nonMasterNodes.get(1)); - internalCluster().stopRandomNonMasterNode(); - internalCluster().stopRandomNonMasterNode(); - - logger.info("--> verify that there is no master anymore on remaining node"); - // spin here to wait till the state is set - assertBusy(() -> { - ClusterState st = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertThat(st.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID), equalTo(true)); - }); - - logger.info("--> start back the 2 nodes "); - internalCluster().startNodes(nonMasterDataPathSettings1, nonMasterDataPathSettings2); - - internalCluster().validateClusterFormed(); - ensureGreen(); - - state = client().admin().cluster().prepareState().execute().actionGet().getState(); - assertThat(state.nodes().getSize(), equalTo(3)); - - logger.info("--> verify we the data back"); - for (int i = 0; i < 10; i++) { - assertHitCount(client().prepareSearch().setSize(0).setQuery(QueryBuilders.matchAllQuery()).execute().actionGet(), 100); - } - } - - public void testCannotCommitStateThreeNodes() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(2); - - Settings settings = Settings.builder().put("discovery.initial_state_timeout", "500ms").build(); - - internalCluster().startNodes(3, settings); - ensureStableCluster(3); - - final String master = internalCluster().getMasterName(); - Set otherNodes = new HashSet<>(Arrays.asList(internalCluster().getNodeNames())); - otherNodes.remove(master); - NetworkDisruption partition = isolateMasterDisruption(NetworkDisruption.DISCONNECT); - internalCluster().setDisruptionScheme(partition); - - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference failure = new AtomicReference<>(); - logger.debug("--> submitting for cluster state to be rejected"); - final ClusterService masterClusterService = internalCluster().clusterService(master); - masterClusterService.submitStateUpdateTask("test", new ClusterStateUpdateTask() { - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - latch.countDown(); - } - - @Override - public ClusterState execute(ClusterState currentState) throws Exception { - logger.debug("--> starting the disruption, preventing cluster state publishing"); - partition.startDisrupting(); - Metadata.Builder metadata = Metadata.builder(currentState.metadata()) - .persistentSettings( - Settings.builder().put(currentState.metadata().persistentSettings()).put("_SHOULD_NOT_BE_THERE_", true).build() - ); - return ClusterState.builder(currentState).metadata(metadata).build(); - } - - @Override - public void onFailure(String source, Exception e) { - failure.set(e); - latch.countDown(); - } - }); - - logger.debug("--> waiting for cluster state to be processed/rejected"); - latch.await(); - - assertThat(failure.get(), instanceOf(FailedToCommitClusterStateException.class)); - - logger.debug("--> check that there is no master in minor partition"); - assertBusy(() -> assertThat(masterClusterService.state().nodes().getMasterNode(), nullValue())); - - // let major partition to elect new master, to ensure that old master is not elected once partition is restored, - // otherwise persistent setting (which is a part of accepted state on old master) will be propagated to other nodes - logger.debug("--> wait for master to be elected in major partition"); - assertBusy(() -> { - DiscoveryNode masterNode = internalCluster().client(randomFrom(otherNodes)) - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode(); - assertThat(masterNode, notNullValue()); - assertThat(masterNode.getName(), not(equalTo(master))); - }); - - partition.stopDisrupting(); - - logger.debug("--> waiting for cluster to heal"); - assertNoTimeout(client().admin().cluster().prepareHealth().setWaitForNodes("3").setWaitForEvents(Priority.LANGUID)); - - for (String node : internalCluster().getNodeNames()) { - Settings nodeSetting = internalCluster().clusterService(node).state().metadata().settings(); - assertThat( - node + " processed the cluster state despite of a min master node violation", - nodeSetting.get("_SHOULD_NOT_BE_THERE_"), - nullValue() - ); - } - - } -} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/NoClusterManagerNodeIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/NoClusterManagerNodeIT.java new file mode 100644 index 0000000000000..da500fa717202 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/NoClusterManagerNodeIT.java @@ -0,0 +1,461 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster; + +import org.opensearch.action.ActionRequestBuilder; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.action.admin.cluster.state.ClusterStateResponse; +import org.opensearch.action.bulk.BulkRequestBuilder; +import org.opensearch.action.get.GetResponse; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.support.AutoCreateIndex; +import org.opensearch.client.Client; +import org.opensearch.client.Requests; +import org.opensearch.cluster.action.index.MappingUpdatedAction; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.coordination.NoClusterManagerBlockService; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.discovery.ClusterManagerNotDiscoveredException; +import org.opensearch.plugins.Plugin; +import org.opensearch.script.Script; +import org.opensearch.script.ScriptType; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; +import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.disruption.NetworkDisruption.IsolateAllNodes; +import org.opensearch.test.transport.MockTransportService; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertExists; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertRequestBuilderThrows; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; + +@ClusterScope(scope = Scope.TEST, numDataNodes = 0) +public class NoClusterManagerNodeIT extends OpenSearchIntegTestCase { + + @Override + protected int numberOfReplicas() { + return 2; + } + + @Override + protected Collection> nodePlugins() { + return Collections.singletonList(MockTransportService.TestPlugin.class); + } + + public void testNoClusterManagerActions() throws Exception { + Settings settings = Settings.builder() + .put(AutoCreateIndex.AUTO_CREATE_INDEX_SETTING.getKey(), true) + .put(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "all") + .build(); + + final TimeValue timeout = TimeValue.timeValueMillis(10); + + final List nodes = internalCluster().startNodes(3, settings); + + createIndex("test"); + client().admin().cluster().prepareHealth("test").setWaitForGreenStatus().execute().actionGet(); + + final NetworkDisruption disruptionScheme = new NetworkDisruption( + new IsolateAllNodes(new HashSet<>(nodes)), + NetworkDisruption.DISCONNECT + ); + internalCluster().setDisruptionScheme(disruptionScheme); + disruptionScheme.startDisrupting(); + + final Client clientToClusterManagerlessNode = client(); + + assertBusy(() -> { + ClusterState state = clientToClusterManagerlessNode.admin() + .cluster() + .prepareState() + .setLocal(true) + .execute() + .actionGet() + .getState(); + assertTrue(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID)); + }); + + assertRequestBuilderThrows( + clientToClusterManagerlessNode.prepareGet("test", "1"), + ClusterBlockException.class, + RestStatus.SERVICE_UNAVAILABLE + ); + + assertRequestBuilderThrows( + clientToClusterManagerlessNode.prepareGet("no_index", "1"), + ClusterBlockException.class, + RestStatus.SERVICE_UNAVAILABLE + ); + + assertRequestBuilderThrows( + clientToClusterManagerlessNode.prepareMultiGet().add("test", "1"), + ClusterBlockException.class, + RestStatus.SERVICE_UNAVAILABLE + ); + + assertRequestBuilderThrows( + clientToClusterManagerlessNode.prepareMultiGet().add("no_index", "1"), + ClusterBlockException.class, + RestStatus.SERVICE_UNAVAILABLE + ); + + assertRequestBuilderThrows( + clientToClusterManagerlessNode.admin().indices().prepareAnalyze("test", "this is a test"), + ClusterBlockException.class, + RestStatus.SERVICE_UNAVAILABLE + ); + + assertRequestBuilderThrows( + clientToClusterManagerlessNode.admin().indices().prepareAnalyze("no_index", "this is a test"), + ClusterBlockException.class, + RestStatus.SERVICE_UNAVAILABLE + ); + + assertRequestBuilderThrows( + clientToClusterManagerlessNode.prepareSearch("test").setSize(0), + ClusterBlockException.class, + RestStatus.SERVICE_UNAVAILABLE + ); + + assertRequestBuilderThrows( + clientToClusterManagerlessNode.prepareSearch("no_index").setSize(0), + ClusterBlockException.class, + RestStatus.SERVICE_UNAVAILABLE + ); + + checkUpdateAction( + false, + timeout, + clientToClusterManagerlessNode.prepareUpdate("test", "1") + .setScript(new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, "test script", Collections.emptyMap())) + .setTimeout(timeout) + ); + + checkUpdateAction( + true, + timeout, + clientToClusterManagerlessNode.prepareUpdate("no_index", "1") + .setScript(new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, "test script", Collections.emptyMap())) + .setTimeout(timeout) + ); + + checkWriteAction( + clientToClusterManagerlessNode.prepareIndex("test") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + .setTimeout(timeout) + ); + + checkWriteAction( + clientToClusterManagerlessNode.prepareIndex("no_index") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + .setTimeout(timeout) + ); + + BulkRequestBuilder bulkRequestBuilder = clientToClusterManagerlessNode.prepareBulk(); + bulkRequestBuilder.add( + clientToClusterManagerlessNode.prepareIndex("test") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + ); + bulkRequestBuilder.add( + clientToClusterManagerlessNode.prepareIndex("test") + .setId("2") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + ); + bulkRequestBuilder.setTimeout(timeout); + checkWriteAction(bulkRequestBuilder); + + bulkRequestBuilder = clientToClusterManagerlessNode.prepareBulk(); + bulkRequestBuilder.add( + clientToClusterManagerlessNode.prepareIndex("no_index") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + ); + bulkRequestBuilder.add( + clientToClusterManagerlessNode.prepareIndex("no_index") + .setId("2") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + ); + bulkRequestBuilder.setTimeout(timeout); + checkWriteAction(bulkRequestBuilder); + + internalCluster().clearDisruptionScheme(true); + } + + void checkUpdateAction(boolean autoCreateIndex, TimeValue timeout, ActionRequestBuilder builder) { + // we clean the metadata when loosing a cluster-manager, therefore all operations on indices will auto create it, if allowed + try { + builder.get(); + fail("expected ClusterBlockException or ClusterManagerNotDiscoveredException"); + } catch (ClusterBlockException | ClusterManagerNotDiscoveredException e) { + if (e instanceof ClusterManagerNotDiscoveredException) { + assertTrue(autoCreateIndex); + } else { + assertFalse(autoCreateIndex); + } + assertThat(e.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE)); + } + } + + void checkWriteAction(ActionRequestBuilder builder) { + try { + builder.get(); + fail("Expected ClusterBlockException"); + } catch (ClusterBlockException e) { + assertThat(e.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE)); + } + } + + public void testNoClusterManagerActionsWriteClusterManagerBlock() throws Exception { + Settings settings = Settings.builder() + .put(AutoCreateIndex.AUTO_CREATE_INDEX_SETTING.getKey(), false) + .put(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "write") + .build(); + + final List nodes = internalCluster().startNodes(3, settings); + + prepareCreate("test1").setSettings( + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) + ).get(); + prepareCreate("test2").setSettings( + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + ).get(); + client().admin().cluster().prepareHealth("_all").setWaitForGreenStatus().get(); + client().prepareIndex("test1").setId("1").setSource("field", "value1").get(); + client().prepareIndex("test2").setId("1").setSource("field", "value1").get(); + refresh(); + + ensureSearchable("test1", "test2"); + + ClusterStateResponse clusterState = client().admin().cluster().prepareState().get(); + logger.info("Cluster state:\n{}", clusterState.getState()); + + final NetworkDisruption disruptionScheme = new NetworkDisruption( + new IsolateAllNodes(new HashSet<>(nodes)), + NetworkDisruption.DISCONNECT + ); + internalCluster().setDisruptionScheme(disruptionScheme); + disruptionScheme.startDisrupting(); + + final Client clientToClusterManagerlessNode = client(); + + assertBusy(() -> { + ClusterState state = clientToClusterManagerlessNode.admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID)); + }); + + GetResponse getResponse = clientToClusterManagerlessNode.prepareGet("test1", "1").get(); + assertExists(getResponse); + + SearchResponse countResponse = clientToClusterManagerlessNode.prepareSearch("test1") + .setAllowPartialSearchResults(true) + .setSize(0) + .get(); + assertHitCount(countResponse, 1L); + + logger.info("--> here 3"); + SearchResponse searchResponse = clientToClusterManagerlessNode.prepareSearch("test1").setAllowPartialSearchResults(true).get(); + assertHitCount(searchResponse, 1L); + + countResponse = clientToClusterManagerlessNode.prepareSearch("test2").setAllowPartialSearchResults(true).setSize(0).get(); + assertThat(countResponse.getTotalShards(), equalTo(3)); + assertThat(countResponse.getSuccessfulShards(), equalTo(1)); + + TimeValue timeout = TimeValue.timeValueMillis(200); + long now = System.currentTimeMillis(); + try { + clientToClusterManagerlessNode.prepareUpdate("test1", "1") + .setDoc(Requests.INDEX_CONTENT_TYPE, "field", "value2") + .setTimeout(timeout) + .get(); + fail("Expected ClusterBlockException"); + } catch (ClusterBlockException e) { + assertThat(System.currentTimeMillis() - now, greaterThan(timeout.millis() - 50)); + assertThat(e.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE)); + } catch (Exception e) { + logger.info("unexpected", e); + throw e; + } + + try { + clientToClusterManagerlessNode.prepareIndex("test1") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + .setTimeout(timeout) + .get(); + fail("Expected ClusterBlockException"); + } catch (ClusterBlockException e) { + assertThat(e.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE)); + } + + internalCluster().clearDisruptionScheme(true); + } + + public void testNoClusterManagerActionsMetadataWriteClusterManagerBlock() throws Exception { + Settings settings = Settings.builder() + .put(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "metadata_write") + .put(MappingUpdatedAction.INDICES_MAPPING_DYNAMIC_TIMEOUT_SETTING.getKey(), "100ms") + .build(); + + final List nodes = internalCluster().startNodes(3, settings); + + prepareCreate("test1").setSettings( + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) + ).get(); + client().admin().cluster().prepareHealth("_all").setWaitForGreenStatus().get(); + client().prepareIndex("test1").setId("1").setSource("field", "value1").get(); + refresh(); + + ensureGreen("test1"); + + ClusterStateResponse clusterState = client().admin().cluster().prepareState().get(); + logger.info("Cluster state:\n{}", clusterState.getState()); + + final List nodesWithShards = clusterState.getState() + .routingTable() + .index("test1") + .shard(0) + .activeShards() + .stream() + .map(shardRouting -> shardRouting.currentNodeId()) + .map(nodeId -> clusterState.getState().nodes().resolveNode(nodeId)) + .map(DiscoveryNode::getName) + .collect(Collectors.toList()); + + client().execute( + AddVotingConfigExclusionsAction.INSTANCE, + new AddVotingConfigExclusionsRequest(nodesWithShards.toArray(new String[0])) + ).get(); + ensureGreen("test1"); + + String partitionedNode = nodes.stream().filter(n -> nodesWithShards.contains(n) == false).findFirst().get(); + + final NetworkDisruption disruptionScheme = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(Collections.singleton(partitionedNode), new HashSet<>(nodesWithShards)), + NetworkDisruption.DISCONNECT + ); + internalCluster().setDisruptionScheme(disruptionScheme); + disruptionScheme.startDisrupting(); + + assertBusy(() -> { + for (String node : nodesWithShards) { + ClusterState state = client(node).admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID)); + } + }); + + GetResponse getResponse = client(randomFrom(nodesWithShards)).prepareGet("test1", "1").get(); + assertExists(getResponse); + + expectThrows(Exception.class, () -> client(partitionedNode).prepareGet("test1", "1").get()); + + SearchResponse countResponse = client(randomFrom(nodesWithShards)).prepareSearch("test1") + .setAllowPartialSearchResults(true) + .setSize(0) + .get(); + assertHitCount(countResponse, 1L); + + expectThrows( + Exception.class, + () -> client(partitionedNode).prepareSearch("test1").setAllowPartialSearchResults(true).setSize(0).get() + ); + + TimeValue timeout = TimeValue.timeValueMillis(200); + client(randomFrom(nodesWithShards)).prepareUpdate("test1", "1") + .setDoc(Requests.INDEX_CONTENT_TYPE, "field", "value2") + .setTimeout(timeout) + .get(); + + expectThrows( + Exception.class, + () -> client(partitionedNode).prepareUpdate("test1", "1") + .setDoc(Requests.INDEX_CONTENT_TYPE, "field", "value2") + .setTimeout(timeout) + .get() + ); + + client(randomFrom(nodesWithShards)).prepareIndex("test1") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + .setTimeout(timeout) + .get(); + + // dynamic mapping updates fail + expectThrows( + ClusterManagerNotDiscoveredException.class, + () -> client(randomFrom(nodesWithShards)).prepareIndex("test1") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().field("new_field", "value").endObject()) + .setTimeout(timeout) + .get() + ); + + // dynamic index creation fails + expectThrows( + ClusterManagerNotDiscoveredException.class, + () -> client(randomFrom(nodesWithShards)).prepareIndex("test2") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + .setTimeout(timeout) + .get() + ); + + expectThrows( + Exception.class, + () -> client(partitionedNode).prepareIndex("test1") + .setId("1") + .setSource(XContentFactory.jsonBuilder().startObject().endObject()) + .setTimeout(timeout) + .get() + ); + + internalCluster().clearDisruptionScheme(true); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/NoMasterNodeIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/NoMasterNodeIT.java deleted file mode 100644 index 5226eed2b6610..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/NoMasterNodeIT.java +++ /dev/null @@ -1,444 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.cluster; - -import org.opensearch.action.ActionRequestBuilder; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; -import org.opensearch.action.admin.cluster.state.ClusterStateResponse; -import org.opensearch.action.bulk.BulkRequestBuilder; -import org.opensearch.action.get.GetResponse; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.action.support.AutoCreateIndex; -import org.opensearch.client.Client; -import org.opensearch.client.Requests; -import org.opensearch.cluster.action.index.MappingUpdatedAction; -import org.opensearch.cluster.block.ClusterBlockException; -import org.opensearch.cluster.coordination.NoMasterBlockService; -import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.discovery.MasterNotDiscoveredException; -import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; -import org.opensearch.script.Script; -import org.opensearch.script.ScriptType; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; -import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.disruption.NetworkDisruption; -import org.opensearch.test.disruption.NetworkDisruption.IsolateAllNodes; -import org.opensearch.test.transport.MockTransportService; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.stream.Collectors; - -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertExists; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertRequestBuilderThrows; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; - -@ClusterScope(scope = Scope.TEST, numDataNodes = 0) -public class NoMasterNodeIT extends OpenSearchIntegTestCase { - - @Override - protected int numberOfReplicas() { - return 2; - } - - @Override - protected Collection> nodePlugins() { - return Collections.singletonList(MockTransportService.TestPlugin.class); - } - - public void testNoMasterActions() throws Exception { - Settings settings = Settings.builder() - .put(AutoCreateIndex.AUTO_CREATE_INDEX_SETTING.getKey(), true) - .put(NoMasterBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "all") - .build(); - - final TimeValue timeout = TimeValue.timeValueMillis(10); - - final List nodes = internalCluster().startNodes(3, settings); - - createIndex("test"); - client().admin().cluster().prepareHealth("test").setWaitForGreenStatus().execute().actionGet(); - - final NetworkDisruption disruptionScheme = new NetworkDisruption( - new IsolateAllNodes(new HashSet<>(nodes)), - NetworkDisruption.DISCONNECT - ); - internalCluster().setDisruptionScheme(disruptionScheme); - disruptionScheme.startDisrupting(); - - final Client clientToMasterlessNode = client(); - - assertBusy(() -> { - ClusterState state = clientToMasterlessNode.admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertTrue(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID)); - }); - - assertRequestBuilderThrows( - clientToMasterlessNode.prepareGet("test", "1"), - ClusterBlockException.class, - RestStatus.SERVICE_UNAVAILABLE - ); - - assertRequestBuilderThrows( - clientToMasterlessNode.prepareGet("no_index", "1"), - ClusterBlockException.class, - RestStatus.SERVICE_UNAVAILABLE - ); - - assertRequestBuilderThrows( - clientToMasterlessNode.prepareMultiGet().add("test", "1"), - ClusterBlockException.class, - RestStatus.SERVICE_UNAVAILABLE - ); - - assertRequestBuilderThrows( - clientToMasterlessNode.prepareMultiGet().add("no_index", "1"), - ClusterBlockException.class, - RestStatus.SERVICE_UNAVAILABLE - ); - - assertRequestBuilderThrows( - clientToMasterlessNode.admin().indices().prepareAnalyze("test", "this is a test"), - ClusterBlockException.class, - RestStatus.SERVICE_UNAVAILABLE - ); - - assertRequestBuilderThrows( - clientToMasterlessNode.admin().indices().prepareAnalyze("no_index", "this is a test"), - ClusterBlockException.class, - RestStatus.SERVICE_UNAVAILABLE - ); - - assertRequestBuilderThrows( - clientToMasterlessNode.prepareSearch("test").setSize(0), - ClusterBlockException.class, - RestStatus.SERVICE_UNAVAILABLE - ); - - assertRequestBuilderThrows( - clientToMasterlessNode.prepareSearch("no_index").setSize(0), - ClusterBlockException.class, - RestStatus.SERVICE_UNAVAILABLE - ); - - checkUpdateAction( - false, - timeout, - clientToMasterlessNode.prepareUpdate("test", "1") - .setScript(new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, "test script", Collections.emptyMap())) - .setTimeout(timeout) - ); - - checkUpdateAction( - true, - timeout, - clientToMasterlessNode.prepareUpdate("no_index", "1") - .setScript(new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, "test script", Collections.emptyMap())) - .setTimeout(timeout) - ); - - checkWriteAction( - clientToMasterlessNode.prepareIndex("test") - .setId("1") - .setSource(XContentFactory.jsonBuilder().startObject().endObject()) - .setTimeout(timeout) - ); - - checkWriteAction( - clientToMasterlessNode.prepareIndex("no_index") - .setId("1") - .setSource(XContentFactory.jsonBuilder().startObject().endObject()) - .setTimeout(timeout) - ); - - BulkRequestBuilder bulkRequestBuilder = clientToMasterlessNode.prepareBulk(); - bulkRequestBuilder.add( - clientToMasterlessNode.prepareIndex("test").setId("1").setSource(XContentFactory.jsonBuilder().startObject().endObject()) - ); - bulkRequestBuilder.add( - clientToMasterlessNode.prepareIndex("test").setId("2").setSource(XContentFactory.jsonBuilder().startObject().endObject()) - ); - bulkRequestBuilder.setTimeout(timeout); - checkWriteAction(bulkRequestBuilder); - - bulkRequestBuilder = clientToMasterlessNode.prepareBulk(); - bulkRequestBuilder.add( - clientToMasterlessNode.prepareIndex("no_index").setId("1").setSource(XContentFactory.jsonBuilder().startObject().endObject()) - ); - bulkRequestBuilder.add( - clientToMasterlessNode.prepareIndex("no_index").setId("2").setSource(XContentFactory.jsonBuilder().startObject().endObject()) - ); - bulkRequestBuilder.setTimeout(timeout); - checkWriteAction(bulkRequestBuilder); - - internalCluster().clearDisruptionScheme(true); - } - - void checkUpdateAction(boolean autoCreateIndex, TimeValue timeout, ActionRequestBuilder builder) { - // we clean the metadata when loosing a master, therefore all operations on indices will auto create it, if allowed - try { - builder.get(); - fail("expected ClusterBlockException or MasterNotDiscoveredException"); - } catch (ClusterBlockException | MasterNotDiscoveredException e) { - if (e instanceof MasterNotDiscoveredException) { - assertTrue(autoCreateIndex); - } else { - assertFalse(autoCreateIndex); - } - assertThat(e.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE)); - } - } - - void checkWriteAction(ActionRequestBuilder builder) { - try { - builder.get(); - fail("Expected ClusterBlockException"); - } catch (ClusterBlockException e) { - assertThat(e.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE)); - } - } - - public void testNoMasterActionsWriteMasterBlock() throws Exception { - Settings settings = Settings.builder() - .put(AutoCreateIndex.AUTO_CREATE_INDEX_SETTING.getKey(), false) - .put(NoMasterBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "write") - .build(); - - final List nodes = internalCluster().startNodes(3, settings); - - prepareCreate("test1").setSettings( - Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) - ).get(); - prepareCreate("test2").setSettings( - Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) - ).get(); - client().admin().cluster().prepareHealth("_all").setWaitForGreenStatus().get(); - client().prepareIndex("test1").setId("1").setSource("field", "value1").get(); - client().prepareIndex("test2").setId("1").setSource("field", "value1").get(); - refresh(); - - ensureSearchable("test1", "test2"); - - ClusterStateResponse clusterState = client().admin().cluster().prepareState().get(); - logger.info("Cluster state:\n{}", clusterState.getState()); - - final NetworkDisruption disruptionScheme = new NetworkDisruption( - new IsolateAllNodes(new HashSet<>(nodes)), - NetworkDisruption.DISCONNECT - ); - internalCluster().setDisruptionScheme(disruptionScheme); - disruptionScheme.startDisrupting(); - - final Client clientToMasterlessNode = client(); - - assertBusy(() -> { - ClusterState state = clientToMasterlessNode.admin().cluster().prepareState().setLocal(true).get().getState(); - assertTrue(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID)); - }); - - GetResponse getResponse = clientToMasterlessNode.prepareGet("test1", "1").get(); - assertExists(getResponse); - - SearchResponse countResponse = clientToMasterlessNode.prepareSearch("test1").setAllowPartialSearchResults(true).setSize(0).get(); - assertHitCount(countResponse, 1L); - - logger.info("--> here 3"); - SearchResponse searchResponse = clientToMasterlessNode.prepareSearch("test1").setAllowPartialSearchResults(true).get(); - assertHitCount(searchResponse, 1L); - - countResponse = clientToMasterlessNode.prepareSearch("test2").setAllowPartialSearchResults(true).setSize(0).get(); - assertThat(countResponse.getTotalShards(), equalTo(3)); - assertThat(countResponse.getSuccessfulShards(), equalTo(1)); - - TimeValue timeout = TimeValue.timeValueMillis(200); - long now = System.currentTimeMillis(); - try { - clientToMasterlessNode.prepareUpdate("test1", "1") - .setDoc(Requests.INDEX_CONTENT_TYPE, "field", "value2") - .setTimeout(timeout) - .get(); - fail("Expected ClusterBlockException"); - } catch (ClusterBlockException e) { - assertThat(System.currentTimeMillis() - now, greaterThan(timeout.millis() - 50)); - assertThat(e.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE)); - } catch (Exception e) { - logger.info("unexpected", e); - throw e; - } - - try { - clientToMasterlessNode.prepareIndex("test1") - .setId("1") - .setSource(XContentFactory.jsonBuilder().startObject().endObject()) - .setTimeout(timeout) - .get(); - fail("Expected ClusterBlockException"); - } catch (ClusterBlockException e) { - assertThat(e.status(), equalTo(RestStatus.SERVICE_UNAVAILABLE)); - } - - internalCluster().clearDisruptionScheme(true); - } - - public void testNoMasterActionsMetadataWriteMasterBlock() throws Exception { - Settings settings = Settings.builder() - .put(NoMasterBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "metadata_write") - .put(MappingUpdatedAction.INDICES_MAPPING_DYNAMIC_TIMEOUT_SETTING.getKey(), "100ms") - .build(); - - final List nodes = internalCluster().startNodes(3, settings); - - prepareCreate("test1").setSettings( - Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) - ).get(); - client().admin().cluster().prepareHealth("_all").setWaitForGreenStatus().get(); - client().prepareIndex("test1").setId("1").setSource("field", "value1").get(); - refresh(); - - ensureGreen("test1"); - - ClusterStateResponse clusterState = client().admin().cluster().prepareState().get(); - logger.info("Cluster state:\n{}", clusterState.getState()); - - final List nodesWithShards = clusterState.getState() - .routingTable() - .index("test1") - .shard(0) - .activeShards() - .stream() - .map(shardRouting -> shardRouting.currentNodeId()) - .map(nodeId -> clusterState.getState().nodes().resolveNode(nodeId)) - .map(DiscoveryNode::getName) - .collect(Collectors.toList()); - - client().execute( - AddVotingConfigExclusionsAction.INSTANCE, - new AddVotingConfigExclusionsRequest(nodesWithShards.toArray(new String[0])) - ).get(); - ensureGreen("test1"); - - String partitionedNode = nodes.stream().filter(n -> nodesWithShards.contains(n) == false).findFirst().get(); - - final NetworkDisruption disruptionScheme = new NetworkDisruption( - new NetworkDisruption.TwoPartitions(Collections.singleton(partitionedNode), new HashSet<>(nodesWithShards)), - NetworkDisruption.DISCONNECT - ); - internalCluster().setDisruptionScheme(disruptionScheme); - disruptionScheme.startDisrupting(); - - assertBusy(() -> { - for (String node : nodesWithShards) { - ClusterState state = client(node).admin().cluster().prepareState().setLocal(true).get().getState(); - assertTrue(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID)); - } - }); - - GetResponse getResponse = client(randomFrom(nodesWithShards)).prepareGet("test1", "1").get(); - assertExists(getResponse); - - expectThrows(Exception.class, () -> client(partitionedNode).prepareGet("test1", "1").get()); - - SearchResponse countResponse = client(randomFrom(nodesWithShards)).prepareSearch("test1") - .setAllowPartialSearchResults(true) - .setSize(0) - .get(); - assertHitCount(countResponse, 1L); - - expectThrows( - Exception.class, - () -> client(partitionedNode).prepareSearch("test1").setAllowPartialSearchResults(true).setSize(0).get() - ); - - TimeValue timeout = TimeValue.timeValueMillis(200); - client(randomFrom(nodesWithShards)).prepareUpdate("test1", "1") - .setDoc(Requests.INDEX_CONTENT_TYPE, "field", "value2") - .setTimeout(timeout) - .get(); - - expectThrows( - Exception.class, - () -> client(partitionedNode).prepareUpdate("test1", "1") - .setDoc(Requests.INDEX_CONTENT_TYPE, "field", "value2") - .setTimeout(timeout) - .get() - ); - - client(randomFrom(nodesWithShards)).prepareIndex("test1") - .setId("1") - .setSource(XContentFactory.jsonBuilder().startObject().endObject()) - .setTimeout(timeout) - .get(); - - // dynamic mapping updates fail - expectThrows( - MasterNotDiscoveredException.class, - () -> client(randomFrom(nodesWithShards)).prepareIndex("test1") - .setId("1") - .setSource(XContentFactory.jsonBuilder().startObject().field("new_field", "value").endObject()) - .setTimeout(timeout) - .get() - ); - - // dynamic index creation fails - expectThrows( - MasterNotDiscoveredException.class, - () -> client(randomFrom(nodesWithShards)).prepareIndex("test2") - .setId("1") - .setSource(XContentFactory.jsonBuilder().startObject().endObject()) - .setTimeout(timeout) - .get() - ); - - expectThrows( - Exception.class, - () -> client(partitionedNode).prepareIndex("test1") - .setId("1") - .setSource(XContentFactory.jsonBuilder().startObject().endObject()) - .setTimeout(timeout) - .get() - ); - - internalCluster().clearDisruptionScheme(true); - } -} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/SimpleClusterStateIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/SimpleClusterStateIT.java index 19f7b0b4c630c..f0337e9c0c84c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/SimpleClusterStateIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/SimpleClusterStateIT.java @@ -45,17 +45,16 @@ import org.opensearch.cluster.routing.RoutingTable; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.UUIDs; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.index.IndexNotFoundException; @@ -65,10 +64,8 @@ import org.opensearch.repositories.RepositoriesService; import org.opensearch.script.ScriptService; import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.test.hamcrest.CollectionAssertions; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; - import org.junit.Before; import java.io.IOException; @@ -76,6 +73,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -85,6 +83,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; /** @@ -238,14 +237,14 @@ private void testFilteringByIndexWorks(String[] indices, String[] expected) { .setIndices(indices) .get(); - ImmutableOpenMap metadata = clusterState.getState().getMetadata().indices(); + final Map metadata = clusterState.getState().getMetadata().indices(); assertThat(metadata.size(), is(expected.length)); RoutingTable routingTable = clusterState.getState().getRoutingTable(); assertThat(routingTable.indicesRouting().size(), is(expected.length)); - for (String expectedIndex : expected) { - assertThat(metadata, CollectionAssertions.hasKey(expectedIndex)); + for (final String expectedIndex : expected) { + assertThat(metadata, hasKey(expectedIndex)); assertThat(routingTable.hasIndex(expectedIndex), is(true)); } } @@ -286,7 +285,12 @@ public void testLargeClusterStatePublishing() throws Exception { .get() ); ensureGreen(); // wait for green state, so its both green, and there are no more pending events - MappingMetadata masterMappingMetadata = client().admin().indices().prepareGetMappings("test").get().getMappings().get("test"); + MappingMetadata clusterManagerMappingMetadata = client().admin() + .indices() + .prepareGetMappings("test") + .get() + .getMappings() + .get("test"); for (Client client : clients()) { MappingMetadata mappingMetadata = client.admin() .indices() @@ -295,8 +299,8 @@ public void testLargeClusterStatePublishing() throws Exception { .get() .getMappings() .get("test"); - assertThat(mappingMetadata.source().string(), equalTo(masterMappingMetadata.source().string())); - assertThat(mappingMetadata, equalTo(masterMappingMetadata)); + assertThat(mappingMetadata.source().string(), equalTo(clusterManagerMappingMetadata.source().string())); + assertThat(mappingMetadata, equalTo(clusterManagerMappingMetadata)); } } @@ -468,7 +472,7 @@ public Collection createComponents( return; } - if (state.nodes().isLocalNodeElectedMaster()) { + if (state.nodes().isLocalNodeElectedClusterManager()) { if (state.custom("test") == null) { if (installed.compareAndSet(false, true)) { clusterService.submitStateUpdateTask("install-metadata-custom", new ClusterStateUpdateTask(Priority.URGENT) { diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/SimpleDataNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/SimpleDataNodesIT.java index d7adf57593953..4f7fda6b94e36 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/SimpleDataNodesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/SimpleDataNodesIT.java @@ -41,7 +41,7 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Priority; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; @@ -50,7 +50,6 @@ import static org.opensearch.common.unit.TimeValue.timeValueSeconds; import static org.opensearch.test.NodeRoles.dataNode; import static org.opensearch.test.NodeRoles.nonDataNode; - import static org.hamcrest.Matchers.equalTo; @ClusterScope(scope = Scope.TEST, numDataNodes = 0) @@ -62,7 +61,7 @@ public void testIndexingBeforeAndAfterDataNodesStart() { internalCluster().startNode(nonDataNode()); client().admin().indices().create(createIndexRequest("test").waitForActiveShards(ActiveShardCount.NONE)).actionGet(); try { - client().index(Requests.indexRequest("test").id("1").source(SOURCE, XContentType.JSON).timeout(timeValueSeconds(1))) + client().index(Requests.indexRequest("test").id("1").source(SOURCE, MediaTypeRegistry.JSON).timeout(timeValueSeconds(1))) .actionGet(); fail("no allocation should happen"); } catch (UnavailableShardsException e) { @@ -85,7 +84,7 @@ public void testIndexingBeforeAndAfterDataNodesStart() { // still no shard should be allocated try { - client().index(Requests.indexRequest("test").id("1").source(SOURCE, XContentType.JSON).timeout(timeValueSeconds(1))) + client().index(Requests.indexRequest("test").id("1").source(SOURCE, MediaTypeRegistry.JSON).timeout(timeValueSeconds(1))) .actionGet(); fail("no allocation should happen"); } catch (UnavailableShardsException e) { @@ -107,7 +106,8 @@ public void testIndexingBeforeAndAfterDataNodesStart() { equalTo(false) ); - IndexResponse indexResponse = client().index(Requests.indexRequest("test").id("1").source(SOURCE, XContentType.JSON)).actionGet(); + IndexResponse indexResponse = client().index(Requests.indexRequest("test").id("1").source(SOURCE, MediaTypeRegistry.JSON)) + .actionGet(); assertThat(indexResponse.getId(), equalTo("1")); } diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/SpecificClusterManagerNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/SpecificClusterManagerNodesIT.java new file mode 100644 index 0000000000000..713873bb222e2 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/SpecificClusterManagerNodesIT.java @@ -0,0 +1,339 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster; + +import org.apache.lucene.search.join.ScoreMode; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.common.settings.Settings; +import org.opensearch.discovery.ClusterManagerNotDiscoveredException; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; +import org.opensearch.test.OpenSearchIntegTestCase.Scope; + +import java.io.IOException; + +import static org.opensearch.test.NodeRoles.clusterManagerNode; +import static org.opensearch.test.NodeRoles.dataOnlyNode; +import static org.opensearch.test.NodeRoles.nonDataNode; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +@ClusterScope(scope = Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) +public class SpecificClusterManagerNodesIT extends OpenSearchIntegTestCase { + + public void testSimpleOnlyClusterManagerNodeElection() throws IOException { + internalCluster().setBootstrapClusterManagerNodeIndex(0); + logger.info("--> start data node / non cluster-manager node"); + internalCluster().startNode(Settings.builder().put(dataOnlyNode()).put("discovery.initial_state_timeout", "1s")); + try { + assertThat( + client().admin() + .cluster() + .prepareState() + .setClusterManagerNodeTimeout("100ms") + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNodeId(), + nullValue() + ); + fail("should not be able to find cluster-manager"); + } catch (ClusterManagerNotDiscoveredException e) { + // all is well, no cluster-manager elected + } + logger.info("--> start cluster-manager node"); + final String clusterManagerNodeName = internalCluster().startClusterManagerOnlyNode(); + assertThat( + internalCluster().nonClusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(clusterManagerNodeName) + ); + assertThat( + internalCluster().clusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(clusterManagerNodeName) + ); + + logger.info("--> stop cluster-manager node"); + Settings clusterManagerDataPathSettings = internalCluster().dataPathSettings(internalCluster().getClusterManagerName()); + internalCluster().stopCurrentClusterManagerNode(); + + try { + assertThat( + client().admin() + .cluster() + .prepareState() + .setClusterManagerNodeTimeout("100ms") + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNodeId(), + nullValue() + ); + fail("should not be able to find cluster-manager"); + } catch (ClusterManagerNotDiscoveredException e) { + // all is well, no cluster-manager elected + } + + logger.info("--> start previous cluster-manager node again"); + final String nextClusterManagerEligibleNodeName = internalCluster().startNode( + Settings.builder().put(nonDataNode(clusterManagerNode())).put(clusterManagerDataPathSettings) + ); + assertThat( + internalCluster().nonClusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(nextClusterManagerEligibleNodeName) + ); + assertThat( + internalCluster().clusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(nextClusterManagerEligibleNodeName) + ); + } + + public void testElectOnlyBetweenClusterManagerNodes() throws Exception { + internalCluster().setBootstrapClusterManagerNodeIndex(0); + logger.info("--> start data node / non cluster-manager node"); + internalCluster().startNode(Settings.builder().put(dataOnlyNode()).put("discovery.initial_state_timeout", "1s")); + try { + assertThat( + client().admin() + .cluster() + .prepareState() + .setClusterManagerNodeTimeout("100ms") + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNodeId(), + nullValue() + ); + fail("should not be able to find cluster-manager"); + } catch (ClusterManagerNotDiscoveredException e) { + // all is well, no cluster-manager elected + } + logger.info("--> start cluster-manager node (1)"); + final String clusterManagerNodeName = internalCluster().startClusterManagerOnlyNode(); + assertThat( + internalCluster().nonClusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(clusterManagerNodeName) + ); + assertThat( + internalCluster().clusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(clusterManagerNodeName) + ); + + logger.info("--> start cluster-manager node (2)"); + final String nextClusterManagerEligableNodeName = internalCluster().startClusterManagerOnlyNode(); + assertThat( + internalCluster().nonClusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(clusterManagerNodeName) + ); + assertThat( + internalCluster().nonClusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(clusterManagerNodeName) + ); + assertThat( + internalCluster().clusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(clusterManagerNodeName) + ); + + logger.info("--> closing cluster-manager node (1)"); + client().execute(AddVotingConfigExclusionsAction.INSTANCE, new AddVotingConfigExclusionsRequest(clusterManagerNodeName)).get(); + // removing the cluster-manager from the voting configuration immediately triggers the cluster-manager to step down + assertBusy(() -> { + assertThat( + internalCluster().nonClusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(nextClusterManagerEligableNodeName) + ); + assertThat( + internalCluster().clusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(nextClusterManagerEligableNodeName) + ); + }); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(clusterManagerNodeName)); + assertThat( + internalCluster().nonClusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(nextClusterManagerEligableNodeName) + ); + assertThat( + internalCluster().clusterManagerClient() + .admin() + .cluster() + .prepareState() + .execute() + .actionGet() + .getState() + .nodes() + .getClusterManagerNode() + .getName(), + equalTo(nextClusterManagerEligableNodeName) + ); + } + + public void testAliasFilterValidation() { + internalCluster().setBootstrapClusterManagerNodeIndex(0); + logger.info("--> start cluster-manager node / non data"); + internalCluster().startClusterManagerOnlyNode(); + + logger.info("--> start data node / non cluster-manager node"); + internalCluster().startDataOnlyNode(); + + assertAcked( + prepareCreate("test").setMapping( + "{\"properties\" : {\"table_a\" : { \"type\" : \"nested\", " + + "\"properties\" : {\"field_a\" : { \"type\" : \"keyword\" },\"field_b\" :{ \"type\" : \"keyword\" }}}}}" + ) + ); + client().admin() + .indices() + .prepareAliases() + .addAlias( + "test", + "a_test", + QueryBuilders.nestedQuery("table_a", QueryBuilders.termQuery("table_a.field_b", "y"), ScoreMode.Avg) + ) + .get(); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/SpecificMasterNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/SpecificMasterNodesIT.java deleted file mode 100644 index fc193163f75cc..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/SpecificMasterNodesIT.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.cluster; - -import org.apache.lucene.search.join.ScoreMode; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; -import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; -import org.opensearch.common.settings.Settings; -import org.opensearch.discovery.MasterNotDiscoveredException; -import org.opensearch.index.query.QueryBuilders; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; -import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalTestCluster; - -import java.io.IOException; - -import static org.opensearch.test.NodeRoles.dataOnlyNode; -import static org.opensearch.test.NodeRoles.masterNode; -import static org.opensearch.test.NodeRoles.nonDataNode; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.nullValue; - -@ClusterScope(scope = Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) -public class SpecificMasterNodesIT extends OpenSearchIntegTestCase { - - public void testSimpleOnlyMasterNodeElection() throws IOException { - internalCluster().setBootstrapMasterNodeIndex(0); - logger.info("--> start data node / non master node"); - internalCluster().startNode(Settings.builder().put(dataOnlyNode()).put("discovery.initial_state_timeout", "1s")); - try { - assertThat( - client().admin() - .cluster() - .prepareState() - .setMasterNodeTimeout("100ms") - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNodeId(), - nullValue() - ); - fail("should not be able to find master"); - } catch (MasterNotDiscoveredException e) { - // all is well, no master elected - } - logger.info("--> start master node"); - final String masterNodeName = internalCluster().startMasterOnlyNode(); - assertThat( - internalCluster().nonMasterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(masterNodeName) - ); - assertThat( - internalCluster().masterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(masterNodeName) - ); - - logger.info("--> stop master node"); - Settings masterDataPathSettings = internalCluster().dataPathSettings(internalCluster().getMasterName()); - internalCluster().stopCurrentMasterNode(); - - try { - assertThat( - client().admin() - .cluster() - .prepareState() - .setMasterNodeTimeout("100ms") - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNodeId(), - nullValue() - ); - fail("should not be able to find master"); - } catch (MasterNotDiscoveredException e) { - // all is well, no master elected - } - - logger.info("--> start previous master node again"); - final String nextMasterEligibleNodeName = internalCluster().startNode( - Settings.builder().put(nonDataNode(masterNode())).put(masterDataPathSettings) - ); - assertThat( - internalCluster().nonMasterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(nextMasterEligibleNodeName) - ); - assertThat( - internalCluster().masterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(nextMasterEligibleNodeName) - ); - } - - public void testElectOnlyBetweenMasterNodes() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); - logger.info("--> start data node / non master node"); - internalCluster().startNode(Settings.builder().put(dataOnlyNode()).put("discovery.initial_state_timeout", "1s")); - try { - assertThat( - client().admin() - .cluster() - .prepareState() - .setMasterNodeTimeout("100ms") - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNodeId(), - nullValue() - ); - fail("should not be able to find master"); - } catch (MasterNotDiscoveredException e) { - // all is well, no master elected - } - logger.info("--> start master node (1)"); - final String masterNodeName = internalCluster().startMasterOnlyNode(); - assertThat( - internalCluster().nonMasterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(masterNodeName) - ); - assertThat( - internalCluster().masterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(masterNodeName) - ); - - logger.info("--> start master node (2)"); - final String nextMasterEligableNodeName = internalCluster().startMasterOnlyNode(); - assertThat( - internalCluster().nonMasterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(masterNodeName) - ); - assertThat( - internalCluster().nonMasterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(masterNodeName) - ); - assertThat( - internalCluster().masterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(masterNodeName) - ); - - logger.info("--> closing master node (1)"); - client().execute(AddVotingConfigExclusionsAction.INSTANCE, new AddVotingConfigExclusionsRequest(masterNodeName)).get(); - // removing the master from the voting configuration immediately triggers the master to step down - assertBusy(() -> { - assertThat( - internalCluster().nonMasterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(nextMasterEligableNodeName) - ); - assertThat( - internalCluster().masterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(nextMasterEligableNodeName) - ); - }); - internalCluster().stopRandomNode(InternalTestCluster.nameFilter(masterNodeName)); - assertThat( - internalCluster().nonMasterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(nextMasterEligableNodeName) - ); - assertThat( - internalCluster().masterClient() - .admin() - .cluster() - .prepareState() - .execute() - .actionGet() - .getState() - .nodes() - .getMasterNode() - .getName(), - equalTo(nextMasterEligableNodeName) - ); - } - - public void testAliasFilterValidation() { - internalCluster().setBootstrapMasterNodeIndex(0); - logger.info("--> start master node / non data"); - internalCluster().startMasterOnlyNode(); - - logger.info("--> start data node / non master node"); - internalCluster().startDataOnlyNode(); - - assertAcked( - prepareCreate("test").setMapping( - "{\"properties\" : {\"table_a\" : { \"type\" : \"nested\", " - + "\"properties\" : {\"field_a\" : { \"type\" : \"keyword\" },\"field_b\" :{ \"type\" : \"keyword\" }}}}}" - ) - ); - client().admin() - .indices() - .prepareAliases() - .addAlias( - "test", - "a_test", - QueryBuilders.nestedQuery("table_a", QueryBuilders.termQuery("table_a.field_b", "y"), ScoreMode.Avg) - ) - .get(); - } - -} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/UpdateSettingsValidationIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/UpdateSettingsValidationIT.java index 66f22a4fdf1b3..4b830acddefdf 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/UpdateSettingsValidationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/UpdateSettingsValidationIT.java @@ -39,14 +39,14 @@ import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import static org.opensearch.test.NodeRoles.nonClusterManagerNode; import static org.opensearch.test.NodeRoles.nonDataNode; -import static org.opensearch.test.NodeRoles.nonMasterNode; import static org.hamcrest.Matchers.equalTo; @ClusterScope(scope = Scope.TEST, numDataNodes = 0) public class UpdateSettingsValidationIT extends OpenSearchIntegTestCase { public void testUpdateSettingsValidation() throws Exception { - internalCluster().startNodes(nonDataNode(), nonMasterNode(), nonMasterNode()); + internalCluster().startNodes(nonDataNode(), nonClusterManagerNode(), nonClusterManagerNode()); createIndex("test"); NumShards test = getNumShards("test"); diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/action/shard/ShardStateActionIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/action/shard/ShardStateActionIT.java index dc1814c132d96..90eb3aa97a050 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/action/shard/ShardStateActionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/action/shard/ShardStateActionIT.java @@ -32,7 +32,6 @@ package org.opensearch.cluster.action.shard; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; import org.opensearch.cluster.ClusterState; @@ -40,6 +39,7 @@ import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; @@ -119,10 +119,13 @@ public void testFollowupRerouteCanBeSetToHigherPriority() { .setPersistentSettings(Settings.builder().put(ShardStateAction.FOLLOW_UP_REROUTE_PRIORITY_SETTING.getKey(), "urgent")) ); - // ensure that the master always has a HIGH priority pending task - final AtomicBoolean stopSpammingMaster = new AtomicBoolean(); - final ClusterService masterClusterService = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()); - masterClusterService.submitStateUpdateTask("spam", new ClusterStateUpdateTask(Priority.HIGH) { + // ensure that the cluster-manager always has a HIGH priority pending task + final AtomicBoolean stopSpammingClusterManager = new AtomicBoolean(); + final ClusterService clusterManagerClusterService = internalCluster().getInstance( + ClusterService.class, + internalCluster().getClusterManagerName() + ); + clusterManagerClusterService.submitStateUpdateTask("spam", new ClusterStateUpdateTask(Priority.HIGH) { @Override public ClusterState execute(ClusterState currentState) { return currentState; @@ -135,18 +138,18 @@ public void onFailure(String source, Exception e) { @Override public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - if (stopSpammingMaster.get() == false) { - masterClusterService.submitStateUpdateTask("spam", this); + if (stopSpammingClusterManager.get() == false) { + clusterManagerClusterService.submitStateUpdateTask("spam", this); } } }); - // even with the master under such pressure, all shards of the index can be assigned; in particular, after the primaries have - // started there's a follow-up reroute at a higher priority than the spam + // even with the cluster-manager under such pressure, all shards of the index can be assigned; + // in particular, after the primaries have started there's a follow-up reroute at a higher priority than the spam createIndex("test"); assertFalse(client().admin().cluster().prepareHealth().setWaitForGreenStatus().get().isTimedOut()); - stopSpammingMaster.set(true); + stopSpammingClusterManager.set(true); assertFalse(client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get().isTimedOut()); assertAcked( diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/AwarenessAllocationIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/AwarenessAllocationIT.java index 224db09d99a99..5c8fc82a1c2b5 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/AwarenessAllocationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/AwarenessAllocationIT.java @@ -32,27 +32,33 @@ package org.opensearch.cluster.allocation; -import com.carrotsearch.hppc.ObjectIntHashMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.IndexMetadata.State; import org.opensearch.cluster.routing.IndexRoutingTable; import org.opensearch.cluster.routing.IndexShardRoutingTable; import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.allocation.AwarenessReplicaBalance; import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; import org.opensearch.common.Priority; import org.opensearch.common.settings.Settings; +import org.opensearch.test.InternalTestCluster; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static org.opensearch.cluster.routing.ShardRoutingState.STARTED; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.empty; @@ -74,6 +80,12 @@ public void testSimpleAwareness() throws Exception { logger.info("--> starting 2 nodes on the same rack"); internalCluster().startNodes(2, Settings.builder().put(commonSettings).put("node.attr.rack_id", "rack_1").build()); + Settings settings = Settings.builder() + .put(AwarenessReplicaBalance.CLUSTER_ROUTING_ALLOCATION_AWARENESS_BALANCE_SETTING.getKey(), false) + .build(); + ClusterUpdateSettingsRequest updateSettingsRequest = new ClusterUpdateSettingsRequest(); + updateSettingsRequest.persistentSettings(settings); + createIndex("test1"); createIndex("test2"); @@ -115,11 +127,11 @@ public void testSimpleAwareness() throws Exception { assertThat("Some indices not closed", notClosedIndices, empty()); // verify that we have all the primaries on node3 - ObjectIntHashMap counts = new ObjectIntHashMap<>(); + final Map counts = new HashMap<>(); for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { for (IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { for (ShardRouting shardRouting : indexShardRoutingTable) { - counts.addTo(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1); + counts.merge(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1, Integer::sum); } } } @@ -171,12 +183,12 @@ public void testAwarenessZones() { assertThat(health.isTimedOut(), equalTo(false)); ClusterState clusterState = client().admin().cluster().prepareState().execute().actionGet().getState(); - ObjectIntHashMap counts = new ObjectIntHashMap<>(); + final Map counts = new HashMap<>(); for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { for (IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { for (ShardRouting shardRouting : indexShardRoutingTable) { - counts.addTo(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1); + counts.merge(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1, Integer::sum); } } } @@ -221,12 +233,12 @@ public void testAwarenessZonesIncrementalNodes() { .actionGet(); assertThat(health.isTimedOut(), equalTo(false)); ClusterState clusterState = client().admin().cluster().prepareState().execute().actionGet().getState(); - ObjectIntHashMap counts = new ObjectIntHashMap<>(); + Map counts = new HashMap<>(); for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { for (IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { for (ShardRouting shardRouting : indexShardRoutingTable) { - counts.addTo(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1); + counts.merge(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1, Integer::sum); } } } @@ -261,12 +273,12 @@ public void testAwarenessZonesIncrementalNodes() { assertThat(health.isTimedOut(), equalTo(false)); clusterState = client().admin().cluster().prepareState().execute().actionGet().getState(); - counts = new ObjectIntHashMap<>(); + counts = new HashMap<>(); for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { for (IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { for (ShardRouting shardRouting : indexShardRoutingTable) { - counts.addTo(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1); + counts.merge(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1, Integer::sum); } } } @@ -301,12 +313,12 @@ public void testAwarenessZonesIncrementalNodes() { assertThat(health.isTimedOut(), equalTo(false)); clusterState = client().admin().cluster().prepareState().execute().actionGet().getState(); - counts = new ObjectIntHashMap<>(); + counts = new HashMap<>(); for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { for (IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { for (ShardRouting shardRouting : indexShardRoutingTable) { - counts.addTo(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1); + counts.merge(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1, Integer::sum); } } } @@ -336,12 +348,12 @@ public void testAwarenessZonesIncrementalNodes() { assertThat(health.isTimedOut(), equalTo(false)); clusterState = client().admin().cluster().prepareState().execute().actionGet().getState(); - counts = new ObjectIntHashMap<>(); + counts = new HashMap<>(); for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { for (IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { for (ShardRouting shardRouting : indexShardRoutingTable) { - counts.addTo(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1); + counts.merge(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1, Integer::sum); } } } @@ -351,4 +363,140 @@ public void testAwarenessZonesIncrementalNodes() { assertThat(counts.get(B_1), equalTo(2)); assertThat(counts.get(noZoneNode), equalTo(2)); } + + public void testThreeZoneOneReplicaWithForceZoneValueAndLoadAwareness() throws Exception { + int nodeCountPerAZ = 5; + int numOfShards = 30; + int numOfReplica = 1; + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.allocation.load_awareness.skew_factor", "0.0") + .put("cluster.routing.allocation.load_awareness.provisioned_capacity", Integer.toString(nodeCountPerAZ * 3)) + .build(); + + logger.info("--> starting 15 nodes on zones 'a' & 'b' & 'c'"); + List nodes_in_zone_a = internalCluster().startNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build() + ); + List nodes_in_zone_b = internalCluster().startNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build() + ); + List nodes_in_zone_c = internalCluster().startNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + // Creating index with 30 primary and 1 replica + createIndex( + "test-1", + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numOfShards) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numOfReplica) + .build() + ); + + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setIndices("test-1") + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(nodeCountPerAZ * 3)) + .setWaitForNoRelocatingShards(true) + .setWaitForNoInitializingShards(true) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + ClusterState clusterState = client().admin().cluster().prepareState().execute().actionGet().getState(); + final Map counts = new HashMap<>(); + + for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { + for (IndexShardRoutingTable indexShardRoutingTable : indexRoutingTable) { + for (ShardRouting shardRouting : indexShardRoutingTable) { + counts.merge(clusterState.nodes().get(shardRouting.currentNodeId()).getName(), 1, Integer::sum); + } + } + } + + assertThat(counts.size(), equalTo(nodeCountPerAZ * 3)); + // All shards should be started + assertThat(clusterState.getRoutingNodes().shardsWithState(STARTED).size(), equalTo(numOfShards * (numOfReplica + 1))); + + // stopping half nodes in zone a + int nodesToStop = nodeCountPerAZ / 2; + List nodeDataPathSettings = new ArrayList<>(); + for (int i = 0; i < nodesToStop; i++) { + nodeDataPathSettings.add(internalCluster().dataPathSettings(nodes_in_zone_a.get(i))); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodes_in_zone_a.get(i))); + } + + client().admin().cluster().prepareReroute().setRetryFailed(true).get(); + health = client().admin() + .cluster() + .prepareHealth() + .setIndices("test-1") + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes(Integer.toString(nodeCountPerAZ * 3 - nodesToStop)) + .setWaitForNoRelocatingShards(true) + .setWaitForNoInitializingShards(true) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + // Creating another index with 30 primary and 1 replica + createIndex( + "test-2", + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numOfShards) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numOfReplica) + .build() + ); + + health = client().admin() + .cluster() + .prepareHealth() + .setIndices("test-1", "test-2") + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes(Integer.toString(nodeCountPerAZ * 3 - nodesToStop)) + .setWaitForNoRelocatingShards(true) + .setWaitForNoInitializingShards(true) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + // Restarting the nodes back + for (int i = 0; i < nodesToStop; i++) { + internalCluster().startNode( + Settings.builder() + .put("node.name", nodes_in_zone_a.get(i)) + .put(nodeDataPathSettings.get(i)) + .put(commonSettings) + .put("node.attr.zone", "a") + .build() + ); + } + client().admin().cluster().prepareReroute().setRetryFailed(true).get(); + + health = client().admin() + .cluster() + .prepareHealth() + .setIndices("test-1", "test-2") + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes(Integer.toString(nodeCountPerAZ * 3)) + .setWaitForGreenStatus() + .setWaitForActiveShards(2 * numOfShards * (numOfReplica + 1)) + .setWaitForNoRelocatingShards(true) + .setWaitForNoInitializingShards(true) + .execute() + .actionGet(); + clusterState = client().admin().cluster().prepareState().execute().actionGet().getState(); + + // All shards should be started now and cluster health should be green + assertThat(clusterState.getRoutingNodes().shardsWithState(STARTED).size(), equalTo(2 * numOfShards * (numOfReplica + 1))); + assertThat(health.isTimedOut(), equalTo(false)); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/ClusterRerouteIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/ClusterRerouteIT.java index ee2a8784fa0ed..dbcb030d8a4f7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/ClusterRerouteIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/ClusterRerouteIT.java @@ -58,18 +58,18 @@ import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider.Allocation; import org.opensearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; import org.opensearch.common.Priority; -import org.opensearch.common.io.FileSystemUtils; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.util.FileSystemUtils; import org.opensearch.env.NodeEnvironment; -import org.opensearch.index.Index; -import org.opensearch.index.shard.ShardId; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.MockLogAppender; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalTestCluster; -import org.opensearch.test.MockLogAppender; import java.nio.file.Path; import java.util.Arrays; diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/FilteringAllocationIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/FilteringAllocationIT.java index 398adbd0d1ca5..ff95cca5ffde9 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/FilteringAllocationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/allocation/FilteringAllocationIT.java @@ -42,9 +42,9 @@ import org.opensearch.cluster.routing.ShardRoutingState; import org.opensearch.cluster.routing.allocation.decider.FilterAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.index.query.QueryBuilders; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/AwarenessAttributeDecommissionIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/AwarenessAttributeDecommissionIT.java new file mode 100644 index 0000000000000..b33d57ed43189 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/AwarenessAttributeDecommissionIT.java @@ -0,0 +1,1157 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.coordination; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LogEvent; +import org.opensearch.OpenSearchTimeoutException; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingResponse; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.cluster.decommission.DecommissioningFailedException; +import org.opensearch.cluster.decommission.NodeDecommissionedException; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.routing.WeightedRouting; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.Priority; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.discovery.Discovery; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.MockLogAppender; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.RemoteTransportException; +import org.opensearch.transport.Transport; +import org.opensearch.transport.TransportService; +import org.junit.After; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; + +import static org.opensearch.test.NodeRoles.onlyRole; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoTimeout; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class AwarenessAttributeDecommissionIT extends OpenSearchIntegTestCase { + private final Logger logger = LogManager.getLogger(AwarenessAttributeDecommissionIT.class); + + @Override + protected Collection> nodePlugins() { + return Collections.singletonList(MockTransportService.TestPlugin.class); + } + + @After + public void cleanup() throws Exception { + assertNoTimeout(client().admin().cluster().prepareHealth().get()); + } + + public void testDecommissionFailedWhenNotZoneAware() throws Exception { + Settings commonSettings = Settings.builder().build(); + // Start 3 cluster manager eligible nodes + internalCluster().startClusterManagerOnlyNodes(3, Settings.builder().put(commonSettings).build()); + // start 3 data nodes + internalCluster().startDataOnlyNodes(3, Settings.builder().put(commonSettings).build()); + ensureStableCluster(6); + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(6)) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "zone-1"); + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + assertBusy(() -> { + DecommissioningFailedException ex = expectThrows( + DecommissioningFailedException.class, + () -> client().execute(DecommissionAction.INSTANCE, decommissionRequest).actionGet() + ); + assertTrue(ex.getMessage().contains("invalid awareness attribute requested for decommissioning")); + }); + } + + public void testDecommissionFailedWhenNotForceZoneAware() throws Exception { + Settings commonSettings = Settings.builder().put("cluster.routing.allocation.awareness.attributes", "zone").build(); + // Start 3 cluster manager eligible nodes + logger.info("--> start 3 cluster manager nodes on zones 'a' & 'b' & 'c'"); + internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + logger.info("--> starting data node each on zones 'a' & 'b' & 'c'"); + internalCluster().startDataOnlyNode(Settings.builder().put(commonSettings).put("node.attr.zone", "a").build()); + internalCluster().startDataOnlyNode(Settings.builder().put(commonSettings).put("node.attr.zone", "b").build()); + internalCluster().startDataOnlyNode(Settings.builder().put(commonSettings).put("node.attr.zone", "c").build()); + ensureStableCluster(6); + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(6)) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "a"); + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + assertBusy(() -> { + DecommissioningFailedException ex = expectThrows( + DecommissioningFailedException.class, + () -> client().execute(DecommissionAction.INSTANCE, decommissionRequest).actionGet() + ); + assertTrue(ex.getMessage().contains("doesn't have the decommissioning attribute")); + }); + } + + public void testNodesRemovedAfterZoneDecommission_ClusterManagerNotInToBeDecommissionedZone() throws Exception { + assertNodesRemovedAfterZoneDecommission(false); + } + + public void testNodesRemovedAfterZoneDecommission_ClusterManagerInToBeDecommissionedZone() throws Exception { + assertNodesRemovedAfterZoneDecommission(true); + } + + public void testInvariantsAndLogsOnDecommissionedNodes() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'a' & 'b' & 'c'"); + List clusterManagerNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + logger.info("--> start 3 data nodes on zones 'a' & 'b' & 'c'"); + List dataNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build() + ); + + ensureStableCluster(6); + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(6)) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 0.0, "b", 1.0, "c", 1.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + logger.info("--> starting decommissioning nodes in zone {}", 'a'); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "a"); + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + decommissionRequest.setNoDelay(true); + DecommissionResponse decommissionResponse = client().execute(DecommissionAction.INSTANCE, decommissionRequest).get(); + assertTrue(decommissionResponse.isAcknowledged()); + + // Will wait for all events to complete + client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get(); + + String decommissionedNode = randomFrom(clusterManagerNodes.get(0), dataNodes.get(0)); + String activeNode = dataNodes.get(1); + + ClusterService decommissionedNodeClusterService = internalCluster().getInstance(ClusterService.class, decommissionedNode); + DecommissionAttributeMetadata metadata = decommissionedNodeClusterService.state() + .metadata() + .custom(DecommissionAttributeMetadata.TYPE); + // The decommissioned node would not be having status as SUCCESS as it was kicked out later + // and not receiving any further state updates + // This also helps to test metadata status updates was received by this node until it got kicked by the leader + assertEquals(metadata.decommissionAttribute(), decommissionAttribute); + assertNotNull(metadata.status()); + assertEquals(metadata.status(), DecommissionStatus.IN_PROGRESS); + + // assert the node has decommissioned attribute + assertEquals(decommissionedNodeClusterService.localNode().getAttributes().get("zone"), "a"); + + // assert exception on decommissioned node + Logger clusterLogger = LogManager.getLogger(JoinHelper.class); + MockLogAppender mockLogAppender = MockLogAppender.createForLoggers(clusterLogger); + mockLogAppender.addExpectation( + new MockLogAppender.PatternSeenEventExpectation( + "test", + JoinHelper.class.getCanonicalName(), + Level.INFO, + "local node is decommissioned \\[.*]\\. Will not be able to join the cluster" + ) + ); + mockLogAppender.addExpectation( + new MockLogAppender.SeenEventExpectation("test", JoinHelper.class.getCanonicalName(), Level.INFO, "failed to join") { + @Override + public boolean innerMatch(LogEvent event) { + return event.getThrown() != null + && event.getThrown().getClass() == RemoteTransportException.class + && event.getThrown().getCause() != null + && event.getThrown().getCause().getClass() == NodeDecommissionedException.class; + } + } + ); + TransportService clusterManagerTransportService = internalCluster().getInstance( + TransportService.class, + internalCluster().getClusterManagerName(activeNode) + ); + MockTransportService decommissionedNodeTransportService = (MockTransportService) internalCluster().getInstance( + TransportService.class, + decommissionedNode + ); + final CountDownLatch countDownLatch = new CountDownLatch(2); + decommissionedNodeTransportService.addSendBehavior( + clusterManagerTransportService, + (connection, requestId, action, request, options) -> { + if (action.equals(JoinHelper.JOIN_ACTION_NAME)) { + countDownLatch.countDown(); + } + connection.sendRequest(requestId, action, request, options); + } + ); + decommissionedNodeTransportService.addConnectBehavior(clusterManagerTransportService, Transport::openConnection); + countDownLatch.await(); + mockLogAppender.assertAllExpectationsMatched(); + + // decommissioned node should have Coordinator#localNodeCommissioned = false + Coordinator coordinator = (Coordinator) internalCluster().getInstance(Discovery.class, decommissionedNode); + assertFalse(coordinator.localNodeCommissioned()); + + // Check cluster health API for decommissioned and active node + ClusterHealthResponse activeNodeLocalHealth = client(activeNode).admin() + .cluster() + .prepareHealth() + .setLocal(true) + .setEnsureNodeWeighedIn(true) + .execute() + .actionGet(); + assertFalse(activeNodeLocalHealth.isTimedOut()); + + ClusterHealthResponse decommissionedNodeLocalHealth = client(decommissionedNode).admin() + .cluster() + .prepareHealth() + .setLocal(true) + .execute() + .actionGet(); + assertFalse(decommissionedNodeLocalHealth.isTimedOut()); + + NodeDecommissionedException ex = expectThrows( + NodeDecommissionedException.class, + () -> client(decommissionedNode).admin() + .cluster() + .prepareHealth() + .setLocal(true) + .setEnsureNodeWeighedIn(true) + .execute() + .actionGet() + ); + assertTrue(ex.getMessage().contains("local node is decommissioned")); + + // Recommissioning the zone back to gracefully succeed the test once above tests succeeds + DeleteDecommissionStateResponse deleteDecommissionStateResponse = client(activeNode).execute( + DeleteDecommissionStateAction.INSTANCE, + new DeleteDecommissionStateRequest() + ).get(); + assertTrue(deleteDecommissionStateResponse.isAcknowledged()); + + ClusterService activeNodeClusterService = internalCluster().getInstance(ClusterService.class, activeNode); + ClusterStateObserver clusterStateObserver = new ClusterStateObserver( + activeNodeClusterService, + null, + logger, + client(activeNode).threadPool().getThreadContext() + ); + CountDownLatch expectedStateLatch = new CountDownLatch(1); + Predicate expectedClusterStatePredicate = clusterState -> { + if (clusterState.metadata().decommissionAttributeMetadata() != null) return false; + if (clusterState.metadata().coordinationMetadata().getVotingConfigExclusions().isEmpty() == false) return false; + if (clusterState.nodes().getNodes().size() != 6) return false; + return clusterState.metadata().coordinationMetadata().getLastCommittedConfiguration().getNodeIds().size() == 3; + }; + + ClusterState currentState = activeNodeClusterService.state(); + if (expectedClusterStatePredicate.test(currentState)) { + logger.info("cluster restored"); + expectedStateLatch.countDown(); + } else { + clusterStateObserver.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + logger.info("cluster restored"); + expectedStateLatch.countDown(); + } + + @Override + public void onClusterServiceClose() { + throw new AssertionError("unexpected close"); + } + + @Override + public void onTimeout(TimeValue timeout) { + throw new AssertionError("unexpected timeout"); + } + }, expectedClusterStatePredicate); + } + // if the below condition is passed, then we are sure that config size is restored + assertTrue(expectedStateLatch.await(180, TimeUnit.SECONDS)); + // will wait for cluster to stabilise with a timeout of 2 min as by then all nodes should have joined the cluster + ensureStableCluster(6); + } + + private void assertNodesRemovedAfterZoneDecommission(boolean originalClusterManagerDecommission) throws Exception { + int dataNodeCountPerAZ = 4; + List zones = new ArrayList<>(Arrays.asList("a", "b", "c")); + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'a' & 'b' & 'c'"); + List clusterManagerNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + Map clusterManagerNameToZone = new HashMap<>(); + clusterManagerNameToZone.put(clusterManagerNodes.get(0), "a"); + clusterManagerNameToZone.put(clusterManagerNodes.get(1), "b"); + clusterManagerNameToZone.put(clusterManagerNodes.get(2), "c"); + + logger.info("--> starting 4 data nodes each on zones 'a' & 'b' & 'c'"); + Map> zoneToNodesMap = new HashMap<>(); + zoneToNodesMap.put( + "a", + internalCluster().startDataOnlyNodes( + dataNodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build() + ) + ); + zoneToNodesMap.put( + "b", + internalCluster().startDataOnlyNodes( + dataNodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build() + ) + ); + zoneToNodesMap.put( + "c", + internalCluster().startDataOnlyNodes( + dataNodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ) + ); + ensureStableCluster(15); + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(15)) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + String originalClusterManager = internalCluster().getClusterManagerName(); + String originalClusterManagerZone = clusterManagerNameToZone.get(originalClusterManager); + logger.info("--> original cluster manager - name {}, zone {}", originalClusterManager, originalClusterManagerZone); + + String zoneToDecommission = originalClusterManagerZone; + + if (originalClusterManagerDecommission == false) { + // decommission one zone where active cluster manager is not present + List tempZones = new ArrayList<>(zones); + tempZones.remove(originalClusterManagerZone); + zoneToDecommission = randomFrom(tempZones); + } + String activeNode; + switch (zoneToDecommission) { + case "a": + activeNode = randomFrom(randomFrom(zoneToNodesMap.get("b")), randomFrom(zoneToNodesMap.get("c"))); + break; + case "b": + activeNode = randomFrom(randomFrom(zoneToNodesMap.get("a")), randomFrom(zoneToNodesMap.get("c"))); + break; + case "c": + activeNode = randomFrom(randomFrom(zoneToNodesMap.get("a")), randomFrom(zoneToNodesMap.get("b"))); + break; + default: + throw new IllegalStateException("unexpected zone decommissioned"); + } + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = new HashMap<>(Map.of("a", 1.0, "b", 1.0, "c", 1.0)); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + weights.put(zoneToDecommission, 0.0); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + logger.info("--> starting decommissioning nodes in zone {}", zoneToDecommission); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", zoneToDecommission); + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + decommissionRequest.setNoDelay(true); + DecommissionResponse decommissionResponse = client().execute(DecommissionAction.INSTANCE, decommissionRequest).get(); + assertTrue(decommissionResponse.isAcknowledged()); + + client(activeNode).admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get(); + + ClusterState clusterState = client(activeNode).admin().cluster().prepareState().execute().actionGet().getState(); + + // assert that number of nodes should be 10 ( 2 cluster manager nodes + 8 data nodes ) + assertEquals(clusterState.nodes().getNodes().size(), 10); + assertEquals(clusterState.nodes().getDataNodes().size(), 8); + assertEquals(clusterState.nodes().getClusterManagerNodes().size(), 2); + + Iterator discoveryNodeIterator = clusterState.nodes().getNodes().values().iterator(); + while (discoveryNodeIterator.hasNext()) { + // assert no node has decommissioned attribute + DiscoveryNode node = discoveryNodeIterator.next(); + assertNotEquals(node.getAttributes().get("zone"), zoneToDecommission); + + // assert no node is decommissioned from Coordinator#localNodeCommissioned + Coordinator coordinator = (Coordinator) internalCluster().getInstance(Discovery.class, node.getName()); + assertTrue(coordinator.localNodeCommissioned()); + } + + // assert that decommission status is successful + GetDecommissionStateResponse response = client(activeNode).execute( + GetDecommissionStateAction.INSTANCE, + new GetDecommissionStateRequest(decommissionAttribute.attributeName()) + ).get(); + assertEquals(response.getAttributeValue(), decommissionAttribute.attributeValue()); + assertEquals(response.getDecommissionStatus(), DecommissionStatus.SUCCESSFUL); + + // assert that no node present in Voting Config Exclusion + assertEquals(clusterState.metadata().coordinationMetadata().getVotingConfigExclusions().size(), 0); + + String currentClusterManager = internalCluster().getClusterManagerName(activeNode); + assertNotNull(currentClusterManager); + if (originalClusterManagerDecommission) { + // assert that cluster manager switched during the test + assertNotEquals(originalClusterManager, currentClusterManager); + } else { + // assert that cluster manager didn't switch during test + assertEquals(originalClusterManager, currentClusterManager); + } + + // Will wait for all events to complete + client(activeNode).admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get(); + + // Recommissioning the zone back to gracefully succeed the test once above tests succeeds + DeleteDecommissionStateResponse deleteDecommissionStateResponse = client(currentClusterManager).execute( + DeleteDecommissionStateAction.INSTANCE, + new DeleteDecommissionStateRequest() + ).get(); + assertTrue(deleteDecommissionStateResponse.isAcknowledged()); + + // will wait for cluster to stabilise with a timeout of 2 min as by then all nodes should have joined the cluster + ensureStableCluster(15, TimeValue.timeValueMinutes(2)); + } + + public void testDecommissionFailedWhenDifferentAttributeAlreadyDecommissioned() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'a' & 'b' & 'c'"); + internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + logger.info("--> starting 1 nodes each on zones 'a' & 'b' & 'c'"); + internalCluster().startDataOnlyNode(Settings.builder().put(commonSettings).put("node.attr.zone", "a").build()); + internalCluster().startDataOnlyNode(Settings.builder().put(commonSettings).put("node.attr.zone", "b").build()); + String node_in_c = internalCluster().startDataOnlyNode(Settings.builder().put(commonSettings).put("node.attr.zone", "c").build()); + ensureStableCluster(6); + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(6)) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 0.0, "b", 1.0, "c", 1.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + logger.info("--> starting decommissioning nodes in zone {}", 'a'); + DecommissionRequest decommissionRequest = new DecommissionRequest(new DecommissionAttribute("zone", "a")); + DecommissionResponse decommissionResponse = client().execute(DecommissionAction.INSTANCE, decommissionRequest).get(); + assertTrue(decommissionResponse.isAcknowledged()); + + DecommissionRequest newDecommissionRequest = new DecommissionRequest(new DecommissionAttribute("zone", "b")); + assertBusy( + () -> expectThrows( + DecommissioningFailedException.class, + () -> client(node_in_c).execute(DecommissionAction.INSTANCE, newDecommissionRequest).actionGet() + ) + ); + + // Will wait for all events to complete + client(node_in_c).admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get(); + + // Recommissioning the zone back to gracefully succeed the test once above tests succeeds + DeleteDecommissionStateResponse deleteDecommissionStateResponse = client(node_in_c).execute( + DeleteDecommissionStateAction.INSTANCE, + new DeleteDecommissionStateRequest() + ).get(); + assertTrue(deleteDecommissionStateResponse.isAcknowledged()); + + // will wait for cluster to stabilise with a timeout of 2 min as by then all nodes should have joined the cluster + ensureStableCluster(6, TimeValue.timeValueMinutes(2)); + } + + public void testDecommissionStatusUpdatePublishedToAllNodes() throws ExecutionException, InterruptedException { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'a' & 'b' & 'c'"); + List clusterManagerNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + + logger.info("--> start 3 data nodes on zones 'a' & 'b' & 'c'"); + List dataNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build() + ); + + ensureStableCluster(6); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + logger.info("--> starting decommissioning nodes in zone {}", 'c'); + String activeNode = randomFrom(dataNodes.get(0), dataNodes.get(1)); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "c"); + // Set the timeout to 0 to do immediate Decommission + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + decommissionRequest.setNoDelay(true); + DecommissionResponse decommissionResponse = client(activeNode).execute(DecommissionAction.INSTANCE, decommissionRequest).get(); + assertTrue(decommissionResponse.isAcknowledged()); + + // Will wait for all events to complete + client(activeNode).admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get(); + + logger.info("--> Received LANGUID event"); + + // assert that decommission status is successful + GetDecommissionStateResponse response = client(activeNode).execute( + GetDecommissionStateAction.INSTANCE, + new GetDecommissionStateRequest(decommissionAttribute.attributeName()) + ).get(); + assertEquals(response.getAttributeValue(), decommissionAttribute.attributeValue()); + assertEquals(DecommissionStatus.SUCCESSFUL, response.getDecommissionStatus()); + + logger.info("--> Decommission status is successful"); + ClusterState clusterState = client(activeNode).admin().cluster().prepareState().execute().actionGet().getState(); + assertEquals(4, clusterState.nodes().getSize()); + + logger.info("--> Got cluster state with 4 nodes."); + // assert status on nodes that are part of cluster currently + Iterator discoveryNodeIterator = clusterState.nodes().getNodes().values().iterator(); + DiscoveryNode clusterManagerNodeAfterDecommission = null; + while (discoveryNodeIterator.hasNext()) { + // assert no node has decommissioned attribute + DiscoveryNode node = discoveryNodeIterator.next(); + assertNotEquals(node.getAttributes().get("zone"), "c"); + if (node.isClusterManagerNode()) { + clusterManagerNodeAfterDecommission = node; + } + // assert all the nodes has status as SUCCESSFUL + ClusterService localNodeClusterService = internalCluster().getInstance(ClusterService.class, node.getName()); + assertEquals( + localNodeClusterService.state().metadata().decommissionAttributeMetadata().status(), + DecommissionStatus.SUCCESSFUL + ); + } + assertNotNull("Cluster Manager not found after decommission", clusterManagerNodeAfterDecommission); + logger.info("--> Cluster Manager node found after decommission"); + + // assert status on decommissioned node + // Here we will verify that until it got kicked out, it received appropriate status updates + // decommissioned nodes hence will have status as IN_PROGRESS as it will be kicked out later after this + // and won't receive status update to SUCCESSFUL + String randomDecommissionedNode = randomFrom(clusterManagerNodes.get(2), dataNodes.get(2)); + ClusterService decommissionedNodeClusterService = internalCluster().getInstance(ClusterService.class, randomDecommissionedNode); + assertEquals( + decommissionedNodeClusterService.state().metadata().decommissionAttributeMetadata().status(), + DecommissionStatus.IN_PROGRESS + ); + logger.info("--> Verified the decommissioned node has in_progress state."); + + // Will wait for all events to complete + client(activeNode).admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get(); + logger.info("--> Got LANGUID event"); + // Recommissioning the zone back to gracefully succeed the test once above tests succeeds + DeleteDecommissionStateResponse deleteDecommissionStateResponse = client(activeNode).execute( + DeleteDecommissionStateAction.INSTANCE, + new DeleteDecommissionStateRequest() + ).get(); + assertTrue(deleteDecommissionStateResponse.isAcknowledged()); + logger.info("--> Deleting decommission done."); + + // will wait for cluster to stabilise with a timeout of 2 min (findPeerInterval for decommissioned nodes) + // as by then all nodes should have joined the cluster + ensureStableCluster(6, TimeValue.timeValueSeconds(121)); + } + + public void testDecommissionFailedWhenAttributeNotWeighedAway() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + // Start 3 cluster manager eligible nodes + internalCluster().startClusterManagerOnlyNodes(3, Settings.builder().put(commonSettings).build()); + // start 3 data nodes + internalCluster().startDataOnlyNodes(3, Settings.builder().put(commonSettings).build()); + ensureStableCluster(6); + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(6)) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "c"); + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + decommissionRequest.setNoDelay(true); + assertBusy(() -> { + DecommissioningFailedException ex = expectThrows( + DecommissioningFailedException.class, + () -> client().execute(DecommissionAction.INSTANCE, decommissionRequest).actionGet() + ); + assertTrue( + ex.getMessage() + .contains("no weights are set to the attribute. Please set appropriate weights before triggering decommission action") + ); + }); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 1.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + assertBusy(() -> { + DecommissioningFailedException ex = expectThrows( + DecommissioningFailedException.class, + () -> client().execute(DecommissionAction.INSTANCE, decommissionRequest).actionGet() + ); + assertTrue(ex.getMessage().contains("weight for decommissioned attribute is expected to be [0.0] but found [1.0]")); + }); + } + + public void testDecommissionFailedWithOnlyOneAttributeValueForLeader() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "b") // force zone values is only set for zones of routing nodes + .build(); + // Start 3 cluster manager eligible nodes in zone a + internalCluster().startClusterManagerOnlyNodes(3, Settings.builder().put(commonSettings).put("node.attr.zone", "a").build()); + // Start 3 data nodes in zone b + internalCluster().startDataOnlyNodes(3, Settings.builder().put(commonSettings).put("node.attr.zone", "b").build()); + ensureStableCluster(6); + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(6)) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + logger.info("--> setting shard routing weights"); + Map weights = Map.of("b", 1.0); // weights are expected to be set only for routing nodes + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + // prepare request to attempt to decommission zone 'a' + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "a"); + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + decommissionRequest.setNoDelay(true); + + // since there is just one zone present in the cluster, and on initiating decommission for that zone, + // although all the nodes would be added to voting config exclusion list, but those nodes won't be able to + // abdicate themselves as we wouldn't have any other leader eligible node which would be declare itself cluster manager + // and hence due to which the leader won't get abdicated and decommission request should eventually fail. + // And in this case, to ensure decommission request doesn't leave mutating change in the cluster, we ensure + // that no exclusion is set to the cluster and state for decommission is marked as FAILED + OpenSearchTimeoutException ex = expectThrows( + OpenSearchTimeoutException.class, + () -> client().execute(DecommissionAction.INSTANCE, decommissionRequest).actionGet() + ); + assertTrue(ex.getMessage().contains("while removing to-be-decommissioned cluster manager eligible nodes")); + + ClusterService leaderClusterService = internalCluster().getInstance( + ClusterService.class, + internalCluster().getClusterManagerName() + ); + ClusterStateObserver clusterStateObserver = new ClusterStateObserver( + leaderClusterService, + null, + logger, + client(internalCluster().getClusterManagerName()).threadPool().getThreadContext() + ); + CountDownLatch expectedStateLatch = new CountDownLatch(1); + + ClusterState currentState = internalCluster().clusterService().state(); + if (currentState.getVotingConfigExclusions().isEmpty()) { + logger.info("exclusion already cleared"); + expectedStateLatch.countDown(); + } else { + clusterStateObserver.waitForNextChange(new WaitForClearVotingConfigExclusion(expectedStateLatch)); + } + // if the below condition is passed, then we are sure exclusion is cleared + assertTrue(expectedStateLatch.await(30, TimeUnit.SECONDS)); + + expectedStateLatch = new CountDownLatch(1); + currentState = internalCluster().clusterService().state(); + DecommissionAttributeMetadata decommissionAttributeMetadata = currentState.metadata().decommissionAttributeMetadata(); + if (decommissionAttributeMetadata != null && decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED)) { + logger.info("decommission status has already turned false"); + expectedStateLatch.countDown(); + } else { + clusterStateObserver.waitForNextChange(new WaitForFailedDecommissionState(expectedStateLatch)); + } + + // if the below condition is passed, then we are sure current decommission status is marked FAILED + assertTrue(expectedStateLatch.await(30, TimeUnit.SECONDS)); + + // ensure all nodes are part of cluster + ensureStableCluster(6, TimeValue.timeValueMinutes(2)); + } + + public void testDecommissionAcknowledgedIfWeightsNotSetForNonRoutingNode() throws ExecutionException, InterruptedException { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'd' & 'e' & 'f'"); + List clusterManagerNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "d") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "e") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "f") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + + logger.info("--> start 3 data nodes on zones 'a' & 'b' & 'c'"); + List dataNodes = internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build() + ); + + ensureStableCluster(6); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + logger.info("--> starting decommissioning nodes in zone {}", 'd'); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "d"); + // Set the timeout to 0 to do immediate Decommission + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + decommissionRequest.setNoDelay(true); + DecommissionResponse decommissionResponse = client(dataNodes.get(0)).execute(DecommissionAction.INSTANCE, decommissionRequest) + .get(); + assertTrue(decommissionResponse.isAcknowledged()); + + client(dataNodes.get(0)).admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get(); + + ClusterState clusterState = client(dataNodes.get(0)).admin().cluster().prepareState().execute().actionGet().getState(); + + // assert that number of nodes should be 5 ( 2 cluster manager nodes + 3 data nodes ) + assertEquals(clusterState.nodes().getNodes().size(), 5); + assertEquals(clusterState.nodes().getDataNodes().size(), 3); + assertEquals(clusterState.nodes().getClusterManagerNodes().size(), 2); + + // Recommissioning the zone back to gracefully succeed the test once above tests succeeds + DeleteDecommissionStateResponse deleteDecommissionStateResponse = client(dataNodes.get(0)).execute( + DeleteDecommissionStateAction.INSTANCE, + new DeleteDecommissionStateRequest() + ).get(); + assertTrue(deleteDecommissionStateResponse.isAcknowledged()); + + // will wait for cluster to stabilise with a timeout of 2 min as by then all nodes should have joined the cluster + ensureStableCluster(6, TimeValue.timeValueMinutes(2)); + } + + public void testConcurrentDecommissionAction() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> start 3 cluster manager nodes on zones 'a' & 'b' & 'c'"); + internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build() + ); + logger.info("--> start 3 data nodes on zones 'a' & 'b' & 'c'"); + internalCluster().startNodes( + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "a") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "b") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build(), + Settings.builder() + .put(commonSettings) + .put("node.attr.zone", "c") + .put(onlyRole(commonSettings, DiscoveryNodeRole.DATA_ROLE)) + .build() + ); + + ensureStableCluster(6); + ClusterHealthResponse health = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForGreenStatus() + .setWaitForNodes(Integer.toString(6)) + .execute() + .actionGet(); + assertFalse(health.isTimedOut()); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 0.0, "b", 1.0, "c", 1.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(weightedRoutingResponse.isAcknowledged()); + + AtomicInteger numRequestAcknowledged = new AtomicInteger(); + AtomicInteger numRequestUnAcknowledged = new AtomicInteger(); + AtomicInteger numRequestFailed = new AtomicInteger(); + int concurrentRuns = randomIntBetween(5, 10); + TestThreadPool testThreadPool = null; + logger.info("--> starting {} concurrent decommission action in zone {}", concurrentRuns, 'a'); + try { + testThreadPool = new TestThreadPool(AwarenessAttributeDecommissionIT.class.getName()); + List operationThreads = new ArrayList<>(); + CountDownLatch countDownLatch = new CountDownLatch(concurrentRuns); + for (int i = 0; i < concurrentRuns; i++) { + Runnable thread = () -> { + logger.info("Triggering decommission action"); + DecommissionAttribute decommissionAttribute = new DecommissionAttribute("zone", "a"); + DecommissionRequest decommissionRequest = new DecommissionRequest(decommissionAttribute); + decommissionRequest.setNoDelay(true); + try { + DecommissionResponse decommissionResponse = client().execute(DecommissionAction.INSTANCE, decommissionRequest) + .get(); + if (decommissionResponse.isAcknowledged()) { + numRequestAcknowledged.incrementAndGet(); + } else { + numRequestUnAcknowledged.incrementAndGet(); + } + } catch (Exception e) { + numRequestFailed.incrementAndGet(); + } + countDownLatch.countDown(); + }; + operationThreads.add(thread); + } + TestThreadPool finalTestThreadPool = testThreadPool; + operationThreads.forEach(runnable -> finalTestThreadPool.executor("generic").execute(runnable)); + countDownLatch.await(); + } finally { + ThreadPool.terminate(testThreadPool, 500, TimeUnit.MILLISECONDS); + } + assertEquals(concurrentRuns, numRequestAcknowledged.get() + numRequestUnAcknowledged.get() + numRequestFailed.get()); + assertEquals(concurrentRuns - 1, numRequestFailed.get()); + assertEquals(1, numRequestAcknowledged.get() + numRequestUnAcknowledged.get()); + } + + private static class WaitForFailedDecommissionState implements ClusterStateObserver.Listener { + + final CountDownLatch doneLatch; + + WaitForFailedDecommissionState(CountDownLatch latch) { + this.doneLatch = latch; + } + + @Override + public void onNewClusterState(ClusterState state) { + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().decommissionAttributeMetadata(); + if (decommissionAttributeMetadata != null && decommissionAttributeMetadata.status().equals(DecommissionStatus.FAILED)) { + doneLatch.countDown(); + } + } + + @Override + public void onClusterServiceClose() { + throw new AssertionError("unexpected close"); + } + + @Override + public void onTimeout(TimeValue timeout) { + throw new AssertionError("unexpected timeout"); + } + } + + private static class WaitForClearVotingConfigExclusion implements ClusterStateObserver.Listener { + + final CountDownLatch doneLatch; + + WaitForClearVotingConfigExclusion(CountDownLatch latch) { + this.doneLatch = latch; + } + + @Override + public void onNewClusterState(ClusterState state) { + if (state.getVotingConfigExclusions().isEmpty()) { + doneLatch.countDown(); + } + } + + @Override + public void onClusterServiceClose() { + throw new AssertionError("unexpected close"); + } + + @Override + public void onTimeout(TimeValue timeout) { + throw new AssertionError("unexpected timeout"); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RareClusterStateIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RareClusterStateIT.java index 6f4c6fca77196..b3cb15d028090 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RareClusterStateIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RareClusterStateIT.java @@ -34,11 +34,8 @@ import org.opensearch.OpenSearchParseException; import org.opensearch.Version; - -import org.opensearch.action.ActionFuture; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestBuilder; -import org.opensearch.action.ActionResponse; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.cluster.ClusterState; @@ -53,10 +50,12 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.index.Index; import org.opensearch.discovery.Discovery; -import org.opensearch.index.Index; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.MapperService; @@ -72,10 +71,8 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static org.opensearch.action.DocWriteResponse.Result.CREATED; - import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; @@ -105,9 +102,9 @@ public void testAssignmentWithJustAddedNodes() { // close to have some unassigned started shards shards.. client().admin().indices().prepareClose(index).get(); - final String masterName = internalCluster().getMasterName(); - final ClusterService clusterService = internalCluster().clusterService(masterName); - final AllocationService allocationService = internalCluster().getInstance(AllocationService.class, masterName); + final String clusterManagerName = internalCluster().getClusterManagerName(); + final ClusterService clusterService = internalCluster().clusterService(clusterManagerName); + final AllocationService allocationService = internalCluster().getInstance(AllocationService.class, clusterManagerName); clusterService.submitStateUpdateTask("test-inject-node-and-reroute", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) { @@ -159,27 +156,27 @@ private ActionFuture { - assertFalse(masterCoordinator.publicationInProgress()); - final long applierVersion = masterCoordinator.getApplierState().version(); + assertFalse(clusterManagerCoordinator.publicationInProgress()); + final long applierVersion = clusterManagerCoordinator.getApplierState().version(); for (Discovery instance : internalCluster().getInstances(Discovery.class)) { assertEquals(((Coordinator) instance).getApplierState().version(), applierVersion); } }); ActionFuture future = req.execute(); - assertBusy(() -> assertTrue(masterCoordinator.cancelCommittedPublication())); + assertBusy(() -> assertTrue(clusterManagerCoordinator.cancelCommittedPublication())); return future; } public void testDeleteCreateInOneBulk() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); String dataNode = internalCluster().startDataOnlyNode(); assertFalse(client().admin().cluster().prepareHealth().setWaitForNodes("2").get().isTimedOut()); prepareCreate("test").setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)).get(); ensureGreen("test"); - // block none master node. + // block none cluster-manager node. BlockClusterStateProcessing disruption = new BlockClusterStateProcessing(dataNode, random()); internalCluster().setDisruptionScheme(disruption); logger.info("--> indexing a doc"); @@ -202,9 +199,11 @@ public void testDeleteCreateInOneBulk() throws Exception { ensureGreen(TimeValue.timeValueMinutes(30), "test"); // due to publish_timeout of 0, wait for data node to have cluster state fully applied assertBusy(() -> { - long masterClusterStateVersion = internalCluster().clusterService(internalCluster().getMasterName()).state().version(); + long clusterManagerClusterStateVersion = internalCluster().clusterService(internalCluster().getClusterManagerName()) + .state() + .version(); long dataClusterStateVersion = internalCluster().clusterService(dataNode).state().version(); - assertThat(masterClusterStateVersion, equalTo(dataClusterStateVersion)); + assertThat(clusterManagerClusterStateVersion, equalTo(dataClusterStateVersion)); }); assertHitCount(client().prepareSearch("test").get(), 0); } @@ -212,7 +211,7 @@ public void testDeleteCreateInOneBulk() throws Exception { public void testDelayedMappingPropagationOnPrimary() throws Exception { // Here we want to test that things go well if there is a first request // that adds mappings but before mappings are propagated to all nodes - // another index request introduces the same mapping. The master node + // another index request introduces the same mapping. The cluster-manager node // will reply immediately since it did not change the cluster state // but the change might not be on the node that performed the indexing // operation yet @@ -220,37 +219,37 @@ public void testDelayedMappingPropagationOnPrimary() throws Exception { final List nodeNames = internalCluster().startNodes(2); assertFalse(client().admin().cluster().prepareHealth().setWaitForNodes("2").get().isTimedOut()); - final String master = internalCluster().getMasterName(); - assertThat(nodeNames, hasItem(master)); + final String clusterManager = internalCluster().getClusterManagerName(); + assertThat(nodeNames, hasItem(clusterManager)); String otherNode = null; for (String node : nodeNames) { - if (node.equals(master) == false) { + if (node.equals(clusterManager) == false) { otherNode = node; break; } } assertNotNull(otherNode); - // Don't allocate the shard on the master node + // Don't allocate the shard on the cluster-manager node assertAcked( prepareCreate("index").setSettings( Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) - .put("index.routing.allocation.exclude._name", master) + .put("index.routing.allocation.exclude._name", clusterManager) ).get() ); ensureGreen(); // Check routing tables ClusterState state = client().admin().cluster().prepareState().get().getState(); - assertEquals(master, state.nodes().getMasterNode().getName()); + assertEquals(clusterManager, state.nodes().getClusterManagerNode().getName()); List shards = state.routingTable().allShards("index"); assertThat(shards, hasSize(1)); for (ShardRouting shard : shards) { if (shard.primary()) { - // primary must not be on the master node - assertFalse(state.nodes().getMasterNodeId().equals(shard.currentNodeId())); + // primary must not be on the cluster-manager node + assertFalse(state.nodes().getClusterManagerNodeId().equals(shard.currentNodeId())); } else { fail(); // only primaries } @@ -266,7 +265,7 @@ public void testDelayedMappingPropagationOnPrimary() throws Exception { client().admin().indices().preparePutMapping("index").setSource("field", "type=long") ); - // ...and wait for mappings to be available on master + // ...and wait for mappings to be available on cluster-manager assertBusy(() -> { MappingMetadata typeMappings = client().admin().indices().prepareGetMappings("index").get().getMappings().get("index"); assertNotNull(typeMappings); @@ -308,24 +307,24 @@ public void testDelayedMappingPropagationOnReplica() throws Exception { final List nodeNames = internalCluster().startNodes(2); assertFalse(client().admin().cluster().prepareHealth().setWaitForNodes("2").get().isTimedOut()); - final String master = internalCluster().getMasterName(); - assertThat(nodeNames, hasItem(master)); + final String clusterManager = internalCluster().getClusterManagerName(); + assertThat(nodeNames, hasItem(clusterManager)); String otherNode = null; for (String node : nodeNames) { - if (node.equals(master) == false) { + if (node.equals(clusterManager) == false) { otherNode = node; break; } } assertNotNull(otherNode); - // Force allocation of the primary on the master node by first only allocating on the master + // Force allocation of the primary on the cluster-manager node by first only allocating on the cluster-manager // and then allowing all nodes so that the replica gets allocated on the other node prepareCreate("index").setSettings( Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) - .put("index.routing.allocation.include._name", master) + .put("index.routing.allocation.include._name", clusterManager) ).get(); client().admin() .indices() @@ -336,13 +335,13 @@ public void testDelayedMappingPropagationOnReplica() throws Exception { // Check routing tables ClusterState state = client().admin().cluster().prepareState().get().getState(); - assertEquals(master, state.nodes().getMasterNode().getName()); + assertEquals(clusterManager, state.nodes().getClusterManagerNode().getName()); List shards = state.routingTable().allShards("index"); assertThat(shards, hasSize(2)); for (ShardRouting shard : shards) { if (shard.primary()) { - // primary must be on the master - assertEquals(state.nodes().getMasterNodeId(), shard.currentNodeId()); + // primary must be on the cluster-manager + assertEquals(state.nodes().getClusterManagerNodeId(), shard.currentNodeId()); } else { assertTrue(shard.active()); } @@ -357,9 +356,9 @@ public void testDelayedMappingPropagationOnReplica() throws Exception { ); final Index index = resolveIndex("index"); - // Wait for mappings to be available on master + // Wait for mappings to be available on cluster-manager assertBusy(() -> { - final IndicesService indicesService = internalCluster().getInstance(IndicesService.class, master); + final IndicesService indicesService = internalCluster().getInstance(IndicesService.class, clusterManager); final IndexService indexService = indicesService.indexServiceSafe(index); assertNotNull(indexService); final MapperService mapperService = indexService.mapperService(); @@ -381,9 +380,9 @@ public void testDelayedMappingPropagationOnReplica() throws Exception { client().prepareIndex("index").setId("2").setSource("field2", 42) ); - // ...and wait for second mapping to be available on master + // ...and wait for second mapping to be available on cluster-manager assertBusy(() -> { - final IndicesService indicesService = internalCluster().getInstance(IndicesService.class, master); + final IndicesService indicesService = internalCluster().getInstance(IndicesService.class, clusterManager); final IndexService indexService = indicesService.indexServiceSafe(index); assertNotNull(indexService); final MapperService mapperService = indexService.mapperService(); @@ -392,7 +391,7 @@ public void testDelayedMappingPropagationOnReplica() throws Exception { assertNotNull(mapper.mappers().getMapper("field2")); }); - assertBusy(() -> assertTrue(client().prepareGet("index", "2").get().isExists())); + assertBusy(() -> assertTrue(client().prepareGet("index", "2").setPreference("_primary").get().isExists())); // The mappings have not been propagated to the replica yet as a consequence the document count not be indexed // We wait on purpose to make sure that the document is not indexed because the shard operation is stalled diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RemoveCustomsCommandIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RemoveCustomsCommandIT.java index 5037f51e2b3f0..05e8cd4e93d0e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RemoveCustomsCommandIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RemoveCustomsCommandIT.java @@ -46,7 +46,7 @@ public class RemoveCustomsCommandIT extends OpenSearchIntegTestCase { public void testRemoveCustomsAbortedByUser() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); Settings dataPathSettings = internalCluster().dataPathSettings(node); ensureStableCluster(1); @@ -59,7 +59,7 @@ public void testRemoveCustomsAbortedByUser() throws Exception { } public void testRemoveCustomsSuccessful() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); createIndex("test"); client().admin().indices().prepareDelete("test").get(); @@ -85,7 +85,7 @@ public void testRemoveCustomsSuccessful() throws Exception { } public void testCustomDoesNotMatch() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); createIndex("test"); client().admin().indices().prepareDelete("test").get(); diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RemoveSettingsCommandIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RemoveSettingsCommandIT.java index f908225e9ba91..fcefbd9fa1273 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RemoveSettingsCommandIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/RemoveSettingsCommandIT.java @@ -49,7 +49,7 @@ public class RemoveSettingsCommandIT extends OpenSearchIntegTestCase { public void testRemoveSettingsAbortedByUser() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); client().admin() .cluster() @@ -78,7 +78,7 @@ public void testRemoveSettingsAbortedByUser() throws Exception { } public void testRemoveSettingsSuccessful() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); client().admin() .cluster() @@ -122,7 +122,7 @@ public void testRemoveSettingsSuccessful() throws Exception { } public void testSettingDoesNotMatch() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); client().admin() .cluster() diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/UnsafeBootstrapAndDetachCommandIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/UnsafeBootstrapAndDetachCommandIT.java index 1447379b93ec8..b30eb1f3e3b39 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/UnsafeBootstrapAndDetachCommandIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/UnsafeBootstrapAndDetachCommandIT.java @@ -44,6 +44,7 @@ import org.opensearch.env.TestEnvironment; import org.opensearch.gateway.GatewayMetaState; import org.opensearch.gateway.PersistedClusterStateService; +import org.opensearch.gateway.remote.RemoteClusterStateService; import org.opensearch.indices.IndicesService; import org.opensearch.node.Node.DiscoverySettings; import org.opensearch.test.InternalTestCluster; @@ -55,16 +56,16 @@ import java.util.Locale; import java.util.Objects; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.notNullValue; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.gateway.DanglingIndicesState.AUTO_IMPORT_DANGLING_INDICES_SETTING; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.indices.recovery.RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING; -import static org.opensearch.test.NodeRoles.nonMasterNode; +import static org.opensearch.test.NodeRoles.nonClusterManagerNode; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0, autoManageMasterNodes = false) public class UnsafeBootstrapAndDetachCommandIT extends OpenSearchIntegTestCase { @@ -109,9 +110,15 @@ private MockTerminal executeCommand( } private MockTerminal unsafeBootstrap(Environment environment, boolean abort, Boolean applyClusterReadOnlyBlock) throws Exception { - final MockTerminal terminal = executeCommand(new UnsafeBootstrapMasterCommand(), environment, 0, abort, applyClusterReadOnlyBlock); - assertThat(terminal.getOutput(), containsString(UnsafeBootstrapMasterCommand.CONFIRMATION_MSG)); - assertThat(terminal.getOutput(), containsString(UnsafeBootstrapMasterCommand.MASTER_NODE_BOOTSTRAPPED_MSG)); + final MockTerminal terminal = executeCommand( + new UnsafeBootstrapClusterManagerCommand(), + environment, + 0, + abort, + applyClusterReadOnlyBlock + ); + assertThat(terminal.getOutput(), containsString(UnsafeBootstrapClusterManagerCommand.CONFIRMATION_MSG)); + assertThat(terminal.getOutput(), containsString(UnsafeBootstrapClusterManagerCommand.CLUSTER_MANAGER_NODE_BOOTSTRAPPED_MSG)); return terminal; } @@ -167,11 +174,21 @@ private void removeBlock() { } } - public void testBootstrapNotMasterEligible() { + public void testBootstrapNotClusterManagerEligible() { + final Environment environment = TestEnvironment.newEnvironment( + Settings.builder().put(nonClusterManagerNode(internalCluster().getDefaultSettings())).build() + ); + expectThrows(() -> unsafeBootstrap(environment), UnsafeBootstrapClusterManagerCommand.NOT_CLUSTER_MANAGER_NODE_MSG); + } + + public void testBootstrapRemoteClusterEnabled() { final Environment environment = TestEnvironment.newEnvironment( - Settings.builder().put(nonMasterNode(internalCluster().getDefaultSettings())).build() + Settings.builder() + .put(internalCluster().getDefaultSettings()) + .put(RemoteClusterStateService.REMOTE_CLUSTER_STATE_ENABLED_SETTING.getKey(), true) + .build() ); - expectThrows(() -> unsafeBootstrap(environment), UnsafeBootstrapMasterCommand.NOT_MASTER_NODE_MSG); + expectThrows(() -> unsafeBootstrap(environment), UnsafeBootstrapClusterManagerCommand.REMOTE_CLUSTER_STATE_ENABLED_NODE); } public void testBootstrapNoDataFolder() { @@ -214,7 +231,7 @@ public void testBootstrapNotBootstrappedCluster() throws Exception { ); assertBusy(() -> { ClusterState state = client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertTrue(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID)); + assertTrue(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID)); }); Settings dataPathSettings = internalCluster().dataPathSettings(node); @@ -224,15 +241,15 @@ public void testBootstrapNotBootstrappedCluster() throws Exception { Environment environment = TestEnvironment.newEnvironment( Settings.builder().put(internalCluster().getDefaultSettings()).put(dataPathSettings).build() ); - expectThrows(() -> unsafeBootstrap(environment), UnsafeBootstrapMasterCommand.EMPTY_LAST_COMMITTED_VOTING_CONFIG_MSG); + expectThrows(() -> unsafeBootstrap(environment), UnsafeBootstrapClusterManagerCommand.EMPTY_LAST_COMMITTED_VOTING_CONFIG_MSG); } public void testBootstrapNoClusterState() throws IOException { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); Settings dataPathSettings = internalCluster().dataPathSettings(node); ensureStableCluster(1); - NodeEnvironment nodeEnvironment = internalCluster().getMasterNodeInstance(NodeEnvironment.class); + NodeEnvironment nodeEnvironment = internalCluster().getClusterManagerNodeInstance(NodeEnvironment.class); internalCluster().stopRandomDataNode(); Environment environment = TestEnvironment.newEnvironment( Settings.builder().put(internalCluster().getDefaultSettings()).put(dataPathSettings).build() @@ -243,11 +260,11 @@ public void testBootstrapNoClusterState() throws IOException { } public void testDetachNoClusterState() throws IOException { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); Settings dataPathSettings = internalCluster().dataPathSettings(node); ensureStableCluster(1); - NodeEnvironment nodeEnvironment = internalCluster().getMasterNodeInstance(NodeEnvironment.class); + NodeEnvironment nodeEnvironment = internalCluster().getClusterManagerNodeInstance(NodeEnvironment.class); internalCluster().stopRandomDataNode(); Environment environment = TestEnvironment.newEnvironment( Settings.builder().put(internalCluster().getDefaultSettings()).put(dataPathSettings).build() @@ -258,7 +275,7 @@ public void testDetachNoClusterState() throws IOException { } public void testBootstrapAbortedByUser() throws IOException { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); Settings dataPathSettings = internalCluster().dataPathSettings(node); ensureStableCluster(1); @@ -271,7 +288,7 @@ public void testBootstrapAbortedByUser() throws IOException { } public void testDetachAbortedByUser() throws IOException { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); String node = internalCluster().startNode(); Settings dataPathSettings = internalCluster().dataPathSettings(node); ensureStableCluster(1); @@ -283,13 +300,13 @@ public void testDetachAbortedByUser() throws IOException { expectThrows(() -> detachCluster(environment, true), OpenSearchNodeCommand.ABORTED_BY_USER_MSG); } - public void test3MasterNodes2Failed() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(2); - List masterNodes = new ArrayList<>(); + public void test3ClusterManagerNodes2Failed() throws Exception { + internalCluster().setBootstrapClusterManagerNodeIndex(2); + List clusterManagerNodes = new ArrayList<>(); - logger.info("--> start 1st master-eligible node"); - masterNodes.add( - internalCluster().startMasterOnlyNode( + logger.info("--> start 1st cluster-manager-eligible node"); + clusterManagerNodes.add( + internalCluster().startClusterManagerOnlyNode( Settings.builder().put(DiscoverySettings.INITIAL_STATE_TIMEOUT_SETTING.getKey(), "0s").build() ) ); // node ordinal 0 @@ -299,13 +316,13 @@ public void test3MasterNodes2Failed() throws Exception { Settings.builder().put(DiscoverySettings.INITIAL_STATE_TIMEOUT_SETTING.getKey(), "0s").build() ); // node ordinal 1 - logger.info("--> start 2nd and 3rd master-eligible nodes and bootstrap"); - masterNodes.addAll(internalCluster().startMasterOnlyNodes(2)); // node ordinals 2 and 3 + logger.info("--> start 2nd and 3rd cluster-manager-eligible nodes and bootstrap"); + clusterManagerNodes.addAll(internalCluster().startClusterManagerOnlyNodes(2)); // node ordinals 2 and 3 logger.info("--> wait for all nodes to join the cluster"); ensureStableCluster(4); - List currentClusterNodes = new ArrayList<>(masterNodes); + List currentClusterNodes = new ArrayList<>(clusterManagerNodes); currentClusterNodes.add(dataNode); currentClusterNodes.forEach(node -> ensureReadOnlyBlock(false, node)); @@ -313,14 +330,14 @@ public void test3MasterNodes2Failed() throws Exception { createIndex("test"); ensureGreen("test"); - Settings master1DataPathSettings = internalCluster().dataPathSettings(masterNodes.get(0)); - Settings master2DataPathSettings = internalCluster().dataPathSettings(masterNodes.get(1)); - Settings master3DataPathSettings = internalCluster().dataPathSettings(masterNodes.get(2)); + Settings clusterManager1DataPathSettings = internalCluster().dataPathSettings(clusterManagerNodes.get(0)); + Settings clusterManager2DataPathSettings = internalCluster().dataPathSettings(clusterManagerNodes.get(1)); + Settings clusterManager3DataPathSettings = internalCluster().dataPathSettings(clusterManagerNodes.get(2)); Settings dataNodeDataPathSettings = internalCluster().dataPathSettings(dataNode); - logger.info("--> stop 2nd and 3d master eligible node"); - internalCluster().stopRandomNode(InternalTestCluster.nameFilter(masterNodes.get(1))); - internalCluster().stopRandomNode(InternalTestCluster.nameFilter(masterNodes.get(2))); + logger.info("--> stop 2nd and 3d cluster-manager eligible node"); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(clusterManagerNodes.get(1))); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(clusterManagerNodes.get(2))); logger.info("--> ensure NO_MASTER_BLOCK on data-only node"); assertBusy(() -> { @@ -332,23 +349,26 @@ public void test3MasterNodes2Failed() throws Exception { .execute() .actionGet() .getState(); - assertTrue(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID)); + assertTrue(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID)); }); - logger.info("--> try to unsafely bootstrap 1st master-eligible node, while node lock is held"); - Environment environmentMaster1 = TestEnvironment.newEnvironment( - Settings.builder().put(internalCluster().getDefaultSettings()).put(master1DataPathSettings).build() + logger.info("--> try to unsafely bootstrap 1st cluster-manager-eligible node, while node lock is held"); + Environment environmentClusterManager1 = TestEnvironment.newEnvironment( + Settings.builder().put(internalCluster().getDefaultSettings()).put(clusterManager1DataPathSettings).build() + ); + expectThrows( + () -> unsafeBootstrap(environmentClusterManager1), + UnsafeBootstrapClusterManagerCommand.FAILED_TO_OBTAIN_NODE_LOCK_MSG ); - expectThrows(() -> unsafeBootstrap(environmentMaster1), UnsafeBootstrapMasterCommand.FAILED_TO_OBTAIN_NODE_LOCK_MSG); - logger.info("--> stop 1st master-eligible node and data-only node"); - NodeEnvironment nodeEnvironment = internalCluster().getMasterNodeInstance(NodeEnvironment.class); - internalCluster().stopRandomNode(InternalTestCluster.nameFilter(masterNodes.get(0))); + logger.info("--> stop 1st cluster-manager-eligible node and data-only node"); + NodeEnvironment nodeEnvironment = internalCluster().getClusterManagerNodeInstance(NodeEnvironment.class); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(clusterManagerNodes.get(0))); assertBusy(() -> internalCluster().getInstance(GatewayMetaState.class, dataNode).allPendingAsyncStatesWritten()); internalCluster().stopRandomDataNode(); - logger.info("--> unsafely-bootstrap 1st master-eligible node"); - MockTerminal terminal = unsafeBootstrap(environmentMaster1, false, true); + logger.info("--> unsafely-bootstrap 1st cluster-manager-eligible node"); + MockTerminal terminal = unsafeBootstrap(environmentClusterManager1, false, true); Metadata metadata = OpenSearchNodeCommand.createPersistedClusterStateService(Settings.EMPTY, nodeEnvironment.nodeDataPaths()) .loadBestOnDiskState().metadata; assertThat( @@ -356,15 +376,15 @@ public void test3MasterNodes2Failed() throws Exception { containsString( String.format( Locale.ROOT, - UnsafeBootstrapMasterCommand.CLUSTER_STATE_TERM_VERSION_MSG_FORMAT, + UnsafeBootstrapClusterManagerCommand.CLUSTER_STATE_TERM_VERSION_MSG_FORMAT, metadata.coordinationMetadata().term(), metadata.version() ) ) ); - logger.info("--> start 1st master-eligible node"); - String masterNode2 = internalCluster().startMasterOnlyNode(master1DataPathSettings); + logger.info("--> start 1st cluster-manager-eligible node"); + String clusterManagerNode2 = internalCluster().startClusterManagerOnlyNode(clusterManager1DataPathSettings); logger.info("--> detach-cluster on data-only node"); Environment environmentData = TestEnvironment.newEnvironment( @@ -385,13 +405,15 @@ public void test3MasterNodes2Failed() throws Exception { .execute() .actionGet() .getState(); - assertFalse(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID)); - assertTrue(state.metadata().persistentSettings().getAsBoolean(UnsafeBootstrapMasterCommand.UNSAFE_BOOTSTRAP.getKey(), false)); + assertFalse(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID)); + assertTrue( + state.metadata().persistentSettings().getAsBoolean(UnsafeBootstrapClusterManagerCommand.UNSAFE_BOOTSTRAP.getKey(), false) + ); }); List bootstrappedNodes = new ArrayList<>(); bootstrappedNodes.add(dataNode2); - bootstrappedNodes.add(masterNode2); + bootstrappedNodes.add(clusterManagerNode2); bootstrappedNodes.forEach(node -> ensureReadOnlyBlock(true, node)); logger.info("--> ensure index test is green"); @@ -399,31 +421,31 @@ public void test3MasterNodes2Failed() throws Exception { IndexMetadata indexMetadata = clusterService().state().metadata().index("test"); assertThat(indexMetadata.getSettings().get(IndexMetadata.SETTING_HISTORY_UUID), notNullValue()); - logger.info("--> detach-cluster on 2nd and 3rd master-eligible nodes"); - Environment environmentMaster2 = TestEnvironment.newEnvironment( - Settings.builder().put(internalCluster().getDefaultSettings()).put(master2DataPathSettings).build() + logger.info("--> detach-cluster on 2nd and 3rd cluster-manager-eligible nodes"); + Environment environmentClusterManager2 = TestEnvironment.newEnvironment( + Settings.builder().put(internalCluster().getDefaultSettings()).put(clusterManager2DataPathSettings).build() ); - detachCluster(environmentMaster2, false); - Environment environmentMaster3 = TestEnvironment.newEnvironment( - Settings.builder().put(internalCluster().getDefaultSettings()).put(master3DataPathSettings).build() + detachCluster(environmentClusterManager2, false); + Environment environmentClusterManager3 = TestEnvironment.newEnvironment( + Settings.builder().put(internalCluster().getDefaultSettings()).put(clusterManager3DataPathSettings).build() ); - detachCluster(environmentMaster3, false); + detachCluster(environmentClusterManager3, false); - logger.info("--> start 2nd and 3rd master-eligible nodes and ensure 4 nodes stable cluster"); - bootstrappedNodes.add(internalCluster().startMasterOnlyNode(master2DataPathSettings)); - bootstrappedNodes.add(internalCluster().startMasterOnlyNode(master3DataPathSettings)); + logger.info("--> start 2nd and 3rd cluster-manager-eligible nodes and ensure 4 nodes stable cluster"); + bootstrappedNodes.add(internalCluster().startClusterManagerOnlyNode(clusterManager2DataPathSettings)); + bootstrappedNodes.add(internalCluster().startClusterManagerOnlyNode(clusterManager3DataPathSettings)); ensureStableCluster(4); bootstrappedNodes.forEach(node -> ensureReadOnlyBlock(true, node)); removeBlock(); } - public void testAllMasterEligibleNodesFailedDanglingIndexImport() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + public void testAllClusterManagerEligibleNodesFailedDanglingIndexImport() throws Exception { + internalCluster().setBootstrapClusterManagerNodeIndex(0); Settings settings = Settings.builder().put(AUTO_IMPORT_DANGLING_INDICES_SETTING.getKey(), true).build(); - logger.info("--> start mixed data and master-eligible node and bootstrap cluster"); - String masterNode = internalCluster().startNode(settings); // node ordinal 0 + logger.info("--> start mixed data and cluster-manager-eligible node and bootstrap cluster"); + String clusterManagerNode = internalCluster().startNode(settings); // node ordinal 0 logger.info("--> start data-only node and ensure 2 nodes stable cluster"); String dataNode = internalCluster().startDataOnlyNode(settings); // node ordinal 1 @@ -457,8 +479,8 @@ public void testAllMasterEligibleNodesFailedDanglingIndexImport() throws Excepti ); detachCluster(environment, false); - logger.info("--> stop master-eligible node, clear its data and start it again - new cluster should form"); - internalCluster().restartNode(masterNode, new InternalTestCluster.RestartCallback() { + logger.info("--> stop cluster-manager-eligible node, clear its data and start it again - new cluster should form"); + internalCluster().restartNode(clusterManagerNode, new InternalTestCluster.RestartCallback() { @Override public boolean clearData(String nodeName) { return true; @@ -478,34 +500,34 @@ public boolean clearData(String nodeName) { } public void testNoInitialBootstrapAfterDetach() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); - String masterNode = internalCluster().startMasterOnlyNode(); - Settings masterNodeDataPathSettings = internalCluster().dataPathSettings(masterNode); - internalCluster().stopCurrentMasterNode(); + internalCluster().setBootstrapClusterManagerNodeIndex(0); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + Settings clusterManagerNodeDataPathSettings = internalCluster().dataPathSettings(clusterManagerNode); + internalCluster().stopCurrentClusterManagerNode(); final Environment environment = TestEnvironment.newEnvironment( - Settings.builder().put(internalCluster().getDefaultSettings()).put(masterNodeDataPathSettings).build() + Settings.builder().put(internalCluster().getDefaultSettings()).put(clusterManagerNodeDataPathSettings).build() ); detachCluster(environment); - String node = internalCluster().startMasterOnlyNode( + String node = internalCluster().startClusterManagerOnlyNode( Settings.builder() - // give the cluster 2 seconds to elect the master (it should not) + // give the cluster 2 seconds to elect the cluster-manager (it should not) .put(DiscoverySettings.INITIAL_STATE_TIMEOUT_SETTING.getKey(), "2s") - .put(masterNodeDataPathSettings) + .put(clusterManagerNodeDataPathSettings) .build() ); ClusterState state = internalCluster().client().admin().cluster().prepareState().setLocal(true).execute().actionGet().getState(); - assertTrue(state.blocks().hasGlobalBlockWithId(NoMasterBlockService.NO_MASTER_BLOCK_ID)); + assertTrue(state.blocks().hasGlobalBlockWithId(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID)); internalCluster().stopRandomNode(InternalTestCluster.nameFilter(node)); } public void testCanRunUnsafeBootstrapAfterErroneousDetachWithoutLoosingMetadata() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); - String masterNode = internalCluster().startMasterOnlyNode(); - Settings masterNodeDataPathSettings = internalCluster().dataPathSettings(masterNode); + internalCluster().setBootstrapClusterManagerNodeIndex(0); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + Settings clusterManagerNodeDataPathSettings = internalCluster().dataPathSettings(clusterManagerNode); ClusterUpdateSettingsRequest req = new ClusterUpdateSettingsRequest().persistentSettings( Settings.builder().put(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey(), "1234kb") ); @@ -514,28 +536,28 @@ public void testCanRunUnsafeBootstrapAfterErroneousDetachWithoutLoosingMetadata( ClusterState state = internalCluster().client().admin().cluster().prepareState().execute().actionGet().getState(); assertThat(state.metadata().persistentSettings().get(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey()), equalTo("1234kb")); - ensureReadOnlyBlock(false, masterNode); + ensureReadOnlyBlock(false, clusterManagerNode); - internalCluster().stopCurrentMasterNode(); + internalCluster().stopCurrentClusterManagerNode(); final Environment environment = TestEnvironment.newEnvironment( - Settings.builder().put(internalCluster().getDefaultSettings()).put(masterNodeDataPathSettings).build() + Settings.builder().put(internalCluster().getDefaultSettings()).put(clusterManagerNodeDataPathSettings).build() ); detachCluster(environment); unsafeBootstrap(environment); // read-only block will remain same as one before bootstrap, in this case it is false - String masterNode2 = internalCluster().startMasterOnlyNode(masterNodeDataPathSettings); + String clusterManagerNode2 = internalCluster().startClusterManagerOnlyNode(clusterManagerNodeDataPathSettings); ensureGreen(); - ensureReadOnlyBlock(false, masterNode2); + ensureReadOnlyBlock(false, clusterManagerNode2); state = internalCluster().client().admin().cluster().prepareState().execute().actionGet().getState(); assertThat(state.metadata().settings().get(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey()), equalTo("1234kb")); } public void testUnsafeBootstrapWithApplyClusterReadOnlyBlockAsFalse() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); - String masterNode = internalCluster().startMasterOnlyNode(); - Settings masterNodeDataPathSettings = internalCluster().dataPathSettings(masterNode); + internalCluster().setBootstrapClusterManagerNodeIndex(0); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + Settings clusterManagerNodeDataPathSettings = internalCluster().dataPathSettings(clusterManagerNode); ClusterUpdateSettingsRequest req = new ClusterUpdateSettingsRequest().persistentSettings( Settings.builder().put(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey(), "1234kb") ); @@ -544,18 +566,18 @@ public void testUnsafeBootstrapWithApplyClusterReadOnlyBlockAsFalse() throws Exc ClusterState state = internalCluster().client().admin().cluster().prepareState().execute().actionGet().getState(); assertThat(state.metadata().persistentSettings().get(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey()), equalTo("1234kb")); - ensureReadOnlyBlock(false, masterNode); + ensureReadOnlyBlock(false, clusterManagerNode); - internalCluster().stopCurrentMasterNode(); + internalCluster().stopCurrentClusterManagerNode(); final Environment environment = TestEnvironment.newEnvironment( - Settings.builder().put(internalCluster().getDefaultSettings()).put(masterNodeDataPathSettings).build() + Settings.builder().put(internalCluster().getDefaultSettings()).put(clusterManagerNodeDataPathSettings).build() ); unsafeBootstrap(environment, false, false); - String masterNode2 = internalCluster().startMasterOnlyNode(masterNodeDataPathSettings); + String clusterManagerNode2 = internalCluster().startClusterManagerOnlyNode(clusterManagerNodeDataPathSettings); ensureGreen(); - ensureReadOnlyBlock(false, masterNode2); + ensureReadOnlyBlock(false, clusterManagerNode2); state = internalCluster().client().admin().cluster().prepareState().execute().actionGet().getState(); assertThat(state.metadata().settings().get(INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey()), equalTo("1234kb")); diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/VotingConfigurationIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/VotingConfigurationIT.java index f73ec8dc4bcd0..de1114e152767 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/VotingConfigurationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/VotingConfigurationIT.java @@ -62,22 +62,23 @@ protected Collection> nodePlugins() { } public void testAbdicateAfterVotingConfigExclusionAdded() throws ExecutionException, InterruptedException { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); internalCluster().startNodes(2); - final String originalMaster = internalCluster().getMasterName(); + final String originalClusterManager = internalCluster().getClusterManagerName(); - logger.info("--> excluding master node {}", originalMaster); - client().execute(AddVotingConfigExclusionsAction.INSTANCE, new AddVotingConfigExclusionsRequest(originalMaster)).get(); + logger.info("--> excluding cluster-manager node {}", originalClusterManager); + client().execute(AddVotingConfigExclusionsAction.INSTANCE, new AddVotingConfigExclusionsRequest(originalClusterManager)).get(); client().admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).get(); - assertNotEquals(originalMaster, internalCluster().getMasterName()); + assertNotEquals(originalClusterManager, internalCluster().getClusterManagerName()); } public void testElectsNodeNotInVotingConfiguration() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); final List nodeNames = internalCluster().startNodes(4); // a 4-node cluster settles on a 3-node configuration; we then prevent the nodes in the configuration from winning an election - // by failing at the pre-voting stage, so that the extra node must be elected instead when the master shuts down. This extra node + // by failing at the pre-voting stage, so that the extra node must be elected instead when the cluster-manager shuts down. This + // extra node // should then add itself into the voting configuration. assertFalse( @@ -104,7 +105,7 @@ public void testElectsNodeNotInVotingConfiguration() throws Exception { final Set votingConfiguration = clusterState.getLastCommittedConfiguration().getNodeIds(); assertThat(votingConfiguration, hasSize(3)); assertThat(clusterState.nodes().getSize(), equalTo(4)); - assertThat(votingConfiguration, hasItem(clusterState.nodes().getMasterNodeId())); + assertThat(votingConfiguration, hasItem(clusterState.nodes().getClusterManagerNodeId())); for (DiscoveryNode discoveryNode : clusterState.nodes()) { if (votingConfiguration.contains(discoveryNode.getId()) == false) { assertThat(excludedNodeName, nullValue()); @@ -133,7 +134,7 @@ public void testElectsNodeNotInVotingConfiguration() throws Exception { } } - internalCluster().stopCurrentMasterNode(); + internalCluster().stopCurrentClusterManagerNode(); assertFalse( internalCluster().client() .admin() @@ -154,7 +155,10 @@ public void testElectsNodeNotInVotingConfiguration() throws Exception { .setMetadata(true) .get() .getState(); - assertThat(newClusterState.nodes().getMasterNode().getName(), equalTo(excludedNodeName)); - assertThat(newClusterState.getLastCommittedConfiguration().getNodeIds(), hasItem(newClusterState.nodes().getMasterNodeId())); + assertThat(newClusterState.nodes().getClusterManagerNode().getName(), equalTo(excludedNodeName)); + assertThat( + newClusterState.getLastCommittedConfiguration().getNodeIds(), + hasItem(newClusterState.nodes().getClusterManagerNodeId()) + ); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/ZenDiscoveryIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/ZenDiscoveryIT.java index 1baac9071110a..9bddb39e79484 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/ZenDiscoveryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/coordination/ZenDiscoveryIT.java @@ -42,9 +42,9 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.discovery.Discovery; import org.opensearch.discovery.DiscoveryStats; import org.opensearch.test.OpenSearchIntegTestCase; @@ -59,8 +59,8 @@ import java.util.concurrent.TimeoutException; import static org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest.Metric.DISCOVERY; +import static org.opensearch.test.NodeRoles.clusterManagerOnlyNode; import static org.opensearch.test.NodeRoles.dataNode; -import static org.opensearch.test.NodeRoles.masterOnlyNode; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -71,10 +71,10 @@ @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0, numClientNodes = 0) public class ZenDiscoveryIT extends OpenSearchIntegTestCase { - public void testNoShardRelocationsOccurWhenElectedMasterNodeFails() throws Exception { + public void testNoShardRelocationsOccurWhenElectedClusterManagerNodeFails() throws Exception { - Settings masterNodeSettings = masterOnlyNode(); - internalCluster().startNodes(2, masterNodeSettings); + Settings clusterManagerNodeSettings = clusterManagerOnlyNode(); + internalCluster().startNodes(2, clusterManagerNodeSettings); Settings dateNodeSettings = dataNode(); internalCluster().startNodes(2, dateNodeSettings); ClusterHealthResponse clusterHealthResponse = client().admin() @@ -89,27 +89,27 @@ public void testNoShardRelocationsOccurWhenElectedMasterNodeFails() throws Excep createIndex("test"); ensureSearchable("test"); RecoveryResponse r = client().admin().indices().prepareRecoveries("test").get(); - int numRecoveriesBeforeNewMaster = r.shardRecoveryStates().get("test").size(); + int numRecoveriesBeforeNewClusterManager = r.shardRecoveryStates().get("test").size(); - final String oldMaster = internalCluster().getMasterName(); - internalCluster().stopCurrentMasterNode(); + final String oldClusterManager = internalCluster().getClusterManagerName(); + internalCluster().stopCurrentClusterManagerNode(); assertBusy(() -> { - String current = internalCluster().getMasterName(); + String current = internalCluster().getClusterManagerName(); assertThat(current, notNullValue()); - assertThat(current, not(equalTo(oldMaster))); + assertThat(current, not(equalTo(oldClusterManager))); }); ensureSearchable("test"); r = client().admin().indices().prepareRecoveries("test").get(); - int numRecoveriesAfterNewMaster = r.shardRecoveryStates().get("test").size(); - assertThat(numRecoveriesAfterNewMaster, equalTo(numRecoveriesBeforeNewMaster)); + int numRecoveriesAfterNewClusterManager = r.shardRecoveryStates().get("test").size(); + assertThat(numRecoveriesAfterNewClusterManager, equalTo(numRecoveriesBeforeNewClusterManager)); } public void testHandleNodeJoin_incompatibleClusterState() throws InterruptedException, ExecutionException, TimeoutException { - String masterNode = internalCluster().startMasterOnlyNode(); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); String node1 = internalCluster().startNode(); ClusterService clusterService = internalCluster().getInstance(ClusterService.class, node1); - Coordinator coordinator = (Coordinator) internalCluster().getInstance(Discovery.class, masterNode); + Coordinator coordinator = (Coordinator) internalCluster().getInstance(Discovery.class, clusterManagerNode); final ClusterState state = clusterService.state(); Metadata.Builder mdBuilder = Metadata.builder(state.metadata()); mdBuilder.putCustom(CustomMetadata.TYPE, new CustomMetadata("data")); @@ -170,7 +170,9 @@ public void testDiscoveryStats() throws Exception { ensureGreen(); // ensures that all events are processed (in particular state recovery fully completed) assertBusy( () -> assertThat( - internalCluster().clusterService(internalCluster().getMasterName()).getMasterService().numberOfPendingTasks(), + internalCluster().clusterService(internalCluster().getClusterManagerName()) + .getClusterManagerService() + .numberOfPendingTasks(), equalTo(0) ) ); // see https://github.com/elastic/elasticsearch/issues/24388 diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/ClusterIndexRefreshIntervalIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/ClusterIndexRefreshIntervalIT.java new file mode 100644 index 0000000000000..54824b67b7abc --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/ClusterIndexRefreshIntervalIT.java @@ -0,0 +1,338 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; +import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.index.Index; +import org.opensearch.index.IndexService; +import org.opensearch.index.IndexSettings; +import org.opensearch.indices.IndicesService; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.Before; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static org.opensearch.indices.IndicesService.CLUSTER_DEFAULT_INDEX_REFRESH_INTERVAL_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_MINIMUM_INDEX_REFRESH_INTERVAL_SETTING; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class ClusterIndexRefreshIntervalIT extends OpenSearchIntegTestCase { + + public static final String INDEX_NAME = "test-index"; + + public static final String OTHER_INDEX_NAME = "other-test-index"; + + @Override + public Settings indexSettings() { + return Settings.builder().put(super.indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1).build(); + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + internalCluster().startClusterManagerOnlyNode(); + } + + public void testDefaultRefreshIntervalWithUpdateClusterAndIndexSettings() throws Exception { + String clusterManagerName = internalCluster().getClusterManagerName(); + List dataNodes = internalCluster().startDataOnlyNodes(2); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + GetIndexResponse getIndexResponse = client(clusterManagerName).admin().indices().getIndex(new GetIndexRequest()).get(); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, randomFrom(dataNodes)); + String uuid = getIndexResponse.getSettings().get(INDEX_NAME).get(IndexMetadata.SETTING_INDEX_UUID); + IndexService indexService = indicesService.indexService(new Index(INDEX_NAME, uuid)); + assertEquals(getDefaultRefreshInterval(), indexService.getRefreshTaskInterval()); + + // Update the cluster.default.index.refresh_interval setting to another value and validate the index refresh interval + TimeValue refreshInterval = TimeValue.timeValueMillis(randomIntBetween(10, 90) * 1000L); + client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(CLUSTER_DEFAULT_INDEX_REFRESH_INTERVAL_SETTING.getKey(), refreshInterval)) + .get(); + assertEquals(refreshInterval, indexService.getRefreshTaskInterval()); + + // Update of cluster.minimum.index.refresh_interval setting to value less than refreshInterval above will fail + TimeValue invalidMinimumRefreshInterval = TimeValue.timeValueMillis(refreshInterval.millis() + randomIntBetween(1, 1000)); + IllegalArgumentException exceptionDuringMinUpdate = assertThrows( + IllegalArgumentException.class, + () -> client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings( + Settings.builder().put(CLUSTER_MINIMUM_INDEX_REFRESH_INTERVAL_SETTING.getKey(), invalidMinimumRefreshInterval) + ) + .get() + ); + assertEquals( + "cluster minimum index refresh interval [" + + invalidMinimumRefreshInterval + + "] more than cluster default index refresh interval [" + + refreshInterval + + "]", + exceptionDuringMinUpdate.getMessage() + ); + + // Update the cluster.minimum.index.refresh_interval setting to a valid value, this will succeed. + TimeValue validMinimumRefreshInterval = TimeValue.timeValueMillis(refreshInterval.millis() - randomIntBetween(1, 1000)); + client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings( + Settings.builder().put(CLUSTER_MINIMUM_INDEX_REFRESH_INTERVAL_SETTING.getKey(), validMinimumRefreshInterval) + ) + .get(); + + // Update with invalid index setting index.refresh_interval, this will fail. + TimeValue invalidRefreshInterval = TimeValue.timeValueMillis(validMinimumRefreshInterval.millis() - randomIntBetween(1, 1000)); + String expectedMessage = "invalid index.refresh_interval [" + + invalidRefreshInterval + + "]: cannot be smaller than cluster.minimum.index.refresh_interval [" + + validMinimumRefreshInterval + + "]"; + + IllegalArgumentException exceptionDuringUpdateSettings = assertThrows( + IllegalArgumentException.class, + () -> client(clusterManagerName).admin() + .indices() + .updateSettings( + new UpdateSettingsRequest(INDEX_NAME).settings( + Settings.builder().put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), invalidRefreshInterval) + ) + ) + .actionGet() + ); + assertEquals(expectedMessage, exceptionDuringUpdateSettings.getMessage()); + + // Create another index with invalid index setting index.refresh_interval, this fails. + Settings indexSettings = Settings.builder() + .put(indexSettings()) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), invalidRefreshInterval) + .build(); + IllegalArgumentException exceptionDuringCreateIndex = assertThrows( + IllegalArgumentException.class, + () -> createIndex(OTHER_INDEX_NAME, indexSettings) + ); + assertEquals(expectedMessage, exceptionDuringCreateIndex.getMessage()); + + // Update with valid index setting index.refresh_interval, this will succeed now. + TimeValue validRefreshInterval = TimeValue.timeValueMillis(validMinimumRefreshInterval.millis() + randomIntBetween(1, 1000)); + client(clusterManagerName).admin() + .indices() + .updateSettings( + new UpdateSettingsRequest(INDEX_NAME).settings( + Settings.builder().put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), validRefreshInterval) + ) + ) + .get(); + // verify refresh task interval is updated. + assertEquals(validRefreshInterval, indexService.getRefreshTaskInterval()); + + // Try to create another index with valid index setting index.refresh_interval, this will pass. + createIndex( + OTHER_INDEX_NAME, + Settings.builder().put(indexSettings).put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), validRefreshInterval).build() + ); + getIndexResponse = client(clusterManagerName).admin().indices().getIndex(new GetIndexRequest()).get(); + String otherUuid = getIndexResponse.getSettings().get(INDEX_NAME).get(IndexMetadata.SETTING_INDEX_UUID); + assertEquals(validRefreshInterval, indicesService.indexService(new Index(OTHER_INDEX_NAME, otherUuid)).getRefreshTaskInterval()); + + // Update the cluster.default.index.refresh_interval & cluster.minimum.index.refresh_interval setting to null + client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings( + Settings.builder() + .putNull(CLUSTER_DEFAULT_INDEX_REFRESH_INTERVAL_SETTING.getKey()) + .putNull(CLUSTER_MINIMUM_INDEX_REFRESH_INTERVAL_SETTING.getKey()) + ) + .get(); + // verify the index is still using the refresh interval passed in the update settings call + assertEquals(validRefreshInterval, indexService.getRefreshTaskInterval()); + + // Remove the index setting as well now, it should reset the refresh task interval to the default refresh interval + client(clusterManagerName).admin() + .indices() + .updateSettings( + new UpdateSettingsRequest(INDEX_NAME).settings( + Settings.builder().putNull(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey()) + ) + ) + .get(); + assertEquals(getDefaultRefreshInterval(), indexService.getRefreshTaskInterval()); + } + + public void testRefreshIntervalDisabled() throws ExecutionException, InterruptedException { + TimeValue clusterMinimumRefreshInterval = client().settings() + .getAsTime(IndicesService.CLUSTER_MINIMUM_INDEX_REFRESH_INTERVAL_SETTING.getKey(), TimeValue.MINUS_ONE); + boolean createIndexSuccess = clusterMinimumRefreshInterval.equals(TimeValue.MINUS_ONE); + String clusterManagerName = internalCluster().getClusterManagerName(); + List dataNodes = internalCluster().startDataOnlyNodes(2); + Settings settings = Settings.builder() + .put(indexSettings()) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), IndexSettings.MINIMUM_REFRESH_INTERVAL) + .build(); + if (createIndexSuccess) { + createIndex(INDEX_NAME, settings); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + GetIndexResponse getIndexResponse = client(clusterManagerName).admin().indices().getIndex(new GetIndexRequest()).get(); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, randomFrom(dataNodes)); + String uuid = getIndexResponse.getSettings().get(INDEX_NAME).get(IndexMetadata.SETTING_INDEX_UUID); + IndexService indexService = indicesService.indexService(new Index(INDEX_NAME, uuid)); + assertEquals(IndexSettings.MINIMUM_REFRESH_INTERVAL, indexService.getRefreshTaskInterval()); + } else { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> createIndex(INDEX_NAME, settings)); + assertEquals( + "invalid index.refresh_interval [-1]: cannot be smaller than cluster.minimum.index.refresh_interval [" + + getMinRefreshIntervalForRefreshDisabled() + + "]", + exception.getMessage() + ); + } + } + + protected TimeValue getMinRefreshIntervalForRefreshDisabled() { + throw new RuntimeException("This is not expected to be called here, but for the implementor"); + } + + public void testInvalidRefreshInterval() { + String invalidRefreshInterval = "-10s"; + internalCluster().startDataOnlyNodes(2); + Settings settings = Settings.builder() + .put(indexSettings()) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), invalidRefreshInterval) + .build(); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> createIndex(INDEX_NAME, settings)); + assertEquals( + "failed to parse setting [index.refresh_interval] with value [" + + invalidRefreshInterval + + "] as a time value: negative durations are not supported", + exception.getMessage() + ); + } + + public void testCreateIndexWithExplicitNullRefreshInterval() throws ExecutionException, InterruptedException { + List dataNodes = internalCluster().startDataOnlyNodes(2); + Settings indexSettings = Settings.builder() + .put(indexSettings()) + .putNull(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey()) + .build(); + createIndex(INDEX_NAME, indexSettings); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + GetIndexResponse getIndexResponse = client(internalCluster().getClusterManagerName()).admin() + .indices() + .getIndex(new GetIndexRequest()) + .get(); + String uuid = getIndexResponse.getSettings().get(INDEX_NAME).get(IndexMetadata.SETTING_INDEX_UUID); + + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, randomFrom(dataNodes)); + IndexService indexService = indicesService.indexService(new Index(INDEX_NAME, uuid)); + + assertEquals(IndexSettings.DEFAULT_REFRESH_INTERVAL, indexService.getRefreshTaskInterval()); + } + + /** + * In this test we check the case where an index is created with index setting `index.refresh_interval` with the value + * being lesser than the `cluster.minimum.index.refresh_interval`. Later we change the cluster minimum to be more than + * the index setting. The underlying index should continue to use the same refresh interval as earlier. + */ + public void testClusterMinimumChangeOnIndexWithCustomRefreshInterval() throws ExecutionException, InterruptedException { + List dataNodes = internalCluster().startDataOnlyNodes(2); + TimeValue customRefreshInterval = TimeValue.timeValueSeconds(getDefaultRefreshInterval().getSeconds() + randomIntBetween(1, 5)); + Settings indexSettings = Settings.builder() + .put(indexSettings()) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), customRefreshInterval) + .build(); + createIndex(INDEX_NAME, indexSettings); + + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + GetIndexResponse getIndexResponse = client(internalCluster().getClusterManagerName()).admin() + .indices() + .getIndex(new GetIndexRequest()) + .get(); + String uuid = getIndexResponse.getSettings().get(INDEX_NAME).get(IndexMetadata.SETTING_INDEX_UUID); + + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, randomFrom(dataNodes)); + IndexService indexService = indicesService.indexService(new Index(INDEX_NAME, uuid)); + + assertEquals(customRefreshInterval, indexService.getRefreshTaskInterval()); + + // Update the cluster.minimum.index.refresh_interval setting to a valid value higher the custom refresh interval. + // At the same time, due to certain degree of randomness in the test, we update the cluster.default.refresh_interval + // to a valid value as well to be deterministic in test behaviour. + TimeValue clusterMinimum = TimeValue.timeValueSeconds(customRefreshInterval.getSeconds() + randomIntBetween(1, 5)); + TimeValue clusterDefault = TimeValue.timeValueSeconds(customRefreshInterval.getSeconds() + 6); + String clusterManagerName = internalCluster().getClusterManagerName(); + client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings( + Settings.builder() + .put(CLUSTER_DEFAULT_INDEX_REFRESH_INTERVAL_SETTING.getKey(), clusterDefault) + .put(CLUSTER_MINIMUM_INDEX_REFRESH_INTERVAL_SETTING.getKey(), clusterMinimum) + ) + .get(); + + // Validate that the index refresh interval is still the existing one that was used during index creation + assertEquals(customRefreshInterval, indexService.getRefreshTaskInterval()); + + // Update index setting to a value >= current cluster minimum and this should happen successfully. + customRefreshInterval = TimeValue.timeValueSeconds(clusterMinimum.getSeconds() + randomIntBetween(1, 5)); + client(clusterManagerName).admin() + .indices() + .updateSettings( + new UpdateSettingsRequest(INDEX_NAME).settings( + Settings.builder().put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), customRefreshInterval) + ) + ) + .get(); + assertEquals(customRefreshInterval, indexService.getRefreshTaskInterval()); + } + + protected TimeValue getDefaultRefreshInterval() { + return IndexSettings.DEFAULT_REFRESH_INTERVAL; + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/ClusterIndexRefreshIntervalWithNodeSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/ClusterIndexRefreshIntervalWithNodeSettingsIT.java new file mode 100644 index 0000000000000..5fc7bfcbcd442 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/ClusterIndexRefreshIntervalWithNodeSettingsIT.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.indices.IndicesService; + +public class ClusterIndexRefreshIntervalWithNodeSettingsIT extends ClusterIndexRefreshIntervalIT { + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(IndicesService.CLUSTER_DEFAULT_INDEX_REFRESH_INTERVAL_SETTING.getKey(), getDefaultRefreshInterval()) + .put( + IndicesService.CLUSTER_MINIMUM_INDEX_REFRESH_INTERVAL_SETTING.getKey(), + getMinRefreshIntervalForRefreshDisabled().toString() + ) + .build(); + } + + @Override + protected TimeValue getMinRefreshIntervalForRefreshDisabled() { + return TimeValue.timeValueSeconds(1); + } + + @Override + protected TimeValue getDefaultRefreshInterval() { + return TimeValue.timeValueSeconds(5); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/TemplateUpgradeServiceIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/TemplateUpgradeServiceIT.java index babe228f544a9..ba1679d873bf4 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/TemplateUpgradeServiceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/metadata/TemplateUpgradeServiceIT.java @@ -32,14 +32,14 @@ package org.opensearch.cluster.metadata; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.opensearch.client.Client; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.plugins.Plugin; @@ -99,11 +99,9 @@ public Collection createComponents( IndexNameExpressionResolver expressionResolver, Supplier repositoriesServiceSupplier ) { - clusterService.getClusterSettings() - .addSettingsUpdateConsumer( - UPDATE_TEMPLATE_DUMMY_SETTING, - integer -> { logger.debug("the template dummy setting was updated to {}", integer); } - ); + clusterService.getClusterSettings().addSettingsUpdateConsumer(UPDATE_TEMPLATE_DUMMY_SETTING, integer -> { + logger.debug("the template dummy setting was updated to {}", integer); + }); return super.createComponents( client, clusterService, diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/node/DiscoveryNodeRoleIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/node/DiscoveryNodeRoleIT.java index 4e30a7f6130fb..8c0eb891475cc 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/node/DiscoveryNodeRoleIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/node/DiscoveryNodeRoleIT.java @@ -38,7 +38,6 @@ import org.opensearch.common.settings.Settings; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; - import org.hamcrest.Matcher; import java.util.Collection; @@ -47,10 +46,12 @@ import java.util.Set; import static org.opensearch.test.NodeRoles.addRoles; +import static org.opensearch.test.NodeRoles.onlyRole; import static org.opensearch.test.NodeRoles.removeRoles; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; public class DiscoveryNodeRoleIT extends OpenSearchIntegTestCase { @@ -126,4 +127,18 @@ private void runTestNodeHasAdditionalRole(final Settings settings) { assertThat(response.getNodes().get(0).getNode().getRoles(), matcher); } + public void testStartNodeWithClusterManagerRoleAndMasterSetting() { + final Settings settings = Settings.builder() + .put("node.master", randomBoolean()) + .put(onlyRole(DiscoveryNodeRole.CLUSTER_MANAGER_ROLE)) + .build(); + + final IllegalArgumentException e1 = expectThrows( + IllegalArgumentException.class, + () -> DiscoveryNode.getRolesFromSettings(settings) + ); + assertThat(e1.getMessage(), startsWith("can not explicitly configure node roles and use legacy role setting")); + final IllegalArgumentException e2 = expectThrows(IllegalArgumentException.class, () -> internalCluster().startNodes(settings)); + assertThat(e2.getMessage(), startsWith("can not explicitly configure node roles and use legacy role setting")); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/AllocationIdIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/AllocationIdIT.java index a20e944caebb2..82159065bcc8a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/AllocationIdIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/AllocationIdIT.java @@ -33,7 +33,6 @@ package org.opensearch.cluster.routing; import org.apache.lucene.store.NIOFSDirectory; - import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplanation; import org.opensearch.action.admin.indices.stats.ShardStats; import org.opensearch.action.index.IndexRequestBuilder; @@ -45,20 +44,20 @@ import org.opensearch.cluster.routing.allocation.ShardAllocationDecision; import org.opensearch.cluster.routing.allocation.command.AllocateStalePrimaryAllocationCommand; import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.MockEngineFactoryPlugin; import org.opensearch.index.engine.Engine; import org.opensearch.index.shard.RemoveCorruptedShardDataCommandIT; -import org.opensearch.index.shard.ShardId; import org.opensearch.index.shard.ShardPath; import org.opensearch.index.store.Store; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.Plugin; import org.opensearch.test.DummyShardLock; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import java.io.IOException; @@ -100,7 +99,7 @@ public void testFailedRecoveryOnAllocateStalePrimaryRequiresAnotherAllocateStale // initial set up final String indexName = "index42"; - final String master = internalCluster().startMasterOnlyNode(); + final String clusterManager = internalCluster().startClusterManagerOnlyNode(); String node1 = internalCluster().startNode(); createIndex( indexName, @@ -135,7 +134,7 @@ public void testFailedRecoveryOnAllocateStalePrimaryRequiresAnotherAllocateStale // create fake corrupted marker on node1 putFakeCorruptionMarker(indexSettings, shardId, indexPath); - // thanks to master node1 is out of sync + // thanks to cluster-manager node1 is out of sync node1 = internalCluster().startNode(node1DataPathSettings); // there is only _stale_ primary diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/DelayedAllocationIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/DelayedAllocationIT.java index b4b08a4c9ad73..45c72eefa2285 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/DelayedAllocationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/DelayedAllocationIT.java @@ -37,8 +37,8 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Collections; import java.util.List; diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/PrimaryAllocationIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/PrimaryAllocationIT.java index 55bdc2a4ac3c4..0dd5f036457ad 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/PrimaryAllocationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/PrimaryAllocationIT.java @@ -32,7 +32,6 @@ package org.opensearch.cluster.routing; -import com.carrotsearch.hppc.cursors.IntObjectCursor; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.admin.cluster.reroute.ClusterRerouteRequestBuilder; import org.opensearch.action.admin.indices.shards.IndicesShardStoresResponse; @@ -47,22 +46,22 @@ import org.opensearch.cluster.routing.allocation.command.AllocateEmptyPrimaryAllocationCommand; import org.opensearch.cluster.routing.allocation.command.AllocateStalePrimaryAllocationCommand; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenIntMap; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.set.Sets; +import org.opensearch.core.common.Strings; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.gateway.GatewayAllocator; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.engine.Engine; import org.opensearch.index.engine.EngineTestCase; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.disruption.NetworkDisruption; import org.opensearch.test.disruption.NetworkDisruption.TwoPartitions; import org.opensearch.test.transport.MockTransportService; @@ -73,6 +72,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -110,7 +110,7 @@ protected boolean addMockInternalEngine() { } public void testBulkWeirdScenario() throws Exception { - String master = internalCluster().startMasterOnlyNode(Settings.EMPTY); + String clusterManager = internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); internalCluster().startDataOnlyNodes(2); assertAcked( @@ -136,7 +136,7 @@ public void testBulkWeirdScenario() throws Exception { assertThat(bulkResponse.hasFailures(), equalTo(false)); assertThat(bulkResponse.getItems().length, equalTo(2)); - logger.info(Strings.toString(bulkResponse, true, true)); + logger.info(Strings.toString(MediaTypeRegistry.JSON, bulkResponse, true, true)); internalCluster().assertSeqNos(); @@ -149,7 +149,7 @@ public void testBulkWeirdScenario() throws Exception { } // returns data paths settings of in-sync shard copy - private Settings createStaleReplicaScenario(String master) throws Exception { + private Settings createStaleReplicaScenario(String clusterManager) throws Exception { client().prepareIndex("test").setSource(jsonBuilder().startObject().field("field", "value1").endObject()).get(); refresh(); ClusterState state = client().admin().cluster().prepareState().all().get().getState(); @@ -167,14 +167,14 @@ private Settings createStaleReplicaScenario(String master) throws Exception { } NetworkDisruption partition = new NetworkDisruption( - new TwoPartitions(Sets.newHashSet(master, replicaNode), Collections.singleton(primaryNode)), + new TwoPartitions(Sets.newHashSet(clusterManager, replicaNode), Collections.singleton(primaryNode)), NetworkDisruption.DISCONNECT ); internalCluster().setDisruptionScheme(partition); logger.info("--> partitioning node with primary shard from rest of cluster"); partition.startDisrupting(); - ensureStableCluster(2, master); + ensureStableCluster(2, clusterManager); logger.info("--> index a document into previous replica shard (that is now primary)"); client(replicaNode).prepareIndex("test").setSource(jsonBuilder().startObject().field("field", "value1").endObject()).get(); @@ -183,27 +183,30 @@ private Settings createStaleReplicaScenario(String master) throws Exception { final Settings inSyncDataPathSettings = internalCluster().dataPathSettings(replicaNode); internalCluster().stopRandomNode(InternalTestCluster.nameFilter(replicaNode)); - ensureStableCluster(1, master); + ensureStableCluster(1, clusterManager); partition.stopDisrupting(); logger.info("--> waiting for node with old primary shard to rejoin the cluster"); - ensureStableCluster(2, master); + ensureStableCluster(2, clusterManager); logger.info("--> check that old primary shard does not get promoted to primary again"); // kick reroute and wait for all shard states to be fetched - client(master).admin().cluster().prepareReroute().get(); + client(clusterManager).admin().cluster().prepareReroute().get(); assertBusy( - () -> assertThat(internalCluster().getInstance(GatewayAllocator.class, master).getNumberOfInFlightFetches(), equalTo(0)) + () -> assertThat(internalCluster().getInstance(GatewayAllocator.class, clusterManager).getNumberOfInFlightFetches(), equalTo(0)) ); // kick reroute a second time and check that all shards are unassigned - assertThat(client(master).admin().cluster().prepareReroute().get().getState().getRoutingNodes().unassigned().size(), equalTo(2)); + assertThat( + client(clusterManager).admin().cluster().prepareReroute().get().getState().getRoutingNodes().unassigned().size(), + equalTo(2) + ); return inSyncDataPathSettings; } public void testDoNotAllowStaleReplicasToBePromotedToPrimary() throws Exception { - logger.info("--> starting 3 nodes, 1 master, 2 data"); - String master = internalCluster().startMasterOnlyNode(Settings.EMPTY); + logger.info("--> starting 3 nodes, 1 cluster-manager, 2 data"); + String clusterManager = internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); internalCluster().startDataOnlyNodes(2); assertAcked( client().admin() @@ -213,7 +216,7 @@ public void testDoNotAllowStaleReplicasToBePromotedToPrimary() throws Exception .get() ); ensureGreen(); - final Settings inSyncDataPathSettings = createStaleReplicaScenario(master); + final Settings inSyncDataPathSettings = createStaleReplicaScenario(clusterManager); logger.info("--> starting node that reuses data folder with the up-to-date primary shard"); internalCluster().startDataOnlyNode(inSyncDataPathSettings); @@ -291,8 +294,8 @@ public void testFailedAllocationOfStalePrimaryToDataNodeWithNoData() throws Exce } public void testForceStaleReplicaToBePromotedToPrimary() throws Exception { - logger.info("--> starting 3 nodes, 1 master, 2 data"); - String master = internalCluster().startMasterOnlyNode(Settings.EMPTY); + logger.info("--> starting 3 nodes, 1 cluster-manager, 2 data"); + String clusterManager = internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); internalCluster().startDataOnlyNodes(2); assertAcked( client().admin() @@ -305,23 +308,23 @@ public void testForceStaleReplicaToBePromotedToPrimary() throws Exception { Set historyUUIDs = Arrays.stream(client().admin().indices().prepareStats("test").clear().get().getShards()) .map(shard -> shard.getCommitStats().getUserData().get(Engine.HISTORY_UUID_KEY)) .collect(Collectors.toSet()); - createStaleReplicaScenario(master); + createStaleReplicaScenario(clusterManager); if (randomBoolean()) { assertAcked(client().admin().indices().prepareClose("test").setWaitForActiveShards(0)); } boolean useStaleReplica = randomBoolean(); // if true, use stale replica, otherwise a completely empty copy logger.info("--> explicitly promote old primary shard"); final String idxName = "test"; - ImmutableOpenIntMap> storeStatuses = client().admin() + final Map> storeStatuses = client().admin() .indices() .prepareShardStores(idxName) .get() .getStoreStatuses() .get(idxName); ClusterRerouteRequestBuilder rerouteBuilder = client().admin().cluster().prepareReroute(); - for (IntObjectCursor> shardStoreStatuses : storeStatuses) { - int shardId = shardStoreStatuses.key; - IndicesShardStoresResponse.StoreStatus storeStatus = randomFrom(shardStoreStatuses.value); + for (var shardStoreStatuses : storeStatuses.entrySet()) { + int shardId = shardStoreStatuses.getKey(); + IndicesShardStoresResponse.StoreStatus storeStatus = randomFrom(shardStoreStatuses.getValue()); logger.info("--> adding allocation command for shard {}", shardId); // force allocation based on node id if (useStaleReplica) { @@ -343,7 +346,7 @@ public void testForceStaleReplicaToBePromotedToPrimary() throws Exception { } logger.info("expected allocation ids: {} actual allocation ids: {}", expectedAllocationIds, allocationIds); }; - final ClusterService clusterService = internalCluster().getInstance(ClusterService.class, master); + final ClusterService clusterService = internalCluster().getInstance(ClusterService.class, clusterManager); clusterService.addListener(clusterStateListener); rerouteBuilder.get(); @@ -390,7 +393,7 @@ public void testForceStaleReplicaToBePromotedToPrimary() throws Exception { } public void testForceStaleReplicaToBePromotedToPrimaryOnWrongNode() throws Exception { - String master = internalCluster().startMasterOnlyNode(Settings.EMPTY); + String clusterManager = internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); internalCluster().startDataOnlyNodes(2); final String idxName = "test"; assertAcked( @@ -401,14 +404,14 @@ public void testForceStaleReplicaToBePromotedToPrimaryOnWrongNode() throws Excep .get() ); ensureGreen(); - createStaleReplicaScenario(master); + createStaleReplicaScenario(clusterManager); // Ensure the stopped primary's data is deleted so that it doesn't get picked up by the next datanode we start internalCluster().wipePendingDataDirectories(); internalCluster().startDataOnlyNodes(1); - ensureStableCluster(3, master); + ensureStableCluster(3, clusterManager); final int shardId = 0; final List nodeNames = new ArrayList<>(Arrays.asList(internalCluster().getNodeNames())); - nodeNames.remove(master); + nodeNames.remove(clusterManager); client().admin() .indices() .prepareShardStores(idxName) @@ -434,7 +437,7 @@ public void testForceStaleReplicaToBePromotedToPrimaryOnWrongNode() throws Excep } public void testForceStaleReplicaToBePromotedForGreenIndex() { - internalCluster().startMasterOnlyNode(Settings.EMPTY); + internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); final List dataNodes = internalCluster().startDataOnlyNodes(2); final String idxName = "test"; assertAcked( @@ -459,7 +462,7 @@ public void testForceStaleReplicaToBePromotedForGreenIndex() { } public void testForceStaleReplicaToBePromotedForMissingIndex() { - internalCluster().startMasterOnlyNode(Settings.EMPTY); + internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); final String dataNode = internalCluster().startDataOnlyNode(); final String idxName = "test"; IndexNotFoundException ex = expectThrows( @@ -497,7 +500,7 @@ public void testForcePrimaryShardIfAllocationDecidersSayNoAfterIndexCreation() t } public void testDoNotRemoveAllocationIdOnNodeLeave() throws Exception { - internalCluster().startMasterOnlyNode(Settings.EMPTY); + internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); internalCluster().startDataOnlyNode(Settings.EMPTY); assertAcked( client().admin() @@ -537,7 +540,7 @@ public boolean clearData(String nodeName) { } public void testRemoveAllocationIdOnWriteAfterNodeLeave() throws Exception { - internalCluster().startMasterOnlyNode(Settings.EMPTY); + internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); internalCluster().startDataOnlyNode(Settings.EMPTY); assertAcked( client().admin() @@ -657,7 +660,7 @@ public void testForceAllocatePrimaryOnNoDecision() throws Exception { * This test asserts that replicas failed to execute resync operations will be failed but not marked as stale. */ public void testPrimaryReplicaResyncFailed() throws Exception { - String master = internalCluster().startMasterOnlyNode(Settings.EMPTY); + String clusterManager = internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); final int numberOfReplicas = between(2, 3); final String oldPrimary = internalCluster().startDataOnlyNode(); assertAcked( @@ -671,7 +674,7 @@ public void testPrimaryReplicaResyncFailed() throws Exception { ensureGreen(); String timeout = randomFrom("0s", "1s", "2s"); assertAcked( - client(master).admin() + client(clusterManager).admin() .cluster() .prepareUpdateSettings() .setTransientSettings(Settings.builder().put("cluster.routing.allocation.enable", "none")) @@ -700,7 +703,7 @@ public void testPrimaryReplicaResyncFailed() throws Exception { internalCluster().stopRandomNode(InternalTestCluster.nameFilter(oldPrimary)); // Checks that we fails replicas in one side but not mark them as stale. assertBusy(() -> { - ClusterState state = client(master).admin().cluster().prepareState().get().getState(); + ClusterState state = client(clusterManager).admin().cluster().prepareState().get().getState(); final IndexShardRoutingTable shardRoutingTable = state.routingTable().shardRoutingTable(shardId); final String newPrimaryNode = state.getRoutingNodes().node(shardRoutingTable.primary.currentNodeId()).node().getName(); assertThat(newPrimaryNode, not(equalTo(oldPrimary))); @@ -712,7 +715,7 @@ public void testPrimaryReplicaResyncFailed() throws Exception { assertThat(state.metadata().index("test").inSyncAllocationIds(shardId.id()), hasSize(numberOfReplicas + 1)); }, 1, TimeUnit.MINUTES); assertAcked( - client(master).admin() + client(clusterManager).admin() .cluster() .prepareUpdateSettings() .setTransientSettings(Settings.builder().put("cluster.routing.allocation.enable", "all")) @@ -722,7 +725,7 @@ public void testPrimaryReplicaResyncFailed() throws Exception { partition.ensureHealthy(internalCluster()); logger.info("--> stop disrupting network and re-enable allocation"); assertBusy(() -> { - ClusterState state = client(master).admin().cluster().prepareState().get().getState(); + ClusterState state = client(clusterManager).admin().cluster().prepareState().get().getState(); assertThat(state.routingTable().shardRoutingTable(shardId).activeShards(), hasSize(numberOfReplicas)); assertThat(state.metadata().index("test").inSyncAllocationIds(shardId.id()), hasSize(numberOfReplicas + 1)); for (String node : replicaNodes) { diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/WeightedRoutingIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/WeightedRoutingIT.java new file mode 100644 index 0000000000000..2e0dd579d6910 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/WeightedRoutingIT.java @@ -0,0 +1,718 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.routing; + +import org.opensearch.ResourceNotFoundException; +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingResponse; +import org.opensearch.cluster.health.ClusterHealthStatus; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.discovery.ClusterManagerNotDiscoveredException; +import org.opensearch.plugins.Plugin; +import org.opensearch.snapshots.mockstore.MockRepository; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.transport.MockTransportService; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.hamcrest.Matchers.equalTo; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0, minNumDataNodes = 3) +public class WeightedRoutingIT extends OpenSearchIntegTestCase { + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class, MockRepository.Plugin.class); + } + + public void testPutWeightedRouting() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> starting 6 nodes on different zones"); + int nodeCountPerAZ = 2; + + logger.info("--> starting a dedicated cluster manager node"); + internalCluster().startClusterManagerOnlyNode(Settings.builder().put(commonSettings).build()); + + logger.info("--> starting 1 nodes on zones 'a' & 'b' & 'c'"); + List nodes_in_zone_a = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build() + ); + List nodes_in_zone_b = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build() + ); + List nodes_in_zone_c = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("7").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertEquals(response.isAcknowledged(), true); + + // put call made on a data node in zone a + response = internalCluster().client(randomFrom(nodes_in_zone_a.get(0), nodes_in_zone_a.get(1))) + .admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(0) + .get(); + assertEquals(response.isAcknowledged(), true); + } + + public void testPutWeightedRouting_InvalidAwarenessAttribute() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + internalCluster().startNodes( + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("3").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + WeightedRouting weightedRouting = new WeightedRouting("zone1", weights); + + assertThrows( + IllegalArgumentException.class, + () -> client().admin().cluster().prepareWeightedRouting().setWeightedRouting(weightedRouting).get() + ); + } + + public void testPutWeightedRouting_MoreThanOneZoneHasZeroWeight() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + internalCluster().startNodes( + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("3").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 0.0, "c", 0.0); + WeightedRouting weightedRouting = new WeightedRouting("zone1", weights); + + assertThrows( + IllegalArgumentException.class, + () -> client().admin().cluster().prepareWeightedRouting().setWeightedRouting(weightedRouting).get() + ); + } + + public void testGetWeightedRouting_WeightsNotSet() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + internalCluster().startNodes( + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("3").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + ClusterGetWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareGetWeightedRouting() + .setAwarenessAttribute("zone") + .get(); + assertNull(weightedRoutingResponse.weights()); + assertNull(weightedRoutingResponse.getDiscoveredClusterManager()); + } + + public void testGetWeightedRouting_WeightsAreSet() throws IOException { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + int nodeCountPerAZ = 2; + + logger.info("--> starting a dedicated cluster manager node"); + internalCluster().startClusterManagerOnlyNode(Settings.builder().put(commonSettings).build()); + + logger.info("--> starting 2 nodes on zones 'a' & 'b' & 'c'"); + List nodes_in_zone_a = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build() + ); + List nodes_in_zone_b = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build() + ); + List nodes_in_zone_c = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("7").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + // put api call to set weights + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertEquals(response.isAcknowledged(), true); + + // get api call to fetch weights + ClusterGetWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareGetWeightedRouting() + .setAwarenessAttribute("zone") + .get(); + assertEquals(weightedRouting, weightedRoutingResponse.weights()); + assertTrue(weightedRoutingResponse.getDiscoveredClusterManager()); + + // get api to fetch local weighted routing for a node in zone a + weightedRoutingResponse = internalCluster().client(randomFrom(nodes_in_zone_a.get(0), nodes_in_zone_a.get(1))) + .admin() + .cluster() + .prepareGetWeightedRouting() + .setAwarenessAttribute("zone") + .setRequestLocal(true) + .get(); + assertEquals(weightedRouting, weightedRoutingResponse.weights()); + assertTrue(weightedRoutingResponse.getDiscoveredClusterManager()); + + // get api to fetch local weighted routing for a node in zone b + weightedRoutingResponse = internalCluster().client(randomFrom(nodes_in_zone_b.get(0), nodes_in_zone_b.get(1))) + .admin() + .cluster() + .prepareGetWeightedRouting() + .setAwarenessAttribute("zone") + .setRequestLocal(true) + .get(); + assertEquals(weightedRouting, weightedRoutingResponse.weights()); + assertTrue(weightedRoutingResponse.getDiscoveredClusterManager()); + + // get api to fetch local weighted routing for a node in zone c + weightedRoutingResponse = internalCluster().client(randomFrom(nodes_in_zone_c.get(0), nodes_in_zone_c.get(1))) + .admin() + .cluster() + .prepareGetWeightedRouting() + .setAwarenessAttribute("zone") + .setRequestLocal(true) + .get(); + assertEquals(weightedRouting, weightedRoutingResponse.weights()); + assertTrue(weightedRoutingResponse.getDiscoveredClusterManager()); + + } + + public void testGetWeightedRouting_ClusterManagerNotDiscovered() throws Exception { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.fault_detection.leader_check.timeout", 10000 + "ms") + .put("cluster.fault_detection.leader_check.retry_count", 1) + .build(); + + int nodeCountPerAZ = 1; + + logger.info("--> starting a dedicated cluster manager node"); + String clusterManager = internalCluster().startClusterManagerOnlyNode(Settings.builder().put(commonSettings).build()); + + logger.info("--> starting 2 nodes on zones 'a' & 'b' & 'c'"); + List nodes_in_zone_a = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build() + ); + List nodes_in_zone_b = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build() + ); + List nodes_in_zone_c = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("4").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + // put api call to set weights + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertEquals(response.isAcknowledged(), true); + + Set nodesInOneSide = Stream.of(nodes_in_zone_a.get(0), nodes_in_zone_b.get(0), nodes_in_zone_c.get(0)) + .collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(clusterManager).collect(Collectors.toCollection(HashSet::new)); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.DISCONNECT + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + // wait for leader checker to fail + Thread.sleep(13000); + + // get api to fetch local weighted routing for a node in zone a or b + ClusterGetWeightedRoutingResponse weightedRoutingResponse = internalCluster().client( + randomFrom(nodes_in_zone_a.get(0), nodes_in_zone_b.get(0)) + ).admin().cluster().prepareGetWeightedRouting().setAwarenessAttribute("zone").setRequestLocal(true).get(); + assertEquals(weightedRouting, weightedRoutingResponse.weights()); + assertFalse(weightedRoutingResponse.getDiscoveredClusterManager()); + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + } + + public void testWeightedRoutingMetadataOnOSProcessRestart() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + internalCluster().startNodes( + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("3").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + // put api call to set weights + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertEquals(response.isAcknowledged(), true); + + ensureStableCluster(3); + + // routing weights are set in cluster metadata + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + + ensureGreen(); + + // Restart a random data node and check that OS process comes healthy + internalCluster().restartRandomDataNode(); + ensureGreen(); + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + } + + public void testWeightedRoutingOnOSProcessRestartAfterWeightDelete() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + internalCluster().startNodes( + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("3").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + // put api call to set weights + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertEquals(response.isAcknowledged(), true); + + ensureStableCluster(3); + + // routing weights are set in cluster metadata + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + + ensureGreen(); + + // delete weighted routing metadata + ClusterDeleteWeightedRoutingResponse deleteResponse = client().admin().cluster().prepareDeleteWeightedRouting().setVersion(0).get(); + assertTrue(deleteResponse.isAcknowledged()); + + // Restart a random data node and check that OS process comes healthy + internalCluster().restartRandomDataNode(); + ensureGreen(); + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + } + + public void testDeleteWeightedRouting_WeightsNotSet() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + internalCluster().startNodes( + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("3").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + assertNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + + // delete weighted routing metadata + ResourceNotFoundException exception = expectThrows( + ResourceNotFoundException.class, + () -> client().admin().cluster().prepareDeleteWeightedRouting().setVersion(-1).get() + ); + assertEquals(RestStatus.NOT_FOUND, exception.status()); + } + + public void testDeleteWeightedRouting_WeightsAreSet() throws IOException { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + internalCluster().startNodes( + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("3").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + // put api call to set weights + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(response.isAcknowledged()); + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + + // delete weighted routing metadata + ClusterDeleteWeightedRoutingResponse deleteResponse = client().admin().cluster().prepareDeleteWeightedRouting().setVersion(0).get(); + assertTrue(deleteResponse.isAcknowledged()); + } + + public void testPutAndDeleteWithVersioning() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> starting 6 nodes on different zones"); + int nodeCountPerAZ = 2; + + logger.info("--> starting a dedicated cluster manager node"); + internalCluster().startClusterManagerOnlyNode(Settings.builder().put(commonSettings).build()); + + logger.info("--> starting 1 nodes on zones 'a' & 'b' & 'c'"); + internalCluster().startDataOnlyNodes(nodeCountPerAZ, Settings.builder().put(commonSettings).put("node.attr.zone", "a").build()); + internalCluster().startDataOnlyNodes(nodeCountPerAZ, Settings.builder().put(commonSettings).put("node.attr.zone", "b").build()); + internalCluster().startDataOnlyNodes(nodeCountPerAZ, Settings.builder().put(commonSettings).put("node.attr.zone", "c").build()); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("7").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + + Map weights = Map.of("a", 1.0, "b", 2.0, "c", 3.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(response.isAcknowledged()); + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + + // update weights api call with correct version number + weights = Map.of("a", 1.0, "b", 2.0, "c", 4.0); + weightedRouting = new WeightedRouting("zone", weights); + response = client().admin().cluster().prepareWeightedRouting().setWeightedRouting(weightedRouting).setVersion(0).get(); + assertTrue(response.isAcknowledged()); + + // update weights api call with incorrect version number + weights = Map.of("a", 1.0, "b", 2.0, "c", 4.0); + WeightedRouting weightedRouting1 = new WeightedRouting("zone", weights); + UnsupportedWeightedRoutingStateException exception = expectThrows( + UnsupportedWeightedRoutingStateException.class, + () -> client().admin().cluster().prepareWeightedRouting().setWeightedRouting(weightedRouting1).setVersion(100).get() + ); + assertEquals(exception.status(), RestStatus.CONFLICT); + + // get weights call + ClusterGetWeightedRoutingResponse weightedRoutingResponse = client().admin() + .cluster() + .prepareGetWeightedRouting() + .setAwarenessAttribute("zone") + .get(); + + // update weights call using version returned by get api call + weights = Map.of("a", 1.0, "b", 2.0, "c", 5.0); + weightedRouting = new WeightedRouting("zone", weights); + response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(weightedRoutingResponse.getVersion()) + .get(); + assertTrue(response.isAcknowledged()); + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + + // delete weights by awareness attribute + ClusterDeleteWeightedRoutingResponse deleteResponse = client().admin() + .cluster() + .prepareDeleteWeightedRouting() + .setAwarenessAttribute("zone") + .setVersion(2) + .get(); + assertTrue(deleteResponse.isAcknowledged()); + + // update weights again and make sure that version number got updated on delete + weights = Map.of("a", 1.0, "b", 2.0, "c", 6.0); + weightedRouting = new WeightedRouting("zone", weights); + response = client().admin().cluster().prepareWeightedRouting().setWeightedRouting(weightedRouting).setVersion(3).get(); + assertTrue(response.isAcknowledged()); + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + + // delete weights + deleteResponse = client().admin().cluster().prepareDeleteWeightedRouting().setVersion(4).get(); + assertTrue(deleteResponse.isAcknowledged()); + + // delete weights call, incorrect version number + UnsupportedWeightedRoutingStateException deleteException = expectThrows( + UnsupportedWeightedRoutingStateException.class, + () -> client().admin().cluster().prepareDeleteWeightedRouting().setVersion(7).get() + ); + assertEquals(RestStatus.CONFLICT, deleteException.status()); + } + + public void testClusterHealthResponseWithEnsureNodeWeighedInParam() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .build(); + + logger.info("--> starting 3 nodes on different zones"); + int nodeCountPerAZ = 1; + + logger.info("--> starting a dedicated cluster manager node"); + String clusterManager = internalCluster().startClusterManagerOnlyNode(Settings.builder().put(commonSettings).build()); + + logger.info("--> starting 2 nodes on zones 'a' & 'b' & 'c'"); + List nodes_in_zone_a = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build() + ); + List nodes_in_zone_b = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build() + ); + List nodes_in_zone_c = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("4").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + + logger.info("--> setting shard routing weights for weighted round robin"); + + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertTrue(response.isAcknowledged()); + assertNotNull(internalCluster().clusterService().state().metadata().weightedRoutingMetadata()); + + // Check cluster health for weighed in node, health check should return a response with 200 status code + ClusterHealthResponse nodeLocalHealth = client(nodes_in_zone_a.get(0)).admin() + .cluster() + .prepareHealth() + .setLocal(true) + .setEnsureNodeWeighedIn(true) + .get(); + assertFalse(nodeLocalHealth.isTimedOut()); + + // Check cluster health for weighed away node, health check should respond with an exception + NodeWeighedAwayException ex = expectThrows( + NodeWeighedAwayException.class, + () -> client(nodes_in_zone_c.get(0)).admin().cluster().prepareHealth().setLocal(true).setEnsureNodeWeighedIn(true).get() + ); + assertTrue(ex.getMessage().contains("local node is weighed away")); + + logger.info("--> running cluster health on an index that does not exists"); + ClusterHealthResponse healthResponse = client(nodes_in_zone_c.get(0)).admin() + .cluster() + .prepareHealth("test1") + .setLocal(true) + .setEnsureNodeWeighedIn(true) + .setTimeout("1s") + .execute() + .actionGet(); + assertThat(healthResponse.isTimedOut(), equalTo(true)); + assertThat(healthResponse.getStatus(), equalTo(ClusterHealthStatus.RED)); + assertThat(healthResponse.getIndices().isEmpty(), equalTo(true)); + + Set nodesInOneSide = Stream.of(nodes_in_zone_a.get(0), nodes_in_zone_b.get(0), nodes_in_zone_c.get(0)) + .collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(clusterManager).collect(Collectors.toCollection(HashSet::new)); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.DISCONNECT + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + // wait for leader checker to fail + Thread.sleep(13000); + + // Check cluster health for weighed in node when cluster manager is not discovered, health check should + // return a response with 503 status code + assertThrows( + ClusterManagerNotDiscoveredException.class, + () -> client(nodes_in_zone_a.get(0)).admin().cluster().prepareHealth().setLocal(true).setEnsureNodeWeighedIn(true).get() + ); + + // Check cluster health for weighed away node when cluster manager is not discovered, health check should + // return a response with 503 status code + assertThrows( + ClusterManagerNotDiscoveredException.class, + () -> client(nodes_in_zone_c.get(0)).admin().cluster().prepareHealth().setLocal(true).setEnsureNodeWeighedIn(true).get() + ); + networkDisruption.stopDisrupting(); + Thread.sleep(1000); + + // delete weights + ClusterDeleteWeightedRoutingResponse deleteResponse = client().admin().cluster().prepareDeleteWeightedRouting().setVersion(0).get(); + assertTrue(deleteResponse.isAcknowledged()); + + // Check local cluster health + nodeLocalHealth = client(nodes_in_zone_c.get(0)).admin() + .cluster() + .prepareHealth() + .setLocal(true) + .setEnsureNodeWeighedIn(true) + .get(); + assertFalse(nodeLocalHealth.isTimedOut()); + assertTrue(nodeLocalHealth.hasDiscoveredClusterManager()); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/allocation/decider/DiskThresholdDeciderIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/allocation/decider/DiskThresholdDeciderIT.java index eb3e61d83a948..100674a44737e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/allocation/decider/DiskThresholdDeciderIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/allocation/decider/DiskThresholdDeciderIT.java @@ -36,14 +36,17 @@ import org.apache.lucene.tests.mockfile.FilterFileSystemProvider; import org.apache.lucene.tests.mockfile.FilterPath; import org.apache.lucene.util.Constants; - import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; +import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest; import org.opensearch.action.admin.indices.stats.ShardStats; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.cluster.ClusterInfoService; +import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.InternalClusterInfoService; +import org.opensearch.cluster.MockInternalClusterInfoService; import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.routing.IndexShardRoutingTable; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.ShardRoutingState; @@ -54,20 +57,21 @@ import org.opensearch.common.io.PathUtils; import org.opensearch.common.io.PathUtilsForTesting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; +import org.opensearch.index.IndexSettings; +import org.opensearch.monitor.fs.FsInfo; import org.opensearch.monitor.fs.FsService; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.fs.FsRepository; import org.opensearch.snapshots.RestoreInfo; import org.opensearch.snapshots.SnapshotInfo; import org.opensearch.snapshots.SnapshotState; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; - +import org.opensearch.test.OpenSearchIntegTestCase; import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; @@ -83,8 +87,8 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -92,6 +96,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import static org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider.INDEX_ROUTING_ALLOCATION_ENABLE_SETTING; import static org.opensearch.common.util.concurrent.ConcurrentCollections.newConcurrentMap; import static org.opensearch.index.store.Store.INDEX_STORE_STATS_REFRESH_INTERVAL_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; @@ -127,6 +132,8 @@ public void removeFilesystemProvider() { } private static final long WATERMARK_BYTES = new ByteSizeValue(10, ByteSizeUnit.KB).getBytes(); + private static final long TOTAL_SPACE_BYTES = new ByteSizeValue(100, ByteSizeUnit.KB).getBytes(); + private static final String INDEX_ROUTING_ALLOCATION_NODE_SETTING = "index.routing.allocation.include._name"; @Override protected Settings nodeSettings(int nodeOrdinal) { @@ -150,36 +157,28 @@ protected Settings nodeSettings(int nodeOrdinal) { @Override protected Collection> nodePlugins() { - return Collections.singletonList(InternalSettingsPlugin.class); + return List.of(InternalSettingsPlugin.class, MockInternalClusterInfoService.TestPlugin.class); } public void testHighWatermarkNotExceeded() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String dataNodeName = internalCluster().startDataOnlyNode(); ensureStableCluster(3); - final InternalClusterInfoService clusterInfoService = (InternalClusterInfoService) internalCluster().getCurrentMasterNodeInstance( - ClusterInfoService.class - ); - internalCluster().getCurrentMasterNodeInstance(ClusterService.class).addListener(event -> clusterInfoService.refresh()); + final InternalClusterInfoService clusterInfoService = (InternalClusterInfoService) internalCluster() + .getCurrentClusterManagerNodeInstance(ClusterInfoService.class); + internalCluster().getCurrentClusterManagerNodeInstance(ClusterService.class).addListener(event -> clusterInfoService.refresh()); final String dataNode0Id = internalCluster().getInstance(NodeEnvironment.class, dataNodeName).nodeId(); final Path dataNode0Path = internalCluster().getInstance(Environment.class, dataNodeName).dataFiles()[0]; final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT); - createIndex( - indexName, - Settings.builder() - .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) - .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 6) - .put(INDEX_STORE_STATS_REFRESH_INTERVAL_SETTING.getKey(), "0ms") - .build() - ); - final long minShardSize = createReasonableSizedShards(indexName); + final long minShardSize = createAndPopulateIndex(indexName, null); - // reduce disk size of node 0 so that no shards fit below the high watermark, forcing all shards onto the other data node - // (subtract the translog size since the disk threshold decider ignores this and may therefore move the shard back again) + // reduce disk size of node 0 so that no shards fit below the high watermark, forcing all shards onto the other + // data node (subtract the translog size since the disk threshold decider ignores this and may therefore move + // the shard back again). fileSystemProvider.getTestFileStore(dataNode0Path).setTotalSpace(minShardSize + WATERMARK_BYTES - 1L); assertBusyWithDiskUsageRefresh(dataNode0Id, indexName, empty()); @@ -188,8 +187,178 @@ public void testHighWatermarkNotExceeded() throws Exception { assertBusyWithDiskUsageRefresh(dataNode0Id, indexName, hasSize(1)); } + public void testIndexCreateBlockWhenAllNodesExceededHighWatermark() throws Exception { + final Settings settings = Settings.builder() + .put(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), false) + .build(); + + internalCluster().startClusterManagerOnlyNode(settings); + internalCluster().startDataOnlyNodes(2, settings); + ensureStableCluster(3); + final MockInternalClusterInfoService clusterInfoService = getMockInternalClusterInfoService(); + // Reduce disk space of all node until all of them is breaching high disk watermark. + clusterInfoService.setDiskUsageFunctionAndRefresh( + (discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, WATERMARK_BYTES - 1) + ); + assertBusy(() -> { + ClusterState state1 = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertFalse(state1.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + } + + public void testIndexCreateBlockNotAppliedWhenAnyNodesBelowHighWatermark() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + internalCluster().startDataOnlyNodes(2); + ensureStableCluster(3); + + final InternalClusterInfoService clusterInfoService = (InternalClusterInfoService) internalCluster() + .getCurrentClusterManagerNodeInstance(ClusterInfoService.class); + internalCluster().getCurrentClusterManagerNodeInstance(ClusterService.class).addListener(event -> clusterInfoService.refresh()); + + // Validate cluster block is not applied on the cluster + ClusterState state = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertFalse(state.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + } + + public void testIndexCreateBlockIsRemovedWhenAnyNodesNotExceedHighWatermarkWithAutoReleaseEnabled() throws Exception { + final Settings settings = Settings.builder() + .put(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), false) + .build(); + + internalCluster().startClusterManagerOnlyNode(settings); + internalCluster().startDataOnlyNodes(2, settings); + ensureStableCluster(3); + + final MockInternalClusterInfoService clusterInfoService = getMockInternalClusterInfoService(); + // Reduce disk space of all node until all of them is breaching high disk watermark. + clusterInfoService.setDiskUsageFunctionAndRefresh( + (discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, WATERMARK_BYTES - 1) + ); + + // Validate if cluster block is applied on the cluster + assertBusy(() -> { + ClusterState state = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + + // Free all the space + clusterInfoService.setDiskUsageFunctionAndRefresh( + (discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, TOTAL_SPACE_BYTES) + ); + + // Validate if index create block is removed on the cluster + assertBusy(() -> { + ClusterState state1 = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertFalse(state1.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + } + + public void testIndexCreateBlockIsRemovedWhenAnyNodesNotExceedHighWatermarkWithAutoReleaseDisabled() throws Exception { + final Settings settings = Settings.builder() + .put(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), false) + .put(DiskThresholdSettings.CLUSTER_CREATE_INDEX_BLOCK_AUTO_RELEASE.getKey(), false) + .build(); + + internalCluster().startClusterManagerOnlyNode(settings); + internalCluster().startDataOnlyNodes(2, settings); + ensureStableCluster(3); + + final MockInternalClusterInfoService clusterInfoService = getMockInternalClusterInfoService(); + // Reduce disk space of all node until all of them is breaching high disk watermark + clusterInfoService.setDiskUsageFunctionAndRefresh( + (discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, WATERMARK_BYTES - 1) + ); + + // Validate if cluster block is applied on the cluster + assertBusy(() -> { + ClusterState state = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + + // Free all the space + clusterInfoService.setDiskUsageFunctionAndRefresh( + (discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, TOTAL_SPACE_BYTES) + ); + + // Validate index create block is not removed on the cluster + assertBusy(() -> { + ClusterState state1 = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state1.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + } + + public void testDiskMonitorAppliesBlockBackWhenUserRemovesIndexCreateBlock() throws Exception { + final Settings settings = Settings.builder() + .put(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), false) + .put(DiskThresholdSettings.CLUSTER_CREATE_INDEX_BLOCK_AUTO_RELEASE.getKey(), false) + .build(); + + internalCluster().startClusterManagerOnlyNode(settings); + internalCluster().startDataOnlyNodes(2, settings); + ensureStableCluster(3); + + // User applies index create block. + Settings createBlockSetting = Settings.builder().put(Metadata.SETTING_CREATE_INDEX_BLOCK_SETTING.getKey(), "true").build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(createBlockSetting).get()); + final MockInternalClusterInfoService clusterInfoService = getMockInternalClusterInfoService(); + // Reduce disk space of all node until all of them is breaching high disk watermark and DiskMonitor applies block. + clusterInfoService.setDiskUsageFunctionAndRefresh( + (discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, WATERMARK_BYTES - 1) + ); + + // Validate if cluster block is applied on the cluster + assertBusy(() -> { + ClusterState state = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + + // User removes the block. + Settings removeBlockSetting = Settings.builder().put(Metadata.SETTING_CREATE_INDEX_BLOCK_SETTING.getKey(), false).build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(removeBlockSetting).get()); + + // Refresh so that DiskThresholdMonitor kicks in and applies block. + getMockInternalClusterInfoService().refresh(); + // Validate index create block is not removed on the cluster + assertBusy(() -> { + ClusterState state1 = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state1.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + } + + public void testIndexCreateBlockWithAReadOnlyBlock() throws Exception { + final Settings settings = Settings.builder() + .put(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), false) + .build(); + + internalCluster().startClusterManagerOnlyNode(settings); + final List dataNodeNames = internalCluster().startDataOnlyNodes(2, settings); + ensureStableCluster(3); + + final MockInternalClusterInfoService clusterInfoService = getMockInternalClusterInfoService(); + // Create one of the index. + final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT); + createAndPopulateIndex(indexName, dataNodeNames.get(0)); + // Apply a read_only_allow_delete_block on one of the index + // (can happen if the corresponding node has breached flood stage watermark). + final Settings readOnlySettings = Settings.builder() + .put(IndexMetadata.SETTING_READ_ONLY_ALLOW_DELETE, Boolean.TRUE.toString()) + .build(); + client().admin().indices().prepareUpdateSettings(indexName).setSettings(readOnlySettings).get(); + + // Reduce disk space of all node until all of them is breaching high disk watermark. + clusterInfoService.setDiskUsageFunctionAndRefresh( + (discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, WATERMARK_BYTES - 1) + ); + + // Validate index create block is applied on the cluster + assertBusy(() -> { + ClusterState state = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + } + public void testRestoreSnapshotAllocationDoesNotExceedWatermark() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String dataNodeName = internalCluster().startDataOnlyNode(); ensureStableCluster(3); @@ -202,24 +371,15 @@ public void testRestoreSnapshotAllocationDoesNotExceedWatermark() throws Excepti .setSettings(Settings.builder().put("location", randomRepoPath()).put("compress", randomBoolean())) ); - final InternalClusterInfoService clusterInfoService = (InternalClusterInfoService) internalCluster().getCurrentMasterNodeInstance( - ClusterInfoService.class - ); - internalCluster().getCurrentMasterNodeInstance(ClusterService.class).addListener(event -> clusterInfoService.refresh()); + final InternalClusterInfoService clusterInfoService = (InternalClusterInfoService) internalCluster() + .getCurrentClusterManagerNodeInstance(ClusterInfoService.class); + internalCluster().getCurrentClusterManagerNodeInstance(ClusterService.class).addListener(event -> clusterInfoService.refresh()); final String dataNode0Id = internalCluster().getInstance(NodeEnvironment.class, dataNodeName).nodeId(); final Path dataNode0Path = internalCluster().getInstance(Environment.class, dataNodeName).dataFiles()[0]; final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT); - createIndex( - indexName, - Settings.builder() - .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) - .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 6) - .put(INDEX_STORE_STATS_REFRESH_INTERVAL_SETTING.getKey(), "0ms") - .build() - ); - final long minShardSize = createReasonableSizedShards(indexName); + final long minShardSize = createAndPopulateIndex(indexName, null); final CreateSnapshotResponse createSnapshotResponse = client().admin() .cluster() @@ -274,6 +434,79 @@ public void testRestoreSnapshotAllocationDoesNotExceedWatermark() throws Excepti assertBusyWithDiskUsageRefresh(dataNode0Id, indexName, hasSize(1)); } + public void testDiskMonitorResetLastRuntimeMilliSecOnlyInFirstCall() throws Exception { + final Settings settings = Settings.builder() + .put(DiskThresholdSettings.CLUSTER_CREATE_INDEX_BLOCK_AUTO_RELEASE.getKey(), false) + .put(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING.getKey(), false) + .build(); + + internalCluster().startClusterManagerOnlyNode(settings); + internalCluster().startDataOnlyNodes(2, settings); + ensureStableCluster(3); + + final MockInternalClusterInfoService clusterInfoService = getMockInternalClusterInfoService(); + // Reduce disk space of all node. + clusterInfoService.setDiskUsageFunctionAndRefresh((discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, 0)); + + // Validate if cluster block is applied on the cluster + assertBusy(() -> { + ClusterState state = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertTrue(state.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + + // User removes index create block. + Settings removeBlockSetting = Settings.builder().put(Metadata.SETTING_CREATE_INDEX_BLOCK_SETTING.getKey(), "false").build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(removeBlockSetting).get()); + // Free all the space + clusterInfoService.setDiskUsageFunctionAndRefresh( + (discoveryNode, fsInfoPath) -> setDiskUsage(fsInfoPath, TOTAL_SPACE_BYTES, TOTAL_SPACE_BYTES) + ); + + // Validate index create block is removed on the cluster + assertBusy(() -> { + ClusterState state = client().admin().cluster().prepareState().setLocal(true).get().getState(); + assertFalse(state.blocks().hasGlobalBlockWithId(Metadata.CLUSTER_CREATE_INDEX_BLOCK.id())); + }, 30L, TimeUnit.SECONDS); + } + + private String populateNode(final String dataNodeName) throws Exception { + final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT); + createAndPopulateIndex(indexName, dataNodeName); + return indexName; + } + + private long createAndPopulateIndex(final String indexName, final String nodeName) throws Exception { + + final Settings.Builder indexSettingBuilder = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(INDEX_STORE_STATS_REFRESH_INTERVAL_SETTING.getKey(), "0ms") + .put(IndexSettings.INDEX_MERGE_ON_FLUSH_ENABLED.getKey(), false); + + // Depending on node name specified or not, we determine whether to enable node name based shard routing for index + // and whether reallocation is disabled on that index or not. + if (nodeName != null) { + indexSettingBuilder.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(INDEX_ROUTING_ALLOCATION_NODE_SETTING, nodeName); + createIndex(indexName, indexSettingBuilder.build()); + assertAcked( + client().admin() + .indices() + .updateSettings( + new UpdateSettingsRequest(indexName).settings( + Settings.builder().put(INDEX_ROUTING_ALLOCATION_ENABLE_SETTING.getKey(), "none") + ) + ) + .get() + ); + + ensureGreen(indexName); + } else { + indexSettingBuilder.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 6); + createIndex(indexName, indexSettingBuilder.build()); + } + + return createReasonableSizedShards(indexName); + } + private Set getShardRoutings(final String nodeId, final String indexName) { final Set shardRoutings = new HashSet<>(); for (IndexShardRoutingTable indexShardRoutingTable : client().admin() @@ -296,7 +529,7 @@ private Set getShardRoutings(final String nodeId, final String ind } /** - * Index documents until all the shards are at least WATERMARK_BYTES in size, and return the size of the smallest shard + * Index documents until all the shards are at least WATERMARK_BYTES in size, and return the size of the smallest shard. */ private long createReasonableSizedShards(final String indexName) throws InterruptedException { while (true) { @@ -328,13 +561,17 @@ private long createReasonableSizedShards(final String indexName) throws Interrup } } + private static FsInfo.Path setDiskUsage(FsInfo.Path original, long totalBytes, long freeBytes) { + return new FsInfo.Path(original.getPath(), original.getMount(), totalBytes, freeBytes, freeBytes); + } + private void refreshDiskUsage() { - final ClusterInfoService clusterInfoService = internalCluster().getCurrentMasterNodeInstance(ClusterInfoService.class); + final ClusterInfoService clusterInfoService = internalCluster().getCurrentClusterManagerNodeInstance(ClusterInfoService.class); ((InternalClusterInfoService) clusterInfoService).refresh(); // if the nodes were all under the low watermark already (but unbalanced) then a change in the disk usage doesn't trigger a reroute // even though it's now possible to achieve better balance, so we have to do an explicit reroute. TODO fix this? if (StreamSupport.stream(clusterInfoService.getClusterInfo().getNodeMostAvailableDiskUsages().values().spliterator(), false) - .allMatch(cur -> cur.value.getFreeBytes() > WATERMARK_BYTES)) { + .allMatch(cur -> cur.getFreeBytes() > WATERMARK_BYTES)) { assertAcked(client().admin().cluster().prepareReroute()); } @@ -353,7 +590,7 @@ private void refreshDiskUsage() { private void assertBusyWithDiskUsageRefresh(String nodeName, String indexName, Matcher> matcher) throws Exception { assertBusy(() -> { - // refresh the master's ClusterInfoService before checking the assigned shards because DiskThresholdMonitor might still + // refresh the cluster-manager's ClusterInfoService before checking the assigned shards because DiskThresholdMonitor might still // be processing a previous ClusterInfo update and will skip the new one (see DiskThresholdMonitor#onNewInfo(ClusterInfo) // and its internal checkInProgress flag) refreshDiskUsage(); @@ -363,6 +600,10 @@ private void assertBusyWithDiskUsageRefresh(String nodeName, String indexName, M }, 30L, TimeUnit.SECONDS); } + private MockInternalClusterInfoService getMockInternalClusterInfoService() { + return (MockInternalClusterInfoService) internalCluster().getCurrentClusterManagerNodeInstance(ClusterInfoService.class); + } + private static class TestFileStore extends FilterFileStore { private final Path path; diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/allocation/decider/MockDiskUsagesIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/allocation/decider/MockDiskUsagesIT.java index 05b0f10be02f3..a7755841c9ea9 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/routing/allocation/decider/MockDiskUsagesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/routing/allocation/decider/MockDiskUsagesIT.java @@ -269,9 +269,9 @@ public void testOnlyMovesEnoughShardsToDropBelowHighWatermark() throws Exception final MockInternalClusterInfoService clusterInfoService = getMockInternalClusterInfoService(); - final AtomicReference masterAppliedClusterState = new AtomicReference<>(); - internalCluster().getCurrentMasterNodeInstance(ClusterService.class).addListener(event -> { - masterAppliedClusterState.set(event.state()); + final AtomicReference clusterManagerAppliedClusterState = new AtomicReference<>(); + internalCluster().getCurrentClusterManagerNodeInstance(ClusterService.class).addListener(event -> { + clusterManagerAppliedClusterState.set(event.state()); clusterInfoService.refresh(); // so that a subsequent reroute sees disk usage according to the current state }); @@ -326,7 +326,7 @@ public void testOnlyMovesEnoughShardsToDropBelowHighWatermark() throws Exception fsInfoPath, 1000L, discoveryNode.getId().equals(nodeIds.get(2)) - ? 101L - masterAppliedClusterState.get().getRoutingNodes().node(nodeIds.get(2)).numberOfOwningShards() + ? 101L - clusterManagerAppliedClusterState.get().getRoutingNodes().node(nodeIds.get(2)).numberOfOwningShards() : 1000L ) ); @@ -349,7 +349,7 @@ public void testDoesNotExceedLowWatermarkWhenRebalancing() throws Exception { internalCluster().startNode(Settings.builder().put(Environment.PATH_DATA_SETTING.getKey(), createTempDir())); } - final AtomicReference masterAppliedClusterState = new AtomicReference<>(); + final AtomicReference clusterManagerAppliedClusterState = new AtomicReference<>(); final MockInternalClusterInfoService clusterInfoService = getMockInternalClusterInfoService(); @@ -358,9 +358,9 @@ public void testDoesNotExceedLowWatermarkWhenRebalancing() throws Exception { false ).map(RoutingNode::nodeId).collect(Collectors.toList()); - internalCluster().getCurrentMasterNodeInstance(ClusterService.class).addListener(event -> { + internalCluster().getCurrentClusterManagerNodeInstance(ClusterService.class).addListener(event -> { assertThat(event.state().getRoutingNodes().node(nodeIds.get(2)).size(), lessThanOrEqualTo(1)); - masterAppliedClusterState.set(event.state()); + clusterManagerAppliedClusterState.set(event.state()); clusterInfoService.refresh(); // so that a subsequent reroute sees disk usage according to the current state }); @@ -385,7 +385,7 @@ public void testDoesNotExceedLowWatermarkWhenRebalancing() throws Exception { fsInfoPath, 1000L, discoveryNode.getId().equals(nodeIds.get(2)) - ? 150L - masterAppliedClusterState.get().getRoutingNodes().node(nodeIds.get(2)).numberOfOwningShards() + ? 150L - clusterManagerAppliedClusterState.get().getRoutingNodes().node(nodeIds.get(2)).numberOfOwningShards() : 1000L ) ); @@ -544,7 +544,7 @@ private Map getShardCountByNodeId() { } private MockInternalClusterInfoService getMockInternalClusterInfoService() { - return (MockInternalClusterInfoService) internalCluster().getCurrentMasterNodeInstance(ClusterInfoService.class); + return (MockInternalClusterInfoService) internalCluster().getCurrentClusterManagerNodeInstance(ClusterInfoService.class); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/service/ClusterServiceIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/service/ClusterServiceIT.java index 252053f4968b4..7d9ffb23a2cf7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/service/ClusterServiceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/service/ClusterServiceIT.java @@ -391,7 +391,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS // The tasks can be re-ordered, so we need to check out-of-order Set controlSources = new HashSet<>(Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")); - List pendingClusterTasks = clusterService.getMasterService().pendingTasks(); + List pendingClusterTasks = clusterService.getClusterManagerService().pendingTasks(); assertThat(pendingClusterTasks.size(), greaterThanOrEqualTo(10)); assertThat(pendingClusterTasks.get(0).getSource().string(), equalTo("1")); assertThat(pendingClusterTasks.get(0).isExecuting(), equalTo(true)); @@ -413,7 +413,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS invoked2.await(); // whenever we test for no tasks, we need to wait since this is a live node - assertBusy(() -> assertTrue("Pending tasks not empty", clusterService.getMasterService().pendingTasks().isEmpty())); + assertBusy(() -> assertTrue("Pending tasks not empty", clusterService.getClusterManagerService().pendingTasks().isEmpty())); waitNoPendingTasksOnAll(); final CountDownLatch block2 = new CountDownLatch(1); @@ -453,7 +453,7 @@ public void onFailure(String source, Exception e) { } Thread.sleep(100); - pendingClusterTasks = clusterService.getMasterService().pendingTasks(); + pendingClusterTasks = clusterService.getClusterManagerService().pendingTasks(); assertThat(pendingClusterTasks.size(), greaterThanOrEqualTo(5)); controlSources = new HashSet<>(Arrays.asList("1", "2", "3", "4", "5")); for (PendingClusterTask task : pendingClusterTasks) { diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/settings/ClusterSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/settings/ClusterSettingsIT.java index 14365bba1e016..708be8b33fda8 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/settings/ClusterSettingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/settings/ClusterSettingsIT.java @@ -41,11 +41,10 @@ import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.test.OpenSearchIntegTestCase; - import org.junit.After; import java.util.Arrays; diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/settings/SettingsFilteringIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/settings/SettingsFilteringIT.java index 6de13ebe457a2..b6fbb21087b4d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/settings/SettingsFilteringIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/settings/SettingsFilteringIT.java @@ -47,10 +47,8 @@ import java.util.List; import static org.opensearch.action.admin.cluster.node.info.NodesInfoRequest.Metric.SETTINGS; - import static org.opensearch.test.OpenSearchIntegTestCase.Scope.SUITE; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; diff --git a/server/src/internalClusterTest/java/org/opensearch/cluster/shards/ClusterShardLimitIT.java b/server/src/internalClusterTest/java/org/opensearch/cluster/shards/ClusterShardLimitIT.java index a92849a077376..fb97ae59aae91 100644 --- a/server/src/internalClusterTest/java/org/opensearch/cluster/shards/ClusterShardLimitIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/cluster/shards/ClusterShardLimitIT.java @@ -33,7 +33,6 @@ package org.opensearch.cluster.shards; import org.opensearch.Version; - import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; @@ -41,20 +40,36 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.Client; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.common.Priority; +import org.opensearch.common.network.NetworkModule; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.unit.ByteSizeUnit; import org.opensearch.indices.ShardLimitValidator; import org.opensearch.snapshots.SnapshotInfo; import org.opensearch.snapshots.SnapshotState; +import org.opensearch.snapshots.mockstore.MockRepository; +import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.MockHttpTransport; +import org.opensearch.test.NodeConfigurationSource; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.transport.nio.MockNioTransportPlugin; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Function; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.indices.ShardLimitValidator.SETTING_CLUSTER_MAX_SHARDS_PER_NODE; +import static org.opensearch.indices.ShardLimitValidator.SETTING_MAX_SHARDS_PER_CLUSTER_KEY; import static org.opensearch.test.NodeRoles.dataNode; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; @@ -62,11 +77,17 @@ @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST) public class ClusterShardLimitIT extends OpenSearchIntegTestCase { - private static final String shardsPerNodeKey = ShardLimitValidator.SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey(); + private static final String shardsPerNodeKey = SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey(); + private static final String ignoreDotIndexKey = ShardLimitValidator.SETTING_CLUSTER_IGNORE_DOT_INDEXES.getKey(); public void testSettingClusterMaxShards() { int shardsPerNode = between(1, 500_000); - setShardsPerNode(shardsPerNode); + setMaxShardLimit(shardsPerNode, shardsPerNodeKey); + } + + public void testSettingIgnoreDotIndexes() { + boolean ignoreDotIndexes = randomBoolean(); + setIgnoreDotIndex(ignoreDotIndexes); } public void testMinimumPerNode() { @@ -99,8 +120,7 @@ public void testIndexCreationOverLimit() { ShardCounts counts = ShardCounts.forDataNodeCount(dataNodes); - setShardsPerNode(counts.getShardsPerNode()); - + setMaxShardLimit(counts.getShardsPerNode(), shardsPerNodeKey); // Create an index that will bring us up to the limit createIndex( "test", @@ -127,12 +147,186 @@ public void testIndexCreationOverLimit() { assertFalse(clusterState.getMetadata().hasIndex("should-fail")); } + /** + * The test checks if the index starting with the dot can be created if the node has + * number of shards equivalent to the cluster.max_shards_per_node and the cluster.ignore_Dot_indexes + * setting is set to true. If the cluster.ignore_Dot_indexes is set to true index creation of + * indexes starting with dot would succeed. + */ + public void testIndexCreationOverLimitForDotIndexesSucceeds() { + int dataNodes = client().admin().cluster().prepareState().get().getState().getNodes().getDataNodes().size(); + + // Setting the cluster.max_shards_per_node setting according to the data node count. + setMaxShardLimit(dataNodes, shardsPerNodeKey); + setIgnoreDotIndex(true); + + /* + Create an index that will bring us up to the limit. It would create index with primary equal to the + dataNodes * dataNodes so that cluster.max_shards_per_node setting is reached. + */ + createIndex( + "test", + Settings.builder() + .put(indexSettings()) + .put(SETTING_NUMBER_OF_SHARDS, dataNodes * dataNodes) + .put(SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + + // Getting total active shards in the cluster. + int currentActiveShards = client().admin().cluster().prepareHealth().get().getActiveShards(); + + // Getting cluster.max_shards_per_node setting + ClusterState clusterState = client().admin().cluster().prepareState().get().getState(); + String maxShardsPerNode = clusterState.getMetadata().settings().get(SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey()); + + // Checking if the total shards created are equivalent to dataNodes * cluster.max_shards_per_node + assertEquals(dataNodes * Integer.parseInt(maxShardsPerNode), currentActiveShards); + + createIndex( + ".test-index", + Settings.builder().put(indexSettings()).put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0).build() + ); + + clusterState = client().admin().cluster().prepareState().get().getState(); + assertTrue(clusterState.getMetadata().hasIndex(".test-index")); + } + + /** + * The test checks if the index starting with the dot should not be created if the node has + * number of shards equivalent to the cluster.max_shards_per_node and the cluster.ignore_Dot_indexes + * setting is set to false. If the cluster.ignore_Dot_indexes is set to false index creation of + * indexes starting with dot would fail as well. + */ + public void testIndexCreationOverLimitForDotIndexesFail() { + int dataNodes = client().admin().cluster().prepareState().get().getState().getNodes().getDataNodes().size(); + int maxAllowedShards = dataNodes * dataNodes; + + // Setting the cluster.max_shards_per_node setting according to the data node count. + setMaxShardLimit(dataNodes, shardsPerNodeKey); + + /* + Create an index that will bring us up to the limit. It would create index with primary equal to the + dataNodes * dataNodes so that cluster.max_shards_per_node setting is reached. + */ + createIndex( + "test", + Settings.builder() + .put(indexSettings()) + .put(SETTING_NUMBER_OF_SHARDS, maxAllowedShards) + .put(SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + + // Getting total active shards in the cluster. + int currentActiveShards = client().admin().cluster().prepareHealth().get().getActiveShards(); + + // Getting cluster.max_shards_per_node setting + ClusterState clusterState = client().admin().cluster().prepareState().get().getState(); + String maxShardsPerNode = clusterState.getMetadata().settings().get(SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey()); + + // Checking if the total shards created are equivalent to dataNodes * cluster.max_shards_per_node + assertEquals(dataNodes * Integer.parseInt(maxShardsPerNode), currentActiveShards); + + int extraShardCount = 1; + try { + createIndex( + ".test-index", + Settings.builder() + .put(indexSettings()) + .put(SETTING_NUMBER_OF_SHARDS, extraShardCount) + .put(SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + } catch (IllegalArgumentException e) { + verifyException(maxAllowedShards, currentActiveShards, extraShardCount, e); + } + clusterState = client().admin().cluster().prepareState().get().getState(); + assertFalse(clusterState.getMetadata().hasIndex(".test-index")); + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/6287") + public void testCreateIndexWithMaxClusterShardSetting() { + int dataNodes = client().admin().cluster().prepareState().get().getState().getNodes().getDataNodes().size(); + ClusterState clusterState = client().admin().cluster().prepareState().get().getState(); + setMaxShardLimit(dataNodes, shardsPerNodeKey); + + int maxAllowedShards = dataNodes + 1; + int extraShardCount = maxAllowedShards + 1; + // Getting total active shards in the cluster. + int currentActiveShards = client().admin().cluster().prepareHealth().get().getActiveShards(); + try { + setMaxShardLimit(maxAllowedShards, SETTING_MAX_SHARDS_PER_CLUSTER_KEY); + prepareCreate("test_index_with_cluster_shard_limit").setSettings( + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, extraShardCount).put(SETTING_NUMBER_OF_REPLICAS, 0).build() + ).get(); + } catch (final IllegalArgumentException ex) { + verifyException(Math.min(maxAllowedShards, dataNodes * dataNodes), currentActiveShards, extraShardCount, ex); + } finally { + setMaxShardLimit(-1, SETTING_MAX_SHARDS_PER_CLUSTER_KEY); + } + } + + /** + * The test checks if the index starting with the .ds- can be created if the node has + * number of shards equivalent to the cluster.max_shards_per_node and the cluster.ignore_Dot_indexes + * setting is set to true. If the cluster.ignore_Dot_indexes is set to true index creation of + * indexes starting with dot would only succeed and dataStream indexes would still have validation applied. + */ + public void testIndexCreationOverLimitForDataStreamIndexes() { + int dataNodes = client().admin().cluster().prepareState().get().getState().getNodes().getDataNodes().size(); + int maxAllowedShards = dataNodes * dataNodes; + + // Setting the cluster.max_shards_per_node setting according to the data node count. + setMaxShardLimit(dataNodes, shardsPerNodeKey); + setIgnoreDotIndex(true); + + /* + Create an index that will bring us up to the limit. It would create index with primary equal to the + dataNodes * dataNodes so that cluster.max_shards_per_node setting is reached. + */ + createIndex( + "test", + Settings.builder() + .put(indexSettings()) + .put(SETTING_NUMBER_OF_SHARDS, maxAllowedShards) + .put(SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + + // Getting total active shards in the cluster. + int currentActiveShards = client().admin().cluster().prepareHealth().get().getActiveShards(); + + // Getting cluster.max_shards_per_node setting + ClusterState clusterState = client().admin().cluster().prepareState().get().getState(); + String maxShardsPerNode = clusterState.getMetadata().settings().get(SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey()); + + // Checking if the total shards created are equivalent to dataNodes * cluster.max_shards_per_node + assertEquals(dataNodes * Integer.parseInt(maxShardsPerNode), currentActiveShards); + + int extraShardCount = 1; + try { + createIndex( + ".ds-test-index", + Settings.builder() + .put(indexSettings()) + .put(SETTING_NUMBER_OF_SHARDS, extraShardCount) + .put(SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + } catch (IllegalArgumentException e) { + verifyException(maxAllowedShards, currentActiveShards, extraShardCount, e); + } + clusterState = client().admin().cluster().prepareState().get().getState(); + assertFalse(clusterState.getMetadata().hasIndex(".ds-test-index")); + } + public void testIndexCreationOverLimitFromTemplate() { int dataNodes = client().admin().cluster().prepareState().get().getState().getNodes().getDataNodes().size(); final ShardCounts counts = ShardCounts.forDataNodeCount(dataNodes); - setShardsPerNode(counts.getShardsPerNode()); + setMaxShardLimit(counts.getShardsPerNode(), shardsPerNodeKey); if (counts.getFirstIndexShards() > 0) { createIndex( @@ -175,7 +369,7 @@ public void testIncreaseReplicasOverLimit() { int firstShardCount = between(2, 10); int shardsPerNode = firstShardCount - 1; - setShardsPerNode(shardsPerNode); + setMaxShardLimit(shardsPerNode, shardsPerNodeKey); prepareCreate( "growing-should-fail", @@ -221,7 +415,7 @@ public void testChangingMultipleIndicesOverLimit() { int secondIndexReplicas = dataNodes; int shardsPerNode = firstIndexFactor + (secondIndexFactor * (1 + secondIndexReplicas)); - setShardsPerNode(shardsPerNode); + setMaxShardLimit(shardsPerNode, shardsPerNodeKey); createIndex( "test-1-index", @@ -272,7 +466,7 @@ public void testPreserveExistingSkipsCheck() { int firstShardCount = between(2, 10); int shardsPerNode = firstShardCount - 1; - setShardsPerNode(shardsPerNode); + setMaxShardLimit(shardsPerNode, shardsPerNodeKey); prepareCreate( "test-index", @@ -345,7 +539,7 @@ public void testRestoreSnapshotOverLimit() { cluster().wipeIndices("snapshot-index"); // Reduce the shard limit and fill it up - setShardsPerNode(counts.getShardsPerNode()); + setMaxShardLimit(counts.getShardsPerNode(), shardsPerNodeKey); createIndex( "test-fill", Settings.builder() @@ -394,7 +588,7 @@ public void testOpenIndexOverLimit() { assertTrue(closeIndexResponse.isAcknowledged()); // Fill up the cluster - setShardsPerNode(counts.getShardsPerNode()); + setMaxShardLimit(counts.getShardsPerNode(), shardsPerNodeKey); createIndex( "test-fill", Settings.builder() @@ -414,6 +608,100 @@ public void testOpenIndexOverLimit() { assertFalse(clusterState.getMetadata().hasIndex("snapshot-index")); } + public void testIgnoreDotSettingOnMultipleNodes() throws IOException, InterruptedException { + int maxAllowedShardsPerNode = 10, indexPrimaryShards = 11, indexReplicaShards = 1; + + InternalTestCluster cluster = new InternalTestCluster( + randomLong(), + createTempDir(), + true, + true, + 0, + 0, + "cluster", + new NodeConfigurationSource() { + @Override + public Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(ClusterShardLimitIT.this.nodeSettings(nodeOrdinal)) + .put(NetworkModule.TRANSPORT_TYPE_KEY, getTestTransportType()) + .build(); + } + + @Override + public Path nodeConfigPath(int nodeOrdinal) { + return null; + } + }, + 0, + "cluster-", + Arrays.asList( + TestSeedPlugin.class, + MockHttpTransport.TestPlugin.class, + MockTransportService.TestPlugin.class, + MockNioTransportPlugin.class, + InternalSettingsPlugin.class, + MockRepository.Plugin.class + ), + Function.identity() + ); + cluster.beforeTest(random()); + + // Starting 3 ClusterManagerOnlyNode nodes + cluster.startClusterManagerOnlyNode(Settings.builder().put("cluster.ignore_dot_indexes", true).build()); + cluster.startClusterManagerOnlyNode(Settings.builder().put("cluster.ignore_dot_indexes", false).build()); + cluster.startClusterManagerOnlyNode(Settings.builder().put("cluster.ignore_dot_indexes", false).build()); + + // Starting 2 data nodes + cluster.startDataOnlyNode(Settings.builder().put("cluster.ignore_dot_indexes", false).build()); + cluster.startDataOnlyNode(Settings.builder().put("cluster.ignore_dot_indexes", false).build()); + + // Setting max shards per node to be 10 + cluster.client() + .admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put(shardsPerNodeKey, maxAllowedShardsPerNode)) + .get(); + + // Creating an index starting with dot having shards greater thn the desired node limit + cluster.client() + .admin() + .indices() + .prepareCreate(".test-index") + .setSettings( + Settings.builder().put(SETTING_NUMBER_OF_SHARDS, indexPrimaryShards).put(SETTING_NUMBER_OF_REPLICAS, indexReplicaShards) + ) + .get(); + + // As active ClusterManagerNode setting takes precedence killing the active one. + // This would be the first one where cluster.ignore_dot_indexes is true because the above calls are blocking. + cluster.stopCurrentClusterManagerNode(); + + // Waiting for all shards to get assigned + cluster.client().admin().cluster().prepareHealth().setWaitForGreenStatus().get(); + + // Creating an index starting with dot having shards greater thn the desired node limit + try { + cluster.client() + .admin() + .indices() + .prepareCreate(".test-index1") + .setSettings( + Settings.builder().put(SETTING_NUMBER_OF_SHARDS, indexPrimaryShards).put(SETTING_NUMBER_OF_REPLICAS, indexReplicaShards) + ) + .get(); + } catch (IllegalArgumentException e) { + ClusterHealthResponse clusterHealth = cluster.client().admin().cluster().prepareHealth().get(); + int currentActiveShards = clusterHealth.getActiveShards(); + int dataNodeCount = clusterHealth.getNumberOfDataNodes(); + int extraShardCount = indexPrimaryShards * (1 + indexReplicaShards); + verifyException(maxAllowedShardsPerNode * dataNodeCount, currentActiveShards, extraShardCount, e); + } + + IOUtils.close(cluster); + } + private int ensureMultipleDataNodes(int dataNodes) { if (dataNodes == 1) { internalCluster().startNode(dataNode()); @@ -434,23 +722,53 @@ private int ensureMultipleDataNodes(int dataNodes) { return dataNodes; } - private void setShardsPerNode(int shardsPerNode) { + /** + * Set max shard limit on either per node level or on cluster level. + * + * @param limit the limit value to set. + * @param key node level or cluster level setting key. + */ + private void setMaxShardLimit(int limit, String key) { + try { + ClusterUpdateSettingsResponse response; + if (frequently()) { + response = client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put(key, limit).build()) + .get(); + assertEquals(limit, response.getPersistentSettings().getAsInt(key, -1).intValue()); + } else { + response = client().admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(key, limit).build()) + .get(); + assertEquals(limit, response.getTransientSettings().getAsInt(key, -1).intValue()); + } + } catch (IllegalArgumentException ex) { + fail(ex.getMessage()); + } + + } + + private void setIgnoreDotIndex(boolean ignoreDotIndex) { try { ClusterUpdateSettingsResponse response; if (frequently()) { response = client().admin() .cluster() .prepareUpdateSettings() - .setPersistentSettings(Settings.builder().put(shardsPerNodeKey, shardsPerNode).build()) + .setPersistentSettings(Settings.builder().put(ignoreDotIndexKey, ignoreDotIndex).build()) .get(); - assertEquals(shardsPerNode, response.getPersistentSettings().getAsInt(shardsPerNodeKey, -1).intValue()); + assertEquals(ignoreDotIndex, response.getPersistentSettings().getAsBoolean(ignoreDotIndexKey, true)); } else { response = client().admin() .cluster() .prepareUpdateSettings() - .setTransientSettings(Settings.builder().put(shardsPerNodeKey, shardsPerNode).build()) + .setTransientSettings(Settings.builder().put(ignoreDotIndexKey, ignoreDotIndex).build()) .get(); - assertEquals(shardsPerNode, response.getTransientSettings().getAsInt(shardsPerNodeKey, -1).intValue()); + assertEquals(ignoreDotIndex, response.getTransientSettings().getAsBoolean(ignoreDotIndexKey, true)); } } catch (IllegalArgumentException ex) { fail(ex.getMessage()); @@ -471,4 +789,15 @@ private void verifyException(int dataNodes, ShardCounts counts, IllegalArgumentE assertEquals(expectedError, e.getMessage()); } + private void verifyException(int maxShards, int currentShards, int extraShards, IllegalArgumentException e) { + String expectedError = "Validation Failed: 1: this action would add [" + + extraShards + + "] total shards, but this cluster currently has [" + + currentShards + + "]/[" + + maxShards + + "] maximum shards open;"; + assertEquals(expectedError, e.getMessage()); + } + } diff --git a/server/src/internalClusterTest/java/org/opensearch/clustermanager/ClusterManagerTaskThrottlingIT.java b/server/src/internalClusterTest/java/org/opensearch/clustermanager/ClusterManagerTaskThrottlingIT.java new file mode 100644 index 0000000000000..3b80ef7820e08 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/clustermanager/ClusterManagerTaskThrottlingIT.java @@ -0,0 +1,214 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.clustermanager; + +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest; +import org.opensearch.cluster.metadata.ProcessClusterEventTimeoutException; +import org.opensearch.cluster.service.ClusterManagerThrottlingException; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.transport.TransportMessageListener; +import org.opensearch.transport.TransportService; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, numDataNodes = 0) +public class ClusterManagerTaskThrottlingIT extends OpenSearchIntegTestCase { + + /* + * This integ test will test end-end cluster manager throttling feature for + * remote cluster manager. + * + * It will check the number of request coming to cluster manager node + * should be total number of requests + throttled requests from cluster manager. + * This will ensure the end-end feature is working as cluster manager is throwing + * Throttling exception and data node is performing retries on it. + * + */ + public void testThrottlingForRemoteClusterManager() throws Exception { + try { + internalCluster().beforeTest(random()); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + String dataNode = internalCluster().startDataOnlyNode(); + int throttlingLimit = randomIntBetween(1, 5); + createIndex("test"); + setPutMappingThrottlingLimit(throttlingLimit); + + TransportService clusterManagerTransportService = (internalCluster().getInstance(TransportService.class, clusterManagerNode)); + AtomicInteger requestCountOnClusterManager = new AtomicInteger(); + AtomicInteger throttledRequest = new AtomicInteger(); + int totalRequest = randomIntBetween(throttlingLimit, 5 * throttlingLimit); + CountDownLatch latch = new CountDownLatch(totalRequest); + + clusterManagerTransportService.addMessageListener(new TransportMessageListener() { + @Override + public void onRequestReceived(long requestId, String action) { + if (action.contains("mapping")) { + requestCountOnClusterManager.incrementAndGet(); + } + } + + @Override + public void onResponseSent(long requestId, String action, Exception error) { + if (action.contains("mapping")) { + throttledRequest.incrementAndGet(); + assertEquals(ClusterManagerThrottlingException.class, error.getClass()); + } + } + }); + + ActionListener listener = new ActionListener() { + @Override + public void onResponse(Object o) { + latch.countDown(); + } + + @Override + public void onFailure(Exception e) { + latch.countDown(); + throw new AssertionError(e); + } + }; + + executePutMappingRequests(totalRequest, dataNode, listener); + latch.await(); + + assertEquals(totalRequest + throttledRequest.get(), requestCountOnClusterManager.get()); + assertBusy( + () -> { assertEquals(clusterService().getMasterService().numberOfThrottledPendingTasks(), throttledRequest.get()); } + ); + } finally { + clusterSettingCleanUp(); + } + } + + /* + * This will test the throttling feature for single node. + * + * Here we will assert the client behaviour that client's request is not + * failed, i.e. Throttling exception is not passed to the client. + * Data node will internally do the retry and request should pass. + * + */ + public void testThrottlingForSingleNode() throws Exception { + try { + internalCluster().beforeTest(random()); + String node = internalCluster().startNode(); + int throttlingLimit = randomIntBetween(1, 5); + createIndex("test"); + setPutMappingThrottlingLimit(throttlingLimit); + + AtomicInteger successfulRequest = new AtomicInteger(); + int totalRequest = randomIntBetween(throttlingLimit, 3 * throttlingLimit); + CountDownLatch latch = new CountDownLatch(totalRequest); + + ActionListener listener = new ActionListener() { + @Override + public void onResponse(Object o) { + successfulRequest.incrementAndGet(); + latch.countDown(); + } + + @Override + public void onFailure(Exception e) { + latch.countDown(); + throw new AssertionError(e); + } + }; + executePutMappingRequests(totalRequest, node, listener); + + latch.await(); + assertEquals(totalRequest, successfulRequest.get()); + } finally { + clusterSettingCleanUp(); + } + } + + /* + * This will test the timeout of tasks during throttling. + * + * Here we will assert the client behaviour that client's request is not + * failed with throttling exception but timeout exception. + * It also verifies that if limit is set to 0, all tasks are getting timedout. + */ + + public void testTimeoutWhileThrottling() throws Exception { + try { + internalCluster().beforeTest(random()); + String node = internalCluster().startNode(); + int throttlingLimit = 0; // throttle all the tasks + createIndex("test"); + setPutMappingThrottlingLimit(throttlingLimit); + + AtomicInteger timedoutRequest = new AtomicInteger(); + int totalRequest = randomIntBetween(1, 5); + CountDownLatch latch = new CountDownLatch(totalRequest); + + ActionListener listener = new ActionListener() { + @Override + public void onResponse(Object o) { + latch.countDown(); + throw new AssertionError("Request should not succeed"); + } + + @Override + public void onFailure(Exception e) { + timedoutRequest.incrementAndGet(); + latch.countDown(); + assertTrue(e instanceof ProcessClusterEventTimeoutException); + } + }; + executePutMappingRequests(totalRequest, node, listener); + + latch.await(); + assertEquals(totalRequest, timedoutRequest.get()); // verifying all requests were timed out with 0 throttling limit + } finally { + clusterSettingCleanUp(); + } + } + + private void executePutMappingRequests(int totalRequest, String node, ActionListener listener) throws Exception { + Thread[] threads = new Thread[totalRequest]; + for (int i = 0; i < totalRequest; i++) { + PutMappingRequest putMappingRequest = new PutMappingRequest("test").source("field" + i, "type=text"); + threads[i] = new Thread(new Runnable() { + @Override + public void run() { + internalCluster().client(node).admin().indices().putMapping(putMappingRequest, listener); + } + }); + } + for (int i = 0; i < totalRequest; i++) { + threads[i].run(); + } + for (int i = 0; i < totalRequest; i++) { + threads[i].join(); + } + } + + private void setPutMappingThrottlingLimit(int throttlingLimit) { + ClusterUpdateSettingsRequest settingsRequest = new ClusterUpdateSettingsRequest(); + Settings settings = Settings.builder().put("cluster_manager.throttling.thresholds.put-mapping.value", throttlingLimit).build(); + settingsRequest.transientSettings(settings); + assertAcked(client().admin().cluster().updateSettings(settingsRequest).actionGet()); + } + + private void clusterSettingCleanUp() { + // We need to remove the throttling limit from setting as part of test cleanup + ClusterUpdateSettingsRequest settingsRequest = new ClusterUpdateSettingsRequest(); + Settings settings = Settings.builder().put("cluster_manager.throttling.thresholds.put-mapping.value", (String) null).build(); + settingsRequest.transientSettings(settings); + assertAcked(client().admin().cluster().updateSettings(settingsRequest).actionGet()); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/common/settings/ConsistentSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/common/settings/ConsistentSettingsIT.java index 0ffddf815c40b..f8f56cf05fbea 100644 --- a/server/src/internalClusterTest/java/org/opensearch/common/settings/ConsistentSettingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/common/settings/ConsistentSettingsIT.java @@ -34,6 +34,7 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Setting.AffixSetting; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; diff --git a/server/src/internalClusterTest/java/org/opensearch/common/settings/UpgradeSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/common/settings/UpgradeSettingsIT.java index 3c8f914ba4f2f..da0ad316db874 100644 --- a/server/src/internalClusterTest/java/org/opensearch/common/settings/UpgradeSettingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/common/settings/UpgradeSettingsIT.java @@ -37,7 +37,6 @@ import org.opensearch.cluster.metadata.Metadata; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchSingleNodeTestCase; - import org.junit.After; import java.util.Arrays; diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterDisruptionCleanSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterDisruptionCleanSettingsIT.java index 61a47d2bb0237..39a4f2aa82828 100644 --- a/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterDisruptionCleanSettingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterDisruptionCleanSettingsIT.java @@ -32,19 +32,20 @@ package org.opensearch.discovery; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.indices.store.IndicesStoreIntegrationIT; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; @@ -63,7 +64,7 @@ protected Collection> nodePlugins() { public void testSearchWithRelocationAndSlowClusterStateProcessing() throws Exception { // Don't use AbstractDisruptionTestCase.DEFAULT_SETTINGS as settings // (which can cause node disconnects on a slow CI machine) - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String node_1 = internalCluster().startDataOnlyNode(); logger.info("--> creating index [test] with one shard and on replica"); @@ -80,7 +81,7 @@ public void testSearchWithRelocationAndSlowClusterStateProcessing() throws Excep final String node_2 = internalCluster().startDataOnlyNode(); List indexRequestBuilderList = new ArrayList<>(); for (int i = 0; i < 100; i++) { - indexRequestBuilderList.add(client().prepareIndex().setIndex("test").setSource("{\"int_field\":1}", XContentType.JSON)); + indexRequestBuilderList.add(client().prepareIndex().setIndex("test").setSource("{\"int_field\":1}", MediaTypeRegistry.JSON)); } indexRandom(true, indexRequestBuilderList); diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterDisruptionIT.java index ceec1315a9d59..38b86d307d197 100644 --- a/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterDisruptionIT.java @@ -34,9 +34,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.lucene.index.CorruptIndexException; - import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.NoShardAvailableActionException; import org.opensearch.action.get.GetResponse; import org.opensearch.action.index.IndexRequestBuilder; @@ -53,13 +51,14 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.ConcurrentCollections; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.VersionType; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardTestCase; import org.opensearch.indices.IndicesService; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.disruption.NetworkDisruption; import org.opensearch.test.disruption.NetworkDisruption.Bridge; import org.opensearch.test.disruption.NetworkDisruption.TwoPartitions; @@ -173,7 +172,10 @@ public void testAckedIndexing() throws Exception { logger.trace("[{}] indexing id [{}] through node [{}] targeting shard [{}]", name, id, node, shard); IndexRequestBuilder indexRequestBuilder = client.prepareIndex("test") .setId(id) - .setSource(Collections.singletonMap(randomFrom(fieldNames), randomNonNegativeLong()), XContentType.JSON) + .setSource( + Collections.singletonMap(randomFrom(fieldNames), randomNonNegativeLong()), + MediaTypeRegistry.JSON + ) .setTimeout(timeout); if (conflictMode == ConflictMode.external) { @@ -240,7 +242,7 @@ public void testAckedIndexing() throws Exception { node ); } - // in case of a bridge partition, shard allocation can fail "index.allocation.max_retries" times if the master + // in case of a bridge partition, shard allocation can fail "index.allocation.max_retries" times if the cluster-manager // is the super-connected node and recovery source and target are on opposite sides of the bridge if (disruptionScheme instanceof NetworkDisruption && ((NetworkDisruption) disruptionScheme).getDisruptedLinks() instanceof Bridge) { @@ -338,26 +340,26 @@ public void testRejoinDocumentExistsInAllShardCopies() throws Exception { // simulate handling of sending shard failure during an isolation public void testSendingShardFailure() throws Exception { List nodes = startCluster(3); - String masterNode = internalCluster().getMasterName(); - List nonMasterNodes = nodes.stream().filter(node -> !node.equals(masterNode)).collect(Collectors.toList()); - String nonMasterNode = randomFrom(nonMasterNodes); + String clusterManagerNode = internalCluster().getClusterManagerName(); + List nonClusterManagerNodes = nodes.stream().filter(node -> !node.equals(clusterManagerNode)).collect(Collectors.toList()); + String nonClusterManagerNode = randomFrom(nonClusterManagerNodes); assertAcked( prepareCreate("test").setSettings( Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) ) ); ensureGreen(); - String nonMasterNodeId = internalCluster().clusterService(nonMasterNode).localNode().getId(); + String nonClusterManagerNodeId = internalCluster().clusterService(nonClusterManagerNode).localNode().getId(); // fail a random shard ShardRouting failedShard = randomFrom( - clusterService().state().getRoutingNodes().node(nonMasterNodeId).shardsWithState(ShardRoutingState.STARTED) + clusterService().state().getRoutingNodes().node(nonClusterManagerNodeId).shardsWithState(ShardRoutingState.STARTED) ); - ShardStateAction service = internalCluster().getInstance(ShardStateAction.class, nonMasterNode); + ShardStateAction service = internalCluster().getInstance(ShardStateAction.class, nonClusterManagerNode); CountDownLatch latch = new CountDownLatch(1); AtomicBoolean success = new AtomicBoolean(); - String isolatedNode = randomBoolean() ? masterNode : nonMasterNode; + String isolatedNode = randomBoolean() ? clusterManagerNode : nonClusterManagerNode; TwoPartitions partitions = isolateNode(isolatedNode); // we cannot use the NetworkUnresponsive disruption type here as it will swallow the "shard failed" request, calling neither // onSuccess nor onFailure on the provided listener. @@ -385,10 +387,10 @@ public void onFailure(Exception e) { } ); - if (isolatedNode.equals(nonMasterNode)) { - assertNoMaster(nonMasterNode); + if (isolatedNode.equals(nonClusterManagerNode)) { + assertNoClusterManager(nonClusterManagerNode); } else { - ensureStableCluster(2, nonMasterNode); + ensureStableCluster(2, nonClusterManagerNode); } // heal the partition @@ -409,11 +411,11 @@ public void onFailure(Exception e) { } } - public void testCannotJoinIfMasterLostDataFolder() throws Exception { - String masterNode = internalCluster().startMasterOnlyNode(); + public void testCannotJoinIfClusterManagerLostDataFolder() throws Exception { + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); String dataNode = internalCluster().startDataOnlyNode(); - internalCluster().restartNode(masterNode, new InternalTestCluster.RestartCallback() { + internalCluster().restartNode(clusterManagerNode, new InternalTestCluster.RestartCallback() { @Override public boolean clearData(String nodeName) { return true; @@ -424,7 +426,7 @@ public Settings onNodeStopped(String nodeName) { return Settings.builder() .put(ClusterBootstrapService.INITIAL_CLUSTER_MANAGER_NODES_SETTING.getKey(), nodeName) /* - * the data node might join while the master is still not fully established as master just yet and bypasses the join + * the data node might join while the cluster-manager is still not fully established as cluster-manager just yet and bypasses the join * validation that is done before adding the node to the cluster. Only the join validation when handling the publish * request takes place, but at this point the cluster state has been successfully committed, and will subsequently be * exposed to the applier. The health check below therefore sees the cluster state with the 2 nodes and thinks all is @@ -442,9 +444,9 @@ public boolean validateClusterForming() { }); assertBusy(() -> { - assertFalse(internalCluster().client(masterNode).admin().cluster().prepareHealth().get().isTimedOut()); + assertFalse(internalCluster().client(clusterManagerNode).admin().cluster().prepareHealth().get().isTimedOut()); assertTrue( - internalCluster().client(masterNode) + internalCluster().client(clusterManagerNode) .admin() .cluster() .prepareHealth() @@ -458,34 +460,35 @@ public boolean validateClusterForming() { } /** - * Tests that indices are properly deleted even if there is a master transition in between. + * Tests that indices are properly deleted even if there is a cluster-manager transition in between. * Test for https://github.com/elastic/elasticsearch/issues/11665 */ public void testIndicesDeleted() throws Exception { final String idxName = "test"; - final List allMasterEligibleNodes = internalCluster().startMasterOnlyNodes(2); + final List allClusterManagerEligibleNodes = internalCluster().startClusterManagerOnlyNodes(2); final String dataNode = internalCluster().startDataOnlyNode(); ensureStableCluster(3); assertAcked(prepareCreate("test")); - final String masterNode1 = internalCluster().getMasterName(); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); NetworkDisruption networkDisruption = new NetworkDisruption( - new TwoPartitions(masterNode1, dataNode), + new TwoPartitions(clusterManagerNode1, dataNode), NetworkDisruption.UNRESPONSIVE ); internalCluster().setDisruptionScheme(networkDisruption); networkDisruption.startDisrupting(); // We know this will time out due to the partition, we check manually below to not proceed until - // the delete has been applied to the master node and the master eligible node. - internalCluster().client(masterNode1).admin().indices().prepareDelete(idxName).setTimeout("0s").get(); - // Don't restart the master node until we know the index deletion has taken effect on master and the master eligible node. + // the delete has been applied to the cluster-manager node and the cluster-manager eligible node. + internalCluster().client(clusterManagerNode1).admin().indices().prepareDelete(idxName).setTimeout("0s").get(); + // Don't restart the cluster-manager node until we know the index deletion has taken effect on cluster-manager and the + // cluster-manager eligible node. assertBusy(() -> { - for (String masterNode : allMasterEligibleNodes) { - final ClusterState masterState = internalCluster().clusterService(masterNode).state(); - assertTrue("index not deleted on " + masterNode, masterState.metadata().hasIndex(idxName) == false); + for (String clusterManagerNode : allClusterManagerEligibleNodes) { + final ClusterState clusterManagerState = internalCluster().clusterService(clusterManagerNode).state(); + assertTrue("index not deleted on " + clusterManagerNode, clusterManagerState.metadata().hasIndex(idxName) == false); } }); - internalCluster().restartNode(masterNode1, InternalTestCluster.EMPTY_CALLBACK); + internalCluster().restartNode(clusterManagerNode1, InternalTestCluster.EMPTY_CALLBACK); ensureYellow(); assertFalse(client().admin().indices().prepareExists(idxName).get().isExists()); } @@ -514,7 +517,10 @@ public void testRestartNodeWhileIndexing() throws Exception { try { IndexResponse response = client().prepareIndex(index) .setId(id) - .setSource(Collections.singletonMap("f" + randomIntBetween(1, 10), randomNonNegativeLong()), XContentType.JSON) + .setSource( + Collections.singletonMap("f" + randomIntBetween(1, 10), randomNonNegativeLong()), + MediaTypeRegistry.JSON + ) .get(); assertThat(response.getResult(), is(oneOf(CREATED, UPDATED))); logger.info("--> index id={} seq_no={}", response.getId(), response.getSeqNo()); diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterManagerDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterManagerDisruptionIT.java new file mode 100644 index 0000000000000..f0d52405efac6 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/discovery/ClusterManagerDisruptionIT.java @@ -0,0 +1,345 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.discovery; + +import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import org.opensearch.action.admin.indices.stats.ShardStats; +import org.opensearch.action.bulk.BulkRequestBuilder; +import org.opensearch.action.bulk.BulkResponse; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.coordination.NoClusterManagerBlockService; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.disruption.BlockClusterManagerServiceOnClusterManager; +import org.opensearch.test.disruption.IntermittentLongGCDisruption; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.disruption.NetworkDisruption.TwoPartitions; +import org.opensearch.test.disruption.ServiceDisruptionScheme; +import org.opensearch.test.disruption.SingleNodeDisruption; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; + +/** + * Tests relating to the loss of the cluster-manager. + */ +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class ClusterManagerDisruptionIT extends AbstractDisruptionTestCase { + + /** + * Test that cluster recovers from a long GC on cluster-manager that causes other nodes to elect a new one + */ + public void testClusterManagerNodeGCs() throws Exception { + List nodes = startCluster(3); + + String oldClusterManagerNode = internalCluster().getClusterManagerName(); + // a very long GC, but it's OK as we remove the disruption when it has had an effect + SingleNodeDisruption clusterManagerNodeDisruption = new IntermittentLongGCDisruption( + random(), + oldClusterManagerNode, + 100, + 200, + 30000, + 60000 + ); + internalCluster().setDisruptionScheme(clusterManagerNodeDisruption); + clusterManagerNodeDisruption.startDisrupting(); + + Set oldNonClusterManagerNodesSet = new HashSet<>(nodes); + oldNonClusterManagerNodesSet.remove(oldClusterManagerNode); + + List oldNonClusterManagerNodes = new ArrayList<>(oldNonClusterManagerNodesSet); + + logger.info("waiting for nodes to de-elect cluster-manager [{}]", oldClusterManagerNode); + for (String node : oldNonClusterManagerNodesSet) { + assertDifferentClusterManager(node, oldClusterManagerNode); + } + + logger.info("waiting for nodes to elect a new cluster-manager"); + ensureStableCluster(2, oldNonClusterManagerNodes.get(0)); + + // restore GC + clusterManagerNodeDisruption.stopDisrupting(); + final TimeValue waitTime = new TimeValue( + DISRUPTION_HEALING_OVERHEAD.millis() + clusterManagerNodeDisruption.expectedTimeToHeal().millis() + ); + ensureStableCluster(3, waitTime, false, oldNonClusterManagerNodes.get(0)); + + // make sure all nodes agree on cluster-manager + String newClusterManager = internalCluster().getClusterManagerName(); + assertThat(newClusterManager, not(equalTo(oldClusterManagerNode))); + assertClusterManager(newClusterManager, nodes); + } + + /** + * This test isolates the cluster-manager from rest of the cluster, waits for a new cluster-manager to be elected, restores the partition + * and verifies that all node agree on the new cluster state + */ + public void testIsolateClusterManagerAndVerifyClusterStateConsensus() throws Exception { + final List nodes = startCluster(3); + + assertAcked( + prepareCreate("test").setSettings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1 + randomInt(2)) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, randomInt(2)) + ) + ); + + ensureGreen(); + String isolatedNode = internalCluster().getClusterManagerName(); + TwoPartitions partitions = isolateNode(isolatedNode); + NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); + networkDisruption.startDisrupting(); + + String nonIsolatedNode = partitions.getMajoritySide().iterator().next(); + + // make sure cluster reforms + ensureStableCluster(2, nonIsolatedNode); + + // make sure isolated need picks up on things. + assertNoClusterManager(isolatedNode, TimeValue.timeValueSeconds(40)); + + // restore isolation + networkDisruption.stopDisrupting(); + + for (String node : nodes) { + ensureStableCluster( + 3, + new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + networkDisruption.expectedTimeToHeal().millis()), + true, + node + ); + } + + logger.info("issue a reroute"); + // trigger a reroute now, instead of waiting for the background reroute of RerouteService + assertAcked(client().admin().cluster().prepareReroute()); + // and wait for it to finish and for the cluster to stabilize + ensureGreen("test"); + + // verify all cluster states are the same + // use assert busy to wait for cluster states to be applied (as publish_timeout has low value) + assertBusy(() -> { + ClusterState state = null; + for (String node : nodes) { + ClusterState nodeState = getNodeClusterState(node); + if (state == null) { + state = nodeState; + continue; + } + // assert nodes are identical + try { + assertEquals("unequal versions", state.version(), nodeState.version()); + assertEquals("unequal node count", state.nodes().getSize(), nodeState.nodes().getSize()); + assertEquals( + "different cluster-managers ", + state.nodes().getClusterManagerNodeId(), + nodeState.nodes().getClusterManagerNodeId() + ); + assertEquals("different meta data version", state.metadata().version(), nodeState.metadata().version()); + assertEquals("different routing", state.routingTable().toString(), nodeState.routingTable().toString()); + } catch (AssertionError t) { + fail( + "failed comparing cluster state: " + + t.getMessage() + + "\n" + + "--- cluster state of node [" + + nodes.get(0) + + "]: ---\n" + + state + + "\n--- cluster state [" + + node + + "]: ---\n" + + nodeState + ); + } + + } + }); + } + + /** + * Verify that the proper block is applied when nodes lose their cluster-manager + */ + public void testVerifyApiBlocksDuringPartition() throws Exception { + internalCluster().startNodes( + 3, + Settings.builder().putNull(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey()).build() + ); + + // Makes sure that the get request can be executed on each node locally: + assertAcked( + prepareCreate("test").setSettings( + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) + ) + ); + + // Everything is stable now, it is now time to simulate evil... + // but first make sure we have no initializing shards and all is green + // (waiting for green here, because indexing / search in a yellow index is fine as long as no other nodes go down) + ensureGreen("test"); + + TwoPartitions partitions = TwoPartitions.random(random(), internalCluster().getNodeNames()); + NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); + + assertEquals(1, partitions.getMinoritySide().size()); + final String isolatedNode = partitions.getMinoritySide().iterator().next(); + assertEquals(2, partitions.getMajoritySide().size()); + final String nonIsolatedNode = partitions.getMajoritySide().iterator().next(); + + // Simulate a network issue between the unlucky node and the rest of the cluster. + networkDisruption.startDisrupting(); + + // The unlucky node must report *no* cluster-manager node, since it can't connect to cluster-manager and in fact it should + // continuously ping until network failures have been resolved. However + // It may a take a bit before the node detects it has been cut off from the elected cluster-manager + logger.info("waiting for isolated node [{}] to have no cluster-manager", isolatedNode); + assertNoClusterManager( + isolatedNode, + NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_METADATA_WRITES, + TimeValue.timeValueSeconds(30) + ); + + logger.info("wait until elected cluster-manager has been removed and a new 2 node cluster was from (via [{}])", isolatedNode); + ensureStableCluster(2, nonIsolatedNode); + + for (String node : partitions.getMajoritySide()) { + ClusterState nodeState = getNodeClusterState(node); + boolean success = true; + if (nodeState.nodes().getClusterManagerNode() == null) { + success = false; + } + if (!nodeState.blocks().global().isEmpty()) { + success = false; + } + if (!success) { + fail( + "node [" + + node + + "] has no cluster-manager or has blocks, despite of being on the right side of the partition. State dump:\n" + + nodeState + ); + } + } + + networkDisruption.stopDisrupting(); + + // Wait until the cluster-manager node sees al 3 nodes again. + ensureStableCluster(3, new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + networkDisruption.expectedTimeToHeal().millis())); + + logger.info( + "Verify no cluster-manager block with {} set to {}", + NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), + "all" + ); + client().admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "all")) + .get(); + + networkDisruption.startDisrupting(); + + // The unlucky node must report *no* cluster-manager node, since it can't connect to cluster-manager and in fact it should + // continuously ping until network failures have been resolved. However + // It may a take a bit before the node detects it has been cut off from the elected cluster-manager + logger.info("waiting for isolated node [{}] to have no cluster-manager", isolatedNode); + assertNoClusterManager(isolatedNode, NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ALL, TimeValue.timeValueSeconds(30)); + + // make sure we have stable cluster & cross partition recoveries are canceled by the removal of the missing node + // the unresponsive partition causes recoveries to only time out after 15m (default) and these will cause + // the test to fail due to unfreed resources + ensureStableCluster(2, nonIsolatedNode); + + } + + public void testMappingTimeout() throws Exception { + startCluster(3); + createIndex( + "test", + Settings.builder() + .put("index.number_of_shards", 1) + .put("index.number_of_replicas", 1) + .put("index.routing.allocation.exclude._name", internalCluster().getClusterManagerName()) + .build() + ); + + // create one field + index("test", "doc", "1", "{ \"f\": 1 }"); + + ensureGreen(); + + assertAcked( + client().admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put("indices.mapping.dynamic_timeout", "1ms")) + ); + + ServiceDisruptionScheme disruption = new BlockClusterManagerServiceOnClusterManager(random()); + setDisruptionScheme(disruption); + + disruption.startDisrupting(); + + BulkRequestBuilder bulk = client().prepareBulk(); + bulk.add(client().prepareIndex("test").setId("2").setSource("{ \"f\": 1 }", MediaTypeRegistry.JSON)); + bulk.add(client().prepareIndex("test").setId("3").setSource("{ \"g\": 1 }", MediaTypeRegistry.JSON)); + bulk.add(client().prepareIndex("test").setId("4").setSource("{ \"f\": 1 }", MediaTypeRegistry.JSON)); + BulkResponse bulkResponse = bulk.get(); + assertTrue(bulkResponse.hasFailures()); + + disruption.stopDisrupting(); + + assertBusy(() -> { + IndicesStatsResponse stats = client().admin().indices().prepareStats("test").clear().get(); + for (ShardStats shardStats : stats.getShards()) { + assertThat( + shardStats.getShardRouting().toString(), + shardStats.getSeqNoStats().getGlobalCheckpoint(), + equalTo(shardStats.getSeqNoStats().getLocalCheckpoint()) + ); + } + }); + + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/DiscoveryDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/DiscoveryDisruptionIT.java index 079aaa714a15c..a2864b6dfd1da 100644 --- a/server/src/internalClusterTest/java/org/opensearch/discovery/DiscoveryDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/discovery/DiscoveryDisruptionIT.java @@ -64,62 +64,79 @@ public class DiscoveryDisruptionIT extends AbstractDisruptionTestCase { * Test cluster join with issues in cluster state publishing * */ public void testClusterJoinDespiteOfPublishingIssues() throws Exception { - String masterNode = internalCluster().startMasterOnlyNode(); - String nonMasterNode = internalCluster().startDataOnlyNode(); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + String nonClusterManagerNode = internalCluster().startDataOnlyNode(); - DiscoveryNodes discoveryNodes = internalCluster().getInstance(ClusterService.class, nonMasterNode).state().nodes(); + DiscoveryNodes discoveryNodes = internalCluster().getInstance(ClusterService.class, nonClusterManagerNode).state().nodes(); - TransportService masterTranspotService = internalCluster().getInstance( + TransportService clusterManagerTranspotService = internalCluster().getInstance( TransportService.class, - discoveryNodes.getMasterNode().getName() + discoveryNodes.getClusterManagerNode().getName() ); - logger.info("blocking requests from non master [{}] to master [{}]", nonMasterNode, masterNode); - MockTransportService nonMasterTransportService = (MockTransportService) internalCluster().getInstance( + logger.info("blocking requests from non cluster-manager [{}] to cluster-manager [{}]", nonClusterManagerNode, clusterManagerNode); + MockTransportService nonClusterManagerTransportService = (MockTransportService) internalCluster().getInstance( TransportService.class, - nonMasterNode + nonClusterManagerNode ); - nonMasterTransportService.addFailToSendNoConnectRule(masterTranspotService); + nonClusterManagerTransportService.addFailToSendNoConnectRule(clusterManagerTranspotService); - assertNoMaster(nonMasterNode); + assertNoClusterManager(nonClusterManagerNode); - logger.info("blocking cluster state publishing from master [{}] to non master [{}]", masterNode, nonMasterNode); - MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( + logger.info( + "blocking cluster state publishing from cluster-manager [{}] to non cluster-manager [{}]", + clusterManagerNode, + nonClusterManagerNode + ); + MockTransportService clusterManagerTransportService = (MockTransportService) internalCluster().getInstance( TransportService.class, - masterNode + clusterManagerNode ); TransportService localTransportService = internalCluster().getInstance( TransportService.class, discoveryNodes.getLocalNode().getName() ); if (randomBoolean()) { - masterTransportService.addFailToSendNoConnectRule(localTransportService, PublicationTransportHandler.PUBLISH_STATE_ACTION_NAME); + clusterManagerTransportService.addFailToSendNoConnectRule( + localTransportService, + PublicationTransportHandler.PUBLISH_STATE_ACTION_NAME + ); } else { - masterTransportService.addFailToSendNoConnectRule(localTransportService, PublicationTransportHandler.COMMIT_STATE_ACTION_NAME); + clusterManagerTransportService.addFailToSendNoConnectRule( + localTransportService, + PublicationTransportHandler.COMMIT_STATE_ACTION_NAME + ); } - logger.info("allowing requests from non master [{}] to master [{}], waiting for two join request", nonMasterNode, masterNode); + logger.info( + "allowing requests from non cluster-manager [{}] to cluster-manager [{}], waiting for two join request", + nonClusterManagerNode, + clusterManagerNode + ); final CountDownLatch countDownLatch = new CountDownLatch(2); - nonMasterTransportService.addSendBehavior(masterTransportService, (connection, requestId, action, request, options) -> { - if (action.equals(JoinHelper.JOIN_ACTION_NAME)) { - countDownLatch.countDown(); + nonClusterManagerTransportService.addSendBehavior( + clusterManagerTransportService, + (connection, requestId, action, request, options) -> { + if (action.equals(JoinHelper.JOIN_ACTION_NAME)) { + countDownLatch.countDown(); + } + connection.sendRequest(requestId, action, request, options); } - connection.sendRequest(requestId, action, request, options); - }); + ); - nonMasterTransportService.addConnectBehavior(masterTransportService, Transport::openConnection); + nonClusterManagerTransportService.addConnectBehavior(clusterManagerTransportService, Transport::openConnection); countDownLatch.await(); logger.info("waiting for cluster to reform"); - masterTransportService.clearOutboundRules(localTransportService); - nonMasterTransportService.clearOutboundRules(localTransportService); + clusterManagerTransportService.clearOutboundRules(localTransportService); + nonClusterManagerTransportService.clearOutboundRules(localTransportService); ensureStableCluster(2); // shutting down the nodes, to avoid the leakage check tripping // on the states associated with the commit requests we may have dropped - internalCluster().stopRandomNonMasterNode(); + internalCluster().stopRandomNonClusterManagerNode(); } public void testClusterFormingWithASlowNode() { @@ -137,7 +154,7 @@ public void testClusterFormingWithASlowNode() { ensureStableCluster(3); } - public void testElectMasterWithLatestVersion() throws Exception { + public void testElectClusterManagerWithLatestVersion() throws Exception { final Set nodes = new HashSet<>(internalCluster().startNodes(3)); ensureStableCluster(3); ServiceDisruptionScheme isolateAllNodes = new NetworkDisruption( @@ -146,22 +163,22 @@ public void testElectMasterWithLatestVersion() throws Exception { ); internalCluster().setDisruptionScheme(isolateAllNodes); - logger.info("--> forcing a complete election to make sure \"preferred\" master is elected"); + logger.info("--> forcing a complete election to make sure \"preferred\" cluster-manager is elected"); isolateAllNodes.startDisrupting(); for (String node : nodes) { - assertNoMaster(node); + assertNoClusterManager(node); } internalCluster().clearDisruptionScheme(); ensureStableCluster(3); - final String preferredMasterName = internalCluster().getMasterName(); - final DiscoveryNode preferredMaster = internalCluster().clusterService(preferredMasterName).localNode(); + final String preferredClusterManagerName = internalCluster().getClusterManagerName(); + final DiscoveryNode preferredClusterManager = internalCluster().clusterService(preferredClusterManagerName).localNode(); - logger.info("--> preferred master is {}", preferredMaster); + logger.info("--> preferred cluster-manager is {}", preferredClusterManager); final Set nonPreferredNodes = new HashSet<>(nodes); - nonPreferredNodes.remove(preferredMasterName); - final ServiceDisruptionScheme isolatePreferredMaster = isolateMasterDisruption(NetworkDisruption.DISCONNECT); - internalCluster().setDisruptionScheme(isolatePreferredMaster); - isolatePreferredMaster.startDisrupting(); + nonPreferredNodes.remove(preferredClusterManagerName); + final ServiceDisruptionScheme isolatePreferredClusterManager = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); + internalCluster().setDisruptionScheme(isolatePreferredClusterManager); + isolatePreferredClusterManager.startDisrupting(); client(randomFrom(nonPreferredNodes)).admin() .indices() @@ -177,7 +194,7 @@ public void testElectMasterWithLatestVersion() throws Exception { logger.info("--> forcing a complete election again"); isolateAllNodes.startDisrupting(); for (String node : nodes) { - assertNoMaster(node); + assertNoClusterManager(node); } isolateAllNodes.stopDisrupting(); @@ -190,41 +207,45 @@ public void testElectMasterWithLatestVersion() throws Exception { } /** - * Adds an asymmetric break between a master and one of the nodes and makes + * Adds an asymmetric break between a cluster-manager and one of the nodes and makes * sure that the node is removed form the cluster, that the node start pinging and that * the cluster reforms when healed. */ - public void testNodeNotReachableFromMaster() throws Exception { + public void testNodeNotReachableFromClusterManager() throws Exception { startCluster(3); - String masterNode = internalCluster().getMasterName(); - String nonMasterNode = null; - while (nonMasterNode == null) { - nonMasterNode = randomFrom(internalCluster().getNodeNames()); - if (nonMasterNode.equals(masterNode)) { - nonMasterNode = null; + String clusterManagerNode = internalCluster().getClusterManagerName(); + String nonClusterManagerNode = null; + while (nonClusterManagerNode == null) { + nonClusterManagerNode = randomFrom(internalCluster().getNodeNames()); + if (nonClusterManagerNode.equals(clusterManagerNode)) { + nonClusterManagerNode = null; } } - logger.info("blocking request from master [{}] to [{}]", masterNode, nonMasterNode); - MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( + logger.info("blocking request from cluster-manager [{}] to [{}]", clusterManagerNode, nonClusterManagerNode); + MockTransportService clusterManagerTransportService = (MockTransportService) internalCluster().getInstance( TransportService.class, - masterNode + clusterManagerNode ); if (randomBoolean()) { - masterTransportService.addUnresponsiveRule(internalCluster().getInstance(TransportService.class, nonMasterNode)); + clusterManagerTransportService.addUnresponsiveRule( + internalCluster().getInstance(TransportService.class, nonClusterManagerNode) + ); } else { - masterTransportService.addFailToSendNoConnectRule(internalCluster().getInstance(TransportService.class, nonMasterNode)); + clusterManagerTransportService.addFailToSendNoConnectRule( + internalCluster().getInstance(TransportService.class, nonClusterManagerNode) + ); } - logger.info("waiting for [{}] to be removed from cluster", nonMasterNode); - ensureStableCluster(2, masterNode); + logger.info("waiting for [{}] to be removed from cluster", nonClusterManagerNode); + ensureStableCluster(2, clusterManagerNode); - logger.info("waiting for [{}] to have no master", nonMasterNode); - assertNoMaster(nonMasterNode); + logger.info("waiting for [{}] to have no cluster-manager", nonClusterManagerNode); + assertNoClusterManager(nonClusterManagerNode); logger.info("healing partition and checking cluster reforms"); - masterTransportService.clearAllRules(); + clusterManagerTransportService.clearAllRules(); ensureStableCluster(3); } diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/DiskDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/DiskDisruptionIT.java index ef00150b7c814..b7aae73056f6f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/discovery/DiskDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/discovery/DiskDisruptionIT.java @@ -32,6 +32,7 @@ package org.opensearch.discovery; import com.carrotsearch.randomizedtesting.RandomizedTest; + import org.apache.lucene.tests.mockfile.FilterFileSystemProvider; import org.opensearch.action.admin.indices.stats.ShardStats; import org.opensearch.cluster.metadata.IndexMetadata; @@ -41,9 +42,8 @@ import org.opensearch.common.unit.TimeValue; import org.opensearch.index.seqno.SequenceNumbers; import org.opensearch.test.BackgroundIndexer; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; - +import org.opensearch.test.OpenSearchIntegTestCase; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/MasterDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/MasterDisruptionIT.java deleted file mode 100644 index 14e7a26bb448e..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/discovery/MasterDisruptionIT.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.discovery; - -import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; -import org.opensearch.action.admin.indices.stats.ShardStats; -import org.opensearch.action.bulk.BulkRequestBuilder; -import org.opensearch.action.bulk.BulkResponse; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.coordination.NoMasterBlockService; -import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.test.disruption.BlockMasterServiceOnMaster; -import org.opensearch.test.disruption.IntermittentLongGCDisruption; -import org.opensearch.test.disruption.NetworkDisruption; -import org.opensearch.test.disruption.NetworkDisruption.TwoPartitions; -import org.opensearch.test.disruption.ServiceDisruptionScheme; -import org.opensearch.test.disruption.SingleNodeDisruption; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; - -/** - * Tests relating to the loss of the master. - */ -@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) -public class MasterDisruptionIT extends AbstractDisruptionTestCase { - - /** - * Test that cluster recovers from a long GC on master that causes other nodes to elect a new one - */ - public void testMasterNodeGCs() throws Exception { - List nodes = startCluster(3); - - String oldMasterNode = internalCluster().getMasterName(); - // a very long GC, but it's OK as we remove the disruption when it has had an effect - SingleNodeDisruption masterNodeDisruption = new IntermittentLongGCDisruption(random(), oldMasterNode, 100, 200, 30000, 60000); - internalCluster().setDisruptionScheme(masterNodeDisruption); - masterNodeDisruption.startDisrupting(); - - Set oldNonMasterNodesSet = new HashSet<>(nodes); - oldNonMasterNodesSet.remove(oldMasterNode); - - List oldNonMasterNodes = new ArrayList<>(oldNonMasterNodesSet); - - logger.info("waiting for nodes to de-elect master [{}]", oldMasterNode); - for (String node : oldNonMasterNodesSet) { - assertDifferentMaster(node, oldMasterNode); - } - - logger.info("waiting for nodes to elect a new master"); - ensureStableCluster(2, oldNonMasterNodes.get(0)); - - // restore GC - masterNodeDisruption.stopDisrupting(); - final TimeValue waitTime = new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + masterNodeDisruption.expectedTimeToHeal().millis()); - ensureStableCluster(3, waitTime, false, oldNonMasterNodes.get(0)); - - // make sure all nodes agree on master - String newMaster = internalCluster().getMasterName(); - assertThat(newMaster, not(equalTo(oldMasterNode))); - assertMaster(newMaster, nodes); - } - - /** - * This test isolates the master from rest of the cluster, waits for a new master to be elected, restores the partition - * and verifies that all node agree on the new cluster state - */ - public void testIsolateMasterAndVerifyClusterStateConsensus() throws Exception { - final List nodes = startCluster(3); - - assertAcked( - prepareCreate("test").setSettings( - Settings.builder() - .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1 + randomInt(2)) - .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, randomInt(2)) - ) - ); - - ensureGreen(); - String isolatedNode = internalCluster().getMasterName(); - TwoPartitions partitions = isolateNode(isolatedNode); - NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); - networkDisruption.startDisrupting(); - - String nonIsolatedNode = partitions.getMajoritySide().iterator().next(); - - // make sure cluster reforms - ensureStableCluster(2, nonIsolatedNode); - - // make sure isolated need picks up on things. - assertNoMaster(isolatedNode, TimeValue.timeValueSeconds(40)); - - // restore isolation - networkDisruption.stopDisrupting(); - - for (String node : nodes) { - ensureStableCluster( - 3, - new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + networkDisruption.expectedTimeToHeal().millis()), - true, - node - ); - } - - logger.info("issue a reroute"); - // trigger a reroute now, instead of waiting for the background reroute of RerouteService - assertAcked(client().admin().cluster().prepareReroute()); - // and wait for it to finish and for the cluster to stabilize - ensureGreen("test"); - - // verify all cluster states are the same - // use assert busy to wait for cluster states to be applied (as publish_timeout has low value) - assertBusy(() -> { - ClusterState state = null; - for (String node : nodes) { - ClusterState nodeState = getNodeClusterState(node); - if (state == null) { - state = nodeState; - continue; - } - // assert nodes are identical - try { - assertEquals("unequal versions", state.version(), nodeState.version()); - assertEquals("unequal node count", state.nodes().getSize(), nodeState.nodes().getSize()); - assertEquals("different masters ", state.nodes().getMasterNodeId(), nodeState.nodes().getMasterNodeId()); - assertEquals("different meta data version", state.metadata().version(), nodeState.metadata().version()); - assertEquals("different routing", state.routingTable().toString(), nodeState.routingTable().toString()); - } catch (AssertionError t) { - fail( - "failed comparing cluster state: " - + t.getMessage() - + "\n" - + "--- cluster state of node [" - + nodes.get(0) - + "]: ---\n" - + state - + "\n--- cluster state [" - + node - + "]: ---\n" - + nodeState - ); - } - - } - }); - } - - /** - * Verify that the proper block is applied when nodes lose their master - */ - public void testVerifyApiBlocksDuringPartition() throws Exception { - internalCluster().startNodes(3, Settings.builder().putNull(NoMasterBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey()).build()); - - // Makes sure that the get request can be executed on each node locally: - assertAcked( - prepareCreate("test").setSettings( - Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) - ) - ); - - // Everything is stable now, it is now time to simulate evil... - // but first make sure we have no initializing shards and all is green - // (waiting for green here, because indexing / search in a yellow index is fine as long as no other nodes go down) - ensureGreen("test"); - - TwoPartitions partitions = TwoPartitions.random(random(), internalCluster().getNodeNames()); - NetworkDisruption networkDisruption = addRandomDisruptionType(partitions); - - assertEquals(1, partitions.getMinoritySide().size()); - final String isolatedNode = partitions.getMinoritySide().iterator().next(); - assertEquals(2, partitions.getMajoritySide().size()); - final String nonIsolatedNode = partitions.getMajoritySide().iterator().next(); - - // Simulate a network issue between the unlucky node and the rest of the cluster. - networkDisruption.startDisrupting(); - - // The unlucky node must report *no* master node, since it can't connect to master and in fact it should - // continuously ping until network failures have been resolved. However - // It may a take a bit before the node detects it has been cut off from the elected master - logger.info("waiting for isolated node [{}] to have no master", isolatedNode); - assertNoMaster(isolatedNode, NoMasterBlockService.NO_MASTER_BLOCK_WRITES, TimeValue.timeValueSeconds(30)); - - logger.info("wait until elected master has been removed and a new 2 node cluster was from (via [{}])", isolatedNode); - ensureStableCluster(2, nonIsolatedNode); - - for (String node : partitions.getMajoritySide()) { - ClusterState nodeState = getNodeClusterState(node); - boolean success = true; - if (nodeState.nodes().getMasterNode() == null) { - success = false; - } - if (!nodeState.blocks().global().isEmpty()) { - success = false; - } - if (!success) { - fail( - "node [" - + node - + "] has no master or has blocks, despite of being on the right side of the partition. State dump:\n" - + nodeState - ); - } - } - - networkDisruption.stopDisrupting(); - - // Wait until the master node sees al 3 nodes again. - ensureStableCluster(3, new TimeValue(DISRUPTION_HEALING_OVERHEAD.millis() + networkDisruption.expectedTimeToHeal().millis())); - - logger.info("Verify no master block with {} set to {}", NoMasterBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "all"); - client().admin() - .cluster() - .prepareUpdateSettings() - .setTransientSettings(Settings.builder().put(NoMasterBlockService.NO_CLUSTER_MANAGER_BLOCK_SETTING.getKey(), "all")) - .get(); - - networkDisruption.startDisrupting(); - - // The unlucky node must report *no* master node, since it can't connect to master and in fact it should - // continuously ping until network failures have been resolved. However - // It may a take a bit before the node detects it has been cut off from the elected master - logger.info("waiting for isolated node [{}] to have no master", isolatedNode); - assertNoMaster(isolatedNode, NoMasterBlockService.NO_MASTER_BLOCK_ALL, TimeValue.timeValueSeconds(30)); - - // make sure we have stable cluster & cross partition recoveries are canceled by the removal of the missing node - // the unresponsive partition causes recoveries to only time out after 15m (default) and these will cause - // the test to fail due to unfreed resources - ensureStableCluster(2, nonIsolatedNode); - - } - - public void testMappingTimeout() throws Exception { - startCluster(3); - createIndex( - "test", - Settings.builder() - .put("index.number_of_shards", 1) - .put("index.number_of_replicas", 1) - .put("index.routing.allocation.exclude._name", internalCluster().getMasterName()) - .build() - ); - - // create one field - index("test", "doc", "1", "{ \"f\": 1 }"); - - ensureGreen(); - - assertAcked( - client().admin() - .cluster() - .prepareUpdateSettings() - .setTransientSettings(Settings.builder().put("indices.mapping.dynamic_timeout", "1ms")) - ); - - ServiceDisruptionScheme disruption = new BlockMasterServiceOnMaster(random()); - setDisruptionScheme(disruption); - - disruption.startDisrupting(); - - BulkRequestBuilder bulk = client().prepareBulk(); - bulk.add(client().prepareIndex("test").setId("2").setSource("{ \"f\": 1 }", XContentType.JSON)); - bulk.add(client().prepareIndex("test").setId("3").setSource("{ \"g\": 1 }", XContentType.JSON)); - bulk.add(client().prepareIndex("test").setId("4").setSource("{ \"f\": 1 }", XContentType.JSON)); - BulkResponse bulkResponse = bulk.get(); - assertTrue(bulkResponse.hasFailures()); - - disruption.stopDisrupting(); - - assertBusy(() -> { - IndicesStatsResponse stats = client().admin().indices().prepareStats("test").clear().get(); - for (ShardStats shardStats : stats.getShards()) { - assertThat( - shardStats.getShardRouting().toString(), - shardStats.getSeqNoStats().getGlobalCheckpoint(), - equalTo(shardStats.getSeqNoStats().getLocalCheckpoint()) - ); - } - }); - - } -} diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/SnapshotDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/SnapshotDisruptionIT.java index 086aeb695c411..68eb367108954 100644 --- a/server/src/internalClusterTest/java/org/opensearch/discovery/SnapshotDisruptionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/discovery/SnapshotDisruptionIT.java @@ -33,7 +33,6 @@ package org.opensearch.discovery; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; import org.opensearch.action.index.IndexRequestBuilder; @@ -43,6 +42,7 @@ import org.opensearch.cluster.metadata.RepositoriesMetadata; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.json.JsonXContent; @@ -86,7 +86,7 @@ protected Settings nodeSettings(int nodeOrdinal) { public void testDisruptionAfterFinalization() throws Exception { final String idxName = "test"; - internalCluster().startMasterOnlyNodes(3); + internalCluster().startClusterManagerOnlyNodes(3); final String dataNode = internalCluster().startDataOnlyNode(); ensureStableCluster(4); @@ -94,12 +94,12 @@ public void testDisruptionAfterFinalization() throws Exception { createRepository("test-repo", "fs"); - final String masterNode1 = internalCluster().getMasterName(); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); - NetworkDisruption networkDisruption = isolateMasterDisruption(NetworkDisruption.UNRESPONSIVE); + NetworkDisruption networkDisruption = isolateClusterManagerDisruption(NetworkDisruption.UNRESPONSIVE); internalCluster().setDisruptionScheme(networkDisruption); - ClusterService clusterService = internalCluster().clusterService(masterNode1); + ClusterService clusterService = internalCluster().clusterService(clusterManagerNode1); CountDownLatch disruptionStarted = new CountDownLatch(1); clusterService.addListener(new ClusterStateListener() { @Override @@ -124,7 +124,7 @@ public void clusterChanged(ClusterChangedEvent event) { final String snapshot = "test-snap"; logger.info("--> starting snapshot"); - ActionFuture future = client(masterNode1).admin() + ActionFuture future = client(clusterManagerNode1).admin() .cluster() .prepareCreateSnapshot("test-repo", snapshot) .setWaitForCompletion(true) @@ -147,7 +147,7 @@ public void clusterChanged(ClusterChangedEvent event) { logger.info("--> stopping disrupting"); networkDisruption.stopDisrupting(); - ensureStableCluster(4, masterNode1); + ensureStableCluster(4, clusterManagerNode1); logger.info("--> done"); try { @@ -158,7 +158,7 @@ public void clusterChanged(ClusterChangedEvent event) { assertNotNull(sne); assertThat( sne.getMessage(), - either(endsWith(" Failed to update cluster state during snapshot finalization")).or(endsWith(" no longer master")) + either(endsWith(" Failed to update cluster state during snapshot finalization")).or(endsWith(" no longer cluster-manager")) ); assertThat(sne.getSnapshotName(), is(snapshot)); } @@ -168,7 +168,7 @@ public void clusterChanged(ClusterChangedEvent event) { public void testDisruptionAfterShardFinalization() throws Exception { final String idxName = "test"; - internalCluster().startMasterOnlyNodes(1); + internalCluster().startClusterManagerOnlyNodes(1); internalCluster().startDataOnlyNode(); ensureStableCluster(2); createIndex(idxName); @@ -177,13 +177,13 @@ public void testDisruptionAfterShardFinalization() throws Exception { final String repoName = "test-repo"; createRepository(repoName, "mock"); - final String masterNode = internalCluster().getMasterName(); + final String clusterManagerNode = internalCluster().getClusterManagerName(); blockAllDataNodes(repoName); final String snapshot = "test-snap"; logger.info("--> starting snapshot"); - ActionFuture future = client(masterNode).admin() + ActionFuture future = client(clusterManagerNode).admin() .cluster() .prepareCreateSnapshot(repoName, snapshot) .setWaitForCompletion(true) @@ -191,7 +191,7 @@ public void testDisruptionAfterShardFinalization() throws Exception { waitForBlockOnAnyDataNode(repoName, TimeValue.timeValueSeconds(10L)); - NetworkDisruption networkDisruption = isolateMasterDisruption(NetworkDisruption.DISCONNECT); + NetworkDisruption networkDisruption = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); internalCluster().setDisruptionScheme(networkDisruption); networkDisruption.startDisrupting(); @@ -203,7 +203,7 @@ public void testDisruptionAfterShardFinalization() throws Exception { networkDisruption.stopDisrupting(); unblockAllDataNodes(repoName); - ensureStableCluster(2, masterNode); + ensureStableCluster(2, clusterManagerNode); logger.info("--> done"); logger.info("--> recreate the index with potentially different shard counts"); @@ -212,18 +212,18 @@ public void testDisruptionAfterShardFinalization() throws Exception { index(idxName, "type", JsonXContent.contentBuilder().startObject().field("foo", "bar").endObject()); logger.info("--> run a snapshot that fails to finalize but succeeds on the data node"); - blockMasterFromFinalizingSnapshotOnIndexFile(repoName); - final ActionFuture snapshotFuture = client(masterNode).admin() + blockClusterManagerFromFinalizingSnapshotOnIndexFile(repoName); + final ActionFuture snapshotFuture = client(clusterManagerNode).admin() .cluster() .prepareCreateSnapshot(repoName, "snapshot-2") .setWaitForCompletion(true) .execute(); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(10L)); - unblockNode(repoName, masterNode); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(10L)); + unblockNode(repoName, clusterManagerNode); assertFutureThrows(snapshotFuture, SnapshotException.class); logger.info("--> create a snapshot expected to be successful"); - final CreateSnapshotResponse successfulSnapshot = client(masterNode).admin() + final CreateSnapshotResponse successfulSnapshot = client(clusterManagerNode).admin() .cluster() .prepareCreateSnapshot(repoName, "snapshot-2") .setWaitForCompletion(true) @@ -235,8 +235,8 @@ public void testDisruptionAfterShardFinalization() throws Exception { assertAcked(client().admin().cluster().prepareDeleteSnapshot(repoName, "snapshot-2").get()); } - public void testMasterFailOverDuringShardSnapshots() throws Exception { - internalCluster().startMasterOnlyNodes(3); + public void testClusterManagerFailOverDuringShardSnapshots() throws Exception { + internalCluster().startClusterManagerOnlyNodes(3); final String dataNode = internalCluster().startDataOnlyNode(); ensureStableCluster(4); final String repoName = "test-repo"; @@ -248,8 +248,8 @@ public void testMasterFailOverDuringShardSnapshots() throws Exception { blockDataNode(repoName, dataNode); - logger.info("--> create snapshot via master node client"); - final ActionFuture snapshotResponse = internalCluster().masterClient() + logger.info("--> create snapshot via cluster-manager node client"); + final ActionFuture snapshotResponse = internalCluster().clusterManagerClient() .admin() .cluster() .prepareCreateSnapshot(repoName, "test-snap") @@ -258,7 +258,7 @@ public void testMasterFailOverDuringShardSnapshots() throws Exception { waitForBlock(dataNode, repoName, TimeValue.timeValueSeconds(30L)); - final NetworkDisruption networkDisruption = isolateMasterDisruption(NetworkDisruption.DISCONNECT); + final NetworkDisruption networkDisruption = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); internalCluster().setDisruptionScheme(networkDisruption); networkDisruption.startDisrupting(); ensureStableCluster(3, dataNode); @@ -267,12 +267,12 @@ public void testMasterFailOverDuringShardSnapshots() throws Exception { networkDisruption.stopDisrupting(); awaitNoMoreRunningOperations(dataNode); - logger.info("--> make sure isolated master responds to snapshot request"); + logger.info("--> make sure isolated cluster-manager responds to snapshot request"); final SnapshotException sne = expectThrows( SnapshotException.class, () -> snapshotResponse.actionGet(TimeValue.timeValueSeconds(30L)) ); - assertThat(sne.getMessage(), endsWith("no longer master")); + assertThat(sne.getMessage(), endsWith("no longer cluster-manager")); } private void assertSnapshotExists(String repository, String snapshot) { diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/StableClusterManagerDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/StableClusterManagerDisruptionIT.java new file mode 100644 index 0000000000000..c12718704e194 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/discovery/StableClusterManagerDisruptionIT.java @@ -0,0 +1,304 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.discovery; + +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.action.admin.cluster.state.ClusterStateRequest; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.coordination.Coordinator; +import org.opensearch.cluster.coordination.FollowersChecker; +import org.opensearch.cluster.coordination.LeaderChecker; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.Priority; +import org.opensearch.common.collect.Tuple; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.set.Sets; +import org.opensearch.core.common.Strings; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.disruption.LongGCDisruption; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.disruption.NetworkDisruption.NetworkLinkDisruptionType; +import org.opensearch.test.disruption.NetworkDisruption.TwoPartitions; +import org.opensearch.test.disruption.SingleNodeDisruption; +import org.opensearch.test.transport.MockTransportService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +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.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static java.util.Collections.singleton; +import static org.hamcrest.Matchers.equalTo; + +/** + * Tests relating to the loss of the cluster-manager, but which work with the default fault detection settings which are rather lenient and will + * not detect a cluster-manager failure too quickly. + */ +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class StableClusterManagerDisruptionIT extends OpenSearchIntegTestCase { + + @Override + protected Collection> nodePlugins() { + return Collections.singletonList(MockTransportService.TestPlugin.class); + } + + /** + * Test that no split brain occurs under partial network partition. See https://github.com/elastic/elasticsearch/issues/2488 + */ + public void testFailWithMinimumClusterManagerNodesConfigured() throws Exception { + List nodes = internalCluster().startNodes(3); + ensureStableCluster(3); + + // Figure out what is the elected cluster-manager node + final String clusterManagerNode = internalCluster().getClusterManagerName(); + logger.info("---> legit elected cluster-manager node={}", clusterManagerNode); + + // Pick a node that isn't the elected cluster-manager. + Set nonClusterManagers = new HashSet<>(nodes); + nonClusterManagers.remove(clusterManagerNode); + final String unluckyNode = randomFrom(nonClusterManagers.toArray(Strings.EMPTY_ARRAY)); + + // Simulate a network issue between the unlucky node and elected cluster-manager node in both directions. + + NetworkDisruption networkDisconnect = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(clusterManagerNode, unluckyNode), + NetworkDisruption.DISCONNECT + ); + setDisruptionScheme(networkDisconnect); + networkDisconnect.startDisrupting(); + + // Wait until elected cluster-manager has removed that the unlucky node... + ensureStableCluster(2, clusterManagerNode); + + // The unlucky node must report *no* cluster-manager node, since it can't connect to cluster-manager and in fact it should + // continuously ping until network failures have been resolved. However + // It may a take a bit before the node detects it has been cut off from the elected cluster-manager + ensureNoMaster(unluckyNode); + + networkDisconnect.stopDisrupting(); + + // Wait until the cluster-manager node sees all 3 nodes again. + ensureStableCluster(3); + + // The elected cluster-manager shouldn't have changed, since the unlucky node never could have elected itself as cluster-manager + assertThat(internalCluster().getClusterManagerName(), equalTo(clusterManagerNode)); + } + + private void ensureNoMaster(String node) throws Exception { + assertBusy( + () -> assertNull( + client(node).admin().cluster().state(new ClusterStateRequest().local(true)).get().getState().nodes().getClusterManagerNode() + ) + ); + } + + /** + * Verify that nodes fault detection detects a disconnected node after cluster-manager reelection + */ + public void testFollowerCheckerDetectsDisconnectedNodeAfterClusterManagerReelection() throws Exception { + testFollowerCheckerAfterClusterManagerReelection(NetworkDisruption.DISCONNECT, Settings.EMPTY); + } + + /** + * Verify that nodes fault detection detects an unresponsive node after cluster-manager reelection + */ + public void testFollowerCheckerDetectsUnresponsiveNodeAfterClusterManagerReelection() throws Exception { + testFollowerCheckerAfterClusterManagerReelection( + NetworkDisruption.UNRESPONSIVE, + Settings.builder() + .put(LeaderChecker.LEADER_CHECK_TIMEOUT_SETTING.getKey(), "1s") + .put(LeaderChecker.LEADER_CHECK_RETRY_COUNT_SETTING.getKey(), "4") + .put(FollowersChecker.FOLLOWER_CHECK_TIMEOUT_SETTING.getKey(), "1s") + .put(FollowersChecker.FOLLOWER_CHECK_RETRY_COUNT_SETTING.getKey(), 1) + .build() + ); + } + + private void testFollowerCheckerAfterClusterManagerReelection(NetworkLinkDisruptionType networkLinkDisruptionType, Settings settings) + throws Exception { + internalCluster().startNodes(4, settings); + ensureStableCluster(4); + + logger.info("--> stopping current cluster-manager"); + internalCluster().stopCurrentClusterManagerNode(); + + ensureStableCluster(3); + + final String clusterManager = internalCluster().getClusterManagerName(); + final List nonClusterManagers = Arrays.stream(internalCluster().getNodeNames()) + .filter(n -> clusterManager.equals(n) == false) + .collect(Collectors.toList()); + final String isolatedNode = randomFrom(nonClusterManagers); + final String otherNode = nonClusterManagers.get(nonClusterManagers.get(0).equals(isolatedNode) ? 1 : 0); + + logger.info("--> isolating [{}]", isolatedNode); + + final NetworkDisruption networkDisruption = new NetworkDisruption( + new TwoPartitions(singleton(isolatedNode), Sets.newHashSet(clusterManager, otherNode)), + networkLinkDisruptionType + ); + setDisruptionScheme(networkDisruption); + networkDisruption.startDisrupting(); + + logger.info("--> waiting for cluster-manager to remove it"); + ensureStableCluster(2, clusterManager); + ensureNoMaster(isolatedNode); + + networkDisruption.stopDisrupting(); + ensureStableCluster(3); + } + + /** + * Tests that emulates a frozen elected cluster-manager node that unfreezes and pushes its cluster state to other nodes that already are + * following another elected cluster-manager node. These nodes should reject this cluster state and prevent them from following the stale cluster-manager. + */ + public void testStaleClusterManagerNotHijackingMajority() throws Exception { + final List nodes = internalCluster().startNodes( + 3, + Settings.builder() + .put(LeaderChecker.LEADER_CHECK_TIMEOUT_SETTING.getKey(), "1s") + .put(Coordinator.PUBLISH_TIMEOUT_SETTING.getKey(), "1s") + .build() + ); + ensureStableCluster(3); + + // Save the current cluster-manager node as old cluster-manager node, because that node will get frozen + final String oldClusterManagerNode = internalCluster().getClusterManagerName(); + + // Simulating a painful gc by suspending all threads for a long time on the current elected cluster-manager node. + SingleNodeDisruption clusterManagerNodeDisruption = new LongGCDisruption(random(), oldClusterManagerNode); + + // Save the majority side + final List majoritySide = new ArrayList<>(nodes); + majoritySide.remove(oldClusterManagerNode); + + // Keeps track of the previous and current cluster-manager when a cluster-manager node transition took place on each node on the + // majority side: + final Map>> clusterManagers = Collections.synchronizedMap(new HashMap<>()); + for (final String node : majoritySide) { + clusterManagers.put(node, new ArrayList<>()); + internalCluster().getInstance(ClusterService.class, node).addListener(event -> { + DiscoveryNode previousClusterManager = event.previousState().nodes().getClusterManagerNode(); + DiscoveryNode currentClusterManager = event.state().nodes().getClusterManagerNode(); + if (!Objects.equals(previousClusterManager, currentClusterManager)) { + logger.info( + "--> node {} received new cluster state: {} \n and had previous cluster state: {}", + node, + event.state(), + event.previousState() + ); + String previousClusterManagerNodeName = previousClusterManager != null ? previousClusterManager.getName() : null; + String currentClusterManagerNodeName = currentClusterManager != null ? currentClusterManager.getName() : null; + clusterManagers.get(node).add(new Tuple<>(previousClusterManagerNodeName, currentClusterManagerNodeName)); + } + }); + } + + final CountDownLatch oldClusterManagerNodeSteppedDown = new CountDownLatch(1); + internalCluster().getInstance(ClusterService.class, oldClusterManagerNode).addListener(event -> { + if (event.state().nodes().getClusterManagerNodeId() == null) { + oldClusterManagerNodeSteppedDown.countDown(); + } + }); + + internalCluster().setDisruptionScheme(clusterManagerNodeDisruption); + logger.info("--> freezing node [{}]", oldClusterManagerNode); + clusterManagerNodeDisruption.startDisrupting(); + + // Wait for majority side to elect a new cluster-manager + assertBusy(() -> { + for (final Map.Entry>> entry : clusterManagers.entrySet()) { + final List> transitions = entry.getValue(); + assertTrue(entry.getKey() + ": " + transitions, transitions.stream().anyMatch(transition -> transition.v2() != null)); + } + }); + + // The old cluster-manager node is frozen, but here we submit a cluster state update task that doesn't get executed, but will be + // queued and + // once the old cluster-manager node un-freezes it gets executed. The old cluster-manager node will send this update + the cluster + // state where it is + // flagged as cluster-manager to the other nodes that follow the new cluster-manager. These nodes should ignore this update. + internalCluster().getInstance(ClusterService.class, oldClusterManagerNode) + .submitStateUpdateTask("sneaky-update", new ClusterStateUpdateTask(Priority.IMMEDIATE) { + @Override + public ClusterState execute(ClusterState currentState) { + return ClusterState.builder(currentState).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.warn(() -> new ParameterizedMessage("failure [{}]", source), e); + } + }); + + // Save the new elected cluster-manager node + final String newClusterManagerNode = internalCluster().getClusterManagerName(majoritySide.get(0)); + logger.info("--> new detected cluster-manager node [{}]", newClusterManagerNode); + + // Stop disruption + logger.info("--> unfreezing node [{}]", oldClusterManagerNode); + clusterManagerNodeDisruption.stopDisrupting(); + + oldClusterManagerNodeSteppedDown.await(30, TimeUnit.SECONDS); + logger.info("--> [{}] stepped down as cluster-manager", oldClusterManagerNode); + ensureStableCluster(3); + + assertThat(clusterManagers.size(), equalTo(2)); + for (Map.Entry>> entry : clusterManagers.entrySet()) { + String nodeName = entry.getKey(); + List> transitions = entry.getValue(); + assertTrue( + "[" + + nodeName + + "] should not apply state from old cluster-manager [" + + oldClusterManagerNode + + "] but it did: " + + transitions, + transitions.stream().noneMatch(t -> oldClusterManagerNode.equals(t.v2())) + ); + } + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/StableMasterDisruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/StableMasterDisruptionIT.java deleted file mode 100644 index 77553ba713540..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/discovery/StableMasterDisruptionIT.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.discovery; - -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.admin.cluster.state.ClusterStateRequest; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateUpdateTask; -import org.opensearch.cluster.coordination.Coordinator; -import org.opensearch.cluster.coordination.FollowersChecker; -import org.opensearch.cluster.coordination.LeaderChecker; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Priority; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.Tuple; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.set.Sets; -import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.test.disruption.LongGCDisruption; -import org.opensearch.test.disruption.NetworkDisruption; -import org.opensearch.test.disruption.NetworkDisruption.NetworkLinkDisruptionType; -import org.opensearch.test.disruption.NetworkDisruption.TwoPartitions; -import org.opensearch.test.disruption.SingleNodeDisruption; -import org.opensearch.test.transport.MockTransportService; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -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.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import static java.util.Collections.singleton; -import static org.hamcrest.Matchers.equalTo; - -/** - * Tests relating to the loss of the master, but which work with the default fault detection settings which are rather lenient and will - * not detect a master failure too quickly. - */ -@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) -public class StableMasterDisruptionIT extends OpenSearchIntegTestCase { - - @Override - protected Collection> nodePlugins() { - return Collections.singletonList(MockTransportService.TestPlugin.class); - } - - /** - * Test that no split brain occurs under partial network partition. See https://github.com/elastic/elasticsearch/issues/2488 - */ - public void testFailWithMinimumMasterNodesConfigured() throws Exception { - List nodes = internalCluster().startNodes(3); - ensureStableCluster(3); - - // Figure out what is the elected master node - final String masterNode = internalCluster().getMasterName(); - logger.info("---> legit elected master node={}", masterNode); - - // Pick a node that isn't the elected master. - Set nonMasters = new HashSet<>(nodes); - nonMasters.remove(masterNode); - final String unluckyNode = randomFrom(nonMasters.toArray(Strings.EMPTY_ARRAY)); - - // Simulate a network issue between the unlucky node and elected master node in both directions. - - NetworkDisruption networkDisconnect = new NetworkDisruption( - new NetworkDisruption.TwoPartitions(masterNode, unluckyNode), - NetworkDisruption.DISCONNECT - ); - setDisruptionScheme(networkDisconnect); - networkDisconnect.startDisrupting(); - - // Wait until elected master has removed that the unlucky node... - ensureStableCluster(2, masterNode); - - // The unlucky node must report *no* master node, since it can't connect to master and in fact it should - // continuously ping until network failures have been resolved. However - // It may a take a bit before the node detects it has been cut off from the elected master - ensureNoMaster(unluckyNode); - - networkDisconnect.stopDisrupting(); - - // Wait until the master node sees all 3 nodes again. - ensureStableCluster(3); - - // The elected master shouldn't have changed, since the unlucky node never could have elected itself as master - assertThat(internalCluster().getMasterName(), equalTo(masterNode)); - } - - private void ensureNoMaster(String node) throws Exception { - assertBusy( - () -> assertNull( - client(node).admin().cluster().state(new ClusterStateRequest().local(true)).get().getState().nodes().getMasterNode() - ) - ); - } - - /** - * Verify that nodes fault detection detects a disconnected node after master reelection - */ - public void testFollowerCheckerDetectsDisconnectedNodeAfterMasterReelection() throws Exception { - testFollowerCheckerAfterMasterReelection(NetworkDisruption.DISCONNECT, Settings.EMPTY); - } - - /** - * Verify that nodes fault detection detects an unresponsive node after master reelection - */ - public void testFollowerCheckerDetectsUnresponsiveNodeAfterMasterReelection() throws Exception { - testFollowerCheckerAfterMasterReelection( - NetworkDisruption.UNRESPONSIVE, - Settings.builder() - .put(LeaderChecker.LEADER_CHECK_TIMEOUT_SETTING.getKey(), "1s") - .put(LeaderChecker.LEADER_CHECK_RETRY_COUNT_SETTING.getKey(), "4") - .put(FollowersChecker.FOLLOWER_CHECK_TIMEOUT_SETTING.getKey(), "1s") - .put(FollowersChecker.FOLLOWER_CHECK_RETRY_COUNT_SETTING.getKey(), 1) - .build() - ); - } - - private void testFollowerCheckerAfterMasterReelection(NetworkLinkDisruptionType networkLinkDisruptionType, Settings settings) - throws Exception { - internalCluster().startNodes(4, settings); - ensureStableCluster(4); - - logger.info("--> stopping current master"); - internalCluster().stopCurrentMasterNode(); - - ensureStableCluster(3); - - final String master = internalCluster().getMasterName(); - final List nonMasters = Arrays.stream(internalCluster().getNodeNames()) - .filter(n -> master.equals(n) == false) - .collect(Collectors.toList()); - final String isolatedNode = randomFrom(nonMasters); - final String otherNode = nonMasters.get(nonMasters.get(0).equals(isolatedNode) ? 1 : 0); - - logger.info("--> isolating [{}]", isolatedNode); - - final NetworkDisruption networkDisruption = new NetworkDisruption( - new TwoPartitions(singleton(isolatedNode), Sets.newHashSet(master, otherNode)), - networkLinkDisruptionType - ); - setDisruptionScheme(networkDisruption); - networkDisruption.startDisrupting(); - - logger.info("--> waiting for master to remove it"); - ensureStableCluster(2, master); - ensureNoMaster(isolatedNode); - - networkDisruption.stopDisrupting(); - ensureStableCluster(3); - } - - /** - * Tests that emulates a frozen elected master node that unfreezes and pushes its cluster state to other nodes that already are - * following another elected master node. These nodes should reject this cluster state and prevent them from following the stale master. - */ - public void testStaleMasterNotHijackingMajority() throws Exception { - final List nodes = internalCluster().startNodes( - 3, - Settings.builder() - .put(LeaderChecker.LEADER_CHECK_TIMEOUT_SETTING.getKey(), "1s") - .put(Coordinator.PUBLISH_TIMEOUT_SETTING.getKey(), "1s") - .build() - ); - ensureStableCluster(3); - - // Save the current master node as old master node, because that node will get frozen - final String oldMasterNode = internalCluster().getMasterName(); - - // Simulating a painful gc by suspending all threads for a long time on the current elected master node. - SingleNodeDisruption masterNodeDisruption = new LongGCDisruption(random(), oldMasterNode); - - // Save the majority side - final List majoritySide = new ArrayList<>(nodes); - majoritySide.remove(oldMasterNode); - - // Keeps track of the previous and current master when a master node transition took place on each node on the majority side: - final Map>> masters = Collections.synchronizedMap(new HashMap<>()); - for (final String node : majoritySide) { - masters.put(node, new ArrayList<>()); - internalCluster().getInstance(ClusterService.class, node).addListener(event -> { - DiscoveryNode previousMaster = event.previousState().nodes().getMasterNode(); - DiscoveryNode currentMaster = event.state().nodes().getMasterNode(); - if (!Objects.equals(previousMaster, currentMaster)) { - logger.info( - "--> node {} received new cluster state: {} \n and had previous cluster state: {}", - node, - event.state(), - event.previousState() - ); - String previousMasterNodeName = previousMaster != null ? previousMaster.getName() : null; - String currentMasterNodeName = currentMaster != null ? currentMaster.getName() : null; - masters.get(node).add(new Tuple<>(previousMasterNodeName, currentMasterNodeName)); - } - }); - } - - final CountDownLatch oldMasterNodeSteppedDown = new CountDownLatch(1); - internalCluster().getInstance(ClusterService.class, oldMasterNode).addListener(event -> { - if (event.state().nodes().getMasterNodeId() == null) { - oldMasterNodeSteppedDown.countDown(); - } - }); - - internalCluster().setDisruptionScheme(masterNodeDisruption); - logger.info("--> freezing node [{}]", oldMasterNode); - masterNodeDisruption.startDisrupting(); - - // Wait for majority side to elect a new master - assertBusy(() -> { - for (final Map.Entry>> entry : masters.entrySet()) { - final List> transitions = entry.getValue(); - assertTrue(entry.getKey() + ": " + transitions, transitions.stream().anyMatch(transition -> transition.v2() != null)); - } - }); - - // The old master node is frozen, but here we submit a cluster state update task that doesn't get executed, but will be queued and - // once the old master node un-freezes it gets executed. The old master node will send this update + the cluster state where it is - // flagged as master to the other nodes that follow the new master. These nodes should ignore this update. - internalCluster().getInstance(ClusterService.class, oldMasterNode) - .submitStateUpdateTask("sneaky-update", new ClusterStateUpdateTask(Priority.IMMEDIATE) { - @Override - public ClusterState execute(ClusterState currentState) { - return ClusterState.builder(currentState).build(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.warn(() -> new ParameterizedMessage("failure [{}]", source), e); - } - }); - - // Save the new elected master node - final String newMasterNode = internalCluster().getMasterName(majoritySide.get(0)); - logger.info("--> new detected master node [{}]", newMasterNode); - - // Stop disruption - logger.info("--> unfreezing node [{}]", oldMasterNode); - masterNodeDisruption.stopDisrupting(); - - oldMasterNodeSteppedDown.await(30, TimeUnit.SECONDS); - logger.info("--> [{}] stepped down as master", oldMasterNode); - ensureStableCluster(3); - - assertThat(masters.size(), equalTo(2)); - for (Map.Entry>> entry : masters.entrySet()) { - String nodeName = entry.getKey(); - List> transitions = entry.getValue(); - assertTrue( - "[" + nodeName + "] should not apply state from old master [" + oldMasterNode + "] but it did: " + transitions, - transitions.stream().noneMatch(t -> oldMasterNode.equals(t.v2())) - ); - } - } - -} diff --git a/server/src/internalClusterTest/java/org/opensearch/discovery/single/SingleNodeDiscoveryIT.java b/server/src/internalClusterTest/java/org/opensearch/discovery/single/SingleNodeDiscoveryIT.java index 13aea01772424..1f6c8eac6c391 100644 --- a/server/src/internalClusterTest/java/org/opensearch/discovery/single/SingleNodeDiscoveryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/discovery/single/SingleNodeDiscoveryIT.java @@ -41,11 +41,11 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; import org.opensearch.node.Node.DiscoverySettings; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; import org.opensearch.test.MockHttpTransport; import org.opensearch.test.MockLogAppender; import org.opensearch.test.NodeConfigurationSource; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.transport.RemoteTransportException; import org.opensearch.transport.TransportService; @@ -76,6 +76,7 @@ public void testSingleNodesDoNotDiscoverEachOther() throws IOException, Interrup @Override public Settings nodeSettings(int nodeOrdinal) { return Settings.builder() + .put(featureFlagSettings()) .put("discovery.type", "single-node") .put("transport.type", getTestTransportType()) /* @@ -112,7 +113,7 @@ public Path nodeConfigPath(int nodeOrdinal) { final ClusterState second = other.getInstance(ClusterService.class).state(); assertThat(first.nodes().getSize(), equalTo(1)); assertThat(second.nodes().getSize(), equalTo(1)); - assertThat(first.nodes().getMasterNodeId(), not(equalTo(second.nodes().getMasterNodeId()))); + assertThat(first.nodes().getClusterManagerNodeId(), not(equalTo(second.nodes().getClusterManagerNodeId()))); assertThat(first.metadata().clusterUUID(), not(equalTo(second.metadata().clusterUUID()))); } } @@ -142,6 +143,7 @@ public boolean innerMatch(final LogEvent event) { @Override public Settings nodeSettings(int nodeOrdinal) { return Settings.builder() + .put(featureFlagSettings()) .put("discovery.type", "zen") .put("transport.type", getTestTransportType()) .put(DiscoverySettings.INITIAL_STATE_TIMEOUT_SETTING.getKey(), "0s") diff --git a/server/src/internalClusterTest/java/org/opensearch/document/DocumentActionsIT.java b/server/src/internalClusterTest/java/org/opensearch/document/DocumentActionsIT.java index 1e40cc14bbb36..0336ccf3f4647 100644 --- a/server/src/internalClusterTest/java/org/opensearch/document/DocumentActionsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/document/DocumentActionsIT.java @@ -43,11 +43,9 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.WriteRequest.RefreshPolicy; import org.opensearch.cluster.health.ClusterHealthStatus; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.mapper.MapperService; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; @@ -58,9 +56,8 @@ import static org.opensearch.client.Requests.getRequest; import static org.opensearch.client.Requests.indexRequest; import static org.opensearch.client.Requests.refreshRequest; -import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.nullValue; @@ -118,10 +115,10 @@ public void testIndexActions() throws Exception { for (int i = 0; i < 5; i++) { getResult = client().prepareGet("test", "1").execute().actionGet(); assertThat(getResult.getIndex(), equalTo(getConcreteIndexName())); - assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(Strings.toString(source("1", "test")))); + assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(source("1", "test").toString())); assertThat("cycle(map) #" + i, (String) getResult.getSourceAsMap().get("name"), equalTo("test")); getResult = client().get(getRequest("test").id("1")).actionGet(); - assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(Strings.toString(source("1", "test")))); + assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(source("1", "test").toString())); assertThat(getResult.getIndex(), equalTo(getConcreteIndexName())); } @@ -169,10 +166,10 @@ public void testIndexActions() throws Exception { for (int i = 0; i < 5; i++) { getResult = client().get(getRequest("test").id("1")).actionGet(); assertThat(getResult.getIndex(), equalTo(getConcreteIndexName())); - assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(Strings.toString(source("1", "test")))); + assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(source("1", "test").toString())); getResult = client().get(getRequest("test").id("2")).actionGet(); String ste1 = getResult.getSourceAsString(); - String ste2 = Strings.toString(source("2", "test2")); + String ste2 = source("2", "test2").toString(); assertThat("cycle #" + i, ste1, equalTo(ste2)); assertThat(getResult.getIndex(), equalTo(getConcreteIndexName())); } @@ -181,11 +178,7 @@ public void testIndexActions() throws Exception { // check count for (int i = 0; i < 5; i++) { // test successful - SearchResponse countResponse = client().prepareSearch("test") - .setSize(0) - .setQuery(termQuery("_type", MapperService.SINGLE_MAPPING_NAME)) - .execute() - .actionGet(); + SearchResponse countResponse = client().prepareSearch("test").setSize(0).setQuery(matchAllQuery()).execute().actionGet(); assertNoFailures(countResponse); assertThat(countResponse.getHits().getTotalHits().value, equalTo(2L)); assertThat(countResponse.getSuccessfulShards(), equalTo(numShards.numPrimaries)); @@ -216,7 +209,7 @@ public void testBulk() throws Exception { .add(client().prepareIndex().setIndex("test").setSource(source("3", "test"))) .add(client().prepareIndex().setIndex("test").setCreate(true).setSource(source("4", "test"))) .add(client().prepareDelete().setIndex("test").setId("1")) - .add(client().prepareIndex().setIndex("test").setSource("{ xxx }", XContentType.JSON)) // failure + .add(client().prepareIndex().setIndex("test").setSource("{ xxx }", MediaTypeRegistry.JSON)) // failure .execute() .actionGet(); @@ -263,15 +256,15 @@ public void testBulk() throws Exception { assertThat("cycle #" + i, getResult.isExists(), equalTo(false)); getResult = client().get(getRequest("test").id("2")).actionGet(); - assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(Strings.toString(source("2", "test")))); + assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(source("2", "test").toString())); assertThat(getResult.getIndex(), equalTo(getConcreteIndexName())); getResult = client().get(getRequest("test").id(generatedId3)).actionGet(); - assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(Strings.toString(source("3", "test")))); + assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(source("3", "test").toString())); assertThat(getResult.getIndex(), equalTo(getConcreteIndexName())); getResult = client().get(getRequest("test").id(generatedId4)).actionGet(); - assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(Strings.toString(source("4", "test")))); + assertThat("cycle #" + i, getResult.getSourceAsString(), equalTo(source("4", "test").toString())); assertThat(getResult.getIndex(), equalTo(getConcreteIndexName())); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/document/ShardInfoIT.java b/server/src/internalClusterTest/java/org/opensearch/document/ShardInfoIT.java index 5f217548794db..77c0c6edef623 100644 --- a/server/src/internalClusterTest/java/org/opensearch/document/ShardInfoIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/document/ShardInfoIT.java @@ -44,11 +44,10 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.test.OpenSearchIntegTestCase; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.not; @@ -60,7 +59,7 @@ public class ShardInfoIT extends OpenSearchIntegTestCase { public void testIndexAndDelete() throws Exception { prepareIndex(1); - IndexResponse indexResponse = client().prepareIndex("idx").setSource("{}", XContentType.JSON).get(); + IndexResponse indexResponse = client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON).get(); assertShardInfo(indexResponse); DeleteResponse deleteResponse = client().prepareDelete("idx", indexResponse.getId()).get(); assertShardInfo(deleteResponse); @@ -68,7 +67,7 @@ public void testIndexAndDelete() throws Exception { public void testUpdate() throws Exception { prepareIndex(1); - UpdateResponse updateResponse = client().prepareUpdate("idx", "1").setDoc("{}", XContentType.JSON).setDocAsUpsert(true).get(); + UpdateResponse updateResponse = client().prepareUpdate("idx", "1").setDoc("{}", MediaTypeRegistry.JSON).setDocAsUpsert(true).get(); assertShardInfo(updateResponse); } @@ -76,7 +75,7 @@ public void testBulkWithIndexAndDeleteItems() throws Exception { prepareIndex(1); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); for (int i = 0; i < 10; i++) { - bulkRequestBuilder.add(client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + bulkRequestBuilder.add(client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); } BulkResponse bulkResponse = bulkRequestBuilder.get(); @@ -98,7 +97,9 @@ public void testBulkWithUpdateItems() throws Exception { prepareIndex(1); BulkRequestBuilder bulkRequestBuilder = client().prepareBulk(); for (int i = 0; i < 10; i++) { - bulkRequestBuilder.add(client().prepareUpdate("idx", Integer.toString(i)).setDoc("{}", XContentType.JSON).setDocAsUpsert(true)); + bulkRequestBuilder.add( + client().prepareUpdate("idx", Integer.toString(i)).setDoc("{}", MediaTypeRegistry.JSON).setDocAsUpsert(true) + ); } BulkResponse bulkResponse = bulkRequestBuilder.get(); diff --git a/server/src/internalClusterTest/java/org/opensearch/env/NodeEnvironmentIT.java b/server/src/internalClusterTest/java/org/opensearch/env/NodeEnvironmentIT.java index 83c103bd82738..43f1608210668 100644 --- a/server/src/internalClusterTest/java/org/opensearch/env/NodeEnvironmentIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/env/NodeEnvironmentIT.java @@ -39,9 +39,9 @@ import org.opensearch.common.settings.Settings; import org.opensearch.gateway.PersistedClusterStateService; import org.opensearch.indices.IndicesService; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; import org.opensearch.test.NodeRoles; +import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; import java.nio.file.Path; @@ -79,10 +79,10 @@ public void testStartFailureOnDataForNonDataNode() throws Exception { ); } - logger.info("--> restarting the node without the data and master roles"); + logger.info("--> restarting the node without the data and cluster-manager roles"); IllegalStateException ex = expectThrows( IllegalStateException.class, - "node not having the data and master roles while having existing index metadata must fail", + "node not having the data and cluster-manager roles while having existing index metadata must fail", () -> internalCluster().restartRandomDataNode(new InternalTestCluster.RestartCallback() { @Override public Settings onNodeStopped(String nodeName) { @@ -100,7 +100,7 @@ public Settings onNodeStopped(String nodeName) { assertThat(ex.getMessage(), startsWith("node does not have the data role but has shard data")); } - logger.info("--> start the node again with data and master roles"); + logger.info("--> start the node again with data and cluster-manager roles"); internalCluster().startNode(dataPathSettings); logger.info("--> indexing a simple document"); diff --git a/server/src/internalClusterTest/java/org/opensearch/env/NodeRepurposeCommandIT.java b/server/src/internalClusterTest/java/org/opensearch/env/NodeRepurposeCommandIT.java index 2547333490f23..420a24d6d3aae 100644 --- a/server/src/internalClusterTest/java/org/opensearch/env/NodeRepurposeCommandIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/env/NodeRepurposeCommandIT.java @@ -37,9 +37,8 @@ import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.common.settings.Settings; import org.opensearch.indices.IndicesService; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.NodeRoles; - +import org.opensearch.test.OpenSearchIntegTestCase; import org.hamcrest.Matcher; import java.util.Arrays; @@ -56,7 +55,7 @@ public void testRepurpose() throws Exception { final String indexName = "test-repurpose"; logger.info("--> starting two nodes"); - final String masterNode = internalCluster().startMasterOnlyNode(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode( Settings.builder().put(IndicesService.WRITE_DANGLING_INDICES_INFO_SETTING.getKey(), false).build() ); @@ -71,20 +70,20 @@ public void testRepurpose() throws Exception { assertTrue(client().prepareGet(indexName, "1").get().isExists()); - final Settings masterNodeDataPathSettings = internalCluster().dataPathSettings(masterNode); + final Settings clusterManagerNodeDataPathSettings = internalCluster().dataPathSettings(clusterManagerNode); final Settings dataNodeDataPathSettings = internalCluster().dataPathSettings(dataNode); - final Settings noMasterNoDataSettings = NodeRoles.removeRoles( + final Settings noClusterManagerNoDataSettings = NodeRoles.removeRoles( Collections.unmodifiableSet(new HashSet<>(Arrays.asList(DiscoveryNodeRole.DATA_ROLE, DiscoveryNodeRole.MASTER_ROLE))) ); - final Settings noMasterNoDataSettingsForMasterNode = Settings.builder() - .put(noMasterNoDataSettings) - .put(masterNodeDataPathSettings) + final Settings noClusterManagerNoDataSettingsForClusterManagerNode = Settings.builder() + .put(noClusterManagerNoDataSettings) + .put(clusterManagerNodeDataPathSettings) .build(); - final Settings noMasterNoDataSettingsForDataNode = Settings.builder() - .put(noMasterNoDataSettings) + final Settings noClusterManagerNoDataSettingsForDataNode = Settings.builder() + .put(noClusterManagerNoDataSettings) .put(dataNodeDataPathSettings) .build(); @@ -99,11 +98,11 @@ public void testRepurpose() throws Exception { ); logger.info("--> Repurposing node 1"); - executeRepurposeCommand(noMasterNoDataSettingsForDataNode, 1, 1); + executeRepurposeCommand(noClusterManagerNoDataSettingsForDataNode, 1, 1); OpenSearchException lockedException = expectThrows( OpenSearchException.class, - () -> executeRepurposeCommand(noMasterNoDataSettingsForMasterNode, 1, 1) + () -> executeRepurposeCommand(noClusterManagerNoDataSettingsForClusterManagerNode, 1, 1) ); assertThat(lockedException.getMessage(), containsString(NodeRepurposeCommand.FAILED_TO_OBTAIN_NODE_LOCK_MSG)); @@ -119,11 +118,11 @@ public void testRepurpose() throws Exception { internalCluster().stopRandomNode(s -> true); internalCluster().stopRandomNode(s -> true); - executeRepurposeCommand(noMasterNoDataSettingsForMasterNode, 1, 0); + executeRepurposeCommand(noClusterManagerNoDataSettingsForClusterManagerNode, 1, 0); - // by restarting as master and data node, we can check that the index definition was really deleted and also that the tool - // does not mess things up so much that the nodes cannot boot as master or data node any longer. - internalCluster().startMasterOnlyNode(masterNodeDataPathSettings); + // by restarting as cluster-manager and data node, we can check that the index definition was really deleted and also that the tool + // does not mess things up so much that the nodes cannot boot as cluster-manager or data node any longer. + internalCluster().startClusterManagerOnlyNode(clusterManagerNodeDataPathSettings); internalCluster().startDataOnlyNode(dataNodeDataPathSettings); ensureGreen(); @@ -136,7 +135,7 @@ private void executeRepurposeCommand(Settings settings, int expectedIndexCount, boolean verbose = randomBoolean(); Settings settingsWithPath = Settings.builder().put(internalCluster().getDefaultSettings()).put(settings).build(); Matcher matcher = allOf( - containsString(NodeRepurposeCommand.noMasterMessage(expectedIndexCount, expectedShardCount, 0)), + containsString(NodeRepurposeCommand.noClusterManagerMessage(expectedIndexCount, expectedShardCount, 0)), NodeRepurposeCommandTests.conditionalNot(containsString("test-repurpose"), verbose == false) ); NodeRepurposeCommandTests.verifySuccess(settingsWithPath, matcher, verbose); diff --git a/server/src/internalClusterTest/java/org/opensearch/explain/ExplainActionIT.java b/server/src/internalClusterTest/java/org/opensearch/explain/ExplainActionIT.java index 53da0309aa602..2949fa34a0795 100644 --- a/server/src/internalClusterTest/java/org/opensearch/explain/ExplainActionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/explain/ExplainActionIT.java @@ -35,10 +35,10 @@ import org.apache.lucene.search.Explanation; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.explain.ExplainResponse; -import org.opensearch.common.io.stream.InputStreamStreamInput; -import org.opensearch.common.io.stream.OutputStreamStreamOutput; import org.opensearch.common.lucene.Lucene; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.io.stream.InputStreamStreamInput; +import org.opensearch.core.common.io.stream.OutputStreamStreamOutput; import org.opensearch.index.query.QueryBuilders; import org.opensearch.test.OpenSearchIntegTestCase; diff --git a/server/src/internalClusterTest/java/org/opensearch/extensions/ExtensionsManagerIT.java b/server/src/internalClusterTest/java/org/opensearch/extensions/ExtensionsManagerIT.java new file mode 100644 index 0000000000000..3c72edc98c764 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/extensions/ExtensionsManagerIT.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.extensions; + +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.test.OpenSearchIntegTestCase; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class ExtensionsManagerIT extends OpenSearchIntegTestCase { + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.EXTENSIONS, "true").build(); + } + + public void testExtensionsManagerCreation() { + String nodeName = internalCluster().startNode(); + + ensureGreen(); + + ExtensionsManager extManager = internalCluster().getInstance(ExtensionsManager.class, nodeName); + + assertEquals(ExtensionsManager.class.getName(), extManager.getClass().getName()); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/gateway/GatewayIndexStateIT.java b/server/src/internalClusterTest/java/org/opensearch/gateway/GatewayIndexStateIT.java index a8828c7ad38b5..47ef55bd61290 100644 --- a/server/src/internalClusterTest/java/org/opensearch/gateway/GatewayIndexStateIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/gateway/GatewayIndexStateIT.java @@ -34,7 +34,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.OpenSearchException; import org.opensearch.Version; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -61,10 +60,10 @@ import org.opensearch.index.mapper.MapperParsingException; import org.opensearch.indices.IndexClosedException; import org.opensearch.indices.ShardLimitValidator; +import org.opensearch.test.InternalTestCluster.RestartCallback; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalTestCluster.RestartCallback; import java.io.IOException; import java.nio.file.Path; @@ -241,16 +240,16 @@ public void testSimpleOpenClose() throws Exception { client().prepareIndex("test").setId("2").setSource("field1", "value1").execute().actionGet(); } - public void testJustMasterNode() throws Exception { + public void testJustClusterManagerNode() throws Exception { logger.info("--> cleaning nodes"); - logger.info("--> starting 1 master node non data"); + logger.info("--> starting 1 cluster-manager node non data"); internalCluster().startNode(nonDataNode()); logger.info("--> create an index"); client().admin().indices().prepareCreate("test").setWaitForActiveShards(ActiveShardCount.NONE).execute().actionGet(); - logger.info("--> restarting master node"); + logger.info("--> restarting cluster-manager node"); internalCluster().fullRestart(new RestartCallback() { @Override public Settings onNodeStopped(String nodeName) { @@ -273,11 +272,11 @@ public Settings onNodeStopped(String nodeName) { assertThat(clusterStateResponse.getState().metadata().hasIndex("test"), equalTo(true)); } - public void testJustMasterNodeAndJustDataNode() { + public void testJustClusterManagerNodeAndJustDataNode() { logger.info("--> cleaning nodes"); - logger.info("--> starting 1 master node non data"); - internalCluster().startMasterOnlyNode(); + logger.info("--> starting 1 cluster-manager node non data"); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); logger.info("--> create an index"); diff --git a/server/src/internalClusterTest/java/org/opensearch/gateway/MetadataNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/gateway/MetadataNodesIT.java index c9807aa24e259..681b112428c92 100644 --- a/server/src/internalClusterTest/java/org/opensearch/gateway/MetadataNodesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/gateway/MetadataNodesIT.java @@ -36,16 +36,15 @@ import org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.opensearch.cluster.coordination.Coordinator; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.Index; import org.opensearch.discovery.Discovery; import org.opensearch.env.NodeEnvironment; -import org.opensearch.index.Index; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.InternalTestCluster.RestartCallback; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalTestCluster; -import org.opensearch.test.InternalTestCluster.RestartCallback; import java.nio.file.Files; import java.nio.file.Path; @@ -60,18 +59,18 @@ public class MetadataNodesIT extends OpenSearchIntegTestCase { public void testMetaWrittenAlsoOnDataNode() throws Exception { // this test checks that index state is written on data only nodes if they have a shard allocated - String masterNode = internalCluster().startMasterOnlyNode(Settings.EMPTY); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); String dataNode = internalCluster().startDataOnlyNode(Settings.EMPTY); assertAcked(prepareCreate("test").setSettings(Settings.builder().put("index.number_of_replicas", 0))); index("test", "_doc", "1", jsonBuilder().startObject().field("text", "some text").endObject()); ensureGreen("test"); assertIndexInMetaState(dataNode, "test"); - assertIndexInMetaState(masterNode, "test"); + assertIndexInMetaState(clusterManagerNode, "test"); } public void testIndexFilesAreRemovedIfAllShardsFromIndexRemoved() throws Exception { // this test checks that the index data is removed from a data only node once all shards have been allocated away from it - String masterNode = internalCluster().startMasterOnlyNode(Settings.EMPTY); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); List nodeNames = internalCluster().startDataOnlyNodes(2); String node1 = nodeNames.get(0); String node2 = nodeNames.get(1); @@ -90,8 +89,8 @@ public void testIndexFilesAreRemovedIfAllShardsFromIndexRemoved() throws Excepti Index resolveIndex = resolveIndex(index); assertIndexDirectoryExists(node1, resolveIndex); assertIndexDirectoryDeleted(node2, resolveIndex); - assertIndexInMetaState(masterNode, index); - assertIndexDirectoryDeleted(masterNode, resolveIndex); + assertIndexInMetaState(clusterManagerNode, index); + assertIndexDirectoryDeleted(clusterManagerNode, resolveIndex); logger.debug("relocating index..."); client().admin() @@ -104,8 +103,8 @@ public void testIndexFilesAreRemovedIfAllShardsFromIndexRemoved() throws Excepti assertIndexDirectoryDeleted(node1, resolveIndex); assertIndexInMetaState(node2, index); assertIndexDirectoryExists(node2, resolveIndex); - assertIndexInMetaState(masterNode, index); - assertIndexDirectoryDeleted(masterNode, resolveIndex); + assertIndexInMetaState(clusterManagerNode, index); + assertIndexDirectoryDeleted(clusterManagerNode, resolveIndex); client().admin().indices().prepareDelete(index).get(); assertIndexDirectoryDeleted(node1, resolveIndex); @@ -114,7 +113,7 @@ public void testIndexFilesAreRemovedIfAllShardsFromIndexRemoved() throws Excepti @SuppressWarnings("unchecked") public void testMetaWrittenWhenIndexIsClosedAndMetaUpdated() throws Exception { - String masterNode = internalCluster().startMasterOnlyNode(Settings.EMPTY); + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); final String dataNode = internalCluster().startDataOnlyNode(Settings.EMPTY); final String index = "index"; @@ -123,7 +122,7 @@ public void testMetaWrittenWhenIndexIsClosedAndMetaUpdated() throws Exception { ensureGreen(); logger.info("--> wait for meta state written for index"); assertIndexInMetaState(dataNode, index); - assertIndexInMetaState(masterNode, index); + assertIndexInMetaState(clusterManagerNode, index); logger.info("--> close index"); client().admin().indices().prepareClose(index).get(); @@ -152,7 +151,7 @@ public void testMetaWrittenWhenIndexIsClosedAndMetaUpdated() throws Exception { ); // make sure it was also written on red node although index is closed - ImmutableOpenMap indicesMetadata = getIndicesMetadataOnNode(dataNode); + Map indicesMetadata = getIndicesMetadataOnNode(dataNode); assertNotNull(((Map) (indicesMetadata.get(index).mapping().getSourceAsMap().get("properties"))).get("integer_field")); assertThat(indicesMetadata.get(index).getState(), equalTo(IndexMetadata.State.CLOSE)); @@ -239,7 +238,7 @@ private boolean indexDirectoryExists(String nodeName, Index index) { return false; } - private ImmutableOpenMap getIndicesMetadataOnNode(String nodeName) { + private Map getIndicesMetadataOnNode(String nodeName) { final Coordinator coordinator = (Coordinator) internalCluster().getInstance(Discovery.class, nodeName); return coordinator.getApplierState().getMetadata().getIndices(); } diff --git a/server/src/internalClusterTest/java/org/opensearch/gateway/QuorumGatewayIT.java b/server/src/internalClusterTest/java/org/opensearch/gateway/QuorumGatewayIT.java index 1e190d3bec345..1d494832d8e55 100644 --- a/server/src/internalClusterTest/java/org/opensearch/gateway/QuorumGatewayIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/gateway/QuorumGatewayIT.java @@ -35,10 +35,10 @@ import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.client.Client; import org.opensearch.cluster.health.ClusterHealthStatus; +import org.opensearch.test.InternalTestCluster.RestartCallback; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalTestCluster.RestartCallback; import java.util.concurrent.TimeUnit; diff --git a/server/src/internalClusterTest/java/org/opensearch/gateway/RecoverAfterNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/gateway/RecoverAfterNodesIT.java index 7365ef1c8847d..b81bfe6b0a51a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/gateway/RecoverAfterNodesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/gateway/RecoverAfterNodesIT.java @@ -43,8 +43,8 @@ import java.util.Set; +import static org.opensearch.test.NodeRoles.clusterManagerOnlyNode; import static org.opensearch.test.NodeRoles.dataOnlyNode; -import static org.opensearch.test.NodeRoles.masterOnlyNode; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; @@ -75,7 +75,7 @@ public Client startNode(Settings.Builder settings) { } public void testRecoverAfterNodes() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); + internalCluster().setBootstrapClusterManagerNodeIndex(0); logger.info("--> start node (1)"); Client clientNode1 = startNode(Settings.builder().put("gateway.recover_after_nodes", 3)); assertThat( @@ -127,12 +127,12 @@ public void testRecoverAfterNodes() throws Exception { assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, clientNode3).isEmpty(), equalTo(true)); } - public void testRecoverAfterMasterNodes() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); - logger.info("--> start master_node (1)"); - Client master1 = startNode(Settings.builder().put("gateway.recover_after_master_nodes", 2).put(masterOnlyNode())); + public void testRecoverAfterClusterManagerNodes() throws Exception { + internalCluster().setBootstrapClusterManagerNodeIndex(0); + logger.info("--> start cluster_manager_node (1)"); + Client clusterManager1 = startNode(Settings.builder().put("gateway.recover_after_master_nodes", 2).put(clusterManagerOnlyNode())); assertThat( - master1.admin() + clusterManager1.admin() .cluster() .prepareState() .setLocal(true) @@ -147,7 +147,7 @@ public void testRecoverAfterMasterNodes() throws Exception { logger.info("--> start data_node (1)"); Client data1 = startNode(Settings.builder().put("gateway.recover_after_master_nodes", 2).put(dataOnlyNode())); assertThat( - master1.admin() + clusterManager1.admin() .cluster() .prepareState() .setLocal(true) @@ -174,7 +174,7 @@ public void testRecoverAfterMasterNodes() throws Exception { logger.info("--> start data_node (2)"); Client data2 = startNode(Settings.builder().put("gateway.recover_after_master_nodes", 2).put(dataOnlyNode())); assertThat( - master1.admin() + clusterManager1.admin() .cluster() .prepareState() .setLocal(true) @@ -210,20 +210,20 @@ public void testRecoverAfterMasterNodes() throws Exception { hasItem(GatewayService.STATE_NOT_RECOVERED_BLOCK) ); - logger.info("--> start master_node (2)"); - Client master2 = startNode(Settings.builder().put("gateway.recover_after_master_nodes", 2).put(masterOnlyNode())); - assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, master1).isEmpty(), equalTo(true)); - assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, master2).isEmpty(), equalTo(true)); + logger.info("--> start cluster_manager_node (2)"); + Client clusterManager2 = startNode(Settings.builder().put("gateway.recover_after_master_nodes", 2).put(clusterManagerOnlyNode())); + assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, clusterManager1).isEmpty(), equalTo(true)); + assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, clusterManager2).isEmpty(), equalTo(true)); assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, data1).isEmpty(), equalTo(true)); assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, data2).isEmpty(), equalTo(true)); } public void testRecoverAfterDataNodes() throws Exception { - internalCluster().setBootstrapMasterNodeIndex(0); - logger.info("--> start master_node (1)"); - Client master1 = startNode(Settings.builder().put("gateway.recover_after_data_nodes", 2).put(masterOnlyNode())); + internalCluster().setBootstrapClusterManagerNodeIndex(0); + logger.info("--> start cluster_manager_node (1)"); + Client clusterManager1 = startNode(Settings.builder().put("gateway.recover_after_data_nodes", 2).put(clusterManagerOnlyNode())); assertThat( - master1.admin() + clusterManager1.admin() .cluster() .prepareState() .setLocal(true) @@ -238,7 +238,7 @@ public void testRecoverAfterDataNodes() throws Exception { logger.info("--> start data_node (1)"); Client data1 = startNode(Settings.builder().put("gateway.recover_after_data_nodes", 2).put(dataOnlyNode())); assertThat( - master1.admin() + clusterManager1.admin() .cluster() .prepareState() .setLocal(true) @@ -262,10 +262,10 @@ public void testRecoverAfterDataNodes() throws Exception { hasItem(GatewayService.STATE_NOT_RECOVERED_BLOCK) ); - logger.info("--> start master_node (2)"); - Client master2 = startNode(Settings.builder().put("gateway.recover_after_data_nodes", 2).put(masterOnlyNode())); + logger.info("--> start cluster_manager_node (2)"); + Client clusterManager2 = startNode(Settings.builder().put("gateway.recover_after_data_nodes", 2).put(clusterManagerOnlyNode())); assertThat( - master2.admin() + clusterManager2.admin() .cluster() .prepareState() .setLocal(true) @@ -289,7 +289,7 @@ public void testRecoverAfterDataNodes() throws Exception { hasItem(GatewayService.STATE_NOT_RECOVERED_BLOCK) ); assertThat( - master2.admin() + clusterManager2.admin() .cluster() .prepareState() .setLocal(true) @@ -303,8 +303,8 @@ public void testRecoverAfterDataNodes() throws Exception { logger.info("--> start data_node (2)"); Client data2 = startNode(Settings.builder().put("gateway.recover_after_data_nodes", 2).put(dataOnlyNode())); - assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, master1).isEmpty(), equalTo(true)); - assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, master2).isEmpty(), equalTo(true)); + assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, clusterManager1).isEmpty(), equalTo(true)); + assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, clusterManager2).isEmpty(), equalTo(true)); assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, data1).isEmpty(), equalTo(true)); assertThat(waitForNoBlocksOnNode(BLOCK_WAIT_TIMEOUT, data2).isEmpty(), equalTo(true)); } diff --git a/server/src/internalClusterTest/java/org/opensearch/gateway/RecoveryFromGatewayIT.java b/server/src/internalClusterTest/java/org/opensearch/gateway/RecoveryFromGatewayIT.java index 92d8dc6a0bb5e..229cd7bffad2f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/gateway/RecoveryFromGatewayIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/gateway/RecoveryFromGatewayIT.java @@ -32,8 +32,6 @@ package org.opensearch.gateway; -import com.carrotsearch.hppc.cursors.ObjectCursor; - import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; @@ -48,26 +46,26 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.UnassignedInfo; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.env.NodeEnvironment; -import org.opensearch.index.Index; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; -import org.opensearch.index.MergePolicyConfig; +import org.opensearch.index.MergePolicyProvider; import org.opensearch.index.engine.Engine; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.index.shard.ShardId; import org.opensearch.index.shard.ShardPath; import org.opensearch.indices.IndicesService; import org.opensearch.indices.recovery.RecoveryState; +import org.opensearch.indices.replication.common.ReplicationLuceneIndex; import org.opensearch.plugins.Plugin; +import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.InternalTestCluster.RestartCallback; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalSettingsPlugin; -import org.opensearch.test.InternalTestCluster.RestartCallback; import org.opensearch.test.store.MockFSIndexStore; import java.nio.file.DirectoryStream; @@ -111,16 +109,15 @@ public void testOneNodeRecoverFromGateway() throws Exception { internalCluster().startNode(); - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("appAccountIds") - .field("type", "text") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("appAccountIds") + .field("type", "text") + .endObject() + .endObject() + .endObject() + .toString(); assertAcked(prepareCreate("test").setMapping(mapping)); client().prepareIndex("test") @@ -180,8 +177,7 @@ private Map assertAndCapturePrimaryTerms(Map pre } final Map result = new HashMap<>(); final ClusterState state = client().admin().cluster().prepareState().get().getState(); - for (ObjectCursor cursor : state.metadata().indices().values()) { - final IndexMetadata indexMetadata = cursor.value; + for (final IndexMetadata indexMetadata : state.metadata().indices().values()) { final String index = indexMetadata.getIndex().getName(); final long[] previous = previousTerms.get(index); final long[] current = IntStream.range(0, indexMetadata.getNumberOfShards()).mapToLong(indexMetadata::primaryTerm).toArray(); @@ -206,19 +202,18 @@ private Map assertAndCapturePrimaryTerms(Map pre public void testSingleNodeNoFlush() throws Exception { internalCluster().startNode(); - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("field") - .field("type", "text") - .endObject() - .startObject("num") - .field("type", "integer") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("field") + .field("type", "text") + .endObject() + .startObject("num") + .field("type", "integer") + .endObject() + .endObject() + .endObject() + .toString(); // note: default replica settings are tied to #data nodes-1 which is 0 here. We can do with 1 in this test. int numberOfShards = numberOfShards(); assertAcked( @@ -444,7 +439,7 @@ public void testLatestVersionLoaded() throws Exception { .setSource(jsonBuilder().startObject().field("field", "value3").endObject()) .execute() .actionGet(); - // TODO: remove once refresh doesn't fail immediately if there a master block: + // TODO: remove once refresh doesn't fail immediately if there a cluster-manager block: // https://github.com/elastic/elasticsearch/issues/9997 // client().admin().cluster().prepareHealth("test").setWaitForYellowStatus().get(); logger.info("--> refreshing all indices after indexing is complete"); @@ -511,7 +506,7 @@ public void testLatestVersionLoaded() throws Exception { } public void testReuseInFileBasedPeerRecovery() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String primaryNode = internalCluster().startDataOnlyNode(nodeSettings(0)); // create the index with our mapping @@ -524,7 +519,7 @@ public void testReuseInFileBasedPeerRecovery() throws Exception { .put("number_of_replicas", 1) // disable merges to keep segments the same - .put(MergePolicyConfig.INDEX_MERGE_ENABLED, false) + .put(MergePolicyProvider.INDEX_MERGE_ENABLED, false) // expire retention leases quickly .put(IndexService.RETENTION_LEASE_SYNC_INTERVAL_SETTING.getKey(), "100ms") @@ -547,7 +542,7 @@ public void testReuseInFileBasedPeerRecovery() throws Exception { final Set files = new HashSet<>(); for (final RecoveryState recoveryState : initialRecoveryReponse.shardRecoveryStates().get("test")) { if (recoveryState.getTargetNode().getName().equals(replicaNode)) { - for (final RecoveryState.FileDetail file : recoveryState.getIndex().fileDetails()) { + for (final ReplicationLuceneIndex.FileMetadata file : recoveryState.getIndex().fileDetails()) { files.add(file.name()); } break; @@ -607,7 +602,7 @@ public Settings onNodeStopped(String nodeName) throws Exception { long reused = 0; int filesRecovered = 0; int filesReused = 0; - for (final RecoveryState.FileDetail file : recoveryState.getIndex().fileDetails()) { + for (final ReplicationLuceneIndex.FileMetadata file : recoveryState.getIndex().fileDetails()) { if (files.contains(file.name()) == false) { recovered += file.length(); filesRecovered++; @@ -664,7 +659,7 @@ public void assertSyncIdsNotNull() { } public void testStartedShardFoundIfStateNotYetProcessed() throws Exception { - // nodes may need to report the shards they processed the initial recovered cluster state from the master + // nodes may need to report the shards they processed the initial recovered cluster state from the cluster-manager final String nodeName = internalCluster().startNode(); createIndex("test", Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).build()); final String customDataPath = IndexMetadata.INDEX_DATA_PATH_SETTING.get( diff --git a/server/src/internalClusterTest/java/org/opensearch/gateway/ReplicaShardAllocatorIT.java b/server/src/internalClusterTest/java/org/opensearch/gateway/ReplicaShardAllocatorIT.java index 345ed668a3bf4..4c668dcf0c974 100644 --- a/server/src/internalClusterTest/java/org/opensearch/gateway/ReplicaShardAllocatorIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/gateway/ReplicaShardAllocatorIT.java @@ -39,10 +39,10 @@ import org.opensearch.cluster.routing.UnassignedInfo; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.breaker.CircuitBreakingException; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.CircuitBreakingException; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.seqno.ReplicationTracker; @@ -54,9 +54,9 @@ import org.opensearch.indices.recovery.RecoveryCleanFilesRequest; import org.opensearch.indices.recovery.RecoveryState; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.transport.TransportService; @@ -327,7 +327,7 @@ public void testFullClusterRestartPerformNoopRecovery() throws Exception { public void testPreferCopyWithHighestMatchingOperations() throws Exception { String indexName = "test"; - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNodes(3); assertAcked( client().admin() @@ -402,7 +402,7 @@ public void testPreferCopyWithHighestMatchingOperations() throws Exception { * Make sure that we do not repeatedly cancel an ongoing recovery for a noop copy on a broken node. */ public void testDoNotCancelRecoveryForBrokenNode() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); String nodeWithPrimary = internalCluster().startDataOnlyNode(); String indexName = "test"; assertAcked( @@ -518,7 +518,7 @@ public void testPeerRecoveryForClosedIndices() throws Exception { * this behavior by changing the global checkpoint in phase1 to unassigned. */ public void testSimulateRecoverySourceOnOldNode() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); String source = internalCluster().startDataOnlyNode(); String indexName = "test"; assertAcked( diff --git a/server/src/internalClusterTest/java/org/opensearch/gateway/remote/RemoteClusterStateServiceIT.java b/server/src/internalClusterTest/java/org/opensearch/gateway/remote/RemoteClusterStateServiceIT.java new file mode 100644 index 0000000000000..6fcc89cfe9e9a --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/gateway/remote/RemoteClusterStateServiceIT.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.gateway.remote; + +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.blobstore.BlobPath; +import org.opensearch.common.settings.Settings; +import org.opensearch.remotestore.RemoteStoreBaseIntegTestCase; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.repositories.blobstore.BlobStoreRepository; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; +import static org.opensearch.gateway.remote.RemoteClusterStateService.REMOTE_CLUSTER_STATE_ENABLED_SETTING; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteClusterStateServiceIT extends RemoteStoreBaseIntegTestCase { + + private static String INDEX_NAME = "test-index"; + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder().put(super.nodeSettings(nodeOrdinal)).put(REMOTE_CLUSTER_STATE_ENABLED_SETTING.getKey(), true).build(); + } + + private void prepareCluster(int numClusterManagerNodes, int numDataOnlyNodes, String indices, int replicaCount, int shardCount) { + internalCluster().startClusterManagerOnlyNodes(numClusterManagerNodes); + internalCluster().startDataOnlyNodes(numDataOnlyNodes); + for (String index : indices.split(",")) { + createIndex(index, remoteStoreIndexSettings(replicaCount, shardCount)); + ensureYellowAndNoInitializingShards(index); + ensureGreen(index); + } + } + + private Map initialTestSetup(int shardCount, int replicaCount, int dataNodeCount, int clusterManagerNodeCount) { + prepareCluster(clusterManagerNodeCount, dataNodeCount, INDEX_NAME, replicaCount, shardCount); + Map indexStats = indexData(1, false, INDEX_NAME); + assertEquals(shardCount * (replicaCount + 1), getNumShards(INDEX_NAME).totalNumShards); + ensureGreen(INDEX_NAME); + return indexStats; + } + + public void testFullClusterRestoreStaleDelete() throws Exception { + int shardCount = randomIntBetween(1, 2); + int replicaCount = 1; + int dataNodeCount = shardCount * (replicaCount + 1); + int clusterManagerNodeCount = 1; + + initialTestSetup(shardCount, replicaCount, dataNodeCount, clusterManagerNodeCount); + setReplicaCount(0); + setReplicaCount(2); + setReplicaCount(0); + setReplicaCount(1); + setReplicaCount(0); + setReplicaCount(1); + setReplicaCount(0); + setReplicaCount(2); + setReplicaCount(0); + + RemoteClusterStateService remoteClusterStateService = internalCluster().getClusterManagerNodeInstance( + RemoteClusterStateService.class + ); + + RepositoriesService repositoriesService = internalCluster().getClusterManagerNodeInstance(RepositoriesService.class); + + BlobStoreRepository repository = (BlobStoreRepository) repositoriesService.repository(REPOSITORY_NAME); + BlobPath baseMetadataPath = repository.basePath() + .add( + Base64.getUrlEncoder() + .withoutPadding() + .encodeToString(getClusterState().getClusterName().value().getBytes(StandardCharsets.UTF_8)) + ) + .add("cluster-state") + .add(getClusterState().metadata().clusterUUID()); + + assertEquals(10, repository.blobStore().blobContainer(baseMetadataPath.add("manifest")).listBlobsByPrefix("manifest").size()); + + Map indexMetadataMap = remoteClusterStateService.getLatestIndexMetadata( + cluster().getClusterName(), + getClusterState().metadata().clusterUUID() + ); + assertEquals(0, indexMetadataMap.values().stream().findFirst().get().getNumberOfReplicas()); + assertEquals(shardCount, indexMetadataMap.values().stream().findFirst().get().getNumberOfShards()); + } + + private void setReplicaCount(int replicaCount) { + client().admin() + .indices() + .prepareUpdateSettings(INDEX_NAME) + .setSettings(Settings.builder().put(SETTING_NUMBER_OF_REPLICAS, replicaCount)) + .get(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/get/GetActionIT.java b/server/src/internalClusterTest/java/org/opensearch/get/GetActionIT.java index 2f811d4a901bf..c44b7c7736d21 100644 --- a/server/src/internalClusterTest/java/org/opensearch/get/GetActionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/get/GetActionIT.java @@ -35,7 +35,6 @@ import org.opensearch.action.DocWriteResponse; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.flush.FlushResponse; - import org.opensearch.action.delete.DeleteResponse; import org.opensearch.action.get.GetRequestBuilder; import org.opensearch.action.get.GetResponse; @@ -43,19 +42,19 @@ import org.opensearch.action.get.MultiGetRequestBuilder; import org.opensearch.action.get.MultiGetResponse; import org.opensearch.action.index.IndexResponse; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.engine.VersionConflictEngineException; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; import java.util.Collection; @@ -288,17 +287,16 @@ public void testSimpleMultiGet() throws Exception { } public void testGetDocWithMultivaluedFields() throws Exception { - String mapping1 = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("field") - .field("type", "text") - .field("store", true) - .endObject() - .endObject() - .endObject() - ); + String mapping1 = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("field") + .field("type", "text") + .field("store", true) + .endObject() + .endObject() + .endObject() + .toString(); assertAcked(prepareCreate("test").setMapping(mapping1)); ensureGreen(); @@ -633,7 +631,7 @@ public void testGetFieldsComplexField() throws Exception { logger.info("indexing documents"); - client().prepareIndex("my-index").setId("1").setSource(source, XContentType.JSON).get(); + client().prepareIndex("my-index").setId("1").setSource(source, MediaTypeRegistry.JSON).get(); logger.info("checking real time retrieval"); @@ -692,7 +690,7 @@ public void testUngeneratedFieldsThatAreNeverStored() throws IOException { + " }\n" + " }\n" + "}"; - assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, XContentType.JSON)); + assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, MediaTypeRegistry.JSON)); ensureGreen(); String doc = "{\n" + " \"suggest\": {\n" @@ -722,10 +720,10 @@ public void testUngeneratedFieldsThatAreAlwaysStored() throws IOException { + " \"refresh_interval\": \"-1\"\n" + " }\n" + "}"; - assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, XContentType.JSON)); + assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, MediaTypeRegistry.JSON)); ensureGreen(); - client().prepareIndex("test").setId("1").setRouting("routingValue").setId("1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId("1").setRouting("routingValue").setId("1").setSource("{}", MediaTypeRegistry.JSON).get(); String[] fieldsList = { "_routing" }; // before refresh - document is only in translog @@ -746,10 +744,10 @@ public void testUngeneratedFieldsNotPartOfSourceStored() throws IOException { + " }\n" + "}"; - assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, XContentType.JSON)); + assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, MediaTypeRegistry.JSON)); ensureGreen(); String doc = "{\n" + " \"text\": \"some text.\"\n" + "}\n"; - client().prepareIndex("test").setId("1").setSource(doc, XContentType.JSON).setRouting("1").get(); + client().prepareIndex("test").setId("1").setSource(doc, MediaTypeRegistry.JSON).setRouting("1").get(); String[] fieldsList = { "_routing" }; // before refresh - document is only in translog assertGetFieldsAlwaysWorks(indexOrAlias(), "_doc", "1", fieldsList, "1"); @@ -817,7 +815,7 @@ void indexSingleDocumentWithStringFieldsGeneratedFromText(boolean stored, boolea + " }\n" + "}"; - assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, XContentType.JSON)); + assertAcked(prepareCreate("test").addAlias(new Alias("alias")).setSource(createIndexSource, MediaTypeRegistry.JSON)); ensureGreen(); String doc = "{\n" + " \"text1\": \"some text.\"\n," + " \"text2\": \"more text.\"\n" + "}\n"; index("test", "_doc", "1", doc); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/FinalPipelineIT.java b/server/src/internalClusterTest/java/org/opensearch/index/FinalPipelineIT.java index 3b2695ad7896e..03b8fb5ff7afc 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/FinalPipelineIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/FinalPipelineIT.java @@ -45,12 +45,13 @@ import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.ingest.AbstractProcessor; @@ -60,12 +61,10 @@ import org.opensearch.plugins.IngestPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.RepositoriesService; -import org.opensearch.rest.RestStatus; import org.opensearch.script.ScriptService; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.threadpool.ThreadPool; import org.opensearch.watcher.ResourceWatcherService; - import org.junit.After; import java.io.IOException; @@ -103,7 +102,10 @@ public void testFinalPipelineCantChangeDestination() { createIndex("index", settings); final BytesReference finalPipelineBody = new BytesArray("{\"processors\": [{\"changing_dest\": {}}]}"); - client().admin().cluster().putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, XContentType.JSON)).actionGet(); + client().admin() + .cluster() + .putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, MediaTypeRegistry.JSON)) + .actionGet(); final IllegalStateException e = expectThrows( IllegalStateException.class, @@ -122,11 +124,14 @@ public void testFinalPipelineOfOldDestinationIsNotInvoked() { BytesReference defaultPipelineBody = new BytesArray("{\"processors\": [{\"changing_dest\": {}}]}"); client().admin() .cluster() - .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, XContentType.JSON)) + .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, MediaTypeRegistry.JSON)) .actionGet(); BytesReference finalPipelineBody = new BytesArray("{\"processors\": [{\"final\": {\"exists\":\"no_such_field\"}}]}"); - client().admin().cluster().putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, XContentType.JSON)).actionGet(); + client().admin() + .cluster() + .putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, MediaTypeRegistry.JSON)) + .actionGet(); IndexResponse indexResponse = client().prepareIndex("index") .setId("1") @@ -149,11 +154,14 @@ public void testFinalPipelineOfNewDestinationIsInvoked() { BytesReference defaultPipelineBody = new BytesArray("{\"processors\": [{\"changing_dest\": {}}]}"); client().admin() .cluster() - .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, XContentType.JSON)) + .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, MediaTypeRegistry.JSON)) .actionGet(); BytesReference finalPipelineBody = new BytesArray("{\"processors\": [{\"final\": {}}]}"); - client().admin().cluster().putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, XContentType.JSON)).actionGet(); + client().admin() + .cluster() + .putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, MediaTypeRegistry.JSON)) + .actionGet(); IndexResponse indexResponse = client().prepareIndex("index") .setId("1") @@ -176,13 +184,13 @@ public void testDefaultPipelineOfNewDestinationIsNotInvoked() { BytesReference defaultPipelineBody = new BytesArray("{\"processors\": [{\"changing_dest\": {}}]}"); client().admin() .cluster() - .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, XContentType.JSON)) + .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, MediaTypeRegistry.JSON)) .actionGet(); BytesReference targetPipeline = new BytesArray("{\"processors\": [{\"final\": {}}]}"); client().admin() .cluster() - .putPipeline(new PutPipelineRequest("target_default_pipeline", targetPipeline, XContentType.JSON)) + .putPipeline(new PutPipelineRequest("target_default_pipeline", targetPipeline, MediaTypeRegistry.JSON)) .actionGet(); IndexResponse indexResponse = client().prepareIndex("index") @@ -212,10 +220,13 @@ public void testRequestPipelineAndFinalPipeline() { final BytesReference requestPipelineBody = new BytesArray("{\"processors\": [{\"request\": {}}]}"); client().admin() .cluster() - .putPipeline(new PutPipelineRequest("request_pipeline", requestPipelineBody, XContentType.JSON)) + .putPipeline(new PutPipelineRequest("request_pipeline", requestPipelineBody, MediaTypeRegistry.JSON)) .actionGet(); final BytesReference finalPipelineBody = new BytesArray("{\"processors\": [{\"final\": {\"exists\":\"request\"}}]}"); - client().admin().cluster().putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, XContentType.JSON)).actionGet(); + client().admin() + .cluster() + .putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, MediaTypeRegistry.JSON)) + .actionGet(); final Settings settings = Settings.builder().put(IndexSettings.FINAL_PIPELINE.getKey(), "final_pipeline").build(); createIndex("index", settings); final IndexRequestBuilder index = client().prepareIndex("index").setId("1"); @@ -238,10 +249,13 @@ public void testDefaultAndFinalPipeline() { final BytesReference defaultPipelineBody = new BytesArray("{\"processors\": [{\"default\": {}}]}"); client().admin() .cluster() - .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, XContentType.JSON)) + .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, MediaTypeRegistry.JSON)) .actionGet(); final BytesReference finalPipelineBody = new BytesArray("{\"processors\": [{\"final\": {\"exists\":\"default\"}}]}"); - client().admin().cluster().putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, XContentType.JSON)).actionGet(); + client().admin() + .cluster() + .putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, MediaTypeRegistry.JSON)) + .actionGet(); final Settings settings = Settings.builder() .put(IndexSettings.DEFAULT_PIPELINE.getKey(), "default_pipeline") .put(IndexSettings.FINAL_PIPELINE.getKey(), "final_pipeline") @@ -266,10 +280,13 @@ public void testDefaultAndFinalPipelineFromTemplates() { final BytesReference defaultPipelineBody = new BytesArray("{\"processors\": [{\"default\": {}}]}"); client().admin() .cluster() - .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, XContentType.JSON)) + .putPipeline(new PutPipelineRequest("default_pipeline", defaultPipelineBody, MediaTypeRegistry.JSON)) .actionGet(); final BytesReference finalPipelineBody = new BytesArray("{\"processors\": [{\"final\": {\"exists\":\"default\"}}]}"); - client().admin().cluster().putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, XContentType.JSON)).actionGet(); + client().admin() + .cluster() + .putPipeline(new PutPipelineRequest("final_pipeline", finalPipelineBody, MediaTypeRegistry.JSON)) + .actionGet(); final int lowOrder = randomIntBetween(0, Integer.MAX_VALUE - 1); final int highOrder = randomIntBetween(lowOrder + 1, Integer.MAX_VALUE); final int finalPipelineOrder; diff --git a/server/src/internalClusterTest/java/org/opensearch/index/IndexRequestBuilderIT.java b/server/src/internalClusterTest/java/org/opensearch/index/IndexRequestBuilderIT.java index 9432f28a0a59e..57bdaff645838 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/IndexRequestBuilderIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/IndexRequestBuilderIT.java @@ -34,9 +34,9 @@ import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.query.QueryBuilders; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; @@ -54,11 +54,11 @@ public void testSetSource() throws InterruptedException, ExecutionException { map.put("test_field", "foobar"); IndexRequestBuilder[] builders = new IndexRequestBuilder[] { client().prepareIndex("test").setSource("test_field", "foobar"), - client().prepareIndex("test").setSource("{\"test_field\" : \"foobar\"}", XContentType.JSON), - client().prepareIndex("test").setSource(new BytesArray("{\"test_field\" : \"foobar\"}"), XContentType.JSON), - client().prepareIndex("test").setSource(new BytesArray("{\"test_field\" : \"foobar\"}"), XContentType.JSON), + client().prepareIndex("test").setSource("{\"test_field\" : \"foobar\"}", MediaTypeRegistry.JSON), + client().prepareIndex("test").setSource(new BytesArray("{\"test_field\" : \"foobar\"}"), MediaTypeRegistry.JSON), + client().prepareIndex("test").setSource(new BytesArray("{\"test_field\" : \"foobar\"}"), MediaTypeRegistry.JSON), client().prepareIndex("test") - .setSource(BytesReference.toBytes(new BytesArray("{\"test_field\" : \"foobar\"}")), XContentType.JSON), + .setSource(BytesReference.toBytes(new BytesArray("{\"test_field\" : \"foobar\"}")), MediaTypeRegistry.JSON), client().prepareIndex("test").setSource(map) }; indexRandom(true, builders); SearchResponse searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.termQuery("test_field", "foobar")).get(); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/IndexSortIT.java b/server/src/internalClusterTest/java/org/opensearch/index/IndexSortIT.java index 69eb34c39c10c..bb6e356db188f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/IndexSortIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/IndexSortIT.java @@ -32,22 +32,45 @@ package org.opensearch.index; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; import org.apache.lucene.search.SortedNumericSortField; import org.apache.lucene.search.SortedSetSortField; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.hamcrest.Matchers.containsString; -public class IndexSortIT extends OpenSearchIntegTestCase { +public class IndexSortIT extends ParameterizedOpenSearchIntegTestCase { private static final XContentBuilder TEST_MAPPING = createTestMapping(); + public IndexSortIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + private static XContentBuilder createTestMapping() { try { return jsonBuilder().startObject() @@ -81,8 +104,8 @@ private static XContentBuilder createTestMapping() { public void testIndexSort() { SortField dateSort = new SortedNumericSortField("date", SortField.Type.LONG, false); dateSort.setMissingValue(Long.MAX_VALUE); - SortField numericSort = new SortedNumericSortField("numeric_dv", SortField.Type.LONG, false); - numericSort.setMissingValue(Long.MAX_VALUE); + SortField numericSort = new SortedNumericSortField("numeric_dv", SortField.Type.INT, false); + numericSort.setMissingValue(Integer.MAX_VALUE); SortField keywordSort = new SortedSetSortField("keyword_dv", false); keywordSort.setMissingValue(SortField.STRING_LAST); Sort indexSort = new Sort(dateSort, numericSort, keywordSort); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/IndexingPressureIT.java b/server/src/internalClusterTest/java/org/opensearch/index/IndexingPressureIT.java index b461322735ce0..766ae502c0f19 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/IndexingPressureIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/IndexingPressureIT.java @@ -31,7 +31,6 @@ package org.opensearch.index; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; import org.opensearch.action.admin.indices.stats.ShardStats; import org.opensearch.action.bulk.BulkRequest; @@ -43,14 +42,15 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.UUIDs; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.collect.Tuple; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -437,7 +437,16 @@ public Settings onNodeStopped(String nodeName) { } private String getCoordinatingOnlyNode() { - return client().admin().cluster().prepareState().get().getState().nodes().getCoordinatingOnlyNodes().iterator().next().value + return client().admin() + .cluster() + .prepareState() + .get() + .getState() + .nodes() + .getCoordinatingOnlyNodes() + .values() + .iterator() + .next() .getName(); } diff --git a/server/src/internalClusterTest/java/org/opensearch/index/SegmentReplicationPressureIT.java b/server/src/internalClusterTest/java/org/opensearch/index/SegmentReplicationPressureIT.java new file mode 100644 index 0000000000000..033ea75b68958 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/index/SegmentReplicationPressureIT.java @@ -0,0 +1,385 @@ +/* + * Copyright OpenSearch Contributors. + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.index; + +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsResponse; +import org.opensearch.action.bulk.BulkItemResponse; +import org.opensearch.action.bulk.BulkRequest; +import org.opensearch.action.bulk.BulkResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.UUIDs; +import org.opensearch.common.lease.Releasable; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.index.shard.IndexShardState; +import org.opensearch.indices.replication.SegmentReplicationBaseIT; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.util.Arrays.asList; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; +import static org.opensearch.index.SegmentReplicationPressureService.MAX_INDEXING_CHECKPOINTS; +import static org.opensearch.index.SegmentReplicationPressureService.MAX_REPLICATION_LIMIT_STALE_REPLICA_SETTING; +import static org.opensearch.index.SegmentReplicationPressureService.MAX_REPLICATION_TIME_BACKPRESSURE_SETTING; +import static org.opensearch.index.SegmentReplicationPressureService.SEGMENT_REPLICATION_INDEXING_PRESSURE_ENABLED; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationPressureIT extends SegmentReplicationBaseIT { + + private static final int MAX_CHECKPOINTS_BEHIND = 2; + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(SEGMENT_REPLICATION_INDEXING_PRESSURE_ENABLED.getKey(), true) + .put(MAX_REPLICATION_TIME_BACKPRESSURE_SETTING.getKey(), TimeValue.timeValueSeconds(1)) + .put(MAX_INDEXING_CHECKPOINTS.getKey(), MAX_CHECKPOINTS_BEHIND) + .build(); + } + + @Override + public Settings indexSettings() { + // we want to control refreshes + return Settings.builder() + .put(super.indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, SHARD_COUNT) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, REPLICA_COUNT) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put("index.refresh_interval", -1) + .build(); + } + + @Override + protected Collection> nodePlugins() { + return asList(MockTransportService.TestPlugin.class); + } + + public void testWritesRejected() throws Exception { + final String primaryNode = internalCluster().startNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replicaNode = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + + final IndexShard primaryShard = getIndexShard(primaryNode, INDEX_NAME); + final List replicaNodes = asList(replicaNode); + assertEqualSegmentInfosVersion(replicaNodes, primaryShard); + + final CountDownLatch latch = new CountDownLatch(1); + final AtomicInteger totalDocs = new AtomicInteger(0); + try (final Releasable ignored = blockReplication(replicaNodes, latch)) { + Thread indexingThread = new Thread(() -> { totalDocs.getAndSet(indexUntilCheckpointCount()); }); + indexingThread.start(); + indexingThread.join(); + latch.await(); + + indexDoc(); + totalDocs.incrementAndGet(); + refresh(INDEX_NAME); + // index again while we are stale. + assertBusy(() -> { + expectThrows(OpenSearchRejectedExecutionException.class, () -> { + indexDoc(); + totalDocs.incrementAndGet(); + }); + }); + // Try to index one more doc. + expectThrows(OpenSearchRejectedExecutionException.class, () -> { + indexDoc(); + totalDocs.incrementAndGet(); + refresh(INDEX_NAME); + }); + + // Verify the rejected doc count. + SegmentReplicationStatsResponse segmentReplicationStatsResponse = dataNodeClient().admin() + .indices() + .prepareSegmentReplicationStats(INDEX_NAME) + .setDetailed(true) + .execute() + .actionGet(); + SegmentReplicationPerGroupStats perGroupStats = segmentReplicationStatsResponse.getReplicationStats().get(INDEX_NAME).get(0); + + assertEquals(perGroupStats.getRejectedRequestCount(), 2L); + } + refresh(INDEX_NAME); + + // wait for the replicas to catch up after block is released. + assertReplicaCheckpointUpdated(primaryShard); + // index another doc showing there is no pressure enforced. + indexDoc(); + refresh(INDEX_NAME); + waitForSearchableDocs(totalDocs.incrementAndGet(), replicaNodes.toArray(new String[] {})); + verifyStoreContent(); + } + + /** + * This test ensures that a replica can be added while the index is under write block. + * Ensuring that only write requests are blocked. + */ + public void testAddReplicaWhileWritesBlocked() throws Exception { + final String primaryNode = internalCluster().startNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replicaNode = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + + final IndexShard primaryShard = getIndexShard(primaryNode, INDEX_NAME); + final List replicaNodes = new ArrayList<>(); + replicaNodes.add(replicaNode); + assertEqualSegmentInfosVersion(replicaNodes, primaryShard); + + final CountDownLatch latch = new CountDownLatch(1); + final AtomicInteger totalDocs = new AtomicInteger(0); + try (final Releasable ignored = blockReplication(replicaNodes, latch)) { + Thread indexingThread = new Thread(() -> { totalDocs.getAndSet(indexUntilCheckpointCount()); }); + indexingThread.start(); + indexingThread.join(); + latch.await(); + indexDoc(); + totalDocs.incrementAndGet(); + refresh(INDEX_NAME); + // index again while we are stale. + assertBusy(() -> { + expectThrows(OpenSearchRejectedExecutionException.class, () -> { + indexDoc(); + totalDocs.incrementAndGet(); + }); + }); + final String replica_2 = internalCluster().startNode(); + assertAcked( + client().admin() + .indices() + .prepareUpdateSettings(INDEX_NAME) + .setSettings(Settings.builder().put(SETTING_NUMBER_OF_REPLICAS, 2)) + ); + replicaNodes.add(replica_2); + } + ensureGreen(INDEX_NAME); + waitForSearchableDocs(totalDocs.get(), replicaNodes); + refresh(INDEX_NAME); + // wait for the replicas to catch up after block is released. + assertReplicaCheckpointUpdated(primaryShard); + + // index another doc showing there is no pressure enforced. + indexDoc(); + refresh(INDEX_NAME); + waitForSearchableDocs(totalDocs.incrementAndGet(), replicaNodes.toArray(new String[] {})); + verifyStoreContent(); + } + + public void testBelowReplicaLimit() throws Exception { + final Settings settings = Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 3).build(); + final String primaryNode = internalCluster().startNode(); + createIndex(INDEX_NAME, settings); + ensureYellowAndNoInitializingShards(INDEX_NAME); + List replicaNodes = new ArrayList<>(); + for (int i = 0; i < 3; i++) { + replicaNodes.add(internalCluster().startNode()); + } + ensureGreen(INDEX_NAME); + + final IndexShard primaryShard = getIndexShard(primaryNode, INDEX_NAME); + assertEqualSegmentInfosVersion(replicaNodes, primaryShard); + + final CountDownLatch latch = new CountDownLatch(1); + final AtomicInteger totalDocs = new AtomicInteger(0); + // only block a single replica, pressure should not get applied. + try (final Releasable ignored = blockReplication(replicaNodes.subList(0, 1), latch)) { + Thread indexingThread = new Thread(() -> totalDocs.getAndSet(indexUntilCheckpointCount())); + indexingThread.start(); + indexingThread.join(); + latch.await(); + indexDoc(); + totalDocs.incrementAndGet(); + refresh(INDEX_NAME); + } + // index another doc showing there is no pressure enforced. + indexDoc(); + refresh(INDEX_NAME); + waitForSearchableDocs(totalDocs.incrementAndGet(), replicaNodes.toArray(new String[] {})); + verifyStoreContent(); + } + + public void testFailStaleReplica() throws Exception { + + Settings settings = Settings.builder() + .put(MAX_REPLICATION_TIME_BACKPRESSURE_SETTING.getKey(), TimeValue.timeValueMillis(500)) + .put(MAX_REPLICATION_LIMIT_STALE_REPLICA_SETTING.getKey(), TimeValue.timeValueMillis(1000)) + .build(); + // Starts a primary and replica node. + final String primaryNode = internalCluster().startNode(settings); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replicaNode = internalCluster().startNode(settings); + ensureGreen(INDEX_NAME); + + final IndexShard primaryShard = getIndexShard(primaryNode, INDEX_NAME); + final List replicaNodes = asList(replicaNode); + assertEqualSegmentInfosVersion(replicaNodes, primaryShard); + IndexShard replicaShard = getIndexShard(replicaNode, INDEX_NAME); + + final CountDownLatch latch = new CountDownLatch(1); + final AtomicInteger totalDocs = new AtomicInteger(0); + try (final Releasable ignored = blockReplication(replicaNodes, latch)) { + // Index docs until replicas are staled. + totalDocs.getAndSet(indexUntilCheckpointCount()); + latch.await(); + // index again while we are stale. + indexDoc(); + refresh(INDEX_NAME); + totalDocs.incrementAndGet(); + + // Verify that replica shard is closed. + assertBusy(() -> { assertTrue(replicaShard.state().equals(IndexShardState.CLOSED)); }, 1, TimeUnit.MINUTES); + } + ensureGreen(INDEX_NAME); + final IndexShard replicaAfterFailure = getIndexShard(replicaNode, INDEX_NAME); + + // Verify that new replica shard after failure is different from old replica shard. + assertNotEquals(replicaAfterFailure.routingEntry().allocationId().getId(), replicaShard.routingEntry().allocationId().getId()); + } + + public void testWithDocumentReplicationEnabledIndex() throws Exception { + assumeFalse( + "Skipping the test as its not compatible with segment replication with remote store. Cannot create DocRep indices with Remote store enabled", + segmentReplicationWithRemoteEnabled() + ); + Settings settings = Settings.builder() + .put(MAX_REPLICATION_TIME_BACKPRESSURE_SETTING.getKey(), TimeValue.timeValueMillis(500)) + .build(); + // Starts a primary and replica node. + final String primaryNode = internalCluster().startNode(settings); + createIndex( + INDEX_NAME, + Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT).build() + ); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replicaNode = internalCluster().startNode(settings); + ensureGreen(INDEX_NAME); + final AtomicInteger totalDocs = new AtomicInteger(0); + // Index docs until replica stale limit is reached. + totalDocs.getAndSet(indexUntilCheckpointCount()); + // index again after stale limit. + indexDoc(); + refresh(INDEX_NAME); + totalDocs.incrementAndGet(); + // verify total doc count is same and docs are not rejected. + assertHitCount(client(primaryNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), totalDocs.get()); + assertHitCount(client(replicaNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), totalDocs.get()); + + } + + public void testBulkWritesRejected() throws Exception { + final String primaryNode = internalCluster().startNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replicaNode = internalCluster().startNode(); + final String coordinator = internalCluster().startCoordinatingOnlyNode(Settings.EMPTY); + ensureGreen(INDEX_NAME); + + final IndexShard primaryShard = getIndexShard(primaryNode, INDEX_NAME); + final List replicaNodes = asList(replicaNode); + assertEqualSegmentInfosVersion(replicaNodes, primaryShard); + + final CountDownLatch latch = new CountDownLatch(1); + List nodes = List.of(primaryNode, replicaNode, coordinator); + + int docsPerBatch = randomIntBetween(1, 200); + int totalDocs = docsPerBatch * MAX_CHECKPOINTS_BEHIND; + try (final Releasable ignored = blockReplication(replicaNodes, latch)) { + Thread indexingThread = new Thread(() -> { + for (int i = 0; i < MAX_CHECKPOINTS_BEHIND + 1; i++) { + executeBulkRequest(nodes, docsPerBatch); + refresh(INDEX_NAME); + } + }); + indexingThread.start(); + indexingThread.join(); + latch.await(); + // try and index again while we are stale. + assertBusy(() -> { assertFailedRequests(executeBulkRequest(nodes, randomIntBetween(1, 200))); }); + } + refresh(INDEX_NAME); + // wait for the replicas to catch up after block is released. + assertReplicaCheckpointUpdated(primaryShard); + + // index another doc showing there is no pressure enforced. + executeBulkRequest(nodes, totalDocs); + waitForSearchableDocs(totalDocs * 2L, replicaNodes.toArray(new String[] {})); + verifyStoreContent(); + } + + private BulkResponse executeBulkRequest(List nodes, int docsPerBatch) { + final BulkRequest bulkRequest = new BulkRequest(); + for (int j = 0; j < docsPerBatch; ++j) { + IndexRequest request = new IndexRequest(INDEX_NAME).id(UUIDs.base64UUID()) + .source(Collections.singletonMap("key", randomAlphaOfLength(50))); + bulkRequest.add(request); + } + final BulkResponse bulkItemResponses = client(randomFrom(nodes)).bulk(bulkRequest).actionGet(); + refresh(INDEX_NAME); + return bulkItemResponses; + } + + /** + * Index and Refresh in batches to force checkpoints behind. + * Asserts that there are no stale replicas according to the primary until cp count is reached. + */ + private int indexUntilCheckpointCount() { + int total = 0; + for (int i = 0; i < MAX_CHECKPOINTS_BEHIND; i++) { + final int numDocs = randomIntBetween(1, 5); + for (int j = 0; j < numDocs; ++j) { + indexDoc(); + } + total += numDocs; + refresh(INDEX_NAME); + } + return total; + } + + private void assertFailedRequests(BulkResponse response) { + assertTrue(Arrays.stream(response.getItems()).allMatch(BulkItemResponse::isFailed)); + assertTrue( + Arrays.stream(response.getItems()) + .map(BulkItemResponse::getFailure) + .allMatch((failure) -> failure.getStatus() == RestStatus.TOO_MANY_REQUESTS) + ); + } + + private void indexDoc() { + client().prepareIndex(INDEX_NAME).setId(UUIDs.base64UUID()).setSource("{}", "{}").execute().actionGet(); + } + + private void assertEqualSegmentInfosVersion(List replicaNames, IndexShard primaryShard) { + for (String replicaName : replicaNames) { + final IndexShard replicaShard = getIndexShard(replicaName, INDEX_NAME); + assertEquals( + primaryShard.getLatestReplicationCheckpoint().getSegmentInfosVersion(), + replicaShard.getLatestReplicationCheckpoint().getSegmentInfosVersion() + ); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/index/ShardIndexingPressureIT.java b/server/src/internalClusterTest/java/org/opensearch/index/ShardIndexingPressureIT.java index f14540cec4e2b..69c394d2da133 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/ShardIndexingPressureIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/ShardIndexingPressureIT.java @@ -6,29 +6,30 @@ package org.opensearch.index; import org.apache.lucene.util.RamUsageEstimator; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; import org.opensearch.action.admin.indices.stats.ShardStats; import org.opensearch.action.bulk.BulkItemRequest; import org.opensearch.action.bulk.BulkRequest; +import org.opensearch.action.bulk.BulkResponse; import org.opensearch.action.bulk.BulkShardRequest; import org.opensearch.action.bulk.TransportShardBulkAction; -import org.opensearch.action.bulk.BulkResponse; import org.opensearch.action.index.IndexRequest; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.UUIDs; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.collect.Tuple; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -778,7 +779,16 @@ private Tuple getPrimaryReplicaNodeNames(String indexName) { } private String getCoordinatingOnlyNode() { - return client().admin().cluster().prepareState().get().getState().nodes().getCoordinatingOnlyNodes().iterator().next().value + return client().admin() + .cluster() + .prepareState() + .get() + .getState() + .nodes() + .getCoordinatingOnlyNodes() + .values() + .iterator() + .next() .getName(); } diff --git a/server/src/internalClusterTest/java/org/opensearch/index/ShardIndexingPressureSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/index/ShardIndexingPressureSettingsIT.java index 1cdf8e702aafa..5426f4037294f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/ShardIndexingPressureSettingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/ShardIndexingPressureSettingsIT.java @@ -6,7 +6,6 @@ package org.opensearch.index; import org.apache.lucene.util.RamUsageEstimator; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; import org.opensearch.action.admin.indices.stats.ShardStats; @@ -21,16 +20,18 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.UUIDs; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.collect.Tuple; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -920,7 +921,16 @@ private Tuple getPrimaryReplicaNodeNames(String indexName) { } private String getCoordinatingOnlyNode() { - return client().admin().cluster().prepareState().get().getState().nodes().getCoordinatingOnlyNodes().iterator().next().value + return client().admin() + .cluster() + .prepareState() + .get() + .getState() + .nodes() + .getCoordinatingOnlyNodes() + .values() + .iterator() + .next() .getName(); } diff --git a/server/src/internalClusterTest/java/org/opensearch/index/WaitUntilRefreshIT.java b/server/src/internalClusterTest/java/org/opensearch/index/WaitUntilRefreshIT.java index e38b128c04fde..eb1b721cdad25 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/WaitUntilRefreshIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/WaitUntilRefreshIT.java @@ -32,7 +32,6 @@ package org.opensearch.index; -import org.opensearch.action.ActionFuture; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.bulk.BulkItemResponse; import org.opensearch.action.bulk.BulkRequestBuilder; @@ -42,14 +41,14 @@ import org.opensearch.action.support.WriteRequest.RefreshPolicy; import org.opensearch.action.update.UpdateResponse; import org.opensearch.client.Requests; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; +import org.opensearch.core.rest.RestStatus; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.test.OpenSearchIntegTestCase; - import org.junit.Before; import java.util.Collection; diff --git a/server/src/internalClusterTest/java/org/opensearch/index/codec/ZstdNotEnabledIT.java b/server/src/internalClusterTest/java/org/opensearch/index/codec/ZstdNotEnabledIT.java new file mode 100644 index 0000000000000..9b1fa77fc9a5a --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/index/codec/ZstdNotEnabledIT.java @@ -0,0 +1,41 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.index.codec; + +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.List; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST) +public class ZstdNotEnabledIT extends OpenSearchIntegTestCase { + + public void testZStdCodecsWithoutPluginInstalled() { + + internalCluster().startNode(); + final String index = "test-index"; + + // creating index with zstd and zstd_no_dict should fail if custom-codecs plugin is not installed + for (String codec : List.of("zstd", "zstd_no_dict")) { + assertThrows( + IllegalArgumentException.class, + () -> createIndex( + index, + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put("index.codec", codec) + .build() + ) + ); + } + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/index/engine/MaxDocsLimitIT.java b/server/src/internalClusterTest/java/org/opensearch/index/engine/MaxDocsLimitIT.java index b548d4061475c..385d33c359559 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/engine/MaxDocsLimitIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/engine/MaxDocsLimitIT.java @@ -36,16 +36,15 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.translog.Translog; import org.opensearch.plugins.EnginePlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; - import org.junit.After; import org.junit.Before; @@ -204,7 +203,7 @@ static IndexingResult indexDocs(int numRequests, int numThreads) throws Exceptio phaser.arriveAndAwaitAdvance(); while (completedRequests.incrementAndGet() <= numRequests) { try { - final IndexResponse resp = client().prepareIndex("test").setSource("{}", XContentType.JSON).get(); + final IndexResponse resp = client().prepareIndex("test").setSource("{}", MediaTypeRegistry.JSON).get(); numSuccess.incrementAndGet(); assertThat(resp.status(), equalTo(RestStatus.CREATED)); } catch (IllegalArgumentException e) { diff --git a/server/src/internalClusterTest/java/org/opensearch/index/mapper/CopyToMapperIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/index/mapper/CopyToMapperIntegrationIT.java index d4733e4f31cb2..0df84261ade63 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/mapper/CopyToMapperIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/mapper/CopyToMapperIntegrationIT.java @@ -33,9 +33,8 @@ package org.opensearch.index.mapper; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.aggregations.AggregationBuilders; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; @@ -78,16 +77,15 @@ public void testDynamicTemplateCopyTo() throws Exception { } public void testDynamicObjectCopyTo() throws Exception { - String mapping = Strings.toString( - jsonBuilder().startObject() - .startObject("properties") - .startObject("foo") - .field("type", "text") - .field("copy_to", "root.top.child") - .endObject() - .endObject() - .endObject() - ); + String mapping = jsonBuilder().startObject() + .startObject("properties") + .startObject("foo") + .field("type", "text") + .field("copy_to", "root.top.child") + .endObject() + .endObject() + .endObject() + .toString(); assertAcked(client().admin().indices().prepareCreate("test-idx").setMapping(mapping)); client().prepareIndex("test-idx").setId("1").setSource("foo", "bar").get(); client().admin().indices().prepareRefresh("test-idx").execute().actionGet(); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/mapper/DynamicMappingIT.java b/server/src/internalClusterTest/java/org/opensearch/index/mapper/DynamicMappingIT.java index d5924155e2ec7..2267ef1bb6739 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/mapper/DynamicMappingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/mapper/DynamicMappingIT.java @@ -42,9 +42,8 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; - +import org.opensearch.test.OpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.io.IOException; @@ -76,7 +75,7 @@ public void testConflictingDynamicMappings() { assertThat(e.getMessage(), Matchers.containsString("failed to parse field [foo] of type [long]")); } catch (IllegalArgumentException e) { // rare case: the node that processes the index request doesn't have the mappings - // yet and sends a mapping update to the master node to map "bar" as "text". This + // yet and sends a mapping update to the cluster-manager node to map "bar" as "text". This // fails as it had been already mapped as a long by the previous index request. assertThat(e.getMessage(), Matchers.containsString("mapper [foo] cannot be changed from type [long] to [text]")); } @@ -140,19 +139,19 @@ public void run() { } } - public void testPreflightCheckAvoidsMaster() throws InterruptedException { + public void testPreflightCheckAvoidsClusterManager() throws InterruptedException { createIndex("index", Settings.builder().put(INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING.getKey(), 2).build()); ensureGreen("index"); client().prepareIndex("index").setId("1").setSource("field1", "value1").get(); - final CountDownLatch masterBlockedLatch = new CountDownLatch(1); + final CountDownLatch clusterManagerBlockedLatch = new CountDownLatch(1); final CountDownLatch indexingCompletedLatch = new CountDownLatch(1); - internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()) + internalCluster().getInstance(ClusterService.class, internalCluster().getClusterManagerName()) .submitStateUpdateTask("block-state-updates", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) throws Exception { - masterBlockedLatch.countDown(); + clusterManagerBlockedLatch.countDown(); indexingCompletedLatch.await(); return currentState; } @@ -163,7 +162,7 @@ public void onFailure(String source, Exception e) { } }); - masterBlockedLatch.await(); + clusterManagerBlockedLatch.await(); final IndexRequestBuilder indexRequestBuilder = client().prepareIndex("index").setId("2").setSource("field2", "value2"); try { assertThat( diff --git a/server/src/internalClusterTest/java/org/opensearch/index/mapper/ExternalValuesMapperIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/index/mapper/ExternalValuesMapperIntegrationIT.java index 138081e56dd63..3734bbbe8aa6c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/mapper/ExternalValuesMapperIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/mapper/ExternalValuesMapperIntegrationIT.java @@ -40,11 +40,12 @@ import org.opensearch.plugins.Plugin; import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder; import org.opensearch.test.OpenSearchIntegTestCase; -import org.locationtech.jts.geom.Coordinate; import java.util.Collection; import java.util.Collections; +import org.locationtech.jts.geom.Coordinate; + import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.equalTo; diff --git a/server/src/internalClusterTest/java/org/opensearch/index/mapper/MultiFieldsIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/index/mapper/MultiFieldsIntegrationIT.java index 7dbb6a45b857d..50440f9527a6a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/mapper/MultiFieldsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/mapper/MultiFieldsIntegrationIT.java @@ -37,9 +37,9 @@ import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.unit.DistanceUnit; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/index/search/MatchPhraseQueryIT.java b/server/src/internalClusterTest/java/org/opensearch/index/search/MatchPhraseQueryIT.java index 6d76ee48a5b95..2d28578dbebcc 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/search/MatchPhraseQueryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/search/MatchPhraseQueryIT.java @@ -32,26 +32,50 @@ package org.opensearch.index.search; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.MatchPhraseQueryBuilder; import org.opensearch.index.search.MatchQuery.ZeroTermsQuery; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.Before; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; import static org.opensearch.index.query.QueryBuilders.matchPhraseQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; -public class MatchPhraseQueryIT extends OpenSearchIntegTestCase { +public class MatchPhraseQueryIT extends ParameterizedOpenSearchIntegTestCase { + private static final String INDEX = "test"; + public MatchPhraseQueryIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Before public void setUp() throws Exception { super.setUp(); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/seqno/GlobalCheckpointSyncIT.java b/server/src/internalClusterTest/java/org/opensearch/index/seqno/GlobalCheckpointSyncIT.java index ce7cb81dbd2df..9388d7344cf3f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/seqno/GlobalCheckpointSyncIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/seqno/GlobalCheckpointSyncIT.java @@ -37,15 +37,15 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.translog.Translog; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.transport.TransportService; @@ -82,7 +82,7 @@ public void testGlobalCheckpointSyncWithAsyncDurability() throws Exception { for (int j = 0; j < 10; j++) { final String id = Integer.toString(j); - client().prepareIndex("test").setId(id).setSource("{\"foo\": " + id + "}", XContentType.JSON).get(); + client().prepareIndex("test").setId(id).setSource("{\"foo\": " + id + "}", MediaTypeRegistry.JSON).get(); } assertBusy(() -> { @@ -194,7 +194,7 @@ private void runGlobalCheckpointSyncTest( } for (int j = 0; j < numberOfDocuments; j++) { final String id = Integer.toString(index * numberOfDocuments + j); - client().prepareIndex("test").setId(id).setSource("{\"foo\": " + id + "}", XContentType.JSON).get(); + client().prepareIndex("test").setId(id).setSource("{\"foo\": " + id + "}", MediaTypeRegistry.JSON).get(); } try { barrier.await(); @@ -251,7 +251,7 @@ public void testPersistGlobalCheckpoint() throws Exception { } int numDocs = randomIntBetween(1, 20); for (int i = 0; i < numDocs; i++) { - client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", MediaTypeRegistry.JSON).get(); } ensureGreen("test"); assertBusy(() -> { @@ -281,7 +281,7 @@ public void testPersistLocalCheckpoint() { logger.info("numDocs {}", numDocs); long maxSeqNo = 0; for (int i = 0; i < numDocs; i++) { - maxSeqNo = client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", XContentType.JSON).get().getSeqNo(); + maxSeqNo = client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", MediaTypeRegistry.JSON).get().getSeqNo(); logger.info("got {}", maxSeqNo); } for (IndicesService indicesService : internalCluster().getDataNodeInstances(IndicesService.class)) { diff --git a/server/src/internalClusterTest/java/org/opensearch/index/seqno/PeerRecoveryRetentionLeaseCreationIT.java b/server/src/internalClusterTest/java/org/opensearch/index/seqno/PeerRecoveryRetentionLeaseCreationIT.java index e1e969345d5ed..83dd409e750b0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/seqno/PeerRecoveryRetentionLeaseCreationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/seqno/PeerRecoveryRetentionLeaseCreationIT.java @@ -38,8 +38,8 @@ import org.opensearch.env.NodeEnvironment; import org.opensearch.env.NodeMetadata; import org.opensearch.index.IndexSettings; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; import java.nio.file.Path; @@ -63,7 +63,7 @@ public void testCanRecoverFromStoreWithoutPeerRecoveryRetentionLease() throws Ex * primary that is recovering from store creates a lease for itself. */ - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final Path[] nodeDataPaths = internalCluster().getInstance(NodeEnvironment.class, dataNode).nodeDataPaths(); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/seqno/RetentionLeaseIT.java b/server/src/internalClusterTest/java/org/opensearch/index/seqno/RetentionLeaseIT.java index ed6074b39c8a7..6163edada9f6e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/seqno/RetentionLeaseIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/seqno/RetentionLeaseIT.java @@ -33,7 +33,6 @@ package org.opensearch.index.seqno; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.action.support.replication.ReplicationResponse; import org.opensearch.cluster.node.DiscoveryNode; @@ -41,10 +40,11 @@ import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.indices.recovery.PeerRecoveryTargetService; import org.opensearch.plugins.Plugin; diff --git a/server/src/internalClusterTest/java/org/opensearch/index/shard/GlobalCheckpointListenersIT.java b/server/src/internalClusterTest/java/org/opensearch/index/shard/GlobalCheckpointListenersIT.java index a2c5c0333bbfe..d60e852a82ca0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/shard/GlobalCheckpointListenersIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/shard/GlobalCheckpointListenersIT.java @@ -34,7 +34,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.indices.IndicesService; import org.opensearch.test.OpenSearchSingleNodeTestCase; @@ -88,7 +88,7 @@ public void accept(final long g, final Exception e) { } }, null); - client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", MediaTypeRegistry.JSON).get(); assertBusy(() -> assertThat(globalCheckpoint.get(), equalTo((long) index))); // adding a listener expecting a lower global checkpoint should fire immediately final AtomicLong immediateGlobalCheckpint = new AtomicLong(); @@ -126,7 +126,7 @@ public void accept(final long g, final Exception e) { } }, null); - shard.close("closed", randomBoolean()); + shard.close("closed", randomBoolean(), false); assertBusy(() -> assertTrue(invoked.get())); } diff --git a/server/src/internalClusterTest/java/org/opensearch/index/shard/IndexShardIT.java b/server/src/internalClusterTest/java/org/opensearch/index/shard/IndexShardIT.java index efc522a1f9741..44a900491d949 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/shard/IndexShardIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/shard/IndexShardIT.java @@ -35,7 +35,6 @@ import org.apache.lucene.store.LockObtainFailedException; import org.opensearch.ExceptionsHelper; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchResponse; @@ -53,20 +52,23 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.CheckedFunction; import org.opensearch.common.CheckedRunnable; -import org.opensearch.common.Strings; import org.opensearch.common.UUIDs; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.indices.breaker.CircuitBreakerService; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.env.ShardLock; -import org.opensearch.index.Index; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.VersionType; @@ -78,19 +80,19 @@ import org.opensearch.index.mapper.SourceToParse; import org.opensearch.index.seqno.RetentionLeaseSyncer; import org.opensearch.index.seqno.SequenceNumbers; +import org.opensearch.index.translog.InternalTranslogFactory; import org.opensearch.index.translog.TestTranslog; import org.opensearch.index.translog.Translog; import org.opensearch.index.translog.TranslogStats; import org.opensearch.indices.IndicesService; -import org.opensearch.indices.breaker.CircuitBreakerService; import org.opensearch.indices.recovery.RecoveryState; +import org.opensearch.indices.replication.checkpoint.SegmentReplicationCheckpointPublisher; import org.opensearch.plugins.Plugin; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.test.DummyShardLock; -import org.opensearch.test.OpenSearchSingleNodeTestCase; import org.opensearch.test.IndexSettingsModule; import org.opensearch.test.InternalSettingsPlugin; - +import org.opensearch.test.OpenSearchSingleNodeTestCase; import org.junit.Assert; import java.io.IOException; @@ -114,7 +116,6 @@ import java.util.function.Predicate; import java.util.stream.Stream; -import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiLettersOfLength; import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; @@ -127,13 +128,13 @@ import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; - import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiLettersOfLength; public class IndexShardIT extends OpenSearchSingleNodeTestCase { @@ -173,7 +174,7 @@ public void testLockTryingToDelete() throws Exception { public void testDurableFlagHasEffect() throws Exception { createIndex("test"); ensureGreen(); - client().prepareIndex("test").setId("1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId("1").setSource("{}", MediaTypeRegistry.JSON).get(); IndicesService indicesService = getInstanceFromNode(IndicesService.class); IndexService test = indicesService.indexService(resolveIndex("test")); IndexShard shard = test.getShardOrNull(0); @@ -193,7 +194,7 @@ public void testDurableFlagHasEffect() throws Exception { setDurability(shard, Translog.Durability.REQUEST); assertFalse(needsSync.test(translog)); setDurability(shard, Translog.Durability.ASYNC); - client().prepareIndex("test").setId("2").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId("2").setSource("{}", MediaTypeRegistry.JSON).get(); assertTrue(needsSync.test(translog)); setDurability(shard, Translog.Durability.REQUEST); client().prepareDelete("test", "1").get(); @@ -205,7 +206,7 @@ public void testDurableFlagHasEffect() throws Exception { setDurability(shard, Translog.Durability.REQUEST); assertNoFailures( client().prepareBulk() - .add(client().prepareIndex("test").setId("3").setSource("{}", XContentType.JSON)) + .add(client().prepareIndex("test").setId("3").setSource("{}", MediaTypeRegistry.JSON)) .add(client().prepareDelete("test", "1")) .get() ); @@ -214,7 +215,7 @@ public void testDurableFlagHasEffect() throws Exception { setDurability(shard, Translog.Durability.ASYNC); assertNoFailures( client().prepareBulk() - .add(client().prepareIndex("test").setId("4").setSource("{}", XContentType.JSON)) + .add(client().prepareIndex("test").setId("4").setSource("{}", MediaTypeRegistry.JSON)) .add(client().prepareDelete("test", "3")) .get() ); @@ -252,7 +253,7 @@ public void testIndexDirIsDeletedWhenShardRemoved() throws Exception { Settings idxSettings = Settings.builder().put(IndexMetadata.SETTING_DATA_PATH, idxPath).build(); createIndex("test", idxSettings); ensureGreen("test"); - client().prepareIndex("test").setId("1").setSource("{}", XContentType.JSON).setRefreshPolicy(IMMEDIATE).get(); + client().prepareIndex("test").setId("1").setSource("{}", MediaTypeRegistry.JSON).setRefreshPolicy(IMMEDIATE).get(); SearchResponse response = client().prepareSearch("test").get(); assertHitCount(response, 1L); client().admin().indices().prepareDelete("test").get(); @@ -268,7 +269,7 @@ public void testExpectedShardSizeIsPresent() throws InterruptedException { .setSettings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0)) ); for (int i = 0; i < 50; i++) { - client().prepareIndex("test").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setSource("{}", MediaTypeRegistry.JSON).get(); } ensureGreen("test"); InternalClusterInfoService clusterInfoService = (InternalClusterInfoService) getInstanceFromNode(ClusterInfoService.class); @@ -368,14 +369,14 @@ public void testMaybeFlush() throws Exception { .get(); client().prepareIndex("test") .setId("0") - .setSource("{}", XContentType.JSON) + .setSource("{}", MediaTypeRegistry.JSON) .setRefreshPolicy(randomBoolean() ? IMMEDIATE : NONE) .get(); assertFalse(shard.shouldPeriodicallyFlush()); shard.applyIndexOperationOnPrimary( Versions.MATCH_ANY, VersionType.INTERNAL, - new SourceToParse("test", "1", new BytesArray("{}"), XContentType.JSON), + new SourceToParse("test", "1", new BytesArray("{}"), MediaTypeRegistry.JSON), SequenceNumbers.UNASSIGNED_SEQ_NO, 0, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, @@ -387,7 +388,7 @@ public void testMaybeFlush() throws Exception { assertThat(shard.flushStats().getTotal(), equalTo(0L)); client().prepareIndex("test") .setId("2") - .setSource("{}", XContentType.JSON) + .setSource("{}", MediaTypeRegistry.JSON) .setRefreshPolicy(randomBoolean() ? IMMEDIATE : NONE) .get(); assertThat(shard.getLastKnownGlobalCheckpoint(), equalTo(2L)); @@ -428,7 +429,7 @@ public void testMaybeFlush() throws Exception { final FlushStats flushStats = shard.flushStats(); logger.info( "--> translog stats [{}] gen [{}] commit_stats [{}] flush_stats [{}/{}]", - Strings.toString(translogStats), + Strings.toString(MediaTypeRegistry.JSON, translogStats), translog.getGeneration().translogFileGeneration, commitStats.getUserData(), flushStats.getPeriodic(), @@ -460,7 +461,7 @@ public void testMaybeRollTranslogGeneration() throws Exception { final Engine.IndexResult result = shard.applyIndexOperationOnPrimary( Versions.MATCH_ANY, VersionType.INTERNAL, - new SourceToParse("test", "1", new BytesArray("{}"), XContentType.JSON), + new SourceToParse("test", "1", new BytesArray("{}"), MediaTypeRegistry.JSON), SequenceNumbers.UNASSIGNED_SEQ_NO, 0, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, @@ -496,7 +497,7 @@ public void testStressMaybeFlushOrRollTranslogGeneration() throws Exception { client().admin().indices().prepareUpdateSettings("test").setSettings(settings).get(); client().prepareIndex("test") .setId("0") - .setSource("{}", XContentType.JSON) + .setSource("{}", MediaTypeRegistry.JSON) .setRefreshPolicy(randomBoolean() ? IMMEDIATE : NONE) .get(); assertFalse(shard.shouldPeriodicallyFlush()); @@ -521,7 +522,7 @@ public void testStressMaybeFlushOrRollTranslogGeneration() throws Exception { final CheckedRunnable check; if (flush) { final FlushStats initialStats = shard.flushStats(); - client().prepareIndex("test").setId("1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId("1").setSource("{}", MediaTypeRegistry.JSON).get(); check = () -> { assertFalse(shard.shouldPeriodicallyFlush()); final FlushStats currentStats = shard.flushStats(); @@ -546,7 +547,7 @@ public void testStressMaybeFlushOrRollTranslogGeneration() throws Exception { }; } else { final long generation = getTranslog(shard).currentFileGeneration(); - client().prepareIndex("test").setId("1").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId("1").setSource("{}", MediaTypeRegistry.JSON).get(); check = () -> { assertFalse(shard.shouldRollTranslogGeneration()); assertEquals(generation + 1, getTranslog(shard).currentFileGeneration()); @@ -567,7 +568,7 @@ public void testFlushStats() throws Exception { client().admin().indices().prepareUpdateSettings("test").setSettings(settings).get(); final int numDocs = between(10, 100); for (int i = 0; i < numDocs; i++) { - client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", MediaTypeRegistry.JSON).get(); } // A flush stats may include the new total count but the old period count - assert eventually. assertBusy(() -> { @@ -578,7 +579,7 @@ public void testFlushStats() throws Exception { settings = Settings.builder().put("index.translog.flush_threshold_size", (String) null).build(); client().admin().indices().prepareUpdateSettings("test").setSettings(settings).get(); - client().prepareIndex("test").setId(UUIDs.randomBase64UUID()).setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId(UUIDs.randomBase64UUID()).setSource("{}", MediaTypeRegistry.JSON).get(); client().admin().indices().prepareFlush("test").setForce(randomBoolean()).setWaitIfOngoing(true).get(); final FlushStats flushStats = client().admin().indices().prepareStats("test").clear().setFlush(true).get().getTotal().flush; assertThat(flushStats.getTotal(), greaterThan(flushStats.getPeriodic())); @@ -590,12 +591,12 @@ public void testShardHasMemoryBufferOnTranslogRecover() throws Throwable { IndicesService indicesService = getInstanceFromNode(IndicesService.class); IndexService indexService = indicesService.indexService(resolveIndex("test")); IndexShard shard = indexService.getShardOrNull(0); - client().prepareIndex("test").setId("0").setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get(); + client().prepareIndex("test").setId("0").setSource("{\"foo\" : \"bar\"}", MediaTypeRegistry.JSON).get(); client().prepareDelete("test", "0").get(); - client().prepareIndex("test").setId("1").setSource("{\"foo\" : \"bar\"}", XContentType.JSON).setRefreshPolicy(IMMEDIATE).get(); + client().prepareIndex("test").setId("1").setSource("{\"foo\" : \"bar\"}", MediaTypeRegistry.JSON).setRefreshPolicy(IMMEDIATE).get(); CheckedFunction wrapper = directoryReader -> directoryReader; - shard.close("simon says", false); + shard.close("simon says", false, false); AtomicReference shardRef = new AtomicReference<>(); List failures = new ArrayList<>(); IndexingOperationListener listener = new IndexingOperationListener() { @@ -626,14 +627,22 @@ public void postDelete(ShardId shardId, Engine.Delete delete, Engine.DeleteResul } } }; - final IndexShard newShard = newIndexShard(indexService, shard, wrapper, getInstanceFromNode(CircuitBreakerService.class), listener); + NodeEnvironment env = getInstanceFromNode(NodeEnvironment.class); + final IndexShard newShard = newIndexShard( + indexService, + shard, + wrapper, + getInstanceFromNode(CircuitBreakerService.class), + env.nodeId(), + listener + ); shardRef.set(newShard); recoverShard(newShard); try { ExceptionsHelper.rethrowAndSuppress(failures); } finally { - newShard.close("just do it", randomBoolean()); + newShard.close("just do it", randomBoolean(), false); } } @@ -650,6 +659,7 @@ public static final IndexShard newIndexShard( final IndexShard shard, CheckedFunction wrapper, final CircuitBreakerService cbs, + final String nodeId, final IndexingOperationListener... listeners ) throws IOException { ShardRouting initializingShardRouting = getInitializingShardRouting(shard.routingEntry()); @@ -673,7 +683,14 @@ public static final IndexShard newIndexShard( Arrays.asList(listeners), () -> {}, RetentionLeaseSyncer.EMPTY, - cbs + cbs, + (indexSettings, shardRouting) -> new InternalTranslogFactory(), + SegmentReplicationCheckpointPublisher.EMPTY, + null, + null, + () -> IndexSettings.DEFAULT_REMOTE_TRANSLOG_BUFFER_INTERVAL, + nodeId, + null ); } @@ -704,7 +721,7 @@ public void testInvalidateIndicesRequestCacheWhenRollbackEngine() throws Excepti final SearchRequest countRequest = new SearchRequest("test").source(new SearchSourceBuilder().size(0)); final long numDocs = between(10, 20); for (int i = 0; i < numDocs; i++) { - client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", MediaTypeRegistry.JSON).get(); if (randomBoolean()) { shard.refresh("test"); } @@ -726,7 +743,7 @@ public void testInvalidateIndicesRequestCacheWhenRollbackEngine() throws Excepti final long moreDocs = between(10, 20); for (int i = 0; i < moreDocs; i++) { - client().prepareIndex("test").setId(Long.toString(i + numDocs)).setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId(Long.toString(i + numDocs)).setSource("{}", MediaTypeRegistry.JSON).get(); if (randomBoolean()) { shard.refresh("test"); } @@ -757,7 +774,7 @@ public void testShardChangesWithDefaultDocType() throws Exception { int numOps = between(1, 10); for (int i = 0; i < numOps; i++) { if (randomBoolean()) { - client().prepareIndex("index").setId(randomFrom("1", "2")).setSource("{}", XContentType.JSON).get(); + client().prepareIndex("index").setId(randomFrom("1", "2")).setSource("{}", MediaTypeRegistry.JSON).get(); } else { client().prepareDelete("index", randomFrom("1", "2")).get(); } @@ -820,7 +837,7 @@ public void testLimitNumberOfRetainedTranslogFiles() throws Exception { } }; for (int i = 0; i < 100; i++) { - client().prepareIndex(indexName).setId(Integer.toString(i)).setSource("{}", XContentType.JSON).get(); + client().prepareIndex(indexName).setId(Integer.toString(i)).setSource("{}", MediaTypeRegistry.JSON).get(); if (randomInt(100) < 10) { client().admin().indices().prepareFlush(indexName).setWaitIfOngoing(true).get(); checkTranslog.run(); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/shard/RemoveCorruptedShardDataCommandIT.java b/server/src/internalClusterTest/java/org/opensearch/index/shard/RemoveCorruptedShardDataCommandIT.java index 2dc241e278768..b431079476624 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/shard/RemoveCorruptedShardDataCommandIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/shard/RemoveCorruptedShardDataCommandIT.java @@ -31,8 +31,8 @@ package org.opensearch.index.shard; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import com.carrotsearch.randomizedtesting.generators.RandomPicks; + import joptsimple.OptionParser; import joptsimple.OptionSet; import org.apache.lucene.index.IndexWriter; @@ -41,9 +41,7 @@ import org.apache.lucene.store.Lock; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.NativeFSLockFactory; - import org.opensearch.ExceptionsHelper; - import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplanation; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.opensearch.action.admin.indices.flush.FlushRequest; @@ -66,15 +64,16 @@ import org.opensearch.cluster.routing.allocation.command.AllocateStalePrimaryAllocationCommand; import org.opensearch.common.io.PathUtils; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.env.TestEnvironment; import org.opensearch.gateway.GatewayMetaState; -import org.opensearch.index.Index; import org.opensearch.index.IndexSettings; -import org.opensearch.index.MergePolicyConfig; +import org.opensearch.index.MergePolicyProvider; import org.opensearch.index.MockEngineFactoryPlugin; import org.opensearch.index.seqno.SeqNoStats; import org.opensearch.index.translog.TestTranslog; @@ -83,9 +82,9 @@ import org.opensearch.indices.recovery.RecoveryState; import org.opensearch.plugins.Plugin; import org.opensearch.test.CorruptionUtils; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.engine.MockEngineSupport; import org.opensearch.test.transport.MockTransportService; @@ -105,11 +104,10 @@ import java.util.stream.StreamSupport; import static org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest.Metric.FS; -import static org.opensearch.common.util.CollectionUtils.iterableAsArrayList; +import static org.opensearch.core.common.util.CollectionUtils.iterableAsArrayList; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; - import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -137,7 +135,7 @@ public void testCorruptIndex() throws Exception { Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) - .put(MergePolicyConfig.INDEX_MERGE_ENABLED, false) + .put(MergePolicyProvider.INDEX_MERGE_ENABLED, false) .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "-1") .put(MockEngineSupport.DISABLE_FLUSH_ON_CLOSE.getKey(), true) .put(IndexSettings.INDEX_CHECK_ON_STARTUP.getKey(), "checksum") @@ -235,10 +233,10 @@ public Settings onNodeStopped(String nodeName) throws Exception { String nodeId = null; final ClusterState state = client().admin().cluster().prepareState().get().getState(); final DiscoveryNodes nodes = state.nodes(); - for (ObjectObjectCursor cursor : nodes.getNodes()) { - final String name = cursor.value.getName(); + for (final Map.Entry cursor : nodes.getNodes().entrySet()) { + final String name = cursor.getValue().getName(); if (name.equals(node)) { - nodeId = cursor.key; + nodeId = cursor.getKey(); break; } } @@ -423,10 +421,10 @@ public Settings onNodeStopped(String nodeName) throws Exception { String primaryNodeId = null; final ClusterState state = client().admin().cluster().prepareState().get().getState(); final DiscoveryNodes nodes = state.nodes(); - for (ObjectObjectCursor cursor : nodes.getNodes()) { - final String name = cursor.value.getName(); + for (final Map.Entry cursor : nodes.getNodes().entrySet()) { + final String name = cursor.getValue().getName(); if (name.equals(node1)) { - primaryNodeId = cursor.key; + primaryNodeId = cursor.getKey(); break; } } @@ -497,7 +495,7 @@ public Settings onNodeStopped(String nodeName) throws Exception { } public void testCorruptTranslogTruncationOfReplica() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String node1 = internalCluster().startDataOnlyNode(); final String node2 = internalCluster().startDataOnlyNode(); @@ -630,8 +628,8 @@ public void testResolvePath() throws Exception { final Map nodeNameToNodeId = new HashMap<>(); final ClusterState state = client().admin().cluster().prepareState().get().getState(); final DiscoveryNodes nodes = state.nodes(); - for (ObjectObjectCursor cursor : nodes.getNodes()) { - nodeNameToNodeId.put(cursor.value.getName(), cursor.key); + for (final Map.Entry cursor : nodes.getNodes().entrySet()) { + nodeNameToNodeId.put(cursor.getValue().getName(), cursor.getKey()); } final GroupShardsIterator shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(new String[] { indexName }, false); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/shard/SearchIdleIT.java b/server/src/internalClusterTest/java/org/opensearch/index/shard/SearchIdleIT.java index 9382960b906e3..43d86b232de77 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/shard/SearchIdleIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/shard/SearchIdleIT.java @@ -32,13 +32,13 @@ package org.opensearch.index.shard; -import org.opensearch.action.ActionListener; import org.opensearch.action.get.GetRequest; import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.index.IndexResponse; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.test.OpenSearchSingleNodeTestCase; @@ -102,7 +102,7 @@ private void runTestAutomaticRefresh(final IntToLongFunction count) throws Inter int numDocs = scaledRandomIntBetween(25, 100); totalNumDocs.set(numDocs); CountDownLatch indexingDone = new CountDownLatch(numDocs); - client().prepareIndex("test").setId("0").setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get(); + client().prepareIndex("test").setId("0").setSource("{\"foo\" : \"bar\"}", MediaTypeRegistry.JSON).get(); indexingDone.countDown(); // one doc is indexed above blocking IndexShard shard = indexService.getShard(0); boolean hasRefreshed = shard.scheduledRefresh(); @@ -135,7 +135,7 @@ private void runTestAutomaticRefresh(final IntToLongFunction count) throws Inter for (int i = 1; i < numDocs; i++) { client().prepareIndex("test") .setId("" + i) - .setSource("{\"foo\" : \"bar\"}", XContentType.JSON) + .setSource("{\"foo\" : \"bar\"}", MediaTypeRegistry.JSON) .execute(new ActionListener() { @Override public void onResponse(IndexResponse indexResponse) { @@ -159,7 +159,7 @@ public void testPendingRefreshWithIntervalChange() throws Exception { IndexService indexService = createIndex("test", builder.build()); assertFalse(indexService.getIndexSettings().isExplicitRefresh()); ensureGreen(); - client().prepareIndex("test").setId("0").setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get(); + client().prepareIndex("test").setId("0").setSource("{\"foo\" : \"bar\"}", MediaTypeRegistry.JSON).get(); IndexShard shard = indexService.getShard(0); assertFalse(shard.scheduledRefresh()); assertTrue(shard.isSearchIdle()); @@ -167,7 +167,7 @@ public void testPendingRefreshWithIntervalChange() throws Exception { client().admin().indices().prepareRefresh().execute(ActionListener.wrap(refreshLatch::countDown));// async on purpose to make sure // it happens concurrently assertHitCount(client().prepareSearch().get(), 1); - client().prepareIndex("test").setId("1").setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get(); + client().prepareIndex("test").setId("1").setSource("{\"foo\" : \"bar\"}", MediaTypeRegistry.JSON).get(); assertFalse(shard.scheduledRefresh()); assertTrue(shard.hasRefreshPending()); @@ -186,7 +186,7 @@ public void testPendingRefreshWithIntervalChange() throws Exception { // We need to ensure a `scheduledRefresh` triggered by the internal refresh setting update is executed before we index a new doc; // otherwise, it will compete to call `Engine#maybeRefresh` with the `scheduledRefresh` that we are going to verify. ensureNoPendingScheduledRefresh(indexService.getThreadPool()); - client().prepareIndex("test").setId("2").setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get(); + client().prepareIndex("test").setId("2").setSource("{\"foo\" : \"bar\"}", MediaTypeRegistry.JSON).get(); assertTrue(shard.scheduledRefresh()); assertFalse(shard.hasRefreshPending()); assertTrue(shard.isSearchIdle()); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedFileIT.java b/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedFileIT.java index 3a5e21fc8ef65..8291fef5d177b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedFileIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedFileIT.java @@ -31,15 +31,14 @@ package org.opensearch.index.store; -import com.carrotsearch.hppc.cursors.IntObjectCursor; import com.carrotsearch.randomizedtesting.generators.RandomPicks; + import org.apache.lucene.index.CheckIndex; import org.apache.lucene.index.SegmentCommitInfo; import org.apache.lucene.index.SegmentInfos; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.util.BytesRef; - import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; @@ -48,6 +47,7 @@ import org.opensearch.action.admin.indices.shards.IndicesShardStoresResponse; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.support.replication.TransportReplicationAction; import org.opensearch.client.Requests; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.health.ClusterHealthStatus; @@ -60,34 +60,35 @@ import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.io.PathUtils; import org.opensearch.common.io.stream.BytesStreamOutput; import org.opensearch.common.lucene.Lucene; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.env.NodeEnvironment; -import org.opensearch.index.Index; import org.opensearch.index.IndexSettings; -import org.opensearch.index.MergePolicyConfig; +import org.opensearch.index.MergePolicyProvider; import org.opensearch.index.shard.IndexEventListener; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardState; -import org.opensearch.index.shard.ShardId; +import org.opensearch.indices.recovery.FileChunkRequest; import org.opensearch.indices.recovery.PeerRecoveryTargetService; -import org.opensearch.indices.recovery.RecoveryFileChunkRequest; import org.opensearch.monitor.fs.FsInfo; import org.opensearch.plugins.Plugin; import org.opensearch.snapshots.SnapshotState; import org.opensearch.test.CorruptionUtils; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.MockIndexEventListener; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.store.MockFSIndexStore; import org.opensearch.test.transport.MockTransportService; import org.opensearch.transport.TransportService; +import org.hamcrest.MatcherAssert; import java.io.IOException; import java.io.OutputStream; @@ -108,13 +109,13 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; import static org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest.Metric.FS; -import static org.opensearch.common.util.CollectionUtils.iterableAsArrayList; +import static org.opensearch.core.common.util.CollectionUtils.iterableAsArrayList; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAllSuccessful; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; - import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -166,7 +167,7 @@ public void testCorruptFileAndRecover() throws ExecutionException, InterruptedEx Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, "1") .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "1") - .put(MergePolicyConfig.INDEX_MERGE_ENABLED, false) + .put(MergePolicyProvider.INDEX_MERGE_ENABLED, false) // no checkindex - we corrupt shards on purpose .put(MockFSIndexStore.INDEX_CHECK_INDEX_ON_CLOSE_SETTING.getKey(), false) // no translog based flush - it might change the .liv / segments.N files @@ -285,7 +286,7 @@ public void testCorruptPrimaryNoReplica() throws ExecutionException, Interrupted prepareCreate("test").setSettings( Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "0") - .put(MergePolicyConfig.INDEX_MERGE_ENABLED, false) + .put(MergePolicyProvider.INDEX_MERGE_ENABLED, false) .put(MockFSIndexStore.INDEX_CHECK_INDEX_ON_CLOSE_SETTING.getKey(), false) // no checkindex - we corrupt shards on // purpose // no translog based flush - it might change the .liv / segments.N files @@ -397,7 +398,7 @@ public void testCorruptionOnNetworkLayerFinalizingRecovery() throws ExecutionExc internalCluster().getInstance(TransportService.class, unluckyNode.getNode().getName()), (connection, requestId, action, request, options) -> { if (corrupt.get() && action.equals(PeerRecoveryTargetService.Actions.FILE_CHUNK)) { - RecoveryFileChunkRequest req = (RecoveryFileChunkRequest) request; + FileChunkRequest req = (FileChunkRequest) request; byte[] array = BytesRef.deepCopyOf(req.content().toBytesRef()).bytes; int i = randomIntBetween(0, req.content().length() - 1); array[i] = (byte) ~array[i]; // flip one byte in the content @@ -474,11 +475,11 @@ public void testCorruptionOnNetworkLayer() throws ExecutionException, Interrupte internalCluster().getInstance(TransportService.class, unluckyNode.getNode().getName()), (connection, requestId, action, request, options) -> { if (action.equals(PeerRecoveryTargetService.Actions.FILE_CHUNK)) { - RecoveryFileChunkRequest req = (RecoveryFileChunkRequest) request; + FileChunkRequest req = (FileChunkRequest) request; if (truncate && req.length() > 1) { BytesRef bytesRef = req.content().toBytesRef(); BytesArray array = new BytesArray(bytesRef.bytes, bytesRef.offset, (int) req.length() - 1); - request = new RecoveryFileChunkRequest( + request = new FileChunkRequest( req.recoveryId(), req.requestSeqNo(), req.shardId(), @@ -551,7 +552,7 @@ public void testCorruptFileThenSnapshotAndRestore() throws ExecutionException, I prepareCreate("test").setSettings( Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "0") // no replicas for this test - .put(MergePolicyConfig.INDEX_MERGE_ENABLED, false) + .put(MergePolicyProvider.INDEX_MERGE_ENABLED, false) // no checkindex - we corrupt shards on purpose .put(MockFSIndexStore.INDEX_CHECK_INDEX_ON_CLOSE_SETTING.getKey(), false) // no translog based flush - it might change the .liv / segments.N files @@ -623,7 +624,7 @@ public void testReplicaCorruption() throws Exception { prepareCreate("test").setSettings( Settings.builder() .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, cluster().numDataNodes() - 1) - .put(MergePolicyConfig.INDEX_MERGE_ENABLED, false) + .put(MergePolicyProvider.INDEX_MERGE_ENABLED, false) .put(MockFSIndexStore.INDEX_CHECK_INDEX_ON_CLOSE_SETTING.getKey(), false) // no checkindex - we corrupt shards on // purpose .put(IndexSettings.INDEX_TRANSLOG_FLUSH_THRESHOLD_SIZE_SETTING.getKey(), new ByteSizeValue(1, ByteSizeUnit.PB)) // no @@ -671,9 +672,9 @@ public void testReplicaCorruption() throws Exception { final IndicesShardStoresResponse stores = client().admin().indices().prepareShardStores(index.getName()).get(); - for (IntObjectCursor> shards : stores.getStoreStatuses().get(index.getName())) { - for (IndicesShardStoresResponse.StoreStatus store : shards.value) { - final ShardId shardId = new ShardId(index, shards.key); + for (var shards : stores.getStoreStatuses().get(index.getName()).entrySet()) { + for (IndicesShardStoresResponse.StoreStatus store : shards.getValue()) { + final ShardId shardId = new ShardId(index, shards.getKey()); if (store.getAllocationStatus().equals(IndicesShardStoresResponse.StoreStatus.AllocationStatus.UNUSED)) { for (Path path : findFilesToCorruptOnNode(store.getNode().getName(), shardId)) { try (OutputStream os = Files.newOutputStream(path)) { @@ -698,6 +699,104 @@ public void testReplicaCorruption() throws Exception { ensureGreen(TimeValue.timeValueSeconds(60)); } + public void testPrimaryCorruptionDuringReplicationDoesNotFailReplicaShard() throws Exception { + internalCluster().ensureAtLeastNumDataNodes(2); + final NodesStatsResponse nodeStats = client().admin().cluster().prepareNodesStats().get(); + final List dataNodeStats = nodeStats.getNodes() + .stream() + .filter(stat -> stat.getNode().isDataNode()) + .collect(Collectors.toUnmodifiableList()); + MatcherAssert.assertThat(dataNodeStats.size(), greaterThanOrEqualTo(2)); + + final NodeStats primaryNode = dataNodeStats.get(0); + final NodeStats replicaNode = dataNodeStats.get(1); + assertAcked( + prepareCreate("test").setSettings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "0") + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put("index.routing.allocation.include._name", primaryNode.getNode().getName()) + .put(EnableAllocationDecider.INDEX_ROUTING_REBALANCE_ENABLE_SETTING.getKey(), EnableAllocationDecider.Rebalance.NONE) + .put("index.allocation.max_retries", Integer.MAX_VALUE) // keep on retrying + + ) + ); + ensureGreen(); + + // Add custom send behavior between primary and replica that will + // count down a latch to indicate that a replication operation is + // currently in flight, and then block on a second latch that will + // be released once the primary shard has been corrupted. + final CountDownLatch indexingInFlight = new CountDownLatch(1); + final CountDownLatch corruptionHasHappened = new CountDownLatch(1); + final MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + primaryNode.getNode().getName() + )); + mockTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, replicaNode.getNode().getName()), + (connection, requestId, action, request, options) -> { + if (request instanceof TransportReplicationAction.ConcreteShardRequest) { + indexingInFlight.countDown(); + try { + corruptionHasHappened.await(); + } catch (InterruptedException e) { + logger.info("Interrupted while waiting for corruption"); + } + } + connection.sendRequest(requestId, action, request, options); + } + ); + + // Configure the modified data node as a replica + final Settings build = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "1") + .put("index.routing.allocation.include._name", primaryNode.getNode().getName() + "," + replicaNode.getNode().getName()) + .build(); + client().admin().indices().prepareUpdateSettings("test").setSettings(build).get(); + client().admin().cluster().prepareReroute().get(); + ensureGreen(); + + // Create a snapshot repository. This repo is used to take a snapshot after + // corrupting a file, which causes the node to notice the corrupt data and + // close the shard. + assertAcked( + client().admin() + .cluster() + .preparePutRepository("test-repo") + .setType("fs") + .setSettings( + Settings.builder() + .put("location", randomRepoPath().toAbsolutePath()) + .put("compress", randomBoolean()) + .put("chunk_size", randomIntBetween(100, 1000), ByteSizeUnit.BYTES) + ) + ); + + client().prepareIndex("test").setSource("field", "value").execute(); + indexingInFlight.await(); + + // Corrupt a file on the primary then take a snapshot. Snapshot should + // finish in the PARTIAL state since the corrupted file will cause a checksum + // validation failure. + final ShardRouting corruptedShardRouting = corruptRandomPrimaryFile(); + logger.info("--> {} corrupted", corruptedShardRouting); + final CreateSnapshotResponse createSnapshotResponse = client().admin() + .cluster() + .prepareCreateSnapshot("test-repo", "test-snap") + .setWaitForCompletion(true) + .setIndices("test") + .get(); + final SnapshotState snapshotState = createSnapshotResponse.getSnapshotInfo().state(); + MatcherAssert.assertThat("Expect file corruption to cause PARTIAL snapshot state", snapshotState, equalTo(SnapshotState.PARTIAL)); + + // Unblock the blocked indexing thread now that corruption on the primary has been confirmed + corruptionHasHappened.countDown(); + + // Assert the cluster returns to green status because the replica will be promoted to primary + ensureGreen(); + } + private int numShards(String... index) { ClusterState state = client().admin().cluster().prepareState().get().getState(); GroupShardsIterator shardIterators = state.getRoutingTable().activePrimaryShardsGrouped(index, false); diff --git a/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedTranslogIT.java b/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedTranslogIT.java index 1dd0f6a3d664e..f749593de13d2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedTranslogIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/store/CorruptedTranslogIT.java @@ -38,16 +38,16 @@ import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.cluster.routing.UnassignedInfo; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.index.IndexSettings; import org.opensearch.index.MockEngineFactoryPlugin; import org.opensearch.index.translog.TestTranslog; import org.opensearch.index.translog.TranslogCorruptedException; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.engine.MockEngineSupport; import org.opensearch.test.transport.MockTransportService; diff --git a/server/src/internalClusterTest/java/org/opensearch/index/store/ExceptionRetryIT.java b/server/src/internalClusterTest/java/org/opensearch/index/store/ExceptionRetryIT.java index 3e2091b2065e5..1e3c22c85b0b2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/store/ExceptionRetryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/store/ExceptionRetryIT.java @@ -44,7 +44,7 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.client.Client; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.engine.SegmentsStats; import org.opensearch.plugins.Plugin; import org.opensearch.search.SearchHit; diff --git a/server/src/internalClusterTest/java/org/opensearch/index/suggest/stats/SuggestStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/index/suggest/stats/SuggestStatsIT.java index 9940b1eb13a52..6332b1b97426f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/index/suggest/stats/SuggestStatsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/index/suggest/stats/SuggestStatsIT.java @@ -32,6 +32,8 @@ package org.opensearch.index.suggest.stats; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; @@ -42,17 +44,22 @@ import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.search.stats.SearchStats; import org.opensearch.search.suggest.SuggestBuilder; import org.opensearch.search.suggest.phrase.PhraseSuggestionBuilder; import org.opensearch.search.suggest.term.TermSuggestionBuilder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAllSuccessful; import static org.hamcrest.Matchers.equalTo; @@ -61,7 +68,25 @@ import static org.hamcrest.Matchers.lessThanOrEqualTo; @OpenSearchIntegTestCase.ClusterScope(minNumDataNodes = 2) -public class SuggestStatsIT extends OpenSearchIntegTestCase { +public class SuggestStatsIT extends ParameterizedOpenSearchIntegTestCase { + + public SuggestStatsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected int numberOfReplicas() { return 0; diff --git a/server/src/internalClusterTest/java/org/opensearch/indexing/IndexActionIT.java b/server/src/internalClusterTest/java/org/opensearch/indexing/IndexActionIT.java index 45fbb2651a96d..73d6d9aff7b72 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indexing/IndexActionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indexing/IndexActionIT.java @@ -288,10 +288,9 @@ public void testInvalidIndexName() { } public void testDocumentWithBlankFieldName() { - MapperParsingException e = expectThrows( - MapperParsingException.class, - () -> { client().prepareIndex("test").setId("1").setSource("", "value1_2").execute().actionGet(); } - ); + MapperParsingException e = expectThrows(MapperParsingException.class, () -> { + client().prepareIndex("test").setId("1").setSource("", "value1_2").execute().actionGet(); + }); assertThat(e.getMessage(), containsString("failed to parse")); assertThat(e.getRootCause().getMessage(), containsString("field name cannot be an empty string")); } diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/DateMathIndexExpressionsIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/DateMathIndexExpressionsIntegrationIT.java index 7236c32697384..2f348e5d6218d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/DateMathIndexExpressionsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/DateMathIndexExpressionsIntegrationIT.java @@ -41,9 +41,8 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.test.OpenSearchIntegTestCase; - import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; @@ -71,9 +70,9 @@ public void testIndexNameDateMathExpressions() { String dateMathExp1 = "<.marvel-{now/d}>"; String dateMathExp2 = "<.marvel-{now/d-1d}>"; String dateMathExp3 = "<.marvel-{now/d-2d}>"; - client().prepareIndex(dateMathExp1).setId("1").setSource("{}", XContentType.JSON).get(); - client().prepareIndex(dateMathExp2).setId("2").setSource("{}", XContentType.JSON).get(); - client().prepareIndex(dateMathExp3).setId("3").setSource("{}", XContentType.JSON).get(); + client().prepareIndex(dateMathExp1).setId("1").setSource("{}", MediaTypeRegistry.JSON).get(); + client().prepareIndex(dateMathExp2).setId("2").setSource("{}", MediaTypeRegistry.JSON).get(); + client().prepareIndex(dateMathExp3).setId("3").setSource("{}", MediaTypeRegistry.JSON).get(); refresh(); SearchResponse searchResponse = client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3).get(); @@ -131,9 +130,9 @@ public void testAutoCreateIndexWithDateMathExpression() throws Exception { String dateMathExp1 = "<.marvel-{now/d}>"; String dateMathExp2 = "<.marvel-{now/d-1d}>"; String dateMathExp3 = "<.marvel-{now/d-2d}>"; - client().prepareIndex(dateMathExp1).setId("1").setSource("{}", XContentType.JSON).get(); - client().prepareIndex(dateMathExp2).setId("2").setSource("{}", XContentType.JSON).get(); - client().prepareIndex(dateMathExp3).setId("3").setSource("{}", XContentType.JSON).get(); + client().prepareIndex(dateMathExp1).setId("1").setSource("{}", MediaTypeRegistry.JSON).get(); + client().prepareIndex(dateMathExp2).setId("2").setSource("{}", MediaTypeRegistry.JSON).get(); + client().prepareIndex(dateMathExp3).setId("3").setSource("{}", MediaTypeRegistry.JSON).get(); refresh(); SearchResponse searchResponse = client().prepareSearch(dateMathExp1, dateMathExp2, dateMathExp3).get(); diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/IndexingMemoryControllerIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/IndexingMemoryControllerIT.java index 0d3c685ab0327..c842ee8564041 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/IndexingMemoryControllerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/IndexingMemoryControllerIT.java @@ -81,30 +81,29 @@ EngineConfig engineConfigWithLargerIndexingMemory(EngineConfig config) { .put("indices.memory.index_buffer_size", "10mb") .build(); IndexSettings indexSettings = new IndexSettings(config.getIndexSettings().getIndexMetadata(), settings); - return new EngineConfig( - config.getShardId(), - config.getThreadPool(), - indexSettings, - config.getWarmer(), - config.getStore(), - config.getMergePolicy(), - config.getAnalyzer(), - config.getSimilarity(), - new CodecService(null, LogManager.getLogger(IndexingMemoryControllerIT.class)), - config.getEventListener(), - config.getQueryCache(), - config.getQueryCachingPolicy(), - config.getTranslogConfig(), - config.getFlushMergesAfter(), - config.getExternalRefreshListener(), - config.getInternalRefreshListener(), - config.getIndexSort(), - config.getCircuitBreakerService(), - config.getGlobalCheckpointSupplier(), - config.retentionLeasesSupplier(), - config.getPrimaryTermSupplier(), - config.getTombstoneDocSupplier() - ); + return new EngineConfig.Builder().shardId(config.getShardId()) + .threadPool(config.getThreadPool()) + .indexSettings(indexSettings) + .warmer(config.getWarmer()) + .store(config.getStore()) + .mergePolicy(config.getMergePolicy()) + .analyzer(config.getAnalyzer()) + .similarity(config.getSimilarity()) + .codecService(new CodecService(null, indexSettings, LogManager.getLogger(IndexingMemoryControllerIT.class))) + .eventListener(config.getEventListener()) + .queryCache(config.getQueryCache()) + .queryCachingPolicy(config.getQueryCachingPolicy()) + .translogConfig(config.getTranslogConfig()) + .flushMergesAfter(config.getFlushMergesAfter()) + .externalRefreshListener(config.getExternalRefreshListener()) + .internalRefreshListener(config.getInternalRefreshListener()) + .indexSort(config.getIndexSort()) + .circuitBreakerService(config.getCircuitBreakerService()) + .globalCheckpointSupplier(config.getGlobalCheckpointSupplier()) + .retentionLeasesSupplier(config.retentionLeasesSupplier()) + .primaryTermSupplier(config.getPrimaryTermSupplier()) + .tombstoneDocSupplier(config.getTombstoneDocSupplier()) + .build(); } @Override diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/IndicesLifecycleListenerIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/IndicesLifecycleListenerIT.java index 72f28e94528ba..f40c1b9ec2362 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/IndicesLifecycleListenerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/IndicesLifecycleListenerIT.java @@ -44,19 +44,18 @@ import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.common.CheckedRunnable; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.common.Strings; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.shard.IndexEventListener; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardState; -import org.opensearch.index.shard.ShardId; import org.opensearch.plugins.Plugin; +import org.opensearch.test.MockIndexEventListener; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.MockIndexEventListener; - import org.hamcrest.Matchers; import java.util.Arrays; @@ -131,7 +130,7 @@ public void beforeIndexCreated(Index index, Settings indexSettings) { } catch (Exception e) { assertTrue(e.getMessage().contains("failing on purpose")); ClusterStateResponse resp = client().admin().cluster().prepareState().get(); - assertFalse(resp.getState().routingTable().indicesRouting().keys().contains("failed")); + assertFalse(resp.getState().routingTable().indicesRouting().keySet().contains("failed")); } } @@ -181,7 +180,7 @@ public void testIndexStateShardChanged() throws Throwable { } catch (OpenSearchException e) { assertTrue(e.getMessage().contains("failing on purpose")); ClusterStateResponse resp = client().admin().cluster().prepareState().get(); - assertFalse(resp.getState().routingTable().indicesRouting().keys().contains("failed")); + assertFalse(resp.getState().routingTable().indicesRouting().keySet().contains("failed")); } // create an index diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/IndicesOptionsIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/IndicesOptionsIntegrationIT.java index 3432cc967bf22..06d2d2a90de87 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/IndicesOptionsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/IndicesOptionsIntegrationIT.java @@ -34,10 +34,8 @@ import org.opensearch.action.ActionRequestBuilder; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotRequestBuilder; import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequestBuilder; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistRequestBuilder; import org.opensearch.action.admin.indices.alias.get.GetAliasesRequestBuilder; import org.opensearch.action.admin.indices.cache.clear.ClearIndicesCacheRequestBuilder; -import org.opensearch.action.admin.indices.exists.types.TypesExistsRequestBuilder; import org.opensearch.action.admin.indices.flush.FlushRequestBuilder; import org.opensearch.action.admin.indices.forcemerge.ForceMergeRequestBuilder; import org.opensearch.action.admin.indices.mapping.get.GetFieldMappingsRequestBuilder; @@ -54,14 +52,14 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.index.IndexNotFoundException; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Arrays; import java.util.Collection; @@ -98,8 +96,6 @@ public void testSpecifiedIndexUnavailableMultipleIndices() throws Exception { verify(forceMerge("test1", "test2"), true); verify(refreshBuilder("test1", "test2"), true); verify(validateQuery("test1", "test2"), true); - verify(aliasExists("test1", "test2"), true); - verify(typesExists("test1", "test2"), true); verify(getAliases("test1", "test2"), true); verify(getFieldMapping("test1", "test2"), true); verify(getMapping("test1", "test2"), true); @@ -115,8 +111,6 @@ public void testSpecifiedIndexUnavailableMultipleIndices() throws Exception { verify(forceMerge("test1", "test2").setIndicesOptions(options), true); verify(refreshBuilder("test1", "test2").setIndicesOptions(options), true); verify(validateQuery("test1", "test2").setIndicesOptions(options), true); - verify(aliasExists("test1", "test2").setIndicesOptions(options), true); - verify(typesExists("test1", "test2").setIndicesOptions(options), true); verify(getAliases("test1", "test2").setIndicesOptions(options), true); verify(getFieldMapping("test1", "test2").setIndicesOptions(options), true); verify(getMapping("test1", "test2").setIndicesOptions(options), true); @@ -132,8 +126,6 @@ public void testSpecifiedIndexUnavailableMultipleIndices() throws Exception { verify(forceMerge("test1", "test2").setIndicesOptions(options), false); verify(refreshBuilder("test1", "test2").setIndicesOptions(options), false); verify(validateQuery("test1", "test2").setIndicesOptions(options), false); - verify(aliasExists("test1", "test2").setIndicesOptions(options), false); - verify(typesExists("test1", "test2").setIndicesOptions(options), false); verify(getAliases("test1", "test2").setIndicesOptions(options), false); verify(getFieldMapping("test1", "test2").setIndicesOptions(options), false); verify(getMapping("test1", "test2").setIndicesOptions(options), false); @@ -150,8 +142,6 @@ public void testSpecifiedIndexUnavailableMultipleIndices() throws Exception { verify(forceMerge("test1", "test2").setIndicesOptions(options), false); verify(refreshBuilder("test1", "test2").setIndicesOptions(options), false); verify(validateQuery("test1", "test2").setIndicesOptions(options), false); - verify(aliasExists("test1", "test2").setIndicesOptions(options), false); - verify(typesExists("test1", "test2").setIndicesOptions(options), false); verify(getAliases("test1", "test2").setIndicesOptions(options), false); verify(getFieldMapping("test1", "test2").setIndicesOptions(options), false); verify(getMapping("test1", "test2").setIndicesOptions(options), false); @@ -177,8 +167,6 @@ public void testSpecifiedIndexUnavailableSingleIndexThatIsClosed() throws Except verify(forceMerge("test1").setIndicesOptions(options), true); verify(refreshBuilder("test1").setIndicesOptions(options), true); verify(validateQuery("test1").setIndicesOptions(options), true); - verify(aliasExists("test1").setIndicesOptions(options), true); - verify(typesExists("test1").setIndicesOptions(options), true); verify(getAliases("test1").setIndicesOptions(options), true); verify(getFieldMapping("test1").setIndicesOptions(options), true); verify(getMapping("test1").setIndicesOptions(options), true); @@ -200,8 +188,6 @@ public void testSpecifiedIndexUnavailableSingleIndexThatIsClosed() throws Except verify(forceMerge("test1").setIndicesOptions(options), false); verify(refreshBuilder("test1").setIndicesOptions(options), false); verify(validateQuery("test1").setIndicesOptions(options), false); - verify(aliasExists("test1").setIndicesOptions(options), false); - verify(typesExists("test1").setIndicesOptions(options), false); verify(getAliases("test1").setIndicesOptions(options), false); verify(getFieldMapping("test1").setIndicesOptions(options), false); verify(getMapping("test1").setIndicesOptions(options), false); @@ -220,8 +206,6 @@ public void testSpecifiedIndexUnavailableSingleIndexThatIsClosed() throws Except verify(forceMerge("test1").setIndicesOptions(options), false); verify(refreshBuilder("test1").setIndicesOptions(options), false); verify(validateQuery("test1").setIndicesOptions(options), false); - verify(aliasExists("test1").setIndicesOptions(options), false); - verify(typesExists("test1").setIndicesOptions(options), false); verify(getAliases("test1").setIndicesOptions(options), false); verify(getFieldMapping("test1").setIndicesOptions(options), false); verify(getMapping("test1").setIndicesOptions(options), false); @@ -239,8 +223,6 @@ public void testSpecifiedIndexUnavailableSingleIndex() throws Exception { verify(forceMerge("test1").setIndicesOptions(options), true); verify(refreshBuilder("test1").setIndicesOptions(options), true); verify(validateQuery("test1").setIndicesOptions(options), true); - verify(aliasExists("test1").setIndicesOptions(options), true); - verify(typesExists("test1").setIndicesOptions(options), true); verify(getAliases("test1").setIndicesOptions(options), true); verify(getFieldMapping("test1").setIndicesOptions(options), true); verify(getMapping("test1").setIndicesOptions(options), true); @@ -262,8 +244,6 @@ public void testSpecifiedIndexUnavailableSingleIndex() throws Exception { verify(forceMerge("test1").setIndicesOptions(options), false); verify(refreshBuilder("test1").setIndicesOptions(options), false); verify(validateQuery("test1").setIndicesOptions(options), false); - verify(aliasExists("test1").setIndicesOptions(options), false); - verify(typesExists("test1").setIndicesOptions(options), false); verify(getAliases("test1").setIndicesOptions(options), false); verify(getFieldMapping("test1").setIndicesOptions(options), false); verify(getMapping("test1").setIndicesOptions(options), false); @@ -281,8 +261,6 @@ public void testSpecifiedIndexUnavailableSingleIndex() throws Exception { verify(forceMerge("test1").setIndicesOptions(options), false); verify(refreshBuilder("test1").setIndicesOptions(options), false); verify(validateQuery("test1").setIndicesOptions(options), false); - verify(aliasExists("test1").setIndicesOptions(options), false); - verify(typesExists("test1").setIndicesOptions(options), false); verify(getAliases("test1").setIndicesOptions(options), false); verify(getFieldMapping("test1").setIndicesOptions(options), false); verify(getMapping("test1").setIndicesOptions(options), false); @@ -335,8 +313,6 @@ public void testWildcardBehaviour() throws Exception { verify(forceMerge(indices), false); verify(refreshBuilder(indices), false); verify(validateQuery(indices), true); - verify(aliasExists(indices), false); - verify(typesExists(indices), false); verify(getAliases(indices), false); verify(getFieldMapping(indices), false); verify(getMapping(indices), false); @@ -353,8 +329,6 @@ public void testWildcardBehaviour() throws Exception { verify(forceMerge(indices).setIndicesOptions(options), false); verify(refreshBuilder(indices).setIndicesOptions(options), false); verify(validateQuery(indices).setIndicesOptions(options), false); - verify(aliasExists(indices).setIndicesOptions(options), false); - verify(typesExists(indices).setIndicesOptions(options), false); verify(getAliases(indices).setIndicesOptions(options), false); verify(getFieldMapping(indices).setIndicesOptions(options), false); verify(getMapping(indices).setIndicesOptions(options), false); @@ -374,8 +348,6 @@ public void testWildcardBehaviour() throws Exception { verify(forceMerge(indices), false); verify(refreshBuilder(indices), false); verify(validateQuery(indices), false); - verify(aliasExists(indices), false); - verify(typesExists(indices), false); verify(getAliases(indices), false); verify(getFieldMapping(indices), false); verify(getMapping(indices), false); @@ -392,8 +364,6 @@ public void testWildcardBehaviour() throws Exception { verify(forceMerge(indices), false); verify(refreshBuilder(indices), false); verify(validateQuery(indices), true); - verify(aliasExists(indices), false); - verify(typesExists(indices), false); verify(getAliases(indices), false); verify(getFieldMapping(indices), false); verify(getMapping(indices), false); @@ -410,8 +380,6 @@ public void testWildcardBehaviour() throws Exception { verify(forceMerge(indices).setIndicesOptions(options), false); verify(refreshBuilder(indices).setIndicesOptions(options), false); verify(validateQuery(indices).setIndicesOptions(options), false); - verify(aliasExists(indices).setIndicesOptions(options), false); - verify(typesExists(indices).setIndicesOptions(options), false); verify(getAliases(indices).setIndicesOptions(options), false); verify(getFieldMapping(indices).setIndicesOptions(options), false); verify(getMapping(indices).setIndicesOptions(options), false); @@ -573,25 +541,23 @@ public void testDeleteIndexWildcard() throws Exception { public void testPutAlias() throws Exception { createIndex("foobar"); verify(client().admin().indices().prepareAliases().addAlias("foobar", "foobar_alias"), false); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("foobar").get().exists(), equalTo(true)); - + assertFalse(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("foobar").get().getAliases().isEmpty()); } public void testPutAliasWildcard() throws Exception { createIndex("foo", "foobar", "bar", "barbaz"); verify(client().admin().indices().prepareAliases().addAlias("foo*", "foobar_alias"), false); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("foo").get().exists(), equalTo(true)); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("foobar").get().exists(), equalTo(true)); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("bar").get().exists(), equalTo(false)); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("barbaz").get().exists(), equalTo(false)); + assertFalse(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("foo").get().getAliases().isEmpty()); + assertFalse(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("foobar").get().getAliases().isEmpty()); + assertTrue(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("bar").get().getAliases().isEmpty()); + assertTrue(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("barbaz").get().getAliases().isEmpty()); verify(client().admin().indices().prepareAliases().addAlias("*", "foobar_alias"), false); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("foo").get().exists(), equalTo(true)); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("foobar").get().exists(), equalTo(true)); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("bar").get().exists(), equalTo(true)); - assertThat(client().admin().indices().prepareAliasesExist("foobar_alias").setIndices("barbaz").get().exists(), equalTo(true)); - + assertFalse(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("foo").get().getAliases().isEmpty()); + assertFalse(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("foobar").get().getAliases().isEmpty()); + assertFalse(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("bar").get().getAliases().isEmpty()); + assertFalse(client().admin().indices().prepareGetAliases("foobar_alias").setIndices("barbaz").get().getAliases().isEmpty()); } public void testPutMapping() throws Exception { @@ -721,14 +687,6 @@ static ValidateQueryRequestBuilder validateQuery(String... indices) { return client().admin().indices().prepareValidateQuery(indices); } - private static AliasesExistRequestBuilder aliasExists(String... indices) { - return client().admin().indices().prepareAliasesExist("dummy").addIndices(indices); - } - - private static TypesExistsRequestBuilder typesExists(String... indices) { - return client().admin().indices().prepareTypesExists(indices).setTypes("dummy"); - } - static GetAliasesRequestBuilder getAliases(String... indices) { return client().admin().indices().prepareGetAliases("dummy").addIndices(indices); } diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/IndicesRequestCacheIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/IndicesRequestCacheIT.java index 12fee85288bc2..98a22717019cf 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/IndicesRequestCacheIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/IndicesRequestCacheIT.java @@ -32,6 +32,8 @@ package org.opensearch.indices; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.forcemerge.ForceMergeResponse; import org.opensearch.action.search.SearchResponse; @@ -40,13 +42,14 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.cache.request.RequestCacheStats; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.aggregations.bucket.global.GlobalAggregationBuilder; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; import java.time.ZoneId; @@ -54,8 +57,10 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; +import java.util.Collection; import java.util.List; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.opensearch.search.aggregations.AggregationBuilders.dateRange; import static org.opensearch.search.aggregations.AggregationBuilders.filter; @@ -64,7 +69,23 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -public class IndicesRequestCacheIT extends OpenSearchIntegTestCase { +public class IndicesRequestCacheIT extends ParameterizedOpenSearchIntegTestCase { + public IndicesRequestCacheIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } // One of the primary purposes of the query cache is to cache aggs results public void testCacheAggs() throws Exception { diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/analysis/PreBuiltAnalyzerIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/analysis/PreBuiltAnalyzerIntegrationIT.java index a857f6cf6e96c..625f509df16f2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/analysis/PreBuiltAnalyzerIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/analysis/PreBuiltAnalyzerIntegrationIT.java @@ -36,7 +36,7 @@ import org.apache.lucene.analysis.TokenStream; import org.opensearch.Version; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/analyze/AnalyzeActionIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/analyze/AnalyzeActionIT.java index 1d25051eefe44..2e6bc7db6cae7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/analyze/AnalyzeActionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/analyze/AnalyzeActionIT.java @@ -37,9 +37,9 @@ import org.opensearch.action.admin.indices.analyze.AnalyzeRequestBuilder; import org.opensearch.common.settings.Settings; import org.opensearch.plugins.Plugin; +import org.opensearch.test.MockKeywordPlugin; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; -import org.opensearch.test.MockKeywordPlugin; import org.hamcrest.core.IsNull; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/ConcurrentDynamicTemplateIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/ConcurrentDynamicTemplateIT.java index e731b0074f04d..f732f5c41dec0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/ConcurrentDynamicTemplateIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/ConcurrentDynamicTemplateIT.java @@ -32,8 +32,8 @@ package org.opensearch.indices.mapping; -import org.opensearch.action.ActionListener; import org.opensearch.action.index.IndexResponse; +import org.opensearch.core.action.ActionListener; import org.opensearch.index.query.QueryBuilders; import org.opensearch.test.OpenSearchIntegTestCase; diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/DedicatedClusterManagerGetFieldMappingIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/DedicatedClusterManagerGetFieldMappingIT.java new file mode 100644 index 0000000000000..72f7bd44541a6 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/DedicatedClusterManagerGetFieldMappingIT.java @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.indices.mapping; + +import org.junit.Before; + +import static org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; +import static org.opensearch.test.OpenSearchIntegTestCase.Scope; + +@ClusterScope(scope = Scope.TEST, numDataNodes = 0) +public class DedicatedClusterManagerGetFieldMappingIT extends SimpleGetFieldMappingsIT { + + @Before + public void before1() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + internalCluster().startNode(); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/DedicatedMasterGetFieldMappingIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/DedicatedMasterGetFieldMappingIT.java deleted file mode 100644 index 98134531d4f31..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/DedicatedMasterGetFieldMappingIT.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.indices.mapping; - -import org.junit.Before; - -import static org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; -import static org.opensearch.test.OpenSearchIntegTestCase.Scope; - -@ClusterScope(scope = Scope.TEST, numDataNodes = 0) -public class DedicatedMasterGetFieldMappingIT extends SimpleGetFieldMappingsIT { - - @Before - public void before1() throws Exception { - internalCluster().startMasterOnlyNode(); - internalCluster().startNode(); - } - -} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/SimpleGetFieldMappingsIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/SimpleGetFieldMappingsIT.java index 3de2001acd1e7..c6519c1fd851b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/SimpleGetFieldMappingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/SimpleGetFieldMappingsIT.java @@ -34,14 +34,13 @@ import org.opensearch.action.admin.indices.mapping.get.GetFieldMappingsResponse; import org.opensearch.action.admin.indices.mapping.get.GetFieldMappingsResponse.FieldMappingMetadata; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; import java.util.Arrays; @@ -187,22 +186,22 @@ public void testSimpleGetFieldMappingsWithPretty() throws Exception { .get(); XContentBuilder responseBuilder = XContentFactory.jsonBuilder().prettyPrint(); response.toXContent(responseBuilder, new ToXContent.MapParams(params)); - String responseStrings = Strings.toString(responseBuilder); + String responseStrings = responseBuilder.toString(); XContentBuilder prettyJsonBuilder = XContentFactory.jsonBuilder().prettyPrint(); prettyJsonBuilder.copyCurrentStructure(createParser(JsonXContent.jsonXContent, responseStrings)); - assertThat(responseStrings, equalTo(Strings.toString(prettyJsonBuilder))); + assertThat(responseStrings, equalTo(prettyJsonBuilder.toString())); params.put("pretty", "false"); response = client().admin().indices().prepareGetFieldMappings("index").setFields("field1", "obj.subfield").get(); responseBuilder = XContentFactory.jsonBuilder().prettyPrint().lfAtEnd(); response.toXContent(responseBuilder, new ToXContent.MapParams(params)); - responseStrings = Strings.toString(responseBuilder); + responseStrings = responseBuilder.toString(); prettyJsonBuilder = XContentFactory.jsonBuilder().prettyPrint(); prettyJsonBuilder.copyCurrentStructure(createParser(JsonXContent.jsonXContent, responseStrings)); - assertThat(responseStrings, not(equalTo(Strings.toString(prettyJsonBuilder)))); + assertThat(responseStrings, not(equalTo(prettyJsonBuilder).toString())); } diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/SimpleGetMappingsIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/SimpleGetMappingsIT.java index f54e60a8baa88..4ca808a1e66e6 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/SimpleGetMappingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/SimpleGetMappingsIT.java @@ -36,10 +36,10 @@ import org.opensearch.action.admin.indices.mapping.get.GetMappingsResponse; import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.common.Priority; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; import java.util.Arrays; diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/UpdateMappingIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/UpdateMappingIntegrationIT.java index 0a29794add5a8..575c1956d0fda 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/mapping/UpdateMappingIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/mapping/UpdateMappingIntegrationIT.java @@ -43,17 +43,16 @@ import org.opensearch.common.Priority; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.mapper.MapperService; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; - +import org.opensearch.test.OpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; @@ -153,7 +152,7 @@ public void testUpdateMappingWithoutType() { AcknowledgedResponse putMappingResponse = client().admin() .indices() .preparePutMapping("test") - .setSource("{\"properties\":{\"date\":{\"type\":\"integer\"}}}", XContentType.JSON) + .setSource("{\"properties\":{\"date\":{\"type\":\"integer\"}}}", MediaTypeRegistry.JSON) .execute() .actionGet(); @@ -178,7 +177,7 @@ public void testUpdateMappingWithoutTypeMultiObjects() { AcknowledgedResponse putMappingResponse = client().admin() .indices() .preparePutMapping("test") - .setSource("{\"properties\":{\"date\":{\"type\":\"integer\"}}}", XContentType.JSON) + .setSource("{\"properties\":{\"date\":{\"type\":\"integer\"}}}", MediaTypeRegistry.JSON) .execute() .actionGet(); @@ -207,7 +206,7 @@ public void testUpdateMappingWithConflicts() { .preparePutMapping("test") .setSource( "{\"" + MapperService.SINGLE_MAPPING_NAME + "\":{\"properties\":{\"body\":{\"type\":\"integer\"}}}}", - XContentType.JSON + MediaTypeRegistry.JSON ) .execute() .actionGet(); @@ -230,7 +229,7 @@ public void testUpdateMappingWithNormsConflicts() { .preparePutMapping("test") .setSource( "{\"" + MapperService.SINGLE_MAPPING_NAME + "\":{\"properties\":{\"body\":{\"type\":\"text\", \"norms\": true }}}}", - XContentType.JSON + MediaTypeRegistry.JSON ) .execute() .actionGet(); @@ -256,7 +255,7 @@ public void testUpdateMappingNoChanges() { AcknowledgedResponse putMappingResponse = client().admin() .indices() .preparePutMapping("test") - .setSource("{\"properties\":{\"body\":{\"type\":\"text\"}}}", XContentType.JSON) + .setSource("{\"properties\":{\"body\":{\"type\":\"text\"}}}", MediaTypeRegistry.JSON) .execute() .actionGet(); @@ -306,7 +305,7 @@ public void testUpdateMappingConcurrently() throws Throwable { .endObject() .endObject() ) - .setMasterNodeTimeout(TimeValue.timeValueMinutes(5)) + .setClusterManagerNodeTimeout(TimeValue.timeValueMinutes(5)) .get(); assertThat(response.isAcknowledged(), equalTo(true)); @@ -347,7 +346,7 @@ public void testPutMappingsWithBlocks() { client().admin() .indices() .preparePutMapping("test") - .setSource("{\"properties\":{\"date\":{\"type\":\"integer\"}}}", XContentType.JSON) + .setSource("{\"properties\":{\"date\":{\"type\":\"integer\"}}}", MediaTypeRegistry.JSON) ); } finally { disableIndexBlock("test", block); @@ -361,7 +360,7 @@ public void testPutMappingsWithBlocks() { client().admin() .indices() .preparePutMapping("test") - .setSource("{\"properties\":{\"date\":{\"type\":\"integer\"}}}", XContentType.JSON) + .setSource("{\"properties\":{\"date\":{\"type\":\"integer\"}}}", MediaTypeRegistry.JSON) ); } finally { disableIndexBlock("test", block); @@ -386,13 +385,13 @@ private void assertConcreteMappingsOnAll(final String index, final String... fie assertNotNull("field " + fieldName + " doesn't exists on " + node, fieldType); } } - assertMappingOnMaster(index, fieldNames); + assertMappingOnClusterManager(index, fieldNames); } /** - * Waits for the given mapping type to exists on the master node. + * Waits for the given mapping type to exists on the cluster-manager node. */ - private void assertMappingOnMaster(final String index, final String... fieldNames) { + private void assertMappingOnClusterManager(final String index, final String... fieldNames) { GetMappingsResponse response = client().admin().indices().prepareGetMappings(index).get(); MappingMetadata mappings = response.getMappings().get(index); assertThat(mappings, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/memory/breaker/CircuitBreakerServiceIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/memory/breaker/CircuitBreakerServiceIT.java index 2dc6b2085b866..dfe6889df2319 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/memory/breaker/CircuitBreakerServiceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/memory/breaker/CircuitBreakerServiceIT.java @@ -32,6 +32,8 @@ package org.opensearch.indices.memory.breaker; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ExceptionsHelper; import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; @@ -46,25 +48,27 @@ import org.opensearch.client.Requests; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.breaker.CircuitBreakingException; -import org.opensearch.common.breaker.NoopCircuitBreaker; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.indices.breaker.CircuitBreakerStats; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.CircuitBreakingException; +import org.opensearch.core.common.breaker.NoopCircuitBreaker; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.indices.breaker.CircuitBreakerStats; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.indices.breaker.HierarchyCircuitBreakerService; -import org.opensearch.rest.RestStatus; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.After; import org.junit.Before; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -73,12 +77,13 @@ import static org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest.Metric.BREAKER; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchBootstrapSettings.CONCURRENT_SEGMENT_SEARCH_TARGET_MAX_SLICE_COUNT_KEY; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.cardinality; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.test.OpenSearchIntegTestCase.Scope.TEST; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFailures; - import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.Matchers.equalTo; @@ -89,7 +94,32 @@ * Integration tests for InternalCircuitBreakerService */ @ClusterScope(scope = TEST, numClientNodes = 0, maxNumDataNodes = 1) -public class CircuitBreakerServiceIT extends OpenSearchIntegTestCase { +public class CircuitBreakerServiceIT extends ParameterizedOpenSearchIntegTestCase { + public CircuitBreakerServiceIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(CONCURRENT_SEGMENT_SEARCH_TARGET_MAX_SLICE_COUNT_KEY, randomIntBetween(1, 2)) + .build(); + } + /** Reset all breaker settings back to their defaults */ private void reset() { logger.info("--> resetting breaker settings"); @@ -197,7 +227,7 @@ public void testRamAccountingTermsEnum() throws Exception { prepareCreate("ramtest").setSource( "{\"mappings\": {\"type\": {\"properties\": {\"test\": " + "{\"type\": \"text\",\"fielddata\": true,\"fielddata_frequency_filter\": {\"max\": 10000}}}}}}", - XContentType.JSON + MediaTypeRegistry.JSON ) ); diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/memory/breaker/RandomExceptionCircuitBreakerIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/memory/breaker/RandomExceptionCircuitBreakerIT.java index 341c0a965f94e..6d87cafdd4216 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/memory/breaker/RandomExceptionCircuitBreakerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/memory/breaker/RandomExceptionCircuitBreakerIT.java @@ -35,7 +35,6 @@ import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.FilterDirectoryReader; import org.apache.lucene.index.LeafReader; - import org.opensearch.OpenSearchException; import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; @@ -43,13 +42,12 @@ import org.opensearch.action.admin.indices.refresh.RefreshResponse; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchRequestBuilder; -import org.opensearch.common.Strings; -import org.opensearch.common.breaker.CircuitBreaker; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.breaker.CircuitBreaker; import org.opensearch.index.MockEngineFactoryPlugin; import org.opensearch.index.query.QueryBuilders; import org.opensearch.indices.IndicesService; @@ -70,7 +68,6 @@ import static org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest.Metric.BREAKER; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAllSuccessful; - import static org.hamcrest.Matchers.equalTo; /** @@ -99,22 +96,20 @@ public void testBreakerWithRandomExceptions() throws IOException, InterruptedExc assertThat("Breaker is not set to 0", node.getBreaker().getStats(CircuitBreaker.FIELDDATA).getEstimated(), equalTo(0L)); } - String mapping = Strings // {} - .toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("test-str") - .field("type", "keyword") - .field("doc_values", randomBoolean()) - .endObject() // test-str - .startObject("test-num") - // I don't use randomNumericType() here because I don't want "byte", and I want "float" and "double" - .field("type", randomFrom(Arrays.asList("float", "long", "double", "short", "integer"))) - .endObject() // test-num - .endObject() // properties - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("test-str") + .field("type", "keyword") + .field("doc_values", randomBoolean()) + .endObject() // test-str + .startObject("test-num") + // I don't use randomNumericType() here because I don't want "byte", and I want "float" and "double" + .field("type", randomFrom(Arrays.asList("float", "long", "double", "short", "integer"))) + .endObject() // test-num + .endObject() // properties + .endObject() + .toString(); final double topLevelRate; final double lowLevelRate; if (frequently()) { diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/recovery/DanglingIndicesIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/recovery/DanglingIndicesIT.java index ede18af80f6ca..8fd7961cab3a7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/recovery/DanglingIndicesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/recovery/DanglingIndicesIT.java @@ -40,11 +40,11 @@ import org.opensearch.action.admin.indices.dangling.list.NodeListDanglingIndicesResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; +import org.opensearch.core.rest.RestStatus; import org.opensearch.indices.IndicesService; -import org.opensearch.rest.RestStatus; +import org.opensearch.test.InternalTestCluster; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; -import org.opensearch.test.InternalTestCluster; import java.util.ArrayList; import java.util.List; diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/recovery/IndexPrimaryRelocationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/recovery/IndexPrimaryRelocationIT.java index 08cf33a342c65..c049c8ed2d4a6 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/recovery/IndexPrimaryRelocationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/recovery/IndexPrimaryRelocationIT.java @@ -56,14 +56,13 @@ public class IndexPrimaryRelocationIT extends OpenSearchIntegTestCase { private static final int RELOCATION_COUNT = 15; + public Settings indexSettings() { + return Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0).build(); + } + public void testPrimaryRelocationWhileIndexing() throws Exception { internalCluster().ensureAtLeastNumDataNodes(randomIntBetween(2, 3)); - client().admin() - .indices() - .prepareCreate("test") - .setSettings(Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0)) - .setMapping("field", "type=text") - .get(); + client().admin().indices().prepareCreate("test").setSettings(indexSettings()).setMapping("field", "type=text").get(); ensureGreen("test"); AtomicInteger numAutoGenDocs = new AtomicInteger(); final AtomicBoolean finished = new AtomicBoolean(false); @@ -83,7 +82,7 @@ public void run() { indexingThread.start(); ClusterState initialState = client().admin().cluster().prepareState().get().getState(); - DiscoveryNode[] dataNodes = initialState.getNodes().getDataNodes().values().toArray(DiscoveryNode.class); + DiscoveryNode[] dataNodes = initialState.getNodes().getDataNodes().values().toArray(new DiscoveryNode[0]); DiscoveryNode relocationSource = initialState.getNodes() .getDataNodes() .get(initialState.getRoutingTable().shardRoutingTable("test", 0).primaryShard().currentNodeId()); diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/recovery/IndexRecoveryIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/recovery/IndexRecoveryIT.java index a7dc77e024d5c..e4f1f8717f899 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/recovery/IndexRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/recovery/IndexRecoveryIT.java @@ -34,7 +34,6 @@ import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.index.IndexCommit; -import org.apache.lucene.util.SetOnce; import org.opensearch.OpenSearchException; import org.opensearch.Version; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -72,18 +71,20 @@ import org.opensearch.cluster.routing.allocation.command.MoveAllocationCommand; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.breaker.CircuitBreakingException; +import org.opensearch.common.SetOnce; import org.opensearch.common.concurrent.GatedCloseable; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.CircuitBreakingException; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.gateway.ReplicaShardAllocatorIT; -import org.opensearch.index.Index; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.MockEngineFactoryPlugin; @@ -95,13 +96,13 @@ import org.opensearch.index.seqno.RetentionLeases; import org.opensearch.index.seqno.SequenceNumbers; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.index.store.Store; import org.opensearch.index.store.StoreStats; import org.opensearch.indices.IndicesService; import org.opensearch.indices.NodeIndicesStats; import org.opensearch.indices.analysis.AnalysisModule; import org.opensearch.indices.recovery.RecoveryState.Stage; +import org.opensearch.indices.replication.common.ReplicationLuceneIndex; import org.opensearch.node.NodeClosedException; import org.opensearch.node.RecoverySettingsChunkSizePlugin; import org.opensearch.plugins.AnalysisPlugin; @@ -130,6 +131,7 @@ import org.opensearch.transport.TransportRequestHandler; import org.opensearch.transport.TransportRequestOptions; import org.opensearch.transport.TransportService; +import org.hamcrest.Matcher; import java.io.IOException; import java.util.ArrayList; @@ -138,6 +140,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Spliterators; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Semaphore; @@ -151,6 +154,11 @@ import static java.util.Collections.singletonMap; import static java.util.stream.Collectors.toList; +import static org.opensearch.action.DocWriteResponse.Result.CREATED; +import static org.opensearch.action.DocWriteResponse.Result.UPDATED; +import static org.opensearch.node.RecoverySettingsChunkSizePlugin.CHUNK_SIZE_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.everyItem; @@ -161,11 +169,6 @@ import static org.hamcrest.Matchers.isOneOf; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.not; -import static org.opensearch.action.DocWriteResponse.Result.CREATED; -import static org.opensearch.action.DocWriteResponse.Result.UPDATED; -import static org.opensearch.node.RecoverySettingsChunkSizePlugin.CHUNK_SIZE_SETTING; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @ClusterScope(scope = Scope.TEST, numDataNodes = 0) public class IndexRecoveryIT extends OpenSearchIntegTestCase { @@ -575,21 +578,25 @@ public void testRerouteRecovery() throws Exception { .clear() .setIndices(new CommonStatsFlags(CommonStatsFlags.Flag.Recovery)) .get(); - assertThat(statsResponse1.getNodes(), hasSize(2)); - for (NodeStats nodeStats : statsResponse1.getNodes()) { + List dataNodeStats = statsResponse1.getNodes() + .stream() + .filter(nodeStats -> nodeStats.getNode().isDataNode()) + .collect(Collectors.toList()); + assertThat(dataNodeStats, hasSize(2)); + for (NodeStats nodeStats : dataNodeStats) { final RecoveryStats recoveryStats = nodeStats.getIndices().getRecoveryStats(); if (nodeStats.getNode().getName().equals(nodeA)) { assertThat( "node A throttling should increase", recoveryStats.throttleTime().millis(), - greaterThan(finalNodeAThrottling) + getMatcherForThrottling(finalNodeAThrottling) ); } if (nodeStats.getNode().getName().equals(nodeB)) { assertThat( "node B throttling should increase", recoveryStats.throttleTime().millis(), - greaterThan(finalNodeBThrottling) + getMatcherForThrottling(finalNodeBThrottling) ); } } @@ -621,7 +628,7 @@ public void testRerouteRecovery() throws Exception { final RecoveryStats recoveryStats = nodeStats.getIndices().getRecoveryStats(); assertThat(recoveryStats.currentAsSource(), equalTo(0)); assertThat(recoveryStats.currentAsTarget(), equalTo(0)); - assertThat(nodeName + " throttling should be >0", recoveryStats.throttleTime().millis(), greaterThan(0L)); + assertThat(nodeName + " throttling should be >0", recoveryStats.throttleTime().millis(), getMatcherForThrottling(0)); }; // we have to use assertBusy as recovery counters are decremented only when the last reference to the RecoveryTarget // is decremented, which may happen after the recovery was done. @@ -642,7 +649,8 @@ public void testRerouteRecovery() throws Exception { logger.info("--> start node C"); String nodeC = internalCluster().startNode(); - assertFalse(client().admin().cluster().prepareHealth().setWaitForNodes("3").get().isTimedOut()); + int nodeCount = internalCluster().getNodeNames().length; + assertFalse(client().admin().cluster().prepareHealth().setWaitForNodes(String.valueOf(nodeCount)).get().isTimedOut()); logger.info("--> slowing down recoveries"); slowDownRecovery(shardSize); @@ -676,7 +684,7 @@ public void testRerouteRecovery() throws Exception { assertOnGoingRecoveryState(nodeCRecoveryStates.get(0), 0, PeerRecoverySource.INSTANCE, false, nodeB, nodeC); validateIndexRecoveryState(nodeCRecoveryStates.get(0).getIndex()); - if (randomBoolean()) { + if (randomBoolean() && shouldAssertOngoingRecoveryInRerouteRecovery()) { // shutdown node with relocation source of replica shard and check if recovery continues internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodeA)); ensureStableCluster(2); @@ -720,6 +728,14 @@ public void testRerouteRecovery() throws Exception { validateIndexRecoveryState(nodeCRecoveryStates.get(0).getIndex()); } + protected boolean shouldAssertOngoingRecoveryInRerouteRecovery() { + return false; + } + + protected Matcher getMatcherForThrottling(long value) { + return greaterThan(value); + } + public void testSnapshotRecovery() throws Exception { logger.info("--> start node A"); String nodeA = internalCluster().startNode(); @@ -774,7 +790,7 @@ public void testSnapshotRecovery() throws Exception { logger.info("--> request recoveries"); RecoveryResponse response = client().admin().indices().prepareRecoveries(INDEX_NAME).execute().actionGet(); - Repository repository = internalCluster().getMasterNodeInstance(RepositoriesService.class).repository(REPO_NAME); + Repository repository = internalCluster().getClusterManagerNodeInstance(RepositoriesService.class).repository(REPO_NAME); final RepositoryData repositoryData = PlainActionFuture.get(repository::getRepositoryData); for (Map.Entry> indexRecoveryStates : response.shardRecoveryStates().entrySet()) { @@ -822,7 +838,7 @@ private IndicesStatsResponse createAndPopulateIndex(String name, int nodeCount, ensureGreen(); logger.info("--> indexing sample data"); - final int numDocs = between(MIN_DOC_COUNT, MAX_DOC_COUNT); + final int numDocs = numDocs(); final IndexRequestBuilder[] docs = new IndexRequestBuilder[numDocs]; for (int i = 0; i < numDocs; i++) { @@ -836,7 +852,7 @@ private IndicesStatsResponse createAndPopulateIndex(String name, int nodeCount, return client().admin().indices().prepareStats(name).execute().actionGet(); } - private void validateIndexRecoveryState(RecoveryState.Index indexState) { + private void validateIndexRecoveryState(ReplicationLuceneIndex indexState) { assertThat(indexState.time(), greaterThanOrEqualTo(0L)); assertThat(indexState.recoveredFilesPercent(), greaterThanOrEqualTo(0.0f)); assertThat(indexState.recoveredFilesPercent(), lessThanOrEqualTo(100.0f)); @@ -844,6 +860,10 @@ private void validateIndexRecoveryState(RecoveryState.Index indexState) { assertThat(indexState.recoveredBytesPercent(), lessThanOrEqualTo(100.0f)); } + protected int numDocs() { + return between(MIN_DOC_COUNT, MAX_DOC_COUNT); + } + public void testTransientErrorsDuringRecoveryAreRetried() throws Exception { final String indexName = "test"; final Settings nodeSettings = Settings.builder() @@ -851,7 +871,7 @@ public void testTransientErrorsDuringRecoveryAreRetried() throws Exception { .put(NodeConnectionsService.CLUSTER_NODE_RECONNECT_INTERVAL_SETTING.getKey(), "500ms") .put(RecoverySettings.INDICES_RECOVERY_INTERNAL_ACTION_TIMEOUT_SETTING.getKey(), "10s") .build(); - // start a master node + // start a cluster-manager node internalCluster().startNode(nodeSettings); final String blueNodeName = internalCluster().startNode( @@ -879,14 +899,14 @@ public void testTransientErrorsDuringRecoveryAreRetried() throws Exception { // is a mix of file chunks and translog ops int threeFourths = (int) (numDocs * 0.75); for (int i = 0; i < threeFourths; i++) { - requests.add(client().prepareIndex(indexName).setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex(indexName).setSource("{}", MediaTypeRegistry.JSON)); } indexRandom(true, requests); flush(indexName); requests.clear(); for (int i = threeFourths; i < numDocs; i++) { - requests.add(client().prepareIndex(indexName).setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex(indexName).setSource("{}", MediaTypeRegistry.JSON)); } indexRandom(true, requests); ensureSearchable(indexName); @@ -1053,7 +1073,7 @@ public void testDisconnectsWhileRecovering() throws Exception { .put(RecoverySettings.INDICES_RECOVERY_INTERNAL_ACTION_TIMEOUT_SETTING.getKey(), "1s") .put(NodeConnectionsService.CLUSTER_NODE_RECONNECT_INTERVAL_SETTING.getKey(), "1s") .build(); - // start a master node + // start a cluster-manager node internalCluster().startNode(nodeSettings); final String blueNodeName = internalCluster().startNode( @@ -1078,7 +1098,7 @@ public void testDisconnectsWhileRecovering() throws Exception { List requests = new ArrayList<>(); int numDocs = scaledRandomIntBetween(25, 250); for (int i = 0; i < numDocs; i++) { - requests.add(client().prepareIndex(indexName).setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex(indexName).setSource("{}", MediaTypeRegistry.JSON)); } indexRandom(true, requests); ensureSearchable(indexName); @@ -1210,8 +1230,8 @@ public void testDisconnectsDuringRecovery() throws Exception { ) .build(); TimeValue disconnectAfterDelay = TimeValue.timeValueMillis(randomIntBetween(0, 100)); - // start a master node - String masterNodeName = internalCluster().startMasterOnlyNode(nodeSettings); + // start a cluster-manager node + String clusterManagerNodeName = internalCluster().startClusterManagerOnlyNode(nodeSettings); final String blueNodeName = internalCluster().startNode( Settings.builder().put("node.attr.color", "blue").put(nodeSettings).build() @@ -1232,15 +1252,15 @@ public void testDisconnectsDuringRecovery() throws Exception { List requests = new ArrayList<>(); int numDocs = scaledRandomIntBetween(25, 250); for (int i = 0; i < numDocs; i++) { - requests.add(client().prepareIndex(indexName).setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex(indexName).setSource("{}", MediaTypeRegistry.JSON)); } indexRandom(true, requests); ensureSearchable(indexName); assertHitCount(client().prepareSearch(indexName).get(), numDocs); - MockTransportService masterTransportService = (MockTransportService) internalCluster().getInstance( + MockTransportService clusterManagerTransportService = (MockTransportService) internalCluster().getInstance( TransportService.class, - masterNodeName + clusterManagerNodeName ); MockTransportService blueMockTransportService = (MockTransportService) internalCluster().getInstance( TransportService.class, @@ -1311,7 +1331,7 @@ public void sendRequest( }); for (MockTransportService mockTransportService : Arrays.asList(redMockTransportService, blueMockTransportService)) { - mockTransportService.addSendBehavior(masterTransportService, (connection, requestId, action, request, options) -> { + mockTransportService.addSendBehavior(clusterManagerTransportService, (connection, requestId, action, request, options) -> { logger.info("--> sending request {} on {}", action, connection.getNode()); if ((primaryRelocation && finalized.get()) == false) { assertNotEquals(action, ShardStateAction.SHARD_FAILED_ACTION_NAME); @@ -1375,17 +1395,17 @@ public void testHistoryRetention() throws Exception { final List requests = new ArrayList<>(); final int replicatedDocCount = scaledRandomIntBetween(25, 250); while (requests.size() < replicatedDocCount) { - requests.add(client().prepareIndex(indexName).setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex(indexName).setSource("{}", MediaTypeRegistry.JSON)); } indexRandom(true, requests); if (randomBoolean()) { flush(indexName); } - String firstNodeToStop = randomFrom(internalCluster().getNodeNames()); + String firstNodeToStop = randomFrom(internalCluster().getDataNodeNames()); Settings firstNodeToStopDataPathSettings = internalCluster().dataPathSettings(firstNodeToStop); internalCluster().stopRandomNode(InternalTestCluster.nameFilter(firstNodeToStop)); - String secondNodeToStop = randomFrom(internalCluster().getNodeNames()); + String secondNodeToStop = randomFrom(internalCluster().getDataNodeNames()); Settings secondNodeToStopDataPathSettings = internalCluster().dataPathSettings(secondNodeToStop); internalCluster().stopRandomNode(InternalTestCluster.nameFilter(secondNodeToStop)); @@ -1397,7 +1417,7 @@ public void testHistoryRetention() throws Exception { final int numNewDocs = scaledRandomIntBetween(25, 250); for (int i = 0; i < numNewDocs; i++) { - client().prepareIndex(indexName).setSource("{}", XContentType.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); + client().prepareIndex(indexName).setSource("{}", MediaTypeRegistry.JSON).setRefreshPolicy(RefreshPolicy.IMMEDIATE).get(); } // Flush twice to update the safe commit's local checkpoint assertThat(client().admin().indices().prepareFlush(indexName).setForce(true).execute().get().getFailedShards(), equalTo(0)); @@ -1438,7 +1458,7 @@ public void testDoNotInfinitelyWaitForMapping() { for (int i = 0; i < numDocs; i++) { client().prepareIndex("test") .setId("u" + i) - .setSource(singletonMap("test_field", Integer.toString(i)), XContentType.JSON) + .setSource(singletonMap("test_field", Integer.toString(i)), MediaTypeRegistry.JSON) .get(); } Semaphore recoveryBlocked = new Semaphore(1); @@ -1465,8 +1485,8 @@ public void testDoNotInfinitelyWaitForMapping() { assertHitCount(client().prepareSearch().get(), numDocs); } - /** Makes sure the new master does not repeatedly fetch index metadata from recovering replicas */ - public void testOngoingRecoveryAndMasterFailOver() throws Exception { + /** Makes sure the new cluster-manager does not repeatedly fetch index metadata from recovering replicas */ + public void testOngoingRecoveryAndClusterManagerFailOver() throws Exception { String indexName = "test"; internalCluster().startNodes(2); String nodeWithPrimary = internalCluster().startDataOnlyNode(); @@ -1510,7 +1530,7 @@ public void testOngoingRecoveryAndMasterFailOver() throws Exception { ); phase1ReadyBlocked.await(); internalCluster().restartNode( - clusterService().state().nodes().getMasterNode().getName(), + clusterService().state().nodes().getClusterManagerNode().getName(), new InternalTestCluster.RestartCallback() ); internalCluster().ensureAtLeastNumDataNodes(3); @@ -1535,8 +1555,8 @@ public void testRecoverLocallyUpToGlobalCheckpoint() throws Exception { internalCluster().ensureAtLeastNumDataNodes(2); List nodes = randomSubsetOf( 2, - StreamSupport.stream(clusterService().state().nodes().getDataNodes().spliterator(), false) - .map(node -> node.value.getName()) + StreamSupport.stream(Spliterators.spliterator(clusterService().state().nodes().getDataNodes().values(), 0), false) + .map(node -> node.getName()) .collect(Collectors.toSet()) ); String indexName = "test-index"; @@ -1588,7 +1608,10 @@ public void testRecoverLocallyUpToGlobalCheckpoint() throws Exception { .getShard(0) .getRetentionLeases(); throw new AssertionError( - "expect an operation-based recovery:" + "retention leases" + Strings.toString(retentionLeases) + "]" + "expect an operation-based recovery:" + + "retention leases" + + Strings.toString(MediaTypeRegistry.JSON, retentionLeases) + + "]" ); } connection.sendRequest(requestId, action, request, options); @@ -2084,7 +2107,7 @@ public void testRepeatedRecovery() throws Exception { } public void testAllocateEmptyPrimaryResetsGlobalCheckpoint() throws Exception { - internalCluster().startMasterOnlyNode(Settings.EMPTY); + internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); final List dataNodes = internalCluster().startDataOnlyNodes(2); final Settings randomNodeDataPathSettings = internalCluster().dataPathSettings(randomFrom(dataNodes)); final String indexName = "test"; @@ -2164,7 +2187,10 @@ public void testPeerRecoveryTrimsLocalTranslog() throws Exception { while (stopped.get() == false) { try { IndexResponse response = client().prepareIndex(indexName) - .setSource(Collections.singletonMap("f" + randomIntBetween(1, 10), randomNonNegativeLong()), XContentType.JSON) + .setSource( + Collections.singletonMap("f" + randomIntBetween(1, 10), randomNonNegativeLong()), + MediaTypeRegistry.JSON + ) .get(); assertThat(response.getResult(), isOneOf(CREATED, UPDATED)); } catch (IllegalStateException | OpenSearchException ignored) {} @@ -2184,7 +2210,7 @@ public void testPeerRecoveryTrimsLocalTranslog() throws Exception { } public void testCancelRecoveryWithAutoExpandReplicas() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); assertAcked( client().admin() .indices() diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/recovery/ReplicaToPrimaryPromotionIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/recovery/ReplicaToPrimaryPromotionIT.java index b74dc2f4236a0..3df4ecff5250c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/recovery/ReplicaToPrimaryPromotionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/recovery/ReplicaToPrimaryPromotionIT.java @@ -38,8 +38,8 @@ import org.opensearch.cluster.routing.IndexShardRoutingTable; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.test.BackgroundIndexer; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Locale; @@ -80,7 +80,7 @@ public void testPromoteReplicaToPrimary() throws Exception { } // pick up a data node that contains a random primary shard - ClusterState state = client(internalCluster().getMasterName()).admin().cluster().prepareState().get().getState(); + ClusterState state = client(internalCluster().getClusterManagerName()).admin().cluster().prepareState().get().getState(); final int numShards = state.metadata().index(indexName).getNumberOfShards(); final ShardRouting primaryShard = state.routingTable().index(indexName).shard(randomIntBetween(0, numShards - 1)).primaryShard(); final DiscoveryNode randomNode = state.nodes().resolveNode(primaryShard.currentNodeId()); @@ -89,7 +89,7 @@ public void testPromoteReplicaToPrimary() throws Exception { internalCluster().stopRandomNode(InternalTestCluster.nameFilter(randomNode.getName())); ensureYellowAndNoInitializingShards(indexName); - state = client(internalCluster().getMasterName()).admin().cluster().prepareState().get().getState(); + state = client(internalCluster().getClusterManagerName()).admin().cluster().prepareState().get().getState(); for (IndexShardRoutingTable shardRoutingTable : state.routingTable().index(indexName)) { for (ShardRouting shardRouting : shardRoutingTable.activeShards()) { assertThat(shardRouting + " should be promoted as a primary", shardRouting.primary(), is(true)); diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationAllocationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationAllocationIT.java new file mode 100644 index 0000000000000..bdefd7a5e199a --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationAllocationIT.java @@ -0,0 +1,283 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.replication; + +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.OpenSearchAllocationTestCase.ShardAllocations; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.routing.IndexRoutingTable; +import org.opensearch.cluster.routing.RoutingNode; +import org.opensearch.cluster.routing.RoutingNodes; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.allocation.allocator.BalancedShardsAllocator; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.index.IndexModule; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.opensearch.cluster.routing.ShardRoutingState.STARTED; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationAllocationIT extends SegmentReplicationBaseIT { + + private void createIndex(String idxName, int shardCount, int replicaCount, boolean isSegRep) { + Settings.Builder builder = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, shardCount) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, replicaCount); + if (isSegRep) { + builder = builder.put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT); + } else { + builder = builder.put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT); + } + prepareCreate(idxName, builder).get(); + } + + public void enablePreferPrimaryBalance() { + assertAcked( + client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put(BalancedShardsAllocator.PREFER_PRIMARY_SHARD_BALANCE.getKey(), "true")) + ); + } + + /** + * This test verifies that the overall primary balance is attained during allocation. This test verifies primary + * balance per index and across all indices is maintained. + */ + public void testGlobalPrimaryAllocation() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + final int maxReplicaCount = 1; + final int maxShardCount = 1; + final int nodeCount = randomIntBetween(maxReplicaCount + 1, 10); + final int numberOfIndices = randomIntBetween(5, 10); + + final List nodeNames = new ArrayList<>(); + logger.info("--> Creating {} nodes", nodeCount); + for (int i = 0; i < nodeCount; i++) { + nodeNames.add(internalCluster().startNode()); + } + enablePreferPrimaryBalance(); + int shardCount, replicaCount; + ClusterState state; + for (int i = 0; i < numberOfIndices; i++) { + shardCount = randomIntBetween(1, maxShardCount); + replicaCount = randomIntBetween(0, maxReplicaCount); + createIndex("test" + i, shardCount, replicaCount, i % 2 == 0); + logger.info("--> Creating index {} with shard count {} and replica count {}", "test" + i, shardCount, replicaCount); + ensureGreen(TimeValue.timeValueSeconds(60)); + } + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info(ShardAllocations.printShardDistribution(state)); + verifyPerIndexPrimaryBalance(); + verifyPrimaryBalance(); + } + + /** + * This test verifies the happy path where primary shard allocation is balanced when multiple indices are created. + * + * This test in general passes without primary shard balance as well due to nature of allocation algorithm which + * assigns all primary shards first followed by replica copies. + */ + public void testPerIndexPrimaryAllocation() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + final int maxReplicaCount = 2; + final int maxShardCount = 5; + final int nodeCount = randomIntBetween(maxReplicaCount + 1, 10); + final int numberOfIndices = randomIntBetween(5, 10); + + final List nodeNames = new ArrayList<>(); + logger.info("--> Creating {} nodes", nodeCount); + for (int i = 0; i < nodeCount; i++) { + nodeNames.add(internalCluster().startNode()); + } + enablePreferPrimaryBalance(); + int shardCount, replicaCount; + ClusterState state; + for (int i = 0; i < numberOfIndices; i++) { + shardCount = randomIntBetween(1, maxShardCount); + replicaCount = randomIntBetween(0, maxReplicaCount); + createIndex("test" + i, shardCount, replicaCount, i % 2 == 0); + logger.info("--> Creating index {} with shard count {} and replica count {}", "test" + i, shardCount, replicaCount); + ensureGreen(TimeValue.timeValueSeconds(60)); + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info(ShardAllocations.printShardDistribution(state)); + } + verifyPerIndexPrimaryBalance(); + } + + /** + * This test verifies balanced primary shard allocation for a single index with large shard count in event of node + * going down and a new node joining the cluster. The results in shard distribution skewness and re-balancing logic + * ensures the primary shard distribution is balanced. + * + */ + public void testSingleIndexShardAllocation() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + final int maxReplicaCount = 1; + final int maxShardCount = 50; + final int nodeCount = 5; + + final List nodeNames = new ArrayList<>(); + logger.info("--> Creating {} nodes", nodeCount); + for (int i = 0; i < nodeCount; i++) { + nodeNames.add(internalCluster().startNode()); + } + enablePreferPrimaryBalance(); + + ClusterState state; + createIndex("test", maxShardCount, maxReplicaCount, true); + ensureGreen(TimeValue.timeValueSeconds(60)); + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info(ShardAllocations.printShardDistribution(state)); + verifyPerIndexPrimaryBalance(); + + // Remove a node + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodeNames.get(0))); + ensureGreen(TimeValue.timeValueSeconds(60)); + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info(ShardAllocations.printShardDistribution(state)); + verifyPerIndexPrimaryBalance(); + + // Add a new node + internalCluster().startDataOnlyNode(); + ensureGreen(TimeValue.timeValueSeconds(60)); + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info(ShardAllocations.printShardDistribution(state)); + verifyPerIndexPrimaryBalance(); + } + + /** + * Similar to testSingleIndexShardAllocation test but creates multiple indices, multiple nodes adding in and getting + * removed. The test asserts post each such event that primary shard distribution is balanced for each index. + */ + public void testAllocationWithDisruption() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + final int maxReplicaCount = 2; + final int maxShardCount = 2; + // Create higher number of nodes than number of shards to reduce chances of SameShardAllocationDecider kicking-in + // and preventing primary relocations + final int nodeCount = randomIntBetween(5, 10); + final int numberOfIndices = randomIntBetween(1, 10); + + logger.info("--> Creating {} nodes", nodeCount); + final List nodeNames = new ArrayList<>(); + for (int i = 0; i < nodeCount; i++) { + nodeNames.add(internalCluster().startNode()); + } + enablePreferPrimaryBalance(); + + int shardCount, replicaCount; + ClusterState state; + for (int i = 0; i < numberOfIndices; i++) { + shardCount = randomIntBetween(1, maxShardCount); + replicaCount = randomIntBetween(1, maxReplicaCount); + logger.info("--> Creating index test{} with primary {} and replica {}", i, shardCount, replicaCount); + createIndex("test" + i, shardCount, replicaCount, i % 2 == 0); + ensureGreen(TimeValue.timeValueSeconds(60)); + if (logger.isTraceEnabled()) { + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info(ShardAllocations.printShardDistribution(state)); + } + } + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info(ShardAllocations.printShardDistribution(state)); + verifyPerIndexPrimaryBalance(); + + final int additionalNodeCount = randomIntBetween(1, 5); + logger.info("--> Adding {} nodes", additionalNodeCount); + + internalCluster().startNodes(additionalNodeCount); + ensureGreen(TimeValue.timeValueSeconds(60)); + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info(ShardAllocations.printShardDistribution(state)); + verifyPerIndexPrimaryBalance(); + + int nodeCountToStop = additionalNodeCount; + while (nodeCountToStop > 0) { + internalCluster().stopRandomDataNode(); + // give replica a chance to promote as primary before terminating node containing the replica + ensureGreen(TimeValue.timeValueSeconds(60)); + nodeCountToStop--; + } + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + logger.info("--> Cluster state post nodes stop {}", state); + logger.info(ShardAllocations.printShardDistribution(state)); + verifyPerIndexPrimaryBalance(); + } + + /** + * Utility method which ensures cluster has balanced primary shard distribution across a single index. + * @throws Exception exception + */ + private void verifyPerIndexPrimaryBalance() throws Exception { + assertBusy(() -> { + final ClusterState currentState = client().admin().cluster().prepareState().execute().actionGet().getState(); + RoutingNodes nodes = currentState.getRoutingNodes(); + for (final Map.Entry index : currentState.getRoutingTable().indicesRouting().entrySet()) { + final int totalPrimaryShards = index.getValue().primaryShardsActive(); + final int lowerBoundPrimaryShardsPerNode = (int) Math.floor(totalPrimaryShards * 1f / currentState.getRoutingNodes().size()) + - 1; + final int upperBoundPrimaryShardsPerNode = (int) Math.ceil(totalPrimaryShards * 1f / currentState.getRoutingNodes().size()) + + 1; + for (RoutingNode node : nodes) { + final int primaryCount = node.shardsWithState(index.getKey(), STARTED) + .stream() + .filter(ShardRouting::primary) + .collect(Collectors.toList()) + .size(); + // Asserts value is within the variance threshold (-1/+1 of the average value). + assertTrue( + "--> Primary balance assertion failure for index " + + index + + "on node " + + node.node().getName() + + " " + + lowerBoundPrimaryShardsPerNode + + " <= " + + primaryCount + + " (assigned) <= " + + upperBoundPrimaryShardsPerNode, + lowerBoundPrimaryShardsPerNode <= primaryCount && primaryCount <= upperBoundPrimaryShardsPerNode + ); + } + } + }, 60, TimeUnit.SECONDS); + } + + private void verifyPrimaryBalance() throws Exception { + assertBusy(() -> { + final ClusterState currentState = client().admin().cluster().prepareState().execute().actionGet().getState(); + RoutingNodes nodes = currentState.getRoutingNodes(); + int totalPrimaryShards = 0; + for (final IndexRoutingTable index : currentState.getRoutingTable().indicesRouting().values()) { + totalPrimaryShards += index.primaryShardsActive(); + } + final int avgPrimaryShardsPerNode = (int) Math.ceil(totalPrimaryShards * 1f / currentState.getRoutingNodes().size()); + for (RoutingNode node : nodes) { + final int primaryCount = node.shardsWithState(STARTED) + .stream() + .filter(ShardRouting::primary) + .collect(Collectors.toList()) + .size(); + assertTrue(primaryCount <= avgPrimaryShardsPerNode); + } + }, 60, TimeUnit.SECONDS); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationBaseIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationBaseIT.java new file mode 100644 index 0000000000000..8e68a8bde39d5 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationBaseIT.java @@ -0,0 +1,249 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.replication; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.IndexRoutingTable; +import org.opensearch.cluster.routing.IndexShardRoutingTable; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.common.Nullable; +import org.opensearch.common.lease.Releasable; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.index.IndexModule; +import org.opensearch.index.IndexService; +import org.opensearch.index.SegmentReplicationShardStats; +import org.opensearch.index.engine.Engine; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.index.store.Store; +import org.opensearch.index.store.StoreFileMetadata; +import org.opensearch.indices.IndicesService; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.transport.TransportService; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static java.util.Arrays.asList; +import static org.opensearch.test.OpenSearchIntegTestCase.client; +import static org.opensearch.test.OpenSearchTestCase.assertBusy; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; + +public class SegmentReplicationBaseIT extends OpenSearchIntegTestCase { + + protected static final String INDEX_NAME = "test-idx-1"; + protected static final int SHARD_COUNT = 1; + protected static final int REPLICA_COUNT = 1; + + @Override + protected Collection> nodePlugins() { + return asList(MockTransportService.TestPlugin.class); + } + + @Override + protected boolean addMockInternalEngine() { + return false; + } + + @Override + public Settings indexSettings() { + return Settings.builder() + .put(super.indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, SHARD_COUNT) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, REPLICA_COUNT) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + } + + @Nullable + protected ShardRouting getShardRoutingForNodeName(String nodeName) { + final ClusterState state = getClusterState(); + for (IndexShardRoutingTable shardRoutingTable : state.routingTable().index(INDEX_NAME)) { + for (ShardRouting shardRouting : shardRoutingTable.activeShards()) { + final String nodeId = shardRouting.currentNodeId(); + final DiscoveryNode discoveryNode = state.nodes().resolveNode(nodeId); + if (discoveryNode.getName().equals(nodeName)) { + return shardRouting; + } + } + } + return null; + } + + protected void assertDocCounts(int expectedDocCount, String... nodeNames) { + for (String node : nodeNames) { + assertHitCount(client(node).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), expectedDocCount); + } + } + + protected DiscoveryNode getNodeContainingPrimaryShard() { + final ClusterState state = getClusterState(); + final ShardRouting primaryShard = state.routingTable().index(INDEX_NAME).shard(0).primaryShard(); + return state.nodes().resolveNode(primaryShard.currentNodeId()); + } + + /** + * Waits until all given nodes have at least the expected docCount. + * + * @param docCount - Expected Doc count. + * @param nodes - List of node names. + */ + protected void waitForSearchableDocs(long docCount, List nodes) throws Exception { + // wait until the replica has the latest segment generation. + waitForSearchableDocs(INDEX_NAME, docCount, nodes); + } + + public static void waitForSearchableDocs(String indexName, long docCount, List nodes) throws Exception { + // wait until the replica has the latest segment generation. + assertBusy(() -> { + for (String node : nodes) { + final SearchResponse response = client(node).prepareSearch(indexName).setSize(0).setPreference("_only_local").get(); + final long hits = response.getHits().getTotalHits().value; + if (hits < docCount) { + fail("Expected search hits on node: " + node + " to be at least " + docCount + " but was: " + hits); + } + } + }, 1, TimeUnit.MINUTES); + } + + protected void waitForSearchableDocs(long docCount, String... nodes) throws Exception { + waitForSearchableDocs(docCount, Arrays.stream(nodes).collect(Collectors.toList())); + } + + protected void verifyStoreContent() throws Exception { + assertBusy(() -> { + final ClusterState clusterState = getClusterState(); + for (IndexRoutingTable indexRoutingTable : clusterState.routingTable()) { + for (IndexShardRoutingTable shardRoutingTable : indexRoutingTable) { + final ShardRouting primaryRouting = shardRoutingTable.primaryShard(); + final String indexName = primaryRouting.getIndexName(); + final List replicaRouting = shardRoutingTable.replicaShards(); + final IndexShard primaryShard = getIndexShard(clusterState, primaryRouting, indexName); + final int primaryDocCount = getDocCountFromShard(primaryShard); + final Map primarySegmentMetadata = primaryShard.getSegmentMetadataMap(); + for (ShardRouting replica : replicaRouting) { + IndexShard replicaShard = getIndexShard(clusterState, replica, indexName); + final Store.RecoveryDiff recoveryDiff = Store.segmentReplicationDiff( + primarySegmentMetadata, + replicaShard.getSegmentMetadataMap() + ); + final int replicaDocCount = getDocCountFromShard(replicaShard); + assertEquals("Doc counts should match", primaryDocCount, replicaDocCount); + if (recoveryDiff.missing.isEmpty() == false || recoveryDiff.different.isEmpty() == false) { + fail( + "Expected no missing or different segments between primary and replica but diff was missing: " + + recoveryDiff.missing + + " Different: " + + recoveryDiff.different + + " Primary Replication Checkpoint : " + + primaryShard.getLatestReplicationCheckpoint() + + " Replica Replication Checkpoint: " + + replicaShard.getLatestReplicationCheckpoint() + ); + } + // calls to readCommit will fail if a valid commit point and all its segments are not in the store. + replicaShard.store().readLastCommittedSegmentsInfo(); + } + } + } + }, 1, TimeUnit.MINUTES); + } + + private int getDocCountFromShard(IndexShard shard) { + try (final Engine.Searcher searcher = shard.acquireSearcher("test")) { + return searcher.getDirectoryReader().numDocs(); + } + } + + private IndexShard getIndexShard(ClusterState state, ShardRouting routing, String indexName) { + return getIndexShard(state.nodes().get(routing.currentNodeId()).getName(), routing.shardId(), indexName); + } + + /** + * Fetch IndexShard by shardId, multiple shards per node allowed. + */ + protected IndexShard getIndexShard(String node, ShardId shardId, String indexName) { + final Index index = resolveIndex(indexName); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, node); + IndexService indexService = indicesService.indexServiceSafe(index); + final Optional id = indexService.shardIds().stream().filter(sid -> sid == shardId.id()).findFirst(); + return indexService.getShard(id.get()); + } + + /** + * Fetch IndexShard, assumes only a single shard per node. + */ + protected IndexShard getIndexShard(String node, String indexName) { + final Index index = resolveIndex(indexName); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, node); + IndexService indexService = indicesService.indexServiceSafe(index); + final Optional shardId = indexService.shardIds().stream().findFirst(); + return indexService.getShard(shardId.get()); + } + + protected boolean segmentReplicationWithRemoteEnabled() { + return IndexMetadata.INDEX_REMOTE_STORE_ENABLED_SETTING.get(indexSettings()).booleanValue(); + } + + protected Releasable blockReplication(List nodes, CountDownLatch latch) { + CountDownLatch pauseReplicationLatch = new CountDownLatch(nodes.size()); + for (String node : nodes) { + + MockTransportService mockTargetTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + node + )); + mockTargetTransportService.addSendBehavior((connection, requestId, action, request, options) -> { + String actionToWaitFor = SegmentReplicationSourceService.Actions.GET_SEGMENT_FILES; + if (segmentReplicationWithRemoteEnabled()) { + actionToWaitFor = SegmentReplicationSourceService.Actions.UPDATE_VISIBLE_CHECKPOINT; + } + if (action.equals(actionToWaitFor)) { + try { + latch.countDown(); + pauseReplicationLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + connection.sendRequest(requestId, action, request, options); + }); + } + return () -> { + while (pauseReplicationLatch.getCount() > 0) { + pauseReplicationLatch.countDown(); + } + }; + } + + protected void assertReplicaCheckpointUpdated(IndexShard primaryShard) throws Exception { + assertBusy(() -> { + Set groupStats = primaryShard.getReplicationStatsForTrackedReplicas(); + assertEquals(primaryShard.indexSettings().getNumberOfReplicas(), groupStats.size()); + for (SegmentReplicationShardStats shardStat : groupStats) { + assertEquals(0, shardStat.getCheckpointsBehindCount()); + } + }, 30, TimeUnit.SECONDS); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationClusterSettingIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationClusterSettingIT.java new file mode 100644 index 0000000000000..a82fd8d845709 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationClusterSettingIT.java @@ -0,0 +1,126 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.replication; + +import org.opensearch.action.admin.indices.settings.get.GetSettingsRequest; +import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.Index; +import org.opensearch.index.IndexModule; +import org.opensearch.indices.IndicesService; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; +import static org.opensearch.indices.IndicesService.CLUSTER_SETTING_REPLICATION_TYPE; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationClusterSettingIT extends OpenSearchIntegTestCase { + + protected static final String INDEX_NAME = "test-idx-1"; + private static final String SYSTEM_INDEX_NAME = ".test-system-index"; + protected static final int SHARD_COUNT = 1; + protected static final int REPLICA_COUNT = 1; + + @Override + public Settings indexSettings() { + return Settings.builder() + .put(super.indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, SHARD_COUNT) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, REPLICA_COUNT) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .build(); + } + + @Override + protected boolean addMockInternalEngine() { + return false; + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(CLUSTER_SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + } + + public void testIndexReplicationSettingOverridesSegRepClusterSetting() throws Exception { + Settings settings = Settings.builder().put(CLUSTER_SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT).build(); + final String ANOTHER_INDEX = "test-index"; + + // Starting two nodes with primary and replica shards respectively. + final String primaryNode = internalCluster().startNode(settings); + prepareCreate( + INDEX_NAME, + Settings.builder() + // we want to override cluster replication setting by passing a index replication setting + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT) + ).get(); + createIndex(ANOTHER_INDEX); + ensureYellowAndNoInitializingShards(INDEX_NAME, ANOTHER_INDEX); + final String replicaNode = internalCluster().startNode(settings); + + // Randomly close and open index. + if (randomBoolean()) { + logger.info("--> Closing the index "); + client().admin().indices().prepareClose(INDEX_NAME).get(); + + logger.info("--> Opening the index"); + client().admin().indices().prepareOpen(INDEX_NAME).get(); + } + ensureGreen(INDEX_NAME, ANOTHER_INDEX); + + final GetSettingsResponse response = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(INDEX_NAME, ANOTHER_INDEX).includeDefaults(true)) + .actionGet(); + assertEquals(response.getSetting(INDEX_NAME, SETTING_REPLICATION_TYPE), ReplicationType.DOCUMENT.toString()); + assertEquals(response.getSetting(ANOTHER_INDEX, SETTING_REPLICATION_TYPE), ReplicationType.SEGMENT.toString()); + + // Verify index setting isSegRepEnabled. + Index index = resolveIndex(INDEX_NAME); + Index anotherIndex = resolveIndex(ANOTHER_INDEX); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, primaryNode); + assertEquals(indicesService.indexService(index).getIndexSettings().isSegRepEnabled(), false); + assertEquals(indicesService.indexService(anotherIndex).getIndexSettings().isSegRepEnabled(), true); + } + + public void testIndexReplicationSettingOverridesDocRepClusterSetting() throws Exception { + Settings settings = Settings.builder().put(CLUSTER_SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT).build(); + final String ANOTHER_INDEX = "test-index"; + final String primaryNode = internalCluster().startNode(settings); + prepareCreate( + INDEX_NAME, + Settings.builder() + // we want to override cluster replication setting by passing a index replication setting + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + ).get(); + createIndex(ANOTHER_INDEX); + ensureYellowAndNoInitializingShards(INDEX_NAME, ANOTHER_INDEX); + final String replicaNode = internalCluster().startNode(settings); + ensureGreen(INDEX_NAME, ANOTHER_INDEX); + + final GetSettingsResponse response = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(INDEX_NAME, ANOTHER_INDEX).includeDefaults(true)) + .actionGet(); + assertEquals(response.getSetting(INDEX_NAME, SETTING_REPLICATION_TYPE), ReplicationType.SEGMENT.toString()); + assertEquals(response.getSetting(ANOTHER_INDEX, SETTING_REPLICATION_TYPE), ReplicationType.DOCUMENT.toString()); + + // Verify index setting isSegRepEnabled. + Index index = resolveIndex(INDEX_NAME); + Index anotherIndex = resolveIndex(ANOTHER_INDEX); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, primaryNode); + assertEquals(indicesService.indexService(index).getIndexSettings().isSegRepEnabled(), true); + assertEquals(indicesService.indexService(anotherIndex).getIndexSettings().isSegRepEnabled(), false); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java new file mode 100644 index 0000000000000..33bc5a8f3afe6 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationIT.java @@ -0,0 +1,1780 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.replication; + +import com.carrotsearch.randomizedtesting.RandomizedTest; + +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.SortedDocValuesField; +import org.apache.lucene.document.StringField; +import org.apache.lucene.document.TextField; +import org.apache.lucene.index.Fields; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.SegmentInfos; +import org.apache.lucene.index.StandardDirectoryReader; +import org.apache.lucene.tests.util.TestUtil; +import org.apache.lucene.util.BytesRef; +import org.opensearch.action.admin.indices.alias.Alias; +import org.opensearch.action.admin.indices.flush.FlushRequest; +import org.opensearch.action.admin.indices.stats.IndicesStatsRequest; +import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import org.opensearch.action.get.GetResponse; +import org.opensearch.action.get.MultiGetRequest; +import org.opensearch.action.get.MultiGetResponse; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.search.CreatePitAction; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitAction; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.PitTestsUtil; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.search.SearchType; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.action.termvectors.TermVectorsRequestBuilder; +import org.opensearch.action.termvectors.TermVectorsResponse; +import org.opensearch.action.update.UpdateResponse; +import org.opensearch.client.Requests; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.health.ClusterHealthStatus; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.Preference; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.ShardRoutingState; +import org.opensearch.cluster.routing.allocation.command.CancelAllocationCommand; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.collect.Tuple; +import org.opensearch.common.concurrent.GatedCloseable; +import org.opensearch.common.lease.Releasable; +import org.opensearch.common.lucene.index.OpenSearchDirectoryReader; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.index.IndexModule; +import org.opensearch.index.SegmentReplicationPerGroupStats; +import org.opensearch.index.SegmentReplicationPressureService; +import org.opensearch.index.SegmentReplicationShardStats; +import org.opensearch.index.codec.CodecService; +import org.opensearch.index.engine.Engine; +import org.opensearch.index.engine.EngineConfig; +import org.opensearch.index.engine.NRTReplicationReaderManager; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.indices.recovery.FileChunkRequest; +import org.opensearch.indices.replication.checkpoint.ReplicationCheckpoint; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.node.NodeClosedException; +import org.opensearch.search.SearchService; +import org.opensearch.search.builder.PointInTimeBuilder; +import org.opensearch.search.internal.PitReaderContext; +import org.opensearch.search.sort.SortOrder; +import org.opensearch.test.BackgroundIndexer; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.transport.TransportService; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static java.util.Arrays.asList; +import static org.opensearch.action.search.PitTestsUtil.assertSegments; +import static org.opensearch.action.search.SearchContextId.decode; +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.index.query.QueryBuilders.boolQuery; +import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.index.query.QueryBuilders.matchQuery; +import static org.opensearch.index.query.QueryBuilders.rangeQuery; +import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.indices.replication.SegmentReplicationTarget.REPLICATION_PREFIX; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAllSuccessful; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchHits; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationIT extends SegmentReplicationBaseIT { + + @Before + private void setup() { + internalCluster().startClusterManagerOnlyNode(); + } + + private static String indexOrAlias() { + return randomBoolean() ? INDEX_NAME : "alias"; + } + + public void testPrimaryStopped_ReplicaPromoted() throws Exception { + final String primary = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + client().prepareIndex(INDEX_NAME).setId("1").setSource("foo", "bar").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + refresh(INDEX_NAME); + + waitForSearchableDocs(1, primary, replica); + + // index another doc but don't refresh, we will ensure this is searchable once replica is promoted. + client().prepareIndex(INDEX_NAME).setId("2").setSource("bar", "baz").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + + // stop the primary node - we only have one shard on here. + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primary)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + + final ShardRouting replicaShardRouting = getShardRoutingForNodeName(replica); + assertNotNull(replicaShardRouting); + assertTrue(replicaShardRouting + " should be promoted as a primary", replicaShardRouting.primary()); + final SearchResponse response = client(replica).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(); + // new primary should have at least the doc count from the first set of segments. + assertTrue(response.getHits().getTotalHits().value >= 1); + + // assert we can index into the new primary. + client().prepareIndex(INDEX_NAME).setId("3").setSource("bar", "baz").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + assertHitCount(client(replica).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), 3); + + // start another node, index another doc and replicate. + String nodeC = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + client().prepareIndex(INDEX_NAME).setId("4").setSource("baz", "baz").get(); + refresh(INDEX_NAME); + waitForSearchableDocs(4, nodeC, replica); + verifyStoreContent(); + } + + public void testRestartPrimary() throws Exception { + final String primary = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + assertEquals(getNodeContainingPrimaryShard().getName(), primary); + + final int initialDocCount = 1; + client().prepareIndex(INDEX_NAME).setId("1").setSource("foo", "bar").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + refresh(INDEX_NAME); + + waitForSearchableDocs(initialDocCount, replica, primary); + + internalCluster().restartNode(primary); + ensureGreen(INDEX_NAME); + + assertEquals(getNodeContainingPrimaryShard().getName(), replica); + + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(initialDocCount, replica, primary); + verifyStoreContent(); + } + + public void testCancelPrimaryAllocation() throws Exception { + // this test cancels allocation on the primary - promoting the new replica and recreating the former primary as a replica. + final String primary = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + final int initialDocCount = 1; + + client().prepareIndex(INDEX_NAME).setId("1").setSource("foo", "bar").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + refresh(INDEX_NAME); + + waitForSearchableDocs(initialDocCount, replica, primary); + + final IndexShard indexShard = getIndexShard(primary, INDEX_NAME); + client().admin() + .cluster() + .prepareReroute() + .add(new CancelAllocationCommand(INDEX_NAME, indexShard.shardId().id(), primary, true)) + .execute() + .actionGet(); + ensureGreen(INDEX_NAME); + + assertEquals(getNodeContainingPrimaryShard().getName(), replica); + + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(initialDocCount, replica, primary); + verifyStoreContent(); + } + + public void testReplicationAfterPrimaryRefreshAndFlush() throws Exception { + final String nodeA = internalCluster().startDataOnlyNode(); + final String nodeB = internalCluster().startDataOnlyNode(); + final Settings settings = Settings.builder() + .put(indexSettings()) + .put(EngineConfig.INDEX_CODEC_SETTING.getKey(), randomFrom(new ArrayList<>(CODECS) { + { + add(CodecService.LUCENE_DEFAULT_CODEC); + } + })) + .build(); + createIndex(INDEX_NAME, settings); + ensureGreen(INDEX_NAME); + + final int initialDocCount = scaledRandomIntBetween(0, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + refresh(INDEX_NAME); + waitForSearchableDocs(initialDocCount, nodeA, nodeB); + + final int additionalDocCount = scaledRandomIntBetween(0, 200); + final int expectedHitCount = initialDocCount + additionalDocCount; + indexer.start(additionalDocCount); + waitForDocs(expectedHitCount, indexer); + + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(expectedHitCount, nodeA, nodeB); + + ensureGreen(INDEX_NAME); + verifyStoreContent(); + } + } + + public void testIndexReopenClose() throws Exception { + final String primary = internalCluster().startDataOnlyNode(); + final String replica = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureGreen(INDEX_NAME); + + final int initialDocCount = scaledRandomIntBetween(100, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + flush(INDEX_NAME); + waitForSearchableDocs(initialDocCount, primary, replica); + } + logger.info("--> Closing the index "); + client().admin().indices().prepareClose(INDEX_NAME).get(); + + logger.info("--> Opening the index"); + client().admin().indices().prepareOpen(INDEX_NAME).get(); + + ensureGreen(INDEX_NAME); + waitForSearchableDocs(initialDocCount, primary, replica); + verifyStoreContent(); + } + + public void testScrollWithConcurrentIndexAndSearch() throws Exception { + final String primary = internalCluster().startDataOnlyNode(); + final String replica = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureGreen(INDEX_NAME); + final List> pendingIndexResponses = new ArrayList<>(); + final List> pendingSearchResponse = new ArrayList<>(); + final int searchCount = randomIntBetween(10, 20); + final WriteRequest.RefreshPolicy refreshPolicy = randomFrom(WriteRequest.RefreshPolicy.values()); + + for (int i = 0; i < searchCount; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(refreshPolicy) + .setSource("field", "value" + i) + .execute() + ); + flush(INDEX_NAME); + forceMerge(); + } + + final SearchResponse searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .setIndices(INDEX_NAME) + .setRequestCache(false) + .setScroll(TimeValue.timeValueDays(1)) + .setSize(10) + .get(); + + for (int i = searchCount; i < searchCount * 2; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(refreshPolicy) + .setSource("field", "value" + i) + .execute() + ); + } + flush(INDEX_NAME); + forceMerge(); + client().prepareClearScroll().addScrollId(searchResponse.getScrollId()).get(); + + assertBusy(() -> { + client().admin().indices().prepareRefresh().execute().actionGet(); + assertTrue(pendingIndexResponses.stream().allMatch(ActionFuture::isDone)); + assertTrue(pendingSearchResponse.stream().allMatch(ActionFuture::isDone)); + }, 1, TimeUnit.MINUTES); + verifyStoreContent(); + waitForSearchableDocs(INDEX_NAME, 2 * searchCount, List.of(primary, replica)); + } + + public void testMultipleShards() throws Exception { + Settings indexSettings = Settings.builder() + .put(super.indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + final String nodeA = internalCluster().startDataOnlyNode(); + final String nodeB = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME, indexSettings); + ensureGreen(INDEX_NAME); + + final int initialDocCount = scaledRandomIntBetween(1, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + refresh(INDEX_NAME); + waitForSearchableDocs(initialDocCount, nodeA, nodeB); + + final int additionalDocCount = scaledRandomIntBetween(0, 200); + final int expectedHitCount = initialDocCount + additionalDocCount; + indexer.start(additionalDocCount); + waitForDocs(expectedHitCount, indexer); + + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(expectedHitCount, nodeA, nodeB); + + ensureGreen(INDEX_NAME); + verifyStoreContent(); + } + } + + public void testReplicationAfterForceMerge() throws Exception { + final String nodeA = internalCluster().startDataOnlyNode(); + final String nodeB = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureGreen(INDEX_NAME); + + final int initialDocCount = scaledRandomIntBetween(0, 200); + final int additionalDocCount = scaledRandomIntBetween(0, 200); + final int expectedHitCount = initialDocCount + additionalDocCount; + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + + flush(INDEX_NAME); + waitForSearchableDocs(initialDocCount, nodeA, nodeB); + + // Index a second set of docs so we can merge into one segment. + indexer.start(additionalDocCount); + waitForDocs(expectedHitCount, indexer); + waitForSearchableDocs(expectedHitCount, nodeA, nodeB); + + // Force a merge here so that the in memory SegmentInfos does not reference old segments on disk. + client().admin().indices().prepareForceMerge(INDEX_NAME).setMaxNumSegments(1).setFlush(false).get(); + refresh(INDEX_NAME); + verifyStoreContent(); + } + } + + /** + * This test verifies that segment replication does not fail for closed indices + */ + public void testClosedIndices() { + List nodes = new ArrayList<>(); + // start 1st node so that it contains the primary + nodes.add(internalCluster().startDataOnlyNode()); + createIndex(INDEX_NAME, super.indexSettings()); + ensureYellowAndNoInitializingShards(INDEX_NAME); + // start 2nd node so that it contains the replica + nodes.add(internalCluster().startDataOnlyNode()); + ensureGreen(INDEX_NAME); + + logger.info("--> Close index"); + assertAcked(client().admin().indices().prepareClose(INDEX_NAME)); + + logger.info("--> waiting for allocation to have shards assigned"); + waitForRelocation(ClusterHealthStatus.GREEN); + } + + /** + * This test validates the primary node drop does not result in shard failure on replica. + * @throws Exception when issue is encountered + */ + public void testNodeDropWithOngoingReplication() throws Exception { + final String primaryNode = internalCluster().startDataOnlyNode(); + createIndex( + INDEX_NAME, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) + .put("index.refresh_interval", -1) + .build() + ); + ensureYellow(INDEX_NAME); + final String replicaNode = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + ClusterState state = client().admin().cluster().prepareState().execute().actionGet().getState(); + // Get replica allocation id + final String replicaAllocationId = state.routingTable() + .index(INDEX_NAME) + .shardsWithState(ShardRoutingState.STARTED) + .stream() + .filter(routing -> routing.primary() == false) + .findFirst() + .get() + .allocationId() + .getId(); + DiscoveryNode primaryDiscovery = state.nodes().resolveNode(primaryNode); + + CountDownLatch blockFileCopy = new CountDownLatch(1); + MockTransportService primaryTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + primaryNode + )); + primaryTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, replicaNode), + (connection, requestId, action, request, options) -> { + if (action.equals(SegmentReplicationTargetService.Actions.FILE_CHUNK)) { + FileChunkRequest req = (FileChunkRequest) request; + logger.debug("file chunk [{}] lastChunk: {}", req, req.lastChunk()); + if (req.name().endsWith("cfs") && req.lastChunk()) { + try { + blockFileCopy.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + throw new NodeClosedException(primaryDiscovery); + } + } + connection.sendRequest(requestId, action, request, options); + } + ); + final int docCount = scaledRandomIntBetween(10, 200); + for (int i = 0; i < docCount; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().get(); + } + // Refresh, this should trigger round of segment replication + refresh(INDEX_NAME); + blockFileCopy.countDown(); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNode)); + ensureYellow(INDEX_NAME); + assertBusy(() -> { assertDocCounts(docCount, replicaNode); }); + state = client().admin().cluster().prepareState().execute().actionGet().getState(); + // replica now promoted as primary should have same allocation id + final String currentAllocationID = state.routingTable() + .index(INDEX_NAME) + .shardsWithState(ShardRoutingState.STARTED) + .stream() + .filter(routing -> routing.primary()) + .findFirst() + .get() + .allocationId() + .getId(); + assertEquals(currentAllocationID, replicaAllocationId); + } + + public void testCancellation() throws Exception { + final String primaryNode = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME, Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1).build()); + ensureYellow(INDEX_NAME); + + final String replicaNode = internalCluster().startDataOnlyNode(); + + final SegmentReplicationSourceService segmentReplicationSourceService = internalCluster().getInstance( + SegmentReplicationSourceService.class, + primaryNode + ); + final IndexShard primaryShard = getIndexShard(primaryNode, INDEX_NAME); + + CountDownLatch latch = new CountDownLatch(1); + + MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + primaryNode + )); + mockTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, replicaNode), + (connection, requestId, action, request, options) -> { + if (action.equals(SegmentReplicationTargetService.Actions.FILE_CHUNK)) { + FileChunkRequest req = (FileChunkRequest) request; + logger.debug("file chunk [{}] lastChunk: {}", req, req.lastChunk()); + if (req.name().endsWith("cfs") && req.lastChunk()) { + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + connection.sendRequest(requestId, action, request, options); + } + ); + + final int docCount = scaledRandomIntBetween(0, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(docCount); + waitForDocs(docCount, indexer); + + flush(INDEX_NAME); + } + segmentReplicationSourceService.beforeIndexShardClosed(primaryShard.shardId(), primaryShard, indexSettings()); + latch.countDown(); + assertDocCounts(docCount, primaryNode); + } + + public void testStartReplicaAfterPrimaryIndexesDocs() throws Exception { + final String primaryNode = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME, Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0).build()); + ensureGreen(INDEX_NAME); + + // Index a doc to create the first set of segments. _s1.si + client().prepareIndex(INDEX_NAME).setId("1").setSource("foo", "bar").get(); + // Flush segments to disk and create a new commit point (Primary: segments_3, _s1.si) + flushAndRefresh(INDEX_NAME); + assertHitCount(client(primaryNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), 1); + + // Index to create another segment + client().prepareIndex(INDEX_NAME).setId("2").setSource("foo", "bar").get(); + + // Force a merge here so that the in memory SegmentInfos does not reference old segments on disk. + client().admin().indices().prepareForceMerge(INDEX_NAME).setMaxNumSegments(1).setFlush(false).get(); + refresh(INDEX_NAME); + + assertAcked( + client().admin() + .indices() + .prepareUpdateSettings(INDEX_NAME) + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)) + ); + final String replicaNode = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + assertHitCount(client(primaryNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), 2); + assertHitCount(client(replicaNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), 2); + + client().prepareIndex(INDEX_NAME).setId("3").setSource("foo", "bar").get(); + refresh(INDEX_NAME); + waitForSearchableDocs(3, primaryNode, replicaNode); + assertHitCount(client(primaryNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), 3); + assertHitCount(client(replicaNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), 3); + verifyStoreContent(); + } + + public void testDeleteOperations() throws Exception { + final String nodeA = internalCluster().startDataOnlyNode(); + final String nodeB = internalCluster().startDataOnlyNode(); + + createIndex(INDEX_NAME); + ensureGreen(INDEX_NAME); + final int initialDocCount = scaledRandomIntBetween(1, 20); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + refresh(INDEX_NAME); + waitForSearchableDocs(initialDocCount, nodeA, nodeB); + + final int additionalDocCount = scaledRandomIntBetween(0, 20); + final int expectedHitCount = initialDocCount + additionalDocCount; + indexer.start(additionalDocCount); + waitForDocs(expectedHitCount, indexer); + waitForSearchableDocs(expectedHitCount, nodeA, nodeB); + + ensureGreen(INDEX_NAME); + + Set ids = indexer.getIds(); + assertFalse(ids.isEmpty()); + String id = ids.toArray()[0].toString(); + client(nodeA).prepareDelete(INDEX_NAME, id).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + + refresh(INDEX_NAME); + waitForSearchableDocs(expectedHitCount - 1, nodeA, nodeB); + verifyStoreContent(); + } + } + + /** + * This tests that the max seqNo we send to replicas is accurate and that after failover + * the new primary starts indexing from the correct maxSeqNo and replays the correct count of docs + * from xlog. + */ + public void testReplicationPostDeleteAndForceMerge() throws Exception { + final String primary = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + final int initialDocCount = scaledRandomIntBetween(10, 200); + for (int i = 0; i < initialDocCount; i++) { + client().prepareIndex(INDEX_NAME).setId(String.valueOf(i)).setSource("foo", "bar").get(); + } + refresh(INDEX_NAME); + waitForSearchableDocs(initialDocCount, primary, replica); + + final int deletedDocCount = randomIntBetween(10, initialDocCount); + for (int i = 0; i < deletedDocCount; i++) { + client(primary).prepareDelete(INDEX_NAME, String.valueOf(i)).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + } + client().admin().indices().prepareForceMerge(INDEX_NAME).setMaxNumSegments(1).setFlush(false).get(); + + // randomly flush here after the force merge to wipe any old segments. + if (randomBoolean()) { + flush(INDEX_NAME); + } + + final IndexShard primaryShard = getIndexShard(primary, INDEX_NAME); + final IndexShard replicaShard = getIndexShard(replica, INDEX_NAME); + assertBusy( + () -> assertEquals( + primaryShard.getLatestReplicationCheckpoint().getSegmentInfosVersion(), + replicaShard.getLatestReplicationCheckpoint().getSegmentInfosVersion() + ) + ); + + // add some docs to the xlog and drop primary. + final int additionalDocs = randomIntBetween(1, 50); + for (int i = initialDocCount; i < initialDocCount + additionalDocs; i++) { + client().prepareIndex(INDEX_NAME).setId(String.valueOf(i)).setSource("foo", "bar").get(); + } + // Drop the primary and wait until replica is promoted. + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primary)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + + final ShardRouting replicaShardRouting = getShardRoutingForNodeName(replica); + assertNotNull(replicaShardRouting); + assertTrue(replicaShardRouting + " should be promoted as a primary", replicaShardRouting.primary()); + refresh(INDEX_NAME); + final long expectedHitCount = initialDocCount + additionalDocs - deletedDocCount; + assertHitCount(client(replica).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), expectedHitCount); + + int expectedMaxSeqNo = initialDocCount + deletedDocCount + additionalDocs - 1; + assertEquals(expectedMaxSeqNo, replicaShard.seqNoStats().getMaxSeqNo()); + + // index another doc. + client().prepareIndex(INDEX_NAME).setId(String.valueOf(expectedMaxSeqNo + 1)).setSource("another", "doc").get(); + refresh(INDEX_NAME); + assertHitCount(client(replica).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), expectedHitCount + 1); + } + + public void testUpdateOperations() throws Exception { + final String primary = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureYellow(INDEX_NAME); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + final int initialDocCount = scaledRandomIntBetween(0, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + refresh(INDEX_NAME); + waitForSearchableDocs(initialDocCount, asList(primary, replica)); + + final int additionalDocCount = scaledRandomIntBetween(0, 200); + final int expectedHitCount = initialDocCount + additionalDocCount; + indexer.start(additionalDocCount); + waitForDocs(expectedHitCount, indexer); + waitForSearchableDocs(expectedHitCount, asList(primary, replica)); + + Set ids = indexer.getIds(); + String id = ids.toArray()[0].toString(); + UpdateResponse updateResponse = client(primary).prepareUpdate(INDEX_NAME, id) + .setDoc(Requests.INDEX_CONTENT_TYPE, "foo", "baz") + .setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL) + .get(); + assertFalse("request shouldn't have forced a refresh", updateResponse.forcedRefresh()); + assertEquals(2, updateResponse.getVersion()); + + refresh(INDEX_NAME); + + verifyStoreContent(); + assertSearchHits(client(primary).prepareSearch(INDEX_NAME).setQuery(matchQuery("foo", "baz")).get(), id); + assertSearchHits(client(replica).prepareSearch(INDEX_NAME).setQuery(matchQuery("foo", "baz")).get(), id); + } + } + + public void testDropPrimaryDuringReplication() throws Exception { + final int replica_count = 6; + final Settings settings = Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, replica_count) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + final String primaryNode = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME, settings); + final List dataNodes = internalCluster().startDataOnlyNodes(6); + ensureGreen(INDEX_NAME); + + int initialDocCount = scaledRandomIntBetween(100, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + refresh(INDEX_NAME); + // don't wait for replication to complete, stop the primary immediately. + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNode)); + ensureYellow(INDEX_NAME); + + // start another replica. + dataNodes.add(internalCluster().startDataOnlyNode()); + ensureGreen(INDEX_NAME); + waitForSearchableDocs(initialDocCount, dataNodes); + + // index another doc and refresh - without this the new replica won't catch up. + String docId = String.valueOf(initialDocCount + 1); + client().prepareIndex(INDEX_NAME).setId(docId).setSource("foo", "bar").get(); + + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(initialDocCount + 1, dataNodes); + verifyStoreContent(); + } + } + + public void testReplicaHasDiffFilesThanPrimary() throws Exception { + final String primaryNode = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME, Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1).build()); + ensureYellow(INDEX_NAME); + final String replicaNode = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + final IndexShard replicaShard = getIndexShard(replicaNode, INDEX_NAME); + IndexWriterConfig iwc = newIndexWriterConfig().setOpenMode(IndexWriterConfig.OpenMode.APPEND); + + // create a doc to index + int numDocs = 2 + random().nextInt(100); + + List docs = new ArrayList<>(); + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + doc.add(new StringField("id", "" + i, random().nextBoolean() ? Field.Store.YES : Field.Store.NO)); + doc.add( + new TextField( + "body", + TestUtil.randomRealisticUnicodeString(random()), + random().nextBoolean() ? Field.Store.YES : Field.Store.NO + ) + ); + doc.add(new SortedDocValuesField("dv", new BytesRef(TestUtil.randomRealisticUnicodeString(random())))); + docs.add(doc); + } + // create some segments on the replica before copy. + try (IndexWriter writer = new IndexWriter(replicaShard.store().directory(), iwc)) { + for (Document d : docs) { + writer.addDocument(d); + } + writer.flush(); + writer.commit(); + } + + final SegmentInfos segmentInfos = SegmentInfos.readLatestCommit(replicaShard.store().directory()); + replicaShard.finalizeReplication(segmentInfos); + ensureYellow(INDEX_NAME); + + final int docCount = scaledRandomIntBetween(10, 200); + for (int i = 0; i < docCount; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().get(); + // Refresh, this should trigger round of segment replication + refresh(INDEX_NAME); + } + ensureGreen(INDEX_NAME); + waitForSearchableDocs(docCount, primaryNode, replicaNode); + verifyStoreContent(); + final IndexShard replicaAfterFailure = getIndexShard(replicaNode, INDEX_NAME); + assertNotEquals(replicaAfterFailure.routingEntry().allocationId().getId(), replicaShard.routingEntry().allocationId().getId()); + } + + public void testPressureServiceStats() throws Exception { + final String primaryNode = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureYellow(INDEX_NAME); + final String replicaNode = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + int initialDocCount = scaledRandomIntBetween(100, 200); + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + INDEX_NAME, + "_doc", + client(), + -1, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + random() + ) + ) { + indexer.start(initialDocCount); + waitForDocs(initialDocCount, indexer); + refresh(INDEX_NAME); + + // get shard references. + final IndexShard primaryShard = getIndexShard(primaryNode, INDEX_NAME); + final IndexShard replicaShard = getIndexShard(replicaNode, INDEX_NAME); + logger.info("Replica aid {}", replicaShard.routingEntry().allocationId()); + logger.info("former primary aid {}", primaryShard.routingEntry().allocationId()); + + // fetch pressure stats from the Primary's Node. + SegmentReplicationPressureService pressureService = internalCluster().getInstance( + SegmentReplicationPressureService.class, + primaryNode + ); + + // Fetch pressure stats from the Replica's Node we will assert replica node returns nothing until it is promoted. + SegmentReplicationPressureService replicaNode_service = internalCluster().getInstance( + SegmentReplicationPressureService.class, + replicaNode + ); + + final Map shardStats = pressureService.nodeStats().getShardStats(); + assertEquals("We should have stats returned for the replication group", 1, shardStats.size()); + + SegmentReplicationPerGroupStats groupStats = shardStats.get(primaryShard.shardId()); + Set replicaStats = groupStats.getReplicaStats(); + assertAllocationIdsInReplicaShardStats(Set.of(replicaShard.routingEntry().allocationId().getId()), replicaStats); + + assertTrue(replicaNode_service.nodeStats().getShardStats().isEmpty()); + + // drop the primary, this won't hand off pressure stats between old/new primary. + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNode)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + + assertTrue("replica should be promoted as a primary", replicaShard.routingEntry().primary()); + assertEquals( + "We should have stats returned for the replication group", + 1, + replicaNode_service.nodeStats().getShardStats().size() + ); + // after the primary is dropped and replica is promoted we won't have a replica assigned yet, so stats per replica should return + // empty. + replicaStats = replicaNode_service.nodeStats().getShardStats().get(primaryShard.shardId()).getReplicaStats(); + assertTrue(replicaStats.isEmpty()); + + // start another replica. + String replicaNode_2 = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + final IndexShard secondReplicaShard = getIndexShard(replicaNode_2, INDEX_NAME); + final String second_replica_aid = secondReplicaShard.routingEntry().allocationId().getId(); + waitForSearchableDocs(initialDocCount, replicaNode_2); + + assertEquals( + "We should have stats returned for the replication group", + 1, + replicaNode_service.nodeStats().getShardStats().size() + ); + replicaStats = replicaNode_service.nodeStats().getShardStats().get(replicaShard.shardId()).getReplicaStats(); + assertAllocationIdsInReplicaShardStats(Set.of(second_replica_aid), replicaStats); + final SegmentReplicationShardStats replica_entry = replicaStats.stream().findFirst().get(); + assertEquals(replica_entry.getCheckpointsBehindCount(), 0); + + // test a checkpoint without any new segments + flush(INDEX_NAME); + assertBusy(() -> { + assertEquals(1, replicaNode_service.nodeStats().getShardStats().size()); + final Set shardStatsSet = replicaNode_service.nodeStats() + .getShardStats() + .get(replicaShard.shardId()) + .getReplicaStats(); + assertAllocationIdsInReplicaShardStats(Set.of(second_replica_aid), shardStatsSet); + final SegmentReplicationShardStats stats = shardStatsSet.stream().findFirst().get(); + assertEquals(0, stats.getCheckpointsBehindCount()); + }); + } + } + + private void assertAllocationIdsInReplicaShardStats(Set expected, Set replicaStats) { + assertEquals(expected, replicaStats.stream().map(SegmentReplicationShardStats::getAllocationId).collect(Collectors.toSet())); + } + + /** + * Tests a scroll query on the replica + * @throws Exception when issue is encountered + */ + public void testScrollCreatedOnReplica() throws Exception { + // create the cluster with one primary node containing primary shard and replica node containing replica shard + final String primary = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + // index 100 docs + for (int i = 0; i < 100; i++) { + client().prepareIndex(INDEX_NAME) + .setId(String.valueOf(i)) + .setSource(jsonBuilder().startObject().field("field", i).endObject()) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .get(); + refresh(INDEX_NAME); + } + assertBusy( + () -> assertEquals( + getIndexShard(primary, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion(), + getIndexShard(replica, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion() + ) + ); + final IndexShard replicaShard = getIndexShard(replica, INDEX_NAME); + final Tuple, ReplicationCheckpoint> tuple = replicaShard.getLatestSegmentInfosAndCheckpoint(); + final Collection snapshottedSegments; + try (final GatedCloseable closeable = tuple.v1()) { + snapshottedSegments = closeable.get().files(false); + } + // opens a scrolled query before a flush is called. + // this is for testing scroll segment consistency between refresh and flush + SearchResponse searchResponse = client(replica).prepareSearch() + .setQuery(matchAllQuery()) + .setIndices(INDEX_NAME) + .setRequestCache(false) + .setPreference("_only_local") + .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + .addSort("field", SortOrder.ASC) + .setSize(10) + .setScroll(TimeValue.timeValueDays(1)) + .get(); + + // force call flush + flush(INDEX_NAME); + + for (int i = 3; i < 50; i++) { + client().prepareDelete(INDEX_NAME, String.valueOf(i)).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + refresh(INDEX_NAME); + if (randomBoolean()) { + client().admin().indices().prepareForceMerge(INDEX_NAME).setMaxNumSegments(1).setFlush(true).get(); + flush(INDEX_NAME); + } + } + assertBusy(() -> { + assertEquals( + getIndexShard(primary, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion(), + getIndexShard(replica, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion() + ); + }); + + client().admin().indices().prepareForceMerge(INDEX_NAME).setMaxNumSegments(1).setFlush(true).get(); + assertBusy(() -> { + assertEquals( + getIndexShard(primary, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion(), + getIndexShard(replica, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion() + ); + }); + // Test stats + logger.info("--> Collect all scroll query hits"); + long scrollHits = 0; + do { + scrollHits += searchResponse.getHits().getHits().length; + searchResponse = client(replica).prepareSearchScroll(searchResponse.getScrollId()).setScroll(TimeValue.timeValueDays(1)).get(); + assertAllSuccessful(searchResponse); + } while (searchResponse.getHits().getHits().length > 0); + + List currentFiles = List.of(replicaShard.store().directory().listAll()); + assertTrue("Files should be preserved", currentFiles.containsAll(snapshottedSegments)); + + client(replica).prepareClearScroll().addScrollId(searchResponse.getScrollId()).get(); + + currentFiles = List.of(replicaShard.store().directory().listAll()); + assertFalse("Files should be cleaned up post scroll clear request", currentFiles.containsAll(snapshottedSegments)); + assertEquals(100, scrollHits); + } + + /** + * Tests that when scroll query is cleared, it does not delete the temporary replication files, which are part of + * ongoing round of segment replication + * + * @throws Exception when issue is encountered + */ + public void testScrollWithOngoingSegmentReplication() throws Exception { + // this test stubs transport calls specific to node-node replication. + assumeFalse( + "Skipping the test as its not compatible with segment replication with remote store.", + segmentReplicationWithRemoteEnabled() + ); + + // create the cluster with one primary node containing primary shard and replica node containing replica shard + final String primary = internalCluster().startDataOnlyNode(); + prepareCreate( + INDEX_NAME, + Settings.builder() + // we want to control refreshes + .put("index.refresh_interval", -1) + ).get(); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + final int initialDocCount = 10; + final int finalDocCount = 20; + for (int i = 0; i < initialDocCount; i++) { + client().prepareIndex(INDEX_NAME) + .setId(String.valueOf(i)) + .setSource(jsonBuilder().startObject().field("field", i).endObject()) + .get(); + } + // catch up replica with primary + refresh(INDEX_NAME); + assertBusy( + () -> assertEquals( + getIndexShard(primary, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion(), + getIndexShard(replica, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion() + ) + ); + logger.info("--> Create scroll query"); + // opens a scrolled query before a flush is called. + SearchResponse searchResponse = client(replica).prepareSearch() + .setQuery(matchAllQuery()) + .setIndices(INDEX_NAME) + .setRequestCache(false) + .setPreference("_only_local") + .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + .addSort("field", SortOrder.ASC) + .setSize(10) + .setScroll(TimeValue.timeValueDays(1)) + .get(); + + // force call flush + flush(INDEX_NAME); + + // Index more documents + for (int i = initialDocCount; i < finalDocCount; i++) { + client().prepareIndex(INDEX_NAME) + .setId(String.valueOf(i)) + .setSource(jsonBuilder().startObject().field("field", i).endObject()) + .get(); + } + // Block file copy operation to ensure replica has few temporary replication files + CountDownLatch blockFileCopy = new CountDownLatch(1); + CountDownLatch waitForFileCopy = new CountDownLatch(1); + MockTransportService primaryTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + primary + )); + primaryTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, replica), + (connection, requestId, action, request, options) -> { + if (action.equals(SegmentReplicationTargetService.Actions.FILE_CHUNK)) { + FileChunkRequest req = (FileChunkRequest) request; + logger.debug("file chunk [{}] lastChunk: {}", req, req.lastChunk()); + if (req.name().endsWith("cfs") && req.lastChunk()) { + try { + waitForFileCopy.countDown(); + logger.info("--> Waiting for file copy"); + blockFileCopy.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + connection.sendRequest(requestId, action, request, options); + } + ); + + // perform refresh to start round of segment replication + refresh(INDEX_NAME); + + // wait for segrep to start and copy temporary files + waitForFileCopy.await(); + + final IndexShard replicaShard = getIndexShard(replica, INDEX_NAME); + // Wait until replica has written a tmp file to disk. + List temporaryFiles = new ArrayList<>(); + assertBusy(() -> { + // verify replica contains temporary files + temporaryFiles.addAll( + Arrays.stream(replicaShard.store().directory().listAll()) + .filter(fileName -> fileName.startsWith(REPLICATION_PREFIX)) + .collect(Collectors.toList()) + ); + logger.info("--> temporaryFiles {}", temporaryFiles); + assertTrue(temporaryFiles.size() > 0); + }); + + // Clear scroll query, this should clean up files on replica + client(replica).prepareClearScroll().addScrollId(searchResponse.getScrollId()).get(); + + // verify temporary files still exist + List temporaryFilesPostClear = Arrays.stream(replicaShard.store().directory().listAll()) + .filter(fileName -> fileName.startsWith(REPLICATION_PREFIX)) + .collect(Collectors.toList()); + logger.info("--> temporaryFilesPostClear {}", temporaryFilesPostClear); + + // Unblock segment replication + blockFileCopy.countDown(); + + assertTrue(temporaryFilesPostClear.containsAll(temporaryFiles)); + + // wait for replica to catch up and verify doc count + assertBusy(() -> { + assertEquals( + getIndexShard(primary, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion(), + getIndexShard(replica, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion() + ); + }); + verifyStoreContent(); + waitForSearchableDocs(finalDocCount, primary, replica); + } + + public void testPitCreatedOnReplica() throws Exception { + final String primary = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + client().prepareIndex(INDEX_NAME) + .setId("1") + .setSource("foo", randomInt()) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .get(); + refresh(INDEX_NAME); + + client().prepareIndex(INDEX_NAME) + .setId("2") + .setSource("foo", randomInt()) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .get(); + for (int i = 3; i < 100; i++) { + client().prepareIndex(INDEX_NAME) + .setId(String.valueOf(i)) + .setSource("foo", randomInt()) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .get(); + refresh(INDEX_NAME); + } + // wait until replication finishes, then make the pit request. + assertBusy( + () -> assertEquals( + getIndexShard(primary, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion(), + getIndexShard(replica, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion() + ) + ); + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), false); + request.setPreference("_only_local"); + request.setIndices(new String[] { INDEX_NAME }); + ActionFuture execute = client(replica).execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + SearchResponse searchResponse = client(replica).prepareSearch(INDEX_NAME) + .setSize(10) + .setPreference("_only_local") + .setRequestCache(false) + .addSort("foo", SortOrder.ASC) + .searchAfter(new Object[] { 30 }) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId()).setKeepAlive(TimeValue.timeValueDays(1))) + .get(); + assertEquals(1, searchResponse.getSuccessfulShards()); + assertEquals(1, searchResponse.getTotalShards()); + FlushRequest flushRequest = Requests.flushRequest(INDEX_NAME); + client().admin().indices().flush(flushRequest).get(); + final IndexShard replicaShard = getIndexShard(replica, INDEX_NAME); + + // fetch the segments snapshotted when the reader context was created. + Collection snapshottedSegments; + SearchService searchService = internalCluster().getInstance(SearchService.class, replica); + NamedWriteableRegistry registry = internalCluster().getInstance(NamedWriteableRegistry.class, replica); + final PitReaderContext pitReaderContext = searchService.getPitReaderContext( + decode(registry, pitResponse.getId()).shards().get(replicaShard.routingEntry().shardId()).getSearchContextId() + ); + try (final Engine.Searcher searcher = pitReaderContext.acquireSearcher("test")) { + final StandardDirectoryReader standardDirectoryReader = NRTReplicationReaderManager.unwrapStandardReader( + (OpenSearchDirectoryReader) searcher.getDirectoryReader() + ); + final SegmentInfos infos = standardDirectoryReader.getSegmentInfos(); + snapshottedSegments = infos.files(false); + } + + flush(INDEX_NAME); + for (int i = 101; i < 200; i++) { + client().prepareIndex(INDEX_NAME) + .setId(String.valueOf(i)) + .setSource("foo", randomInt()) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .get(); + refresh(INDEX_NAME); + if (randomBoolean()) { + client().admin().indices().prepareForceMerge(INDEX_NAME).setMaxNumSegments(1).setFlush(true).get(); + flush(INDEX_NAME); + } + } + assertBusy(() -> { + assertEquals( + getIndexShard(primary, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion(), + getIndexShard(replica, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion() + ); + }); + + client().admin().indices().prepareForceMerge(INDEX_NAME).setMaxNumSegments(1).setFlush(true).get(); + assertBusy(() -> { + assertEquals( + getIndexShard(primary, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion(), + getIndexShard(replica, INDEX_NAME).getLatestReplicationCheckpoint().getSegmentInfosVersion() + ); + }); + // Test stats + IndicesStatsRequest indicesStatsRequest = new IndicesStatsRequest(); + indicesStatsRequest.indices(INDEX_NAME); + indicesStatsRequest.all(); + IndicesStatsResponse indicesStatsResponse = client().admin().indices().stats(indicesStatsRequest).get(); + long pitCurrent = indicesStatsResponse.getIndex(INDEX_NAME).getTotal().search.getTotal().getPitCurrent(); + long openContexts = indicesStatsResponse.getIndex(INDEX_NAME).getTotal().search.getOpenContexts(); + assertEquals(1, pitCurrent); + assertEquals(1, openContexts); + SearchResponse resp = client(replica).prepareSearch(INDEX_NAME) + .setSize(10) + .setPreference("_only_local") + .addSort("foo", SortOrder.ASC) + .searchAfter(new Object[] { 30 }) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId()).setKeepAlive(TimeValue.timeValueDays(1))) + .setRequestCache(false) + .get(); + PitTestsUtil.assertUsingGetAllPits(client(replica), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, INDEX_NAME, 1, client(replica), pitResponse.getId()); + + List currentFiles = List.of(replicaShard.store().directory().listAll()); + assertTrue("Files should be preserved", currentFiles.containsAll(snapshottedSegments)); + + // delete the PIT + DeletePitRequest deletePITRequest = new DeletePitRequest(pitResponse.getId()); + client().execute(DeletePitAction.INSTANCE, deletePITRequest).actionGet(); + + currentFiles = List.of(replicaShard.store().directory().listAll()); + assertFalse("Files should be cleaned up", currentFiles.containsAll(snapshottedSegments)); + } + + /** + * This tests that if a primary receives docs while a replica is performing round of segrep during recovery + * the replica will catch up to latest checkpoint once recovery completes without requiring an additional primary refresh/flush. + */ + public void testPrimaryReceivesDocsDuringReplicaRecovery() throws Exception { + final List nodes = new ArrayList<>(); + final String primaryNode = internalCluster().startDataOnlyNode(); + nodes.add(primaryNode); + final Settings settings = Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0).build(); + createIndex(INDEX_NAME, settings); + ensureGreen(INDEX_NAME); + // start a replica node, initially will be empty with no shard assignment. + final String replicaNode = internalCluster().startDataOnlyNode(); + nodes.add(replicaNode); + + // index a doc. + client().prepareIndex(INDEX_NAME).setId("1").setSource("foo", randomInt()).get(); + refresh(INDEX_NAME); + + CountDownLatch latch = new CountDownLatch(1); + // block replication + try (final Releasable ignored = blockReplication(List.of(replicaNode), latch)) { + // update to add replica, initiating recovery, this will get stuck at last step + assertAcked( + client().admin() + .indices() + .prepareUpdateSettings(INDEX_NAME) + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)) + ); + ensureYellow(INDEX_NAME); + // index another doc while blocked, this would not get replicated to replica. + client().prepareIndex(INDEX_NAME).setId("2").setSource("foo2", randomInt()).get(); + refresh(INDEX_NAME); + } + ensureGreen(INDEX_NAME); + waitForSearchableDocs(2, nodes); + } + + public void testIndexWhileRecoveringReplica() throws Exception { + final String primaryNode = internalCluster().startDataOnlyNode(); + assertAcked( + prepareCreate(INDEX_NAME).setMapping( + jsonBuilder().startObject() + .startObject("_routing") + .field("required", true) + .endObject() + .startObject("properties") + .startObject("online") + .field("type", "boolean") + .endObject() + .startObject("ts") + .field("type", "date") + .field("ignore_malformed", false) + .field("format", "epoch_millis") + .endObject() + .startObject("bs") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + ) + ); + ensureYellow(INDEX_NAME); + final String replicaNode = internalCluster().startDataOnlyNode(); + + client().prepareIndex(INDEX_NAME) + .setId("1") + .setRouting("Y") + .setSource("online", false, "bs", "Y", "ts", System.currentTimeMillis() - 100, "type", "s") + .get(); + client().prepareIndex(INDEX_NAME) + .setId("2") + .setRouting("X") + .setSource("online", true, "bs", "X", "ts", System.currentTimeMillis() - 10000000, "type", "s") + .get(); + client().prepareIndex(INDEX_NAME) + .setId("3") + .setRouting(randomAlphaOfLength(2)) + .setSource("online", false, "ts", System.currentTimeMillis() - 100, "type", "bs") + .get(); + client().prepareIndex(INDEX_NAME) + .setId("4") + .setRouting(randomAlphaOfLength(2)) + .setSource("online", true, "ts", System.currentTimeMillis() - 123123, "type", "bs") + .get(); + refresh(); + ensureGreen(INDEX_NAME); + waitForSearchableDocs(4, primaryNode, replicaNode); + + SearchResponse response = client().prepareSearch(INDEX_NAME) + .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) + .setQuery( + boolQuery().must(termQuery("online", true)) + .must( + boolQuery().should( + boolQuery().must(rangeQuery("ts").lt(System.currentTimeMillis() - (15 * 1000))).must(termQuery("type", "bs")) + ) + .should( + boolQuery().must(rangeQuery("ts").lt(System.currentTimeMillis() - (15 * 1000))).must(termQuery("type", "s")) + ) + ) + ) + .setVersion(true) + .setFrom(0) + .setSize(100) + .setExplain(true) + .get(); + assertNoFailures(response); + } + + public void testRestartPrimary_NoReplicas() throws Exception { + final String primary = internalCluster().startDataOnlyNode(); + createIndex(INDEX_NAME); + ensureYellow(INDEX_NAME); + + assertEquals(getNodeContainingPrimaryShard().getName(), primary); + + client().prepareIndex(INDEX_NAME).setId("1").setSource("foo", "bar").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).get(); + if (randomBoolean()) { + flush(INDEX_NAME); + } else { + refresh(INDEX_NAME); + } + + internalCluster().restartNode(primary); + ensureYellow(INDEX_NAME); + assertDocCounts(1, primary); + } + + /** + * Tests whether segment replication supports realtime get requests and reads and parses source from the translog to serve strong reads. + */ + public void testRealtimeGetRequestsSuccessful() { + final String primary = internalCluster().startDataOnlyNode(); + // refresh interval disabled to ensure refresh rate of index (when data is ready for search) doesn't affect realtime get + assertAcked( + prepareCreate(INDEX_NAME).setSettings(Settings.builder().put("index.refresh_interval", -1).put(indexSettings())) + .addAlias(new Alias("alias")) + ); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + final String id = routingKeyForShard(INDEX_NAME, 0); + + GetResponse response = client(replica).prepareGet(indexOrAlias(), "1").get(); + assertFalse(response.isExists()); + + // index doc 1 + client().prepareIndex(indexOrAlias()).setId("1").setSource("foo", "bar").get(); + + // non realtime get 1 + response = client().prepareGet(indexOrAlias(), "1").setRealtime(false).get(); + assertFalse(response.isExists()); + + // realtime get 1 + response = client(replica).prepareGet(indexOrAlias(), "1").get(); + assertTrue(response.isExists()); + assertThat(response.getIndex(), equalTo(INDEX_NAME)); + assertThat(response.getSourceAsMap().get("foo").toString(), equalTo("bar")); + + // index doc 2 + client().prepareIndex(indexOrAlias()).setId("2").setSource("foo2", "bar2").setRouting(id).get(); + + // realtime get 2 (with routing) + response = client(replica).prepareGet(indexOrAlias(), "2").setRouting(id).get(); + assertTrue(response.isExists()); + assertThat(response.getIndex(), equalTo(INDEX_NAME)); + assertThat(response.getSourceAsMap().get("foo2").toString(), equalTo("bar2")); + } + + public void testRealtimeGetRequestsUnsuccessful() { + final String primary = internalCluster().startDataOnlyNode(); + assertAcked( + prepareCreate(INDEX_NAME).setSettings( + Settings.builder().put("index.refresh_interval", -1).put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2) + ).addAlias(new Alias("alias")) + ); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + final String id = routingKeyForShard(INDEX_NAME, 0); + final String routingOtherShard = routingKeyForShard(INDEX_NAME, 1); + + // index doc 1 + client().prepareIndex(indexOrAlias()).setId("1").setSource("foo", "bar").setRouting(id).get(); + + // non realtime get 1 + GetResponse response = client().prepareGet(indexOrAlias(), "1").setRealtime(false).get(); + assertFalse(response.isExists()); + + // realtime get 1 (preference = _replica) + response = client(replica).prepareGet(indexOrAlias(), "1").setPreference(Preference.REPLICA.type()).get(); + assertFalse(response.isExists()); + assertThat(response.getIndex(), equalTo(INDEX_NAME)); + + // realtime get 1 (with routing set) + response = client(replica).prepareGet(INDEX_NAME, "1").setRouting(routingOtherShard).get(); + assertFalse(response.isExists()); + assertThat(response.getIndex(), equalTo(INDEX_NAME)); + } + + /** + * Tests whether segment replication supports realtime MultiGet requests and reads and parses source from the translog to serve strong reads. + */ + public void testRealtimeMultiGetRequestsSuccessful() { + final String primary = internalCluster().startDataOnlyNode(); + // refresh interval disabled to ensure refresh rate of index (when data is ready for search) doesn't affect realtime multi get + assertAcked( + prepareCreate(INDEX_NAME).setSettings( + Settings.builder().put("index.refresh_interval", -1).put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2) + ).addAlias(new Alias("alias")) + ); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + final String id = routingKeyForShard(INDEX_NAME, 0); + + // index doc 1 + client().prepareIndex(INDEX_NAME).setId("1").setSource("foo", "bar").get(); + + // index doc 2 + client().prepareIndex(INDEX_NAME).setId("2").setSource("foo2", "bar2").setRouting(id).get(); + + // multi get non realtime 1 + MultiGetResponse mgetResponse = client().prepareMultiGet() + .add(new MultiGetRequest.Item(INDEX_NAME, "1")) + .add(new MultiGetRequest.Item("nonExistingIndex", "1")) + .setRealtime(false) + .get(); + assertThat(mgetResponse.getResponses().length, is(2)); + + assertThat(mgetResponse.getResponses()[0].getIndex(), is(INDEX_NAME)); + assertFalse(mgetResponse.getResponses()[0].isFailed()); + assertFalse(mgetResponse.getResponses()[0].getResponse().isExists()); + + // multi get realtime 1 + mgetResponse = client(replica).prepareMultiGet() + .add(new MultiGetRequest.Item(INDEX_NAME, "1")) + .add(new MultiGetRequest.Item(INDEX_NAME, "2").routing(id)) + .add(new MultiGetRequest.Item("nonExistingIndex", "1")) + .get(); + + assertThat(mgetResponse.getResponses().length, is(3)); + assertThat(mgetResponse.getResponses()[0].getIndex(), is(INDEX_NAME)); + assertFalse(mgetResponse.getResponses()[0].isFailed()); + assertThat(mgetResponse.getResponses()[0].getResponse().getSourceAsMap().get("foo").toString(), equalTo("bar")); + + assertThat(mgetResponse.getResponses()[1].getIndex(), is(INDEX_NAME)); + assertFalse(mgetResponse.getResponses()[1].isFailed()); + assertThat(mgetResponse.getResponses()[1].getResponse().getSourceAsMap().get("foo2").toString(), equalTo("bar2")); + + assertThat(mgetResponse.getResponses()[2].getIndex(), is("nonExistingIndex")); + assertTrue(mgetResponse.getResponses()[2].isFailed()); + assertThat(mgetResponse.getResponses()[2].getFailure().getMessage(), is("no such index [nonExistingIndex]")); + } + + public void testRealtimeMultiGetRequestsUnsuccessful() { + final String primary = internalCluster().startDataOnlyNode(); + assertAcked( + prepareCreate(INDEX_NAME).setSettings( + Settings.builder().put("index.refresh_interval", -1).put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2) + ).addAlias(new Alias("alias")) + ); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + + final String id = routingKeyForShard(INDEX_NAME, 0); + final String routingOtherShard = routingKeyForShard(INDEX_NAME, 1); + + // index doc 1 + client().prepareIndex(indexOrAlias()).setId("1").setSource("foo", "bar").setRouting(id).get(); + + // realtime multi get 1 (preference = _replica) + MultiGetResponse mgetResponse = client(replica).prepareMultiGet() + .add(new MultiGetRequest.Item(INDEX_NAME, "1")) + .setPreference(Preference.REPLICA.type()) + .add(new MultiGetRequest.Item("nonExistingIndex", "1")) + .get(); + assertThat(mgetResponse.getResponses().length, is(2)); + assertThat(mgetResponse.getResponses()[0].getIndex(), is(INDEX_NAME)); + assertFalse(mgetResponse.getResponses()[0].getResponse().isExists()); + + assertThat(mgetResponse.getResponses()[1].getIndex(), is("nonExistingIndex")); + assertTrue(mgetResponse.getResponses()[1].isFailed()); + + // realtime multi get 1 (routing set) + mgetResponse = client(replica).prepareMultiGet() + .add(new MultiGetRequest.Item(INDEX_NAME, "1").routing(routingOtherShard)) + .add(new MultiGetRequest.Item("nonExistingIndex", "1")) + .get(); + assertThat(mgetResponse.getResponses().length, is(2)); + assertThat(mgetResponse.getResponses()[0].getIndex(), is(INDEX_NAME)); + // expecting failure since we explicitly route request to a shard on which it doesn't exist + assertFalse(mgetResponse.getResponses()[0].getResponse().isExists()); + assertThat(mgetResponse.getResponses()[1].getIndex(), is("nonExistingIndex")); + assertTrue(mgetResponse.getResponses()[1].isFailed()); + + } + + /** + * Tests whether segment replication supports realtime termvector requests and reads and parses source from the translog to serve strong reads. + */ + public void testRealtimeTermVectorRequestsSuccessful() throws IOException { + final String primary = internalCluster().startDataOnlyNode(); + XContentBuilder mapping = jsonBuilder().startObject() + .startObject("properties") + .startObject("field") + .field("type", "text") + .field("term_vector", "with_positions_offsets_payloads") + .field("analyzer", "tv_test") + .endObject() + .endObject() + .endObject(); + // refresh interval disabled to ensure refresh rate of index (when data is ready for search) doesn't affect realtime termvectors + assertAcked( + prepareCreate(INDEX_NAME).setMapping(mapping) + .addAlias(new Alias("alias")) + .setSettings( + Settings.builder() + .put(indexSettings()) + .put("index.analysis.analyzer.tv_test.tokenizer", "standard") + .put("index.refresh_interval", -1) + .putList("index.analysis.analyzer.tv_test.filter", "lowercase") + ) + ); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + final String id = routingKeyForShard(INDEX_NAME, 0); + + TermVectorsResponse response = client(replica).prepareTermVectors(indexOrAlias(), "1").get(); + assertFalse(response.isExists()); + + // index doc 1 + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(1)) + .setSource(jsonBuilder().startObject().field("field", "the quick brown fox jumps over the lazy dog").endObject()) + .execute() + .actionGet(); + + // non realtime termvectors 1 + response = client().prepareTermVectors(indexOrAlias(), Integer.toString(1)).setRealtime(false).get(); + assertFalse(response.isExists()); + + // realtime termvectors 1 + TermVectorsRequestBuilder resp = client().prepareTermVectors(indexOrAlias(), Integer.toString(1)) + .setPayloads(true) + .setOffsets(true) + .setPositions(true) + .setRealtime(true) + .setSelectedFields(); + response = resp.execute().actionGet(); + assertThat(response.getIndex(), equalTo(INDEX_NAME)); + assertThat("doc id: " + 1 + " doesn't exists but should", response.isExists(), equalTo(true)); + Fields fields = response.getFields(); + assertThat(fields.size(), equalTo(1)); + + // index doc 2 with routing + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(2)) + .setRouting(id) + .setSource(jsonBuilder().startObject().field("field", "the quick brown fox jumps over the lazy dog").endObject()) + .execute() + .actionGet(); + + // realtime termvectors 2 with routing + resp = client().prepareTermVectors(indexOrAlias(), Integer.toString(2)) + .setPayloads(true) + .setOffsets(true) + .setPositions(true) + .setRouting(id) + .setSelectedFields(); + response = resp.execute().actionGet(); + assertThat(response.getIndex(), equalTo(INDEX_NAME)); + assertThat("doc id: " + 1 + " doesn't exists but should", response.isExists(), equalTo(true)); + fields = response.getFields(); + assertThat(fields.size(), equalTo(1)); + + } + + public void testRealtimeTermVectorRequestsUnSuccessful() throws IOException { + final String primary = internalCluster().startDataOnlyNode(); + XContentBuilder mapping = jsonBuilder().startObject() + .startObject("properties") + .startObject("field") + .field("type", "text") + .field("term_vector", "with_positions_offsets_payloads") + .field("analyzer", "tv_test") + .endObject() + .endObject() + .endObject(); + // refresh interval disabled to ensure refresh rate of index (when data is ready for search) doesn't affect realtime termvectors + assertAcked( + prepareCreate(INDEX_NAME).setMapping(mapping) + .addAlias(new Alias("alias")) + .setSettings( + Settings.builder() + .put(indexSettings()) + .put("index.analysis.analyzer.tv_test.tokenizer", "standard") + .put("index.refresh_interval", -1) + .putList("index.analysis.analyzer.tv_test.filter", "lowercase") + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2) + ) + ); + final String replica = internalCluster().startDataOnlyNode(); + ensureGreen(INDEX_NAME); + final String id = routingKeyForShard(INDEX_NAME, 0); + final String routingOtherShard = routingKeyForShard(INDEX_NAME, 1); + + // index doc 1 + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(1)) + .setSource(jsonBuilder().startObject().field("field", "the quick brown fox jumps over the lazy dog").endObject()) + .setRouting(id) + .execute() + .actionGet(); + + // non realtime termvectors 1 + TermVectorsResponse response = client().prepareTermVectors(indexOrAlias(), Integer.toString(1)).setRealtime(false).get(); + assertFalse(response.isExists()); + + // realtime termvectors (preference = _replica) + TermVectorsRequestBuilder resp = client(replica).prepareTermVectors(indexOrAlias(), Integer.toString(1)) + .setPayloads(true) + .setOffsets(true) + .setPositions(true) + .setPreference(Preference.REPLICA.type()) + .setRealtime(true) + .setSelectedFields(); + response = resp.execute().actionGet(); + + assertFalse(response.isExists()); + assertThat(response.getIndex(), equalTo(INDEX_NAME)); + + // realtime termvectors (with routing set) + resp = client(replica).prepareTermVectors(indexOrAlias(), Integer.toString(1)) + .setPayloads(true) + .setOffsets(true) + .setPositions(true) + .setRouting(routingOtherShard) + .setSelectedFields(); + response = resp.execute().actionGet(); + + assertFalse(response.isExists()); + assertThat(response.getIndex(), equalTo(INDEX_NAME)); + + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationRelocationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationRelocationIT.java new file mode 100644 index 0000000000000..97e2045285d2f --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationRelocationIT.java @@ -0,0 +1,580 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.replication; + +import org.opensearch.OpenSearchCorruptionException; +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.admin.cluster.reroute.ClusterRerouteResponse; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsResponse; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.routing.ShardRoutingState; +import org.opensearch.cluster.routing.allocation.command.MoveAllocationCommand; +import org.opensearch.common.Priority; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.index.SegmentReplicationShardStats; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.indices.IndicesService; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.transport.TransportService; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; + +/** + * This test class verifies primary shard relocation with segment replication as replication strategy. + */ +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationRelocationIT extends SegmentReplicationBaseIT { + private final TimeValue ACCEPTABLE_RELOCATION_TIME = new TimeValue(5, TimeUnit.MINUTES); + + private void createIndex(int replicaCount) { + prepareCreate(INDEX_NAME, Settings.builder().put(SETTING_NUMBER_OF_REPLICAS, replicaCount)).get(); + } + + /** + * This test verifies happy path when primary shard is relocated newly added node (target) in the cluster. Before + * relocation and after relocation documents are indexed and documents are verified + */ + public void testPrimaryRelocation() throws Exception { + final String oldPrimary = internalCluster().startNode(); + createIndex(1); + final String replica = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + final int initialDocCount = scaledRandomIntBetween(10, 100); + final WriteRequest.RefreshPolicy refreshPolicy = randomFrom(WriteRequest.RefreshPolicy.values()); + final List> pendingIndexResponses = new ArrayList<>(); + for (int i = 0; i < initialDocCount; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(refreshPolicy) + .setSource("field", "value" + i) + .execute() + ); + } + + logger.info("--> start another node"); + final String newPrimary = internalCluster().startNode(featureFlagSettings()); + ClusterHealthResponse clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("3") + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + + logger.info("--> relocate the shard"); + client().admin() + .cluster() + .prepareReroute() + .add(new MoveAllocationCommand(INDEX_NAME, 0, oldPrimary, newPrimary)) + .execute() + .actionGet(); + clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNoRelocatingShards(true) + .setTimeout(ACCEPTABLE_RELOCATION_TIME) + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + + logger.info("--> get the state, verify shard 1 primary moved from node1 to node2"); + ClusterState state = client().admin().cluster().prepareState().execute().actionGet().getState(); + assertEquals( + state.getRoutingNodes().node(state.nodes().resolveNode(newPrimary).getId()).iterator().next().state(), + ShardRoutingState.STARTED + ); + + for (int i = initialDocCount; i < 2 * initialDocCount; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(refreshPolicy) + .setSource("field", "value" + i) + .execute() + ); + } + assertBusy(() -> { + client().admin().indices().prepareRefresh().execute().actionGet(); + assertTrue(pendingIndexResponses.stream().allMatch(ActionFuture::isDone)); + }, 1, TimeUnit.MINUTES); + flushAndRefresh(INDEX_NAME); + logger.info("--> verify count again {}", 2 * initialDocCount); + waitForSearchableDocs(2 * initialDocCount, newPrimary, replica); + verifyStoreContent(); + } + + /** + * This test verifies the primary relocation behavior when segment replication round fails during recovery. Post + * failure, more documents are ingested and verified on replica; which confirms older primary still refreshing the + * replicas. + */ + public void testPrimaryRelocationWithSegRepFailure() throws Exception { + final String oldPrimary = internalCluster().startNode(); + createIndex(1); + final String replica = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + final int initialDocCount = scaledRandomIntBetween(10, 100); + final WriteRequest.RefreshPolicy refreshPolicy = randomFrom(WriteRequest.RefreshPolicy.values()); + final List> pendingIndexResponses = new ArrayList<>(); + for (int i = 0; i < initialDocCount; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(refreshPolicy) + .setSource("field", "value" + i) + .execute() + ); + } + + logger.info("--> start another node"); + final String newPrimary = internalCluster().startNode(featureFlagSettings()); + ClusterHealthResponse clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("3") + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + + // Mock transport service to add behaviour of throwing corruption exception during segment replication process. + MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + oldPrimary + )); + mockTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, newPrimary), + (connection, requestId, action, request, options) -> { + if (action.equals(SegmentReplicationTargetService.Actions.FILE_CHUNK)) { + throw new OpenSearchCorruptionException("expected"); + } + connection.sendRequest(requestId, action, request, options); + } + ); + + logger.info("--> relocate the shard"); + client().admin() + .cluster() + .prepareReroute() + .add(new MoveAllocationCommand(INDEX_NAME, 0, oldPrimary, newPrimary)) + .execute() + .actionGet(); + clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNoRelocatingShards(true) + .setTimeout(ACCEPTABLE_RELOCATION_TIME) + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + + for (int i = initialDocCount; i < 2 * initialDocCount; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(refreshPolicy) + .setSource("field", "value" + i) + .execute() + ); + } + + logger.info("Verify older primary is still refreshing replica nodes"); + assertBusy(() -> { + client().admin().indices().prepareRefresh().execute().actionGet(); + assertTrue(pendingIndexResponses.stream().allMatch(ActionFuture::isDone)); + }, 1, TimeUnit.MINUTES); + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(2 * initialDocCount, oldPrimary, replica); + verifyStoreContent(); + } + + /** + * This test verifies primary recovery behavior with continuous ingestion + * + */ + public void testRelocateWhileContinuouslyIndexingAndWaitingForRefresh() throws Exception { + final String primary = internalCluster().startNode(); + createIndex(1); + final String replica = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + final int totalDocCount = 1000; + for (int i = 0; i < 10; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + logger.info("--> flush to have segments on disk"); + client().admin().indices().prepareFlush().execute().actionGet(); + + logger.info("--> index more docs so there are ops in the transaction log"); + final List> pendingIndexResponses = new ArrayList<>(); + for (int i = 10; i < 20; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL) + .setSource("field", "value" + i) + .execute() + ); + } + + final String newPrimary = internalCluster().startNode(); + ClusterHealthResponse clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("3") + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + + logger.info("--> relocate the shard from primary to replica"); + ActionFuture relocationListener = client().admin() + .cluster() + .prepareReroute() + .add(new MoveAllocationCommand(INDEX_NAME, 0, primary, newPrimary)) + .execute(); + for (int i = 20; i < totalDocCount; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL) + .setSource("field", "value" + i) + .execute() + ); + } + relocationListener.actionGet(); + clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNoRelocatingShards(true) + .setTimeout(ACCEPTABLE_RELOCATION_TIME) + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + + logger.info("--> verifying count"); + assertBusy(() -> { + client().admin().indices().prepareRefresh().execute().actionGet(); + assertTrue(pendingIndexResponses.stream().allMatch(ActionFuture::isDone)); + }, 1, TimeUnit.MINUTES); + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(totalDocCount, newPrimary, replica); + verifyStoreContent(); + } + + /** + * This test verifies delayed operations during primary handoff are replayed and searchable. It does so by halting + * segment replication which is performed while holding primary indexing permits which results in queuing of + * operations during handoff. The test verifies all docs ingested are searchable on new primary. + * + */ + public void testRelocateWithQueuedOperationsDuringHandoff() throws Exception { + final String primary = internalCluster().startNode(); + createIndex(1); + final String replica = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + final int totalDocCount = 2000; + + for (int i = 0; i < 10; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + logger.info("--> flush to have segments on disk"); + client().admin().indices().prepareFlush().execute().actionGet(); + final WriteRequest.RefreshPolicy refreshPolicy = randomFrom(WriteRequest.RefreshPolicy.values()); + + logger.info("--> index more docs so there are ops in the transaction log"); + final List> pendingIndexResponses = new ArrayList<>(); + for (int i = 10; i < 20; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME) + .setId(Integer.toString(i)) + .setRefreshPolicy(refreshPolicy) + .setSource("field", "value" + i) + .execute() + ); + } + final String newPrimary = internalCluster().startNode(); + ClusterHealthResponse clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("3") + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + ensureGreen(INDEX_NAME); + + // Get mock transport service from newPrimary, halt recovery during segment replication (during handoff) to allow indexing in + // parallel. + MockTransportService mockTargetTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + newPrimary + )); + CountDownLatch blockSegRepLatch = new CountDownLatch(1); + CountDownLatch waitForIndexingLatch = new CountDownLatch(1); + mockTargetTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, primary), + (connection, requestId, action, request, options) -> { + if (action.equals(SegmentReplicationSourceService.Actions.GET_SEGMENT_FILES)) { + blockSegRepLatch.countDown(); + try { + waitForIndexingLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + connection.sendRequest(requestId, action, request, options); + } + ); + Thread indexingThread = new Thread(() -> { + // Wait for relocation to halt at SegRep. Ingest docs at that point. + try { + blockSegRepLatch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + for (int i = 20; i < totalDocCount; i++) { + pendingIndexResponses.add( + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute() + ); + } + waitForIndexingLatch.countDown(); + }); + + logger.info("--> relocate the shard from primary to newPrimary"); + ActionFuture relocationListener = client().admin() + .cluster() + .prepareReroute() + .add(new MoveAllocationCommand(INDEX_NAME, 0, primary, newPrimary)) + .execute(); + + // This thread first waits for recovery to halt during segment replication. After which it ingests data to ensure + // documents are queued. + indexingThread.start(); + indexingThread.join(); + relocationListener.actionGet(); + clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNoRelocatingShards(true) + .setTimeout(ACCEPTABLE_RELOCATION_TIME) + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + + logger.info("--> verifying count"); + assertBusy(() -> { + client().admin().indices().prepareRefresh().execute().actionGet(); + assertTrue(pendingIndexResponses.stream().allMatch(ActionFuture::isDone)); + }, 2, TimeUnit.MINUTES); + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(totalDocCount, replica, newPrimary); + verifyStoreContent(); + } + + /** + * This test verifies that adding a new node which results in peer recovery as replica; also bring replica's + * replication checkpoint upto the primary's by performing a round of segment replication. + */ + public void testNewlyAddedReplicaIsUpdated() throws Exception { + final String primary = internalCluster().startNode(); + prepareCreate(INDEX_NAME, Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0)) + .get(); + for (int i = 0; i < 10; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + logger.info("--> flush so we have some segment files on disk"); + flush(INDEX_NAME); + logger.info("--> index more docs so we have something in the translog"); + for (int i = 10; i < 20; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + refresh(INDEX_NAME); + assertEquals(client().prepareSearch(INDEX_NAME).setSize(0).execute().actionGet().getHits().getTotalHits().value, 20L); + + logger.info("--> start empty node to add replica shard"); + final String replica = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + // Update replica count settings to 1 so that peer recovery triggers and recover replica + assertAcked( + client().admin().indices().prepareUpdateSettings(INDEX_NAME).setSettings(Settings.builder().put(SETTING_NUMBER_OF_REPLICAS, 1)) + ); + ensureGreen(INDEX_NAME); + flushAndRefresh(INDEX_NAME); + waitForSearchableDocs(20, primary, replica); + verifyStoreContent(); + } + + /** + * This test verifies that replica shard is not added to the cluster when doing a round of segment replication fails during peer recovery. + */ + public void testAddNewReplicaFailure() throws Exception { + logger.info("--> starting [Primary Node] ..."); + final String primaryNode = internalCluster().startNode(); + + logger.info("--> creating test index ..."); + prepareCreate( + INDEX_NAME, + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0) + + ).get(); + + logger.info("--> index 10 docs"); + for (int i = 0; i < 10; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + logger.info("--> flush so we have some segment files on disk"); + flush(INDEX_NAME); + logger.info("--> index more docs so we have something in the translog"); + for (int i = 10; i < 20; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + refresh(INDEX_NAME); + logger.info("--> verifying count"); + assertEquals(client().prepareSearch(INDEX_NAME).setSize(0).execute().actionGet().getHits().getTotalHits().value, 20L); + + logger.info("--> start empty node to add replica shard"); + final String replica = internalCluster().startNode(); + + final CountDownLatch waitForRecovery = new CountDownLatch(1); + // Mock transport service to add behaviour of throwing corruption exception during segment replication process. + MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + primaryNode + )); + mockTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, replica), + (connection, requestId, action, request, options) -> { + if (action.equals(SegmentReplicationTargetService.Actions.FILE_CHUNK)) { + waitForRecovery.countDown(); + throw new OpenSearchCorruptionException("expected"); + } + connection.sendRequest(requestId, action, request, options); + } + ); + ensureGreen(INDEX_NAME); + // Add Replica shard to the new empty replica node + assertAcked( + client().admin().indices().prepareUpdateSettings(INDEX_NAME).setSettings(Settings.builder().put(SETTING_NUMBER_OF_REPLICAS, 1)) + ); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, replica); + waitForRecovery.await(); + assertBusy(() -> assertTrue(indicesService.hasIndex(resolveIndex(INDEX_NAME)))); + + // Verify that cluster state is not green and replica shard failed during a round of segment replication is not added to the cluster + ClusterHealthResponse clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("2") + .setWaitForGreenStatus() + .setTimeout(TimeValue.timeValueSeconds(2)) + .execute() + .actionGet(); + assertTrue(clusterHealthResponse.isTimedOut()); + ensureYellow(INDEX_NAME); + } + + public void testFlushAfterRelocation() throws Exception { + // Starting two nodes with primary and replica shards respectively. + final String primaryNode = internalCluster().startNode(); + prepareCreate( + INDEX_NAME, + Settings.builder() + // we want to control refreshes + .put("index.refresh_interval", -1) + ).get(); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replicaNode = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + + // Start another empty node for relocation + final String newPrimary = internalCluster().startNode(); + ClusterHealthResponse clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNodes("3") + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + ensureGreen(INDEX_NAME); + + // Start indexing docs + final int initialDocCount = scaledRandomIntBetween(2000, 3000); + for (int i = 0; i < initialDocCount; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + + final IndexShard replicaShard = getIndexShard(replicaNode, INDEX_NAME); + + // Verify segment replication event never happened on replica shard other than recovery. + assertHitCount(client(primaryNode).prepareSearch(INDEX_NAME).setPreference("_only_local").setSize(0).get(), 0); + assertHitCount(client(replicaNode).prepareSearch(INDEX_NAME).setPreference("_only_local").setSize(0).get(), 0); + + SegmentReplicationStatsResponse segmentReplicationStatsResponse = client().admin() + .indices() + .prepareSegmentReplicationStats(INDEX_NAME) + .execute() + .actionGet(); + final Set replicaStats = segmentReplicationStatsResponse.getReplicationStats() + .get(INDEX_NAME) + .get(0) + .getReplicaStats(); + assertEquals( + Set.of(replicaShard.routingEntry().allocationId().getId()), + replicaStats.stream().map(SegmentReplicationShardStats::getAllocationId).collect(Collectors.toSet()) + ); + // the primary still has not refreshed to update its checkpoint, so our replica is not yet behind. + assertEquals(0, replicaStats.stream().findFirst().get().getCheckpointsBehindCount()); + + // Relocate primary to new primary. When new primary starts it does perform a flush. + logger.info("--> relocate the shard from primary to newPrimary"); + ActionFuture relocationListener = client().admin() + .cluster() + .prepareReroute() + .add(new MoveAllocationCommand(INDEX_NAME, 0, primaryNode, newPrimary)) + .execute(); + clusterHealthResponse = client().admin() + .cluster() + .prepareHealth() + .setWaitForEvents(Priority.LANGUID) + .setWaitForNoRelocatingShards(true) + .setTimeout(ACCEPTABLE_RELOCATION_TIME) + .execute() + .actionGet(); + assertEquals(clusterHealthResponse.isTimedOut(), false); + + // Verify if all docs are present in replica after relocation, if new relocated primary doesn't flush after relocation the below + // assert will fail. + assertBusy(() -> { + assertHitCount(client(replicaNode).prepareSearch(INDEX_NAME).setPreference("_only_local").setSize(0).get(), initialDocCount); + }); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationResizeRequestIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationResizeRequestIT.java new file mode 100644 index 0000000000000..fb06a97bd51c2 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationResizeRequestIT.java @@ -0,0 +1,248 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.replication; + +import org.opensearch.action.admin.indices.shrink.ResizeType; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.Preference; +import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; +import org.opensearch.common.lease.Releasable; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.index.query.TermsQueryBuilder; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; + +/** + * This test class verifies Resize Reequests (Shrink, Split, Clone) with segment replication as replication strategy. + */ +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 2) +public class SegmentReplicationResizeRequestIT extends SegmentReplicationBaseIT { + + public void testCreateShrinkIndexThrowsExceptionWhenReplicasBehind() throws Exception { + + // create index with -1 as refresh interval as we are blocking segrep and we want to control refreshes. + prepareCreate("test").setSettings( + Settings.builder() + .put(indexSettings()) + .put("index.refresh_interval", -1) + .put("index.number_of_replicas", 1) + .put("number_of_shards", 2) + ).get(); + + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); + assertTrue("at least 2 nodes but was: " + dataNodes.size(), dataNodes.size() >= 2); + DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); + // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node + // if we change the setting too quickly we will end up with one replica unassigned which can't be assigned anymore due + // to the require._name below. + ensureGreen(); + + // block Segment Replication so that replicas never get the docs from primary + CountDownLatch latch = new CountDownLatch(1); + try (final Releasable ignored = blockReplication(List.of(discoveryNodes[0].getName()), latch)) { + final int docs = 500; + for (int i = 0; i < docs; i++) { + client().prepareIndex("test").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); + } + + // block writes on index before performing shrink operation + client().admin() + .indices() + .prepareUpdateSettings("test") + .setSettings( + Settings.builder() + .put("index.routing.allocation.require._name", discoveryNodes[0].getName()) + .put("index.blocks.write", true) + ) + .get(); + ensureGreen(); + + // Trigger Shrink operation, as replicas don't have any docs it will throw exception that replicas haven't caught up + IllegalStateException exception = assertThrows( + IllegalStateException.class, + () -> client().admin() + .indices() + .prepareResizeIndex("test", "target") + .setResizeType(ResizeType.SHRINK) + .setSettings( + Settings.builder() + .put("index.number_of_replicas", 1) + .putNull("index.blocks.write") + .putNull("index.routing.allocation.require._name") + .build() + ) + .get() + ); + assertEquals( + " For index [test] replica shards haven't caught up with primary, please retry after sometime.", + exception.getMessage() + ); + + } + + } + + public void testCreateSplitIndexWithSegmentReplicationBlocked() throws Exception { + internalCluster().ensureAtLeastNumDataNodes(3); + + // create index with -1 as refresh interval as we are blocking segrep and we want to control refreshes. + prepareCreate("test").setSettings( + Settings.builder() + .put(indexSettings()) + .put("index.refresh_interval", -1) + .put("index.number_of_replicas", 1) + .put("number_of_shards", 3) + ).get(); + + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); + assertTrue("at least 2 nodes but was: " + dataNodes.size(), dataNodes.size() >= 2); + DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); + // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node + // if we change the setting too quickly we will end up with one replica unassigned which can't be assigned anymore due + // to the require._name below. + ensureGreen(); + + CountDownLatch latch = new CountDownLatch(1); + + // block Segment Replication so that replicas never get the docs from primary + try (final Releasable ignored = blockReplication(List.of(discoveryNodes[0].getName()), latch)) { + final int docs = 500; + for (int i = 0; i < docs; i++) { + client().prepareIndex("test").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); + } + refresh(); + assertBusy(() -> { + assertHitCount( + client().prepareSearch("test") + .setQuery(new TermsQueryBuilder("foo", "bar")) + .setPreference(Preference.PRIMARY.type()) + .get(), + docs + ); + }); + + // block writes on index before performing split operation + client().admin().indices().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.write", true)).get(); + ensureGreen(); + + client().admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings( + Settings.builder().put(EnableAllocationDecider.CLUSTER_ROUTING_REBALANCE_ENABLE_SETTING.getKey(), "none") + ) + .get(); + + // Trigger split operation + assertAcked( + client().admin() + .indices() + .prepareResizeIndex("test", "target") + .setResizeType(ResizeType.SPLIT) + .setSettings( + Settings.builder() + .put("index.number_of_replicas", 1) + .put("index.number_of_shards", 6) + .putNull("index.blocks.write") + .build() + ) + .get() + ); + ensureGreen(); + + // verify that all docs are present in new target index + assertHitCount( + client().prepareSearch("target") + .setQuery(new TermsQueryBuilder("foo", "bar")) + .setPreference(Preference.PRIMARY.type()) + .get(), + docs + ); + } + + } + + public void testCloneIndex() throws Exception { + internalCluster().ensureAtLeastNumDataNodes(3); + + // create index with -1 as refresh interval as we are blocking segrep and we want to control refreshes. + prepareCreate("test").setSettings( + Settings.builder().put(indexSettings()).put("index.number_of_replicas", 1).put("number_of_shards", randomIntBetween(1, 5)) + ).get(); + + final Map dataNodes = client().admin().cluster().prepareState().get().getState().nodes().getDataNodes(); + assertTrue("at least 2 nodes but was: " + dataNodes.size(), dataNodes.size() >= 2); + DiscoveryNode[] discoveryNodes = dataNodes.values().toArray(new DiscoveryNode[0]); + // ensure all shards are allocated otherwise the ensure green below might not succeed since we require the merge node + // if we change the setting too quickly we will end up with one replica unassigned which can't be assigned anymore due + // to the require._name below. + ensureGreen(); + + CountDownLatch latch = new CountDownLatch(1); + + // block Segment Replication so that replicas never get the docs from primary + try (final Releasable ignored = blockReplication(List.of(discoveryNodes[0].getName()), latch)) { + final int docs = 500; + for (int i = 0; i < docs; i++) { + client().prepareIndex("test").setSource("{\"foo\" : \"bar\", \"i\" : " + i + "}", MediaTypeRegistry.JSON).get(); + } + refresh(); + assertBusy(() -> { + assertHitCount( + client().prepareSearch("test") + .setQuery(new TermsQueryBuilder("foo", "bar")) + .setPreference(Preference.PRIMARY.type()) + .get(), + docs + ); + }); + + // block writes on index before performing clone operation + client().admin().indices().prepareUpdateSettings("test").setSettings(Settings.builder().put("index.blocks.write", true)).get(); + ensureGreen(); + + client().admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings( + Settings.builder().put(EnableAllocationDecider.CLUSTER_ROUTING_REBALANCE_ENABLE_SETTING.getKey(), "none") + ) + .get(); + + // Trigger split operation + assertAcked( + client().admin() + .indices() + .prepareResizeIndex("test", "target") + .setResizeType(ResizeType.CLONE) + .setSettings(Settings.builder().put("index.number_of_replicas", 1).putNull("index.blocks.write").build()) + .get() + ); + ensureGreen(); + + // verify that all docs are present in new target index + assertHitCount( + client().prepareSearch("target") + .setQuery(new TermsQueryBuilder("foo", "bar")) + .setPreference(Preference.PRIMARY.type()) + .get(), + docs + ); + } + + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationStatsIT.java new file mode 100644 index 0000000000000..766471fdc0756 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationStatsIT.java @@ -0,0 +1,433 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.replication; + +import org.opensearch.action.admin.cluster.node.stats.NodeStats; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsResponse; +import org.opensearch.action.admin.indices.stats.CommonStatsFlags; +import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.lease.Releasable; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.ReplicationStats; +import org.opensearch.index.SegmentReplicationPerGroupStats; +import org.opensearch.index.SegmentReplicationShardStats; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; +import org.opensearch.transport.TransportService; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static java.util.Arrays.asList; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationStatsIT extends SegmentReplicationBaseIT { + + public void testSegmentReplicationStatsResponse() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + String dataNode = internalCluster().startDataOnlyNode(); + String anotherDataNode = internalCluster().startDataOnlyNode(); + + int numShards = 4; + assertAcked( + prepareCreate( + INDEX_NAME, + 0, + Settings.builder() + .put("number_of_shards", numShards) + .put("number_of_replicas", 1) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + ) + ); + ensureGreen(); + final long numDocs = scaledRandomIntBetween(50, 100); + for (int i = 0; i < numDocs; i++) { + index(INDEX_NAME, "doc", Integer.toString(i)); + } + refresh(INDEX_NAME); + ensureSearchable(INDEX_NAME); + + assertBusy(() -> { + SegmentReplicationStatsResponse segmentReplicationStatsResponse = dataNodeClient().admin() + .indices() + .prepareSegmentReplicationStats(INDEX_NAME) + .setDetailed(true) + .execute() + .actionGet(); + SegmentReplicationPerGroupStats perGroupStats = segmentReplicationStatsResponse.getReplicationStats().get(INDEX_NAME).get(0); + final SegmentReplicationState currentReplicationState = perGroupStats.getReplicaStats() + .stream() + .findFirst() + .get() + .getCurrentReplicationState(); + assertEquals(segmentReplicationStatsResponse.getReplicationStats().size(), 1); + assertEquals(segmentReplicationStatsResponse.getTotalShards(), numShards * 2); + assertEquals(segmentReplicationStatsResponse.getSuccessfulShards(), numShards * 2); + assertNotNull(currentReplicationState); + assertEquals(currentReplicationState.getStage(), SegmentReplicationState.Stage.DONE); + assertTrue(currentReplicationState.getIndex().recoveredFileCount() > 0); + }, 1, TimeUnit.MINUTES); + } + + public void testSegmentReplicationStatsResponseForActiveOnly() throws Exception { + final String primaryNode = internalCluster().startNode(); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replicaNode = internalCluster().startNode(); + ensureGreen(INDEX_NAME); + + // index 10 docs + for (int i = 0; i < 10; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + refresh(INDEX_NAME); + + // index 10 more docs + waitForSearchableDocs(10L, asList(primaryNode, replicaNode)); + for (int i = 10; i < 20; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + final CountDownLatch waitForReplication = new CountDownLatch(1); + + final CountDownLatch waitForAssertions = new CountDownLatch(1); + // Mock transport service to add behaviour of waiting in GET_SEGMENT_FILES Stage of a segment replication event. + MockTransportService mockTransportService = ((MockTransportService) internalCluster().getInstance( + TransportService.class, + replicaNode + )); + mockTransportService.addSendBehavior( + internalCluster().getInstance(TransportService.class, primaryNode), + (connection, requestId, action, request, options) -> { + if (action.equals(SegmentReplicationSourceService.Actions.GET_SEGMENT_FILES)) { + waitForReplication.countDown(); + try { + waitForAssertions.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + connection.sendRequest(requestId, action, request, options); + } + ); + refresh(INDEX_NAME); + try { + waitForReplication.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + // verifying active_only by checking if current stage is GET_FILES STAGE + SegmentReplicationStatsResponse activeOnlyResponse = client().admin() + .indices() + .prepareSegmentReplicationStats(INDEX_NAME) + .setActiveOnly(true) + .setDetailed(true) + .execute() + .actionGet(); + SegmentReplicationPerGroupStats perGroupStats = activeOnlyResponse.getReplicationStats().get(INDEX_NAME).get(0); + SegmentReplicationState.Stage stage = perGroupStats.getReplicaStats() + .stream() + .findFirst() + .get() + .getCurrentReplicationState() + .getStage(); + assertEquals(SegmentReplicationState.Stage.GET_FILES, stage); + waitForAssertions.countDown(); + } + + public void testNonDetailedResponse() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + int numReplicas = 4; + List nodes = new ArrayList<>(); + final String primaryNode = internalCluster().startNode(); + nodes.add(primaryNode); + createIndex( + INDEX_NAME, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .build() + ); + ensureYellow(INDEX_NAME); + for (int i = 0; i < numReplicas; i++) { + nodes.add(internalCluster().startNode()); + } + ensureGreen(INDEX_NAME); + + final long numDocs = scaledRandomIntBetween(50, 100); + for (int i = 0; i < numDocs; i++) { + index(INDEX_NAME, "doc", Integer.toString(i)); + } + refresh(INDEX_NAME); + waitForSearchableDocs(numDocs, nodes); + + final IndexShard indexShard = getIndexShard(primaryNode, INDEX_NAME); + + assertBusy(() -> { + SegmentReplicationStatsResponse segmentReplicationStatsResponse = dataNodeClient().admin() + .indices() + .prepareSegmentReplicationStats(INDEX_NAME) + .execute() + .actionGet(); + + final Map> replicationStats = segmentReplicationStatsResponse + .getReplicationStats(); + assertEquals(1, replicationStats.size()); + final List replicationPerGroupStats = replicationStats.get(INDEX_NAME); + assertEquals(1, replicationPerGroupStats.size()); + final SegmentReplicationPerGroupStats perGroupStats = replicationPerGroupStats.get(0); + assertEquals(perGroupStats.getShardId(), indexShard.shardId()); + final Set replicaStats = perGroupStats.getReplicaStats(); + assertEquals(4, replicaStats.size()); + for (SegmentReplicationShardStats replica : replicaStats) { + assertNotNull(replica.getCurrentReplicationState()); + } + }); + } + + public void testGetSpecificShard() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + List nodes = new ArrayList<>(); + final String primaryNode = internalCluster().startNode(); + nodes.add(primaryNode); + createIndex( + INDEX_NAME, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) + .build() + ); + ensureYellowAndNoInitializingShards(INDEX_NAME); + nodes.add(internalCluster().startNode()); + ensureGreen(INDEX_NAME); + + final long numDocs = scaledRandomIntBetween(50, 100); + for (int i = 0; i < numDocs; i++) { + index(INDEX_NAME, "doc", Integer.toString(i)); + } + refresh(INDEX_NAME); + waitForSearchableDocs(numDocs, nodes); + + final IndexShard indexShard = getIndexShard(primaryNode, INDEX_NAME); + + // search for all + SegmentReplicationStatsResponse segmentReplicationStatsResponse = client().admin() + .indices() + .prepareSegmentReplicationStats(INDEX_NAME) + .setActiveOnly(true) + .execute() + .actionGet(); + + Map> replicationStats = segmentReplicationStatsResponse.getReplicationStats(); + assertEquals(1, replicationStats.size()); + List replicationPerGroupStats = replicationStats.get(INDEX_NAME); + assertEquals(2, replicationPerGroupStats.size()); + for (SegmentReplicationPerGroupStats group : replicationPerGroupStats) { + assertEquals(1, group.getReplicaStats().size()); + } + + // now search for one shard. + final int id = indexShard.shardId().getId(); + segmentReplicationStatsResponse = client().admin() + .indices() + .prepareSegmentReplicationStats(INDEX_NAME) + .setActiveOnly(true) + .shards(String.valueOf(id)) + .execute() + .actionGet(); + + replicationStats = segmentReplicationStatsResponse.getReplicationStats(); + assertEquals(1, replicationStats.size()); + replicationPerGroupStats = replicationStats.get(INDEX_NAME); + assertEquals(1, replicationPerGroupStats.size()); + for (SegmentReplicationPerGroupStats group : replicationPerGroupStats) { + assertEquals(group.getShardId(), indexShard.shardId()); + assertEquals(1, group.getReplicaStats().size()); + } + + } + + public void testMultipleIndices() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + final String index_2 = "tst-index-2"; + List nodes = new ArrayList<>(); + final String primaryNode = internalCluster().startNode(); + nodes.add(primaryNode); + createIndex(INDEX_NAME, index_2); + + ensureYellowAndNoInitializingShards(INDEX_NAME, index_2); + nodes.add(internalCluster().startNode()); + ensureGreen(INDEX_NAME, index_2); + + final long numDocs = scaledRandomIntBetween(50, 100); + for (int i = 0; i < numDocs; i++) { + index(INDEX_NAME, "doc", Integer.toString(i)); + index(index_2, "doc", Integer.toString(i)); + } + refresh(INDEX_NAME, index_2); + waitForSearchableDocs(INDEX_NAME, numDocs, nodes); + waitForSearchableDocs(index_2, numDocs, nodes); + + final IndexShard index_1_primary = getIndexShard(primaryNode, INDEX_NAME); + final IndexShard index_2_primary = getIndexShard(primaryNode, index_2); + + assertTrue(index_1_primary.routingEntry().primary()); + assertTrue(index_2_primary.routingEntry().primary()); + + // test both indices are returned in the response. + SegmentReplicationStatsResponse segmentReplicationStatsResponse = client().admin() + .indices() + .prepareSegmentReplicationStats() + .execute() + .actionGet(); + + Map> replicationStats = segmentReplicationStatsResponse.getReplicationStats(); + assertEquals(2, replicationStats.size()); + List replicationPerGroupStats = replicationStats.get(INDEX_NAME); + assertEquals(1, replicationPerGroupStats.size()); + SegmentReplicationPerGroupStats perGroupStats = replicationPerGroupStats.get(0); + assertEquals(perGroupStats.getShardId(), index_1_primary.shardId()); + Set replicaStats = perGroupStats.getReplicaStats(); + assertEquals(1, replicaStats.size()); + for (SegmentReplicationShardStats replica : replicaStats) { + assertNotNull(replica.getCurrentReplicationState()); + } + + replicationPerGroupStats = replicationStats.get(index_2); + assertEquals(1, replicationPerGroupStats.size()); + perGroupStats = replicationPerGroupStats.get(0); + assertEquals(perGroupStats.getShardId(), index_2_primary.shardId()); + replicaStats = perGroupStats.getReplicaStats(); + assertEquals(1, replicaStats.size()); + for (SegmentReplicationShardStats replica : replicaStats) { + assertNotNull(replica.getCurrentReplicationState()); + } + + // test only single index queried. + segmentReplicationStatsResponse = client().admin() + .indices() + .prepareSegmentReplicationStats() + .setIndices(index_2) + .execute() + .actionGet(); + assertEquals(1, segmentReplicationStatsResponse.getReplicationStats().size()); + assertTrue(segmentReplicationStatsResponse.getReplicationStats().containsKey(index_2)); + } + + public void testQueryAgainstDocRepIndex() { + internalCluster().startClusterManagerOnlyNode(); + List nodes = new ArrayList<>(); + final String primaryNode = internalCluster().startNode(); + nodes.add(primaryNode); + createIndex( + INDEX_NAME, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 2) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT) + .build() + ); + ensureYellowAndNoInitializingShards(INDEX_NAME); + nodes.add(internalCluster().startNode()); + ensureGreen(INDEX_NAME); + + final long numDocs = scaledRandomIntBetween(50, 100); + for (int i = 0; i < numDocs; i++) { + index(INDEX_NAME, "doc", Integer.toString(i)); + } + refresh(INDEX_NAME); + + // search for all + SegmentReplicationStatsResponse segmentReplicationStatsResponse = client().admin() + .indices() + .prepareSegmentReplicationStats(INDEX_NAME) + .execute() + .actionGet(); + assertTrue(segmentReplicationStatsResponse.getReplicationStats().isEmpty()); + } + + public void testSegmentReplicationNodeAndIndexStats() throws Exception { + logger.info("--> start primary node"); + final String primaryNode = internalCluster().startNode(); + + logger.info("--> create index on node: {}", primaryNode); + assertAcked(prepareCreate(INDEX_NAME, Settings.builder().put(indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2))); + + ensureYellow(); + logger.info("--> start first replica node"); + final String replicaNode1 = internalCluster().startNode(); + + logger.info("--> start second replica node"); + final String replicaNode2 = internalCluster().startNode(); + + ensureGreen(); + CountDownLatch latch = new CountDownLatch(1); + // block replication + try (final Releasable ignored = blockReplication(List.of(replicaNode1, replicaNode2), latch)) { + // index another doc while blocked, this would not get replicated to the replicas. + Thread indexingThread = new Thread(() -> { + client().prepareIndex(INDEX_NAME).setId("2").setSource("foo2", randomInt()).get(); + refresh(INDEX_NAME); + }); + + indexingThread.start(); + indexingThread.join(); + latch.await(); + + NodesStatsResponse nodesStatsResponse = client().admin() + .cluster() + .prepareNodesStats() + .clear() + .setIndices(new CommonStatsFlags(CommonStatsFlags.Flag.Segments)) + .get(); + + for (NodeStats nodeStats : nodesStatsResponse.getNodes()) { + ReplicationStats replicationStats = nodeStats.getIndices().getSegments().getReplicationStats(); + // primary node - should hold replication statistics + if (nodeStats.getNode().getName().equals(primaryNode)) { + assertTrue(replicationStats.getMaxBytesBehind() > 0); + assertTrue(replicationStats.getTotalBytesBehind() > 0); + assertTrue(replicationStats.getMaxReplicationLag() > 0); + // 2 replicas so total bytes should be double of max + assertEquals(replicationStats.getMaxBytesBehind() * 2, replicationStats.getTotalBytesBehind()); + } + // replica nodes - should hold empty replication statistics + if (nodeStats.getNode().getName().equals(replicaNode1) || nodeStats.getNode().getName().equals(replicaNode2)) { + assertEquals(0, replicationStats.getMaxBytesBehind()); + assertEquals(0, replicationStats.getTotalBytesBehind()); + assertEquals(0, replicationStats.getMaxReplicationLag()); + } + } + // get replication statistics at index level + IndicesStatsResponse stats = client().admin().indices().prepareStats().execute().actionGet(); + + // stats should be of non-zero value when aggregated at index level + ReplicationStats indexReplicationStats = stats.getIndex(INDEX_NAME).getTotal().getSegments().getReplicationStats(); + assertNotNull(indexReplicationStats); + assertTrue(indexReplicationStats.getMaxBytesBehind() > 0); + assertTrue(indexReplicationStats.getTotalBytesBehind() > 0); + assertTrue(indexReplicationStats.getMaxReplicationLag() > 0); + assertEquals(2 * indexReplicationStats.getMaxBytesBehind(), indexReplicationStats.getTotalBytesBehind()); + } + + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationSuiteIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationSuiteIT.java new file mode 100644 index 0000000000000..800704eae7fa7 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/replication/SegmentReplicationSuiteIT.java @@ -0,0 +1,88 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.replication; + +import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.Before; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, minNumDataNodes = 2) +public class SegmentReplicationSuiteIT extends SegmentReplicationBaseIT { + + @Before + public void setup() { + internalCluster().startClusterManagerOnlyNode(); + createIndex(INDEX_NAME); + } + + @Override + public Settings indexSettings() { + final Settings.Builder builder = Settings.builder() + .put(super.indexSettings()) + // reset shard & replica count to random values set by OpenSearchIntegTestCase. + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numberOfShards()) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numberOfReplicas()) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT); + + // TODO: Randomly enable remote store on these tests. + return builder.build(); + } + + public void testBasicReplication() throws Exception { + final int docCount = scaledRandomIntBetween(10, 200); + for (int i = 0; i < docCount; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().get(); + } + refresh(); + ensureGreen(INDEX_NAME); + verifyStoreContent(); + } + + public void testDropRandomNodeDuringReplication() throws Exception { + internalCluster().ensureAtLeastNumDataNodes(2); + internalCluster().startClusterManagerOnlyNodes(1); + + final int docCount = scaledRandomIntBetween(10, 200); + for (int i = 0; i < docCount; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().get(); + } + refresh(); + + internalCluster().restartRandomDataNode(); + + ensureYellow(INDEX_NAME); + client().prepareIndex(INDEX_NAME).setId(Integer.toString(docCount)).setSource("field", "value" + docCount).execute().get(); + internalCluster().startDataOnlyNode(); + client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).actionGet(); + } + + public void testDeleteIndexWhileReplicating() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + final int docCount = scaledRandomIntBetween(10, 200); + for (int i = 0; i < docCount; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().get(); + } + refresh(INDEX_NAME); + client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).actionGet(); + } + + public void testFullRestartDuringReplication() throws Exception { + internalCluster().startNode(); + final int docCount = scaledRandomIntBetween(10, 200); + for (int i = 0; i < docCount; i++) { + client().prepareIndex(INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().get(); + } + refresh(INDEX_NAME); + internalCluster().fullRestart(); + ensureGreen(INDEX_NAME); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/settings/ArchivedIndexSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/settings/ArchivedIndexSettingsIT.java new file mode 100644 index 0000000000000..8dc343abf8da2 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/indices/settings/ArchivedIndexSettingsIT.java @@ -0,0 +1,147 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.indices.settings; + +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.opensearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK; +import static org.hamcrest.Matchers.startsWith; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0, supportsDedicatedMasters = false) +public class ArchivedIndexSettingsIT extends OpenSearchIntegTestCase { + private volatile boolean installPlugin; + + public void testArchiveSettings() throws Exception { + installPlugin = true; + // Set up the cluster with an index containing dummy setting(owned by dummy plugin) + String oldClusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + String oldDataNode = internalCluster().startDataOnlyNode(); + assertEquals(2, internalCluster().numDataAndClusterManagerNodes()); + createIndex("test"); + ensureYellow(); + // Add a dummy setting + client().admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().put("index.dummy", "foobar").put("index.dummy2", "foobar")) + .execute() + .actionGet(); + + // Remove dummy plugin and replace the cluster manager node so that the stale plugin setting moves to "archived". + installPlugin = false; + String newClusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(oldClusterManagerNode)); + internalCluster().restartNode(newClusterManagerNode); + + // Verify that archived settings exists. + assertBusy(() -> { + // Verify that cluster state is in recovered state. + assertFalse(client().admin().cluster().prepareState().get().getState().blocks().hasGlobalBlock(STATE_NOT_RECOVERED_BLOCK)); + assertTrue( + client().admin() + .indices() + .prepareGetSettings("test") + .get() + .getIndexToSettings() + .get("test") + .hasValue("archived.index.dummy") + ); + assertTrue( + client().admin() + .indices() + .prepareGetSettings("test") + .get() + .getIndexToSettings() + .get("test") + .hasValue("archived.index.dummy2") + ); + }, 30, TimeUnit.SECONDS); + + // Archived setting update should fail on open index. + IllegalArgumentException exception = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().putNull("archived.index.dummy")) + .execute() + .actionGet() + ); + assertThat( + exception.getMessage(), + startsWith("Can't update non dynamic settings [[archived.index.dummy]] for open indices [[test") + ); + + // close the index. + client().admin().indices().prepareClose("test").get(); + + // Remove archived.index.dummy explicitly. + assertTrue( + client().admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().putNull("archived.index.dummy")) + .execute() + .actionGet() + .isAcknowledged() + ); + + // Remove archived.index.dummy2 using wildcard. + assertTrue( + client().admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().putNull("archived.*")) + .execute() + .actionGet() + .isAcknowledged() + ); + + // Verify that archived settings are cleaned up successfully. + assertFalse( + client().admin().indices().prepareGetSettings("test").get().getIndexToSettings().get("test").hasValue("archived.index.dummy") + ); + assertFalse( + client().admin().indices().prepareGetSettings("test").get().getIndexToSettings().get("test").hasValue("archived.index.dummy2") + ); + } + + @Override + protected Collection> nodePlugins() { + return installPlugin ? Arrays.asList(DummySettingPlugin.class) : Collections.emptyList(); + } + + public static class DummySettingPlugin extends Plugin { + public static final Setting DUMMY_SETTING = Setting.simpleString( + "index.dummy", + Setting.Property.IndexScope, + Setting.Property.Dynamic + ); + public static final Setting DUMMY_SETTING2 = Setting.simpleString( + "index.dummy2", + Setting.Property.IndexScope, + Setting.Property.Dynamic + ); + + @Override + public List> getSettings() { + return Arrays.asList(DUMMY_SETTING, DUMMY_SETTING2); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/settings/UpdateNumberOfReplicasIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/settings/UpdateNumberOfReplicasIT.java index f78ecd82834c2..c73168ec6ad17 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/settings/UpdateNumberOfReplicasIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/settings/UpdateNumberOfReplicasIT.java @@ -33,9 +33,11 @@ package org.opensearch.indices.settings; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.admin.indices.template.delete.DeleteIndexTemplateRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; +import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Priority; @@ -606,4 +608,135 @@ public void testUpdateNumberOfReplicasAllowNoIndices() { assertThat(numberOfReplicas, equalTo(0)); } + public void testAwarenessReplicaBalance() { + createIndex("aware-replica", Settings.builder().put("index.number_of_replicas", 0).build()); + createIndex(".system-index", Settings.builder().put("index.number_of_replicas", 0).build()); + manageReplicaBalanceSetting(true); + int updated = 0; + + try { + // replica count of 1 is ideal + client().admin() + .indices() + .prepareUpdateSettings("aware-replica") + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)) + .execute() + .actionGet(); + updated++; + + // Since auto expand replica setting take precedence, this should pass + client().admin() + .indices() + .prepareUpdateSettings("aware-replica") + .setSettings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2) + .put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-1") + ) + .execute() + .actionGet(); + updated++; + + // system index - should be able to update + client().admin() + .indices() + .prepareUpdateSettings(".system-index") + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2)) + .execute() + .actionGet(); + updated++; + + client().admin() + .indices() + .prepareUpdateSettings("aware-replica") + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2)) + .execute() + .actionGet(); + fail("should have thrown an exception about the replica count"); + + } catch (IllegalArgumentException e) { + assertEquals( + "Validation Failed: 1: expected total copies needs to be a multiple of total awareness attributes [2];", + e.getMessage() + ); + assertEquals(3, updated); + } finally { + manageReplicaBalanceSetting(false); + } + } + + public void testAwarenessReplicaBalanceWithUseZoneForDefaultReplicaCount() { + createIndex("aware-replica", Settings.builder().put("index.number_of_replicas", 0).build()); + createIndex(".system-index", Settings.builder().put("index.number_of_replicas", 0).build()); + DeleteIndexTemplateRequestBuilder deleteTemplate = client().admin().indices().prepareDeleteTemplate("random_index_template"); + assertAcked(deleteTemplate.execute().actionGet()); + manageReplicaSettingForDefaultReplica(true); + int updated = 0; + + try { + + // replica count should not be changed and we should see the original replica count + client().admin() + .indices() + .prepareUpdateSettings("aware-replica") + .setSettings(Settings.builder().put("refresh_interval", "1s")) + .execute() + .actionGet(); + updated++; + + final ClusterState state = client().admin().cluster().prepareState().get().getState(); + final IndexMetadata newIndex = state.metadata().index("aware-replica"); + assertThat(newIndex.getNumberOfReplicas(), equalTo(0)); + + // replica count of 2 is ideal + client().admin() + .indices() + .prepareUpdateSettings("aware-replica") + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2)) + .execute() + .actionGet(); + updated++; + + // Since auto expand replica setting take precedence, this should pass + client().admin() + .indices() + .prepareUpdateSettings("aware-replica") + .setSettings( + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) + .put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-2") + ) + .execute() + .actionGet(); + updated++; + + // system index - should be able to update + client().admin() + .indices() + .prepareUpdateSettings(".system-index") + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 3)) + .execute() + .actionGet(); + updated++; + + client().admin() + .indices() + .prepareUpdateSettings("aware-replica") + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)) + .execute() + .actionGet(); + fail("should have thrown an exception about the replica count"); + + } catch (IllegalArgumentException e) { + assertEquals( + "Validation Failed: 1: expected total copies needs to be a multiple of total awareness attributes [3];", + e.getMessage() + ); + assertEquals(4, updated); + } finally { + manageReplicaSettingForDefaultReplica(false); + randomIndexTemplate(); + } + } + } diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/settings/UpdateSettingsIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/settings/UpdateSettingsIT.java index 6dab7781e08db..6e94c50eec42a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/settings/UpdateSettingsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/settings/UpdateSettingsIT.java @@ -136,11 +136,9 @@ public static class DummySettingPlugin extends Plugin { @Override public void onIndexModule(IndexModule indexModule) { - indexModule.addSettingsUpdateConsumer( - DUMMY_SETTING, - (s) -> {}, - (s) -> { if (s.equals("boom")) throw new IllegalArgumentException("this setting goes boom"); } - ); + indexModule.addSettingsUpdateConsumer(DUMMY_SETTING, (s) -> {}, (s) -> { + if (s.equals("boom")) throw new IllegalArgumentException("this setting goes boom"); + }); } @Override @@ -836,7 +834,7 @@ private void runTestDefaultNumberOfReplicasTest(final boolean closeIndex) { public void testNoopUpdate() { internalCluster().ensureAtLeastNumDataNodes(2); - final ClusterService clusterService = internalCluster().getMasterNodeInstance(ClusterService.class); + final ClusterService clusterService = internalCluster().getClusterManagerNodeInstance(ClusterService.class); assertAcked( client().admin() .indices() diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseIndexDisableCloseAllIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseIndexDisableCloseAllIT.java index 9fa78811017be..3579ec61f0120 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseIndexDisableCloseAllIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseIndexDisableCloseAllIT.java @@ -36,7 +36,6 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; import org.opensearch.test.OpenSearchIntegTestCase; - import org.junit.After; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseIndexIT.java index 41749a9bfd0f4..ae88dd76d54e0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseIndexIT.java @@ -46,9 +46,9 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.util.set.Sets; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.IndexSettings; import org.opensearch.index.shard.IndexShard; @@ -56,8 +56,8 @@ import org.opensearch.indices.IndicesService; import org.opensearch.indices.recovery.RecoveryState; import org.opensearch.test.BackgroundIndexer; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.ArrayList; import java.util.Arrays; @@ -462,7 +462,7 @@ public void testRecoverExistingReplica() throws Exception { internalCluster().ensureAtLeastNumDataNodes(2); List dataNodes = randomSubsetOf( 2, - Sets.newHashSet(clusterService().state().nodes().getDataNodes().valuesIt()) + Sets.newHashSet(clusterService().state().nodes().getDataNodes().values().iterator()) .stream() .map(DiscoveryNode::getName) .collect(Collectors.toSet()) diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseWhileRelocatingShardsIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseWhileRelocatingShardsIT.java index caf741e9b8882..d6dce78061a7a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseWhileRelocatingShardsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/state/CloseWhileRelocatingShardsIT.java @@ -48,7 +48,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.ConcurrentCollections; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.indices.recovery.PeerRecoverySourceService; import org.opensearch.indices.recovery.StartRecoveryRequest; import org.opensearch.plugins.Plugin; @@ -147,10 +147,13 @@ public void testCloseWhileRelocatingShards() throws Exception { ); final String targetNode = internalCluster().startDataOnlyNode(); - ensureClusterSizeConsistency(); // wait for the master to finish processing join. + ensureClusterSizeConsistency(); // wait for the cluster-manager to finish processing join. try { - final ClusterService clusterService = internalCluster().getInstance(ClusterService.class, internalCluster().getMasterName()); + final ClusterService clusterService = internalCluster().getInstance( + ClusterService.class, + internalCluster().getClusterManagerName() + ); final ClusterState state = clusterService.state(); final CountDownLatch latch = new CountDownLatch(indices.length); final CountDownLatch release = new CountDownLatch(indices.length); diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/state/OpenCloseIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/state/OpenCloseIndexIT.java index ca1e1399f8fdc..0bf561c606a2d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/state/OpenCloseIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/state/OpenCloseIndexIT.java @@ -44,12 +44,11 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.Client; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.rest.RestStatus; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchIntegTestCase; import java.io.IOException; @@ -301,16 +300,15 @@ public void testOpenWaitingForActiveShardsFailed() throws Exception { } public void testOpenCloseWithDocs() throws IOException, ExecutionException, InterruptedException { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("test") - .field("type", "keyword") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("test") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + .toString(); assertAcked(client().admin().indices().prepareCreate("test").setMapping(mapping)); ensureGreen(); diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/state/ReopenWhileClosingIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/state/ReopenWhileClosingIT.java index 38e50a64c8105..e93bd68dca583 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/state/ReopenWhileClosingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/state/ReopenWhileClosingIT.java @@ -32,17 +32,17 @@ package org.opensearch.indices.state; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.indices.close.CloseIndexResponse; import org.opensearch.action.admin.indices.close.TransportVerifyShardBeforeCloseAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.Glob; -import org.opensearch.common.Strings; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.RunOnce; +import org.opensearch.core.common.Strings; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; @@ -150,7 +150,7 @@ private void createIndexWithDocs(final String indexName, final Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -562,8 +589,8 @@ public void testNonThrottleStats() throws Exception { prepareCreate("test").setSettings( settingsBuilder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, "1") .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "0") - .put(MergePolicyConfig.INDEX_MERGE_POLICY_MAX_MERGE_AT_ONCE_SETTING.getKey(), "2") - .put(MergePolicyConfig.INDEX_MERGE_POLICY_SEGMENTS_PER_TIER_SETTING.getKey(), "2") + .put(TieredMergePolicyProvider.INDEX_MERGE_POLICY_MAX_MERGE_AT_ONCE_SETTING.getKey(), "2") + .put(TieredMergePolicyProvider.INDEX_MERGE_POLICY_SEGMENTS_PER_TIER_SETTING.getKey(), "2") .put(MergeSchedulerConfig.MAX_THREAD_COUNT_SETTING.getKey(), "1") .put(MergeSchedulerConfig.MAX_MERGE_COUNT_SETTING.getKey(), "10000") ) @@ -594,8 +621,8 @@ public void testThrottleStats() throws Exception { prepareCreate("test").setSettings( settingsBuilder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, "1") .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, "0") - .put(MergePolicyConfig.INDEX_MERGE_POLICY_MAX_MERGE_AT_ONCE_SETTING.getKey(), "2") - .put(MergePolicyConfig.INDEX_MERGE_POLICY_SEGMENTS_PER_TIER_SETTING.getKey(), "2") + .put(TieredMergePolicyProvider.INDEX_MERGE_POLICY_MAX_MERGE_AT_ONCE_SETTING.getKey(), "2") + .put(TieredMergePolicyProvider.INDEX_MERGE_POLICY_SEGMENTS_PER_TIER_SETTING.getKey(), "2") .put(MergeSchedulerConfig.MAX_THREAD_COUNT_SETTING.getKey(), "1") .put(MergeSchedulerConfig.MAX_MERGE_COUNT_SETTING.getKey(), "1") .put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Translog.Durability.ASYNC.name()) @@ -1011,7 +1038,10 @@ public void testCompletionFieldsParam() throws Exception { ); ensureGreen(); - client().prepareIndex("test1").setId(Integer.toString(1)).setSource("{\"bar\":\"bar\",\"baz\":\"baz\"}", XContentType.JSON).get(); + client().prepareIndex("test1") + .setId(Integer.toString(1)) + .setSource("{\"bar\":\"bar\",\"baz\":\"baz\"}", MediaTypeRegistry.JSON) + .get(); refresh(); IndicesStatsRequestBuilder builder = client().admin().indices().prepareStats(); @@ -1319,7 +1349,7 @@ public void testFilterCacheStats() throws Exception { * Test that we can safely concurrently index and get stats. This test was inspired by a serialization issue that arose due to a race * getting doc stats during heavy indexing. The race could lead to deleted docs being negative which would then be serialized as a * variable-length long. Since serialization of negative longs using a variable-length format was unsupported - * ({@link org.opensearch.common.io.stream.StreamOutput#writeVLong(long)}), the stream would become corrupted. Here, we want to test + * ({@link StreamOutput#writeVLong(long)}), the stream would become corrupted. Here, we want to test * that we can continue to get stats while indexing. */ public void testConcurrentIndexingAndStatsRequests() throws BrokenBarrierException, InterruptedException, ExecutionException { @@ -1356,7 +1386,7 @@ public void testConcurrentIndexingAndStatsRequests() throws BrokenBarrierExcepti } while (!stop.get()) { final String id = Integer.toString(idGenerator.incrementAndGet()); - final IndexResponse response = client().prepareIndex("test").setId(id).setSource("{}", XContentType.JSON).get(); + final IndexResponse response = client().prepareIndex("test").setId(id).setSource("{}", MediaTypeRegistry.JSON).get(); assertThat(response.getResult(), equalTo(DocWriteResponse.Result.CREATED)); } }); @@ -1414,6 +1444,50 @@ public void testConcurrentIndexingAndStatsRequests() throws BrokenBarrierExcepti assertThat(executionFailures.get(), emptyCollectionOf(Exception.class)); } + public void testZeroRemoteStoreStatsOnNonRemoteStoreIndex() { + String indexName = "test-index"; + createIndex(indexName, Settings.builder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0).build()); + ensureGreen(indexName); + assertEquals( + RestStatus.CREATED, + client().prepareIndex(indexName) + .setId(UUIDs.randomBase64UUID()) + .setSource("field", "value1", "field2", "value1") + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) + .get() + .status() + ); + ShardStats shard = client().admin().indices().prepareStats(indexName).setSegments(true).setTranslog(true).get().getShards()[0]; + RemoteSegmentStats remoteSegmentStatsFromIndexStats = shard.getStats().getSegments().getRemoteSegmentStats(); + assertZeroRemoteSegmentStats(remoteSegmentStatsFromIndexStats); + RemoteTranslogStats remoteTranslogStatsFromIndexStats = shard.getStats().getTranslog().getRemoteTranslogStats(); + assertZeroRemoteTranslogStats(remoteTranslogStatsFromIndexStats); + + NodesStatsResponse nodesStatsResponse = client().admin().cluster().prepareNodesStats(primaryNodeName(indexName)).get(); + RemoteSegmentStats remoteSegmentStatsFromNodesStats = nodesStatsResponse.getNodes() + .get(0) + .getIndices() + .getSegments() + .getRemoteSegmentStats(); + assertZeroRemoteSegmentStats(remoteSegmentStatsFromNodesStats); + RemoteTranslogStats remoteTranslogStatsFromNodesStats = nodesStatsResponse.getNodes() + .get(0) + .getIndices() + .getTranslog() + .getRemoteTranslogStats(); + assertZeroRemoteTranslogStats(remoteTranslogStatsFromNodesStats); + } + + private void assertZeroRemoteSegmentStats(RemoteSegmentStats remoteSegmentStats) { + // Compare with fresh object because all values default to 0 in default fresh object + assertEquals(new RemoteSegmentStats(), remoteSegmentStats); + } + + private void assertZeroRemoteTranslogStats(RemoteTranslogStats remoteTranslogStats) { + // Compare with fresh object because all values default to 0 in default fresh object + assertEquals(new RemoteTranslogStats(), remoteTranslogStats); + } + /** * Persist the global checkpoint on all shards of the given index into disk. * This makes sure that the persisted global checkpoint on those shards will equal to the in-memory value. @@ -1430,4 +1504,37 @@ private void persistGlobalCheckpoint(String index) throws Exception { } } } + + public void testSegmentReplicationStats() { + String indexName = "test-index"; + createIndex( + indexName, + Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 1).build() + ); + + ensureGreen(indexName); + + IndicesStatsRequestBuilder builder = client().admin().indices().prepareStats(); + IndicesStatsResponse stats = builder.execute().actionGet(); + + // document replication enabled index should return empty segment replication stats + assertNotNull(stats.getIndex(indexName).getTotal().getSegments().getReplicationStats()); + + indexName = "test-index2"; + createIndex( + indexName, + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build() + ); + ensureGreen(indexName); + + builder = client().admin().indices().prepareStats(); + stats = builder.execute().actionGet(); + + // segment replication enabled index should return segment replication stats + assertNotNull(stats.getIndex(indexName).getTotal().getSegments().getReplicationStats()); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/store/IndicesStoreIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/store/IndicesStoreIntegrationIT.java index b85afa80496d0..00f74559ebbf6 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/store/IndicesStoreIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/store/IndicesStoreIntegrationIT.java @@ -52,17 +52,17 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; -import org.opensearch.index.Index; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.indices.recovery.PeerRecoveryTargetService; import org.opensearch.plugins.Plugin; +import org.opensearch.test.InternalTestCluster; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalTestCluster; import org.opensearch.test.disruption.BlockClusterStateProcessing; import org.opensearch.test.transport.MockTransportService; import org.opensearch.transport.ConnectTransportException; @@ -79,8 +79,8 @@ import java.util.concurrent.TimeUnit; import static java.lang.Thread.sleep; +import static org.opensearch.test.NodeRoles.nonClusterManagerNode; import static org.opensearch.test.NodeRoles.nonDataNode; -import static org.opensearch.test.NodeRoles.nonMasterNode; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; @@ -105,14 +105,14 @@ protected Collection> nodePlugins() { @Override protected void ensureClusterStateConsistency() throws IOException { - // testShardActiveElseWhere might change the state of a non-master node + // testShardActiveElseWhere might change the state of a non-cluster-manager node // so we cannot check state consistency of this cluster } public void testIndexCleanup() throws Exception { internalCluster().startNode(nonDataNode()); - final String node_1 = internalCluster().startNode(nonMasterNode()); - final String node_2 = internalCluster().startNode(nonMasterNode()); + final String node_1 = internalCluster().startNode(nonClusterManagerNode()); + final String node_2 = internalCluster().startNode(nonClusterManagerNode()); logger.info("--> creating index [test] with one shard and on replica"); assertAcked( prepareCreate("test").setSettings( @@ -133,7 +133,7 @@ public void testIndexCleanup() throws Exception { assertThat(Files.exists(indexDirectory(node_2, index)), equalTo(true)); logger.info("--> starting node server3"); - final String node_3 = internalCluster().startNode(nonMasterNode()); + final String node_3 = internalCluster().startNode(nonClusterManagerNode()); logger.info("--> running cluster_health"); ClusterHealthResponse clusterHealth = client().admin() .cluster() @@ -345,7 +345,7 @@ public void testShardsCleanup() throws Exception { } public void testShardActiveElsewhereDoesNotDeleteAnother() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final List nodes = internalCluster().startDataOnlyNodes(4); final String node1 = nodes.get(0); @@ -459,11 +459,11 @@ public void testShardActiveElsewhereDoesNotDeleteAnother() throws Exception { public void testShardActiveElseWhere() throws Exception { List nodes = internalCluster().startNodes(2); - final String masterNode = internalCluster().getMasterName(); - final String nonMasterNode = nodes.get(0).equals(masterNode) ? nodes.get(1) : nodes.get(0); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + final String nonClusterManagerNode = nodes.get(0).equals(clusterManagerNode) ? nodes.get(1) : nodes.get(0); - final String masterId = internalCluster().clusterService(masterNode).localNode().getId(); - final String nonMasterId = internalCluster().clusterService(nonMasterNode).localNode().getId(); + final String clusterManagerId = internalCluster().clusterService(clusterManagerNode).localNode().getId(); + final String nonClusterManagerId = internalCluster().clusterService(nonClusterManagerNode).localNode().getId(); final int numShards = scaledRandomIntBetween(2, 10); assertAcked( @@ -476,14 +476,14 @@ public void testShardActiveElseWhere() throws Exception { waitNoPendingTasksOnAll(); ClusterStateResponse stateResponse = client().admin().cluster().prepareState().get(); final Index index = stateResponse.getState().metadata().index("test").getIndex(); - RoutingNode routingNode = stateResponse.getState().getRoutingNodes().node(nonMasterId); + RoutingNode routingNode = stateResponse.getState().getRoutingNodes().node(nonClusterManagerId); final int[] node2Shards = new int[routingNode.numberOfOwningShards()]; int i = 0; for (ShardRouting shardRouting : routingNode) { node2Shards[i] = shardRouting.shardId().id(); i++; } - logger.info("Node [{}] has shards: {}", nonMasterNode, Arrays.toString(node2Shards)); + logger.info("Node [{}] has shards: {}", nonClusterManagerNode, Arrays.toString(node2Shards)); // disable relocations when we do this, to make sure the shards are not relocated from node2 // due to rebalancing, and delete its content @@ -496,14 +496,14 @@ public void testShardActiveElseWhere() throws Exception { ) .get(); - ClusterApplierService clusterApplierService = internalCluster().getInstance(ClusterService.class, nonMasterNode) + ClusterApplierService clusterApplierService = internalCluster().getInstance(ClusterService.class, nonClusterManagerNode) .getClusterApplierService(); ClusterState currentState = clusterApplierService.state(); IndexRoutingTable.Builder indexRoutingTableBuilder = IndexRoutingTable.builder(index); for (int j = 0; j < numShards; j++) { indexRoutingTableBuilder.addIndexShard( new IndexShardRoutingTable.Builder(new ShardId(index, j)).addShard( - TestShardRouting.newShardRouting("test", j, masterId, true, ShardRoutingState.STARTED) + TestShardRouting.newShardRouting("test", j, clusterManagerId, true, ShardRoutingState.STARTED) ).build() ); } @@ -528,7 +528,7 @@ public void onFailure(String source, Exception e) { waitNoPendingTasksOnAll(); logger.info("Checking if shards aren't removed"); for (int shard : node2Shards) { - assertShardExists(nonMasterNode, index, shard); + assertShardExists(nonClusterManagerNode, index, shard); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/template/IndexTemplateBlocksIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/template/IndexTemplateBlocksIT.java index 21e3e58d4d091..f2852de865b27 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/template/IndexTemplateBlocksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/template/IndexTemplateBlocksIT.java @@ -34,7 +34,6 @@ import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.template.get.GetIndexTemplatesResponse; - import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; diff --git a/server/src/internalClusterTest/java/org/opensearch/indices/template/SimpleIndexTemplateIT.java b/server/src/internalClusterTest/java/org/opensearch/indices/template/SimpleIndexTemplateIT.java index 0e15a0c895432..2421b97991b1c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/indices/template/SimpleIndexTemplateIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/indices/template/SimpleIndexTemplateIT.java @@ -38,25 +38,25 @@ import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; import org.opensearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.opensearch.action.admin.indices.template.put.PutIndexTemplateRequestBuilder; - import org.opensearch.action.bulk.BulkResponse; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.AliasMetadata; -import org.opensearch.common.ParsingException; -import org.opensearch.common.bytes.BytesArray; +import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.mapper.MapperParsingException; import org.opensearch.index.query.QueryBuilders; import org.opensearch.indices.InvalidAliasNameException; +import org.opensearch.indices.InvalidIndexTemplateException; import org.opensearch.plugins.Plugin; import org.opensearch.search.SearchHit; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; - +import org.opensearch.test.OpenSearchIntegTestCase; import org.junit.After; import java.io.IOException; @@ -477,7 +477,7 @@ public void testBrokenMapping() throws Exception { .indices() .preparePutTemplate("template_1") .setPatterns(Collections.singletonList("te*")) - .setMapping("{\"foo\": \"abcde\"}", XContentType.JSON) + .setMapping("{\"foo\": \"abcde\"}", MediaTypeRegistry.JSON) .get() ); assertThat(e.getMessage(), containsString("Failed to parse mapping ")); @@ -590,7 +590,7 @@ public void testIndexTemplateWithAliasesInSource() { + " }\n" + "}" ), - XContentType.JSON + MediaTypeRegistry.JSON ) .get(); @@ -802,8 +802,8 @@ public void testStrictAliasParsingInIndicesCreatedViaTemplates() throws Exceptio .addAlias(new Alias("alias4").filter(termQuery("field", "value"))) .get(); - client().prepareIndex("a1").setId("test").setSource("{}", XContentType.JSON).get(); - BulkResponse response = client().prepareBulk().add(new IndexRequest("a2").id("test").source("{}", XContentType.JSON)).get(); + client().prepareIndex("a1").setId("test").setSource("{}", MediaTypeRegistry.JSON).get(); + BulkResponse response = client().prepareBulk().add(new IndexRequest("a2").id("test").source("{}", MediaTypeRegistry.JSON)).get(); assertThat(response.hasFailures(), is(false)); assertThat(response.getItems()[0].isFailed(), equalTo(false)); assertThat(response.getItems()[0].getIndex(), equalTo("a2")); @@ -818,9 +818,9 @@ public void testStrictAliasParsingInIndicesCreatedViaTemplates() throws Exceptio // So the aliases defined in the index template for this index will not fail // even though the fields in the alias fields don't exist yet and indexing into // an index that doesn't exist yet will succeed - client().prepareIndex("b1").setId("test").setSource("{}", XContentType.JSON).get(); + client().prepareIndex("b1").setId("test").setSource("{}", MediaTypeRegistry.JSON).get(); - response = client().prepareBulk().add(new IndexRequest("b2").id("test").source("{}", XContentType.JSON)).get(); + response = client().prepareBulk().add(new IndexRequest("b2").id("test").source("{}", MediaTypeRegistry.JSON)).get(); assertThat(response.hasFailures(), is(false)); assertThat(response.getItems()[0].isFailed(), equalTo(false)); assertThat(response.getItems()[0].getId(), equalTo("test")); @@ -853,7 +853,7 @@ public void testCombineTemplates() throws Exception { + " }\n" + " }\n" + " }\n", - XContentType.JSON + MediaTypeRegistry.JSON ) .get(); @@ -991,7 +991,7 @@ public void testPartitionedTemplate() throws Exception { .indices() .preparePutTemplate("template_2") .setPatterns(Collections.singletonList("te*")) - .setMapping("{\"_routing\":{\"required\":false}}", XContentType.JSON) + .setMapping("{\"_routing\":{\"required\":false}}", MediaTypeRegistry.JSON) .setSettings(Settings.builder().put("index.number_of_shards", "6").put("index.routing_partition_size", "3")) .get() ); @@ -1029,4 +1029,52 @@ public void testPartitionedTemplate() throws Exception { GetSettingsResponse getSettingsResponse = client().admin().indices().prepareGetSettings("test_good").get(); assertEquals("6", getSettingsResponse.getIndexToSettings().get("test_good").get("index.routing_partition_size")); } + + public void testAwarenessReplicaBalance() throws IOException { + manageReplicaSettingForDefaultReplica(true); + int updated = 0; + try { + client().admin() + .indices() + .preparePutTemplate("template_1") + .setPatterns(Arrays.asList("a*", "b*")) + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 2)) + .get(); + updated++; + + client().admin() + .indices() + .preparePutTemplate("template_1") + .setPatterns(Arrays.asList("a*", "b*")) + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 4)) + .get(); + updated++; + + client().admin() + .indices() + .preparePutTemplate("template_1") + .setPatterns(Arrays.asList("a*", "b*")) + .setSettings(Settings.builder().put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-2")) + .get(); + updated++; + + client().admin() + .indices() + .preparePutTemplate("template_1") + .setPatterns(Arrays.asList("a*", "b*")) + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)) + .get(); + + fail("should have thrown an exception about the replica count"); + } catch (InvalidIndexTemplateException e) { + assertEquals( + "index_template [template_1] invalid, cause [Validation Failed: 1: expected total copies needs to be a multiple of total awareness attributes [3];]", + e.getMessage() + ); + assertEquals(3, updated); + } finally { + manageReplicaSettingForDefaultReplica(false); + } + } + } diff --git a/server/src/internalClusterTest/java/org/opensearch/ingest/IngestClientIT.java b/server/src/internalClusterTest/java/org/opensearch/ingest/IngestClientIT.java index 2f666bbd65d4d..e2cedea331412 100644 --- a/server/src/internalClusterTest/java/org/opensearch/ingest/IngestClientIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/ingest/IngestClientIT.java @@ -32,9 +32,9 @@ package org.opensearch.ingest; +import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; import org.opensearch.OpenSearchParseException; -import org.opensearch.ExceptionsHelper; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.bulk.BulkItemResponse; import org.opensearch.action.bulk.BulkRequest; @@ -51,10 +51,10 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.action.update.UpdateRequest; import org.opensearch.client.Requests; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; @@ -100,7 +100,7 @@ public void testSimulate() throws Exception { .endArray() .endObject() ); - client().admin().cluster().preparePutPipeline("_id", pipelineSource, XContentType.JSON).get(); + client().admin().cluster().preparePutPipeline("_id", pipelineSource, MediaTypeRegistry.JSON).get(); GetPipelineResponse getResponse = client().admin().cluster().prepareGetPipeline("_id").get(); assertThat(getResponse.isFound(), is(true)); assertThat(getResponse.pipelines().size(), equalTo(1)); @@ -111,7 +111,6 @@ public void testSimulate() throws Exception { .startArray("docs") .startObject() .field("_index", "index") - .field("_type", "type") .field("_id", "id") .startObject("_source") .field("foo", "bar") @@ -123,9 +122,9 @@ public void testSimulate() throws Exception { ); SimulatePipelineResponse response; if (randomBoolean()) { - response = client().admin().cluster().prepareSimulatePipeline(bytes, XContentType.JSON).setId("_id").get(); + response = client().admin().cluster().prepareSimulatePipeline(bytes, MediaTypeRegistry.JSON).setId("_id").get(); } else { - SimulatePipelineRequest request = new SimulatePipelineRequest(bytes, XContentType.JSON); + SimulatePipelineRequest request = new SimulatePipelineRequest(bytes, MediaTypeRegistry.JSON); request.setId("_id"); response = client().admin().cluster().simulatePipeline(request).get(); } @@ -161,7 +160,7 @@ public void testBulkWithIngestFailures() throws Exception { .endArray() .endObject() ); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id", source, XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id", source, MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); int numRequests = scaledRandomIntBetween(32, 128); @@ -212,7 +211,7 @@ public void testBulkWithUpsert() throws Exception { .endArray() .endObject() ); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id", source, XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id", source, MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); BulkRequest bulkRequest = new BulkRequest(); @@ -221,7 +220,7 @@ public void testBulkWithUpsert() throws Exception { bulkRequest.add(indexRequest); UpdateRequest updateRequest = new UpdateRequest("index", "2"); updateRequest.doc("{}", Requests.INDEX_CONTENT_TYPE); - updateRequest.upsert("{\"field1\":\"upserted_val\"}", XContentType.JSON).upsertRequest().setPipeline("_id"); + updateRequest.upsert("{\"field1\":\"upserted_val\"}", MediaTypeRegistry.JSON).upsertRequest().setPipeline("_id"); bulkRequest.add(updateRequest); BulkResponse response = client().bulk(bulkRequest).actionGet(); @@ -247,7 +246,7 @@ public void test() throws Exception { .endArray() .endObject() ); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id", source, XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id", source, MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); GetPipelineRequest getPipelineRequest = new GetPipelineRequest("_id"); @@ -291,7 +290,7 @@ public void testPutWithPipelineFactoryError() throws Exception { .endArray() .endObject() ); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id2", source, XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id2", source, MediaTypeRegistry.JSON); Exception e = expectThrows( OpenSearchParseException.class, () -> client().admin().cluster().putPipeline(putPipelineRequest).actionGet() @@ -302,8 +301,8 @@ public void testPutWithPipelineFactoryError() throws Exception { assertFalse(response.isFound()); } - public void testWithDedicatedMaster() throws Exception { - String masterOnlyNode = internalCluster().startMasterOnlyNode(); + public void testWithDedicatedClusterManager() throws Exception { + String clusterManagerOnlyNode = internalCluster().startClusterManagerOnlyNode(); BytesReference source = BytesReference.bytes( jsonBuilder().startObject() .field("description", "my_pipeline") @@ -315,10 +314,10 @@ public void testWithDedicatedMaster() throws Exception { .endArray() .endObject() ); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id", source, XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("_id", source, MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); - BulkItemResponse item = client(masterOnlyNode).prepareBulk() + BulkItemResponse item = client(clusterManagerOnlyNode).prepareBulk() .add(client().prepareIndex("test").setSource("field", "value2", "drop", true).setPipeline("_id")) .get() .getItems()[0]; @@ -341,7 +340,7 @@ public void testPipelineOriginHeader() throws Exception { source.endArray(); } source.endObject(); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("1", BytesReference.bytes(source), XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("1", BytesReference.bytes(source), MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); } { @@ -358,7 +357,7 @@ public void testPipelineOriginHeader() throws Exception { source.endArray(); } source.endObject(); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("2", BytesReference.bytes(source), XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("2", BytesReference.bytes(source), MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); } { @@ -374,13 +373,13 @@ public void testPipelineOriginHeader() throws Exception { source.endArray(); } source.endObject(); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("3", BytesReference.bytes(source), XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("3", BytesReference.bytes(source), MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); } Exception e = expectThrows(Exception.class, () -> { IndexRequest indexRequest = new IndexRequest("test"); - indexRequest.source("{}", XContentType.JSON); + indexRequest.source("{}", MediaTypeRegistry.JSON); indexRequest.setPipeline("1"); client().index(indexRequest).get(); }); @@ -414,7 +413,7 @@ public void testPipelineProcessorOnFailure() throws Exception { source.endArray(); } source.endObject(); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("1", BytesReference.bytes(source), XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("1", BytesReference.bytes(source), MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); } { @@ -431,7 +430,7 @@ public void testPipelineProcessorOnFailure() throws Exception { source.endArray(); } source.endObject(); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("2", BytesReference.bytes(source), XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("2", BytesReference.bytes(source), MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); } { @@ -447,11 +446,11 @@ public void testPipelineProcessorOnFailure() throws Exception { source.endArray(); } source.endObject(); - PutPipelineRequest putPipelineRequest = new PutPipelineRequest("3", BytesReference.bytes(source), XContentType.JSON); + PutPipelineRequest putPipelineRequest = new PutPipelineRequest("3", BytesReference.bytes(source), MediaTypeRegistry.JSON); client().admin().cluster().putPipeline(putPipelineRequest).get(); } - client().prepareIndex("test").setId("1").setSource("{}", XContentType.JSON).setPipeline("1").get(); + client().prepareIndex("test").setId("1").setSource("{}", MediaTypeRegistry.JSON).setPipeline("1").get(); Map inserted = client().prepareGet("test", "1").get().getSourceAsMap(); assertThat(inserted.get("readme"), equalTo("pipeline with id [3] is a bad pipeline")); } diff --git a/server/src/internalClusterTest/java/org/opensearch/ingest/IngestProcessorNotInstalledOnAllNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/ingest/IngestProcessorNotInstalledOnAllNodesIT.java index 1aed4de79cd63..38f1375bc7504 100644 --- a/server/src/internalClusterTest/java/org/opensearch/ingest/IngestProcessorNotInstalledOnAllNodesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/ingest/IngestProcessorNotInstalledOnAllNodesIT.java @@ -34,8 +34,8 @@ import org.opensearch.OpenSearchParseException; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.node.NodeService; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; @@ -84,20 +84,20 @@ public void testFailPipelineCreation() throws Exception { ensureStableCluster(2, node2); try { - client().admin().cluster().preparePutPipeline("_id", pipelineSource, XContentType.JSON).get(); + client().admin().cluster().preparePutPipeline("_id", pipelineSource, MediaTypeRegistry.JSON).get(); fail("exception expected"); } catch (OpenSearchParseException e) { assertThat(e.getMessage(), containsString("Processor type [test] is not installed on node")); } } - public void testFailPipelineCreationProcessorNotInstalledOnMasterNode() throws Exception { + public void testFailPipelineCreationProcessorNotInstalledOnClusterManagerNode() throws Exception { internalCluster().startNode(); installPlugin = true; internalCluster().startNode(); try { - client().admin().cluster().preparePutPipeline("_id", pipelineSource, XContentType.JSON).get(); + client().admin().cluster().preparePutPipeline("_id", pipelineSource, MediaTypeRegistry.JSON).get(); fail("exception expected"); } catch (OpenSearchParseException e) { assertThat(e.getMessage(), equalTo("No processor type exists with name [test]")); @@ -110,7 +110,7 @@ public void testFailStartNode() throws Exception { installPlugin = true; String node1 = internalCluster().startNode(); - AcknowledgedResponse response = client().admin().cluster().preparePutPipeline("_id", pipelineSource, XContentType.JSON).get(); + AcknowledgedResponse response = client().admin().cluster().preparePutPipeline("_id", pipelineSource, MediaTypeRegistry.JSON).get(); assertThat(response.isAcknowledged(), is(true)); Pipeline pipeline = internalCluster().getInstance(NodeService.class, node1).getIngestService().getPipeline("_id"); assertThat(pipeline, notNullValue()); diff --git a/server/src/internalClusterTest/java/org/opensearch/mget/SimpleMgetIT.java b/server/src/internalClusterTest/java/org/opensearch/mget/SimpleMgetIT.java index c9d18e64ca038..f77ae80a55276 100644 --- a/server/src/internalClusterTest/java/org/opensearch/mget/SimpleMgetIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/mget/SimpleMgetIT.java @@ -32,6 +32,8 @@ package org.opensearch.mget; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.get.MultiGetItemResponse; @@ -39,17 +41,21 @@ import org.opensearch.action.get.MultiGetRequestBuilder; import org.opensearch.action.get.MultiGetResponse; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.search.fetch.subphase.FetchSourceContext; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import java.util.Map; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -57,7 +63,24 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; -public class SimpleMgetIT extends OpenSearchIntegTestCase { +public class SimpleMgetIT extends ParameterizedOpenSearchIntegTestCase { + + public SimpleMgetIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } public void testThatMgetShouldWorkWithOneIndexMissing() throws IOException { createIndex("test"); @@ -159,7 +182,7 @@ public void testThatSourceFilteringIsSupported() throws Exception { .endObject() ); for (int i = 0; i < 100; i++) { - client().prepareIndex("test").setId(Integer.toString(i)).setSource(sourceBytesRef, XContentType.JSON).get(); + client().prepareIndex("test").setId(Integer.toString(i)).setSource(sourceBytesRef, MediaTypeRegistry.JSON).get(); } MultiGetRequestBuilder request = client().prepareMultiGet(); diff --git a/server/src/internalClusterTest/java/org/opensearch/nodesinfo/SimpleNodesInfoIT.java b/server/src/internalClusterTest/java/org/opensearch/nodesinfo/SimpleNodesInfoIT.java index 086badb52a46c..7e066a610b82c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/nodesinfo/SimpleNodesInfoIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/nodesinfo/SimpleNodesInfoIT.java @@ -45,9 +45,7 @@ import java.util.List; import static org.opensearch.action.admin.cluster.node.info.NodesInfoRequest.Metric.INDICES; - import static org.opensearch.client.Requests.nodesInfoRequest; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; diff --git a/server/src/internalClusterTest/java/org/opensearch/nodestats/NodeStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/nodestats/NodeStatsIT.java new file mode 100644 index 0000000000000..f270cb1399072 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/nodestats/NodeStatsIT.java @@ -0,0 +1,276 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.nodestats; + +import org.opensearch.ExceptionsHelper; +import org.opensearch.action.DocWriteResponse; +import org.opensearch.action.bulk.BulkItemResponse; +import org.opensearch.action.bulk.BulkRequest; +import org.opensearch.action.bulk.BulkResponse; +import org.opensearch.action.delete.DeleteRequest; +import org.opensearch.action.delete.DeleteResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.update.UpdateRequest; +import org.opensearch.action.update.UpdateResponse; +import org.opensearch.index.IndexNotFoundException; +import org.opensearch.index.engine.DocumentMissingException; +import org.opensearch.index.engine.VersionConflictEngineException; +import org.opensearch.index.shard.IndexingStats.Stats.DocStatusStats; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; +import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import org.hamcrest.MatcherAssert; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; + +@ClusterScope(scope = Scope.TEST, numDataNodes = 1, numClientNodes = 0, supportsDedicatedMasters = false) +public class NodeStatsIT extends OpenSearchIntegTestCase { + + private final DocStatusStats expectedDocStatusStats = new DocStatusStats(); + private static final String FIELD = "dummy_field"; + private static final String VALUE = "dummy_value"; + private static final Map SOURCE = singletonMap(FIELD, VALUE); + + public void testNodeIndicesStatsDocStatusStatsIndexBulk() { + { // Testing Index + final String INDEX = "test_index"; + final String ID = "id"; + { // Testing Normal Index + IndexResponse response = client().index(new IndexRequest(INDEX).id(ID).source(SOURCE)).actionGet(); + updateExpectedDocStatusCounter(response); + + MatcherAssert.assertThat(response.getResult(), equalTo(DocWriteResponse.Result.CREATED)); + assertDocStatusStats(); + } + { // Testing Missing Alias + updateExpectedDocStatusCounter( + expectThrows( + IndexNotFoundException.class, + () -> client().index(new IndexRequest(INDEX).id("missing_alias").setRequireAlias(true).source(SOURCE)).actionGet() + ) + ); + assertDocStatusStats(); + } + { + // Test Missing Pipeline: Ingestion failure, not Indexing failure + expectThrows( + IllegalArgumentException.class, + () -> client().index(new IndexRequest(INDEX).id("missing_pipeline").setPipeline("missing").source(SOURCE)).actionGet() + ); + assertDocStatusStats(); + } + { // Testing Version Conflict + final String docId = "version_conflict"; + + updateExpectedDocStatusCounter(client().index(new IndexRequest(INDEX).id(docId).source(SOURCE)).actionGet()); + updateExpectedDocStatusCounter( + expectThrows( + VersionConflictEngineException.class, + () -> client().index(new IndexRequest(INDEX).id(docId).source(SOURCE).setIfSeqNo(1L).setIfPrimaryTerm(99L)) + .actionGet() + ) + ); + assertDocStatusStats(); + } + } + { // Testing Bulk + final String INDEX = "bulk_index"; + + int sizeOfIndexRequests = scaledRandomIntBetween(10, 20); + int sizeOfDeleteRequests = scaledRandomIntBetween(5, sizeOfIndexRequests); + int sizeOfNotFoundRequests = scaledRandomIntBetween(5, sizeOfIndexRequests); + + BulkRequest bulkRequest = new BulkRequest(); + + for (int i = 0; i < sizeOfIndexRequests; ++i) { + bulkRequest.add(new IndexRequest(INDEX).id(String.valueOf(i)).source(SOURCE)); + } + + BulkResponse response = client().bulk(bulkRequest).actionGet(); + + MatcherAssert.assertThat(response.hasFailures(), equalTo(false)); + MatcherAssert.assertThat(response.getItems().length, equalTo(sizeOfIndexRequests)); + + for (BulkItemResponse itemResponse : response.getItems()) { + updateExpectedDocStatusCounter(itemResponse.getResponse()); + } + + refresh(INDEX); + bulkRequest.requests().clear(); + + for (int i = 0; i < sizeOfDeleteRequests; ++i) { + bulkRequest.add(new DeleteRequest(INDEX, String.valueOf(i))); + } + for (int i = 0; i < sizeOfNotFoundRequests; ++i) { + bulkRequest.add(new DeleteRequest(INDEX, String.valueOf(25 + i))); + } + + response = client().bulk(bulkRequest).actionGet(); + + MatcherAssert.assertThat(response.hasFailures(), equalTo(false)); + MatcherAssert.assertThat(response.getItems().length, equalTo(sizeOfDeleteRequests + sizeOfNotFoundRequests)); + + for (BulkItemResponse itemResponse : response.getItems()) { + updateExpectedDocStatusCounter(itemResponse.getResponse()); + } + + refresh(INDEX); + assertDocStatusStats(); + } + } + + public void testNodeIndicesStatsDocStatusStatsCreateDeleteUpdate() { + { // Testing Create + final String INDEX = "create_index"; + final String ID = "id"; + { // Testing Creation + IndexResponse response = client().index(new IndexRequest(INDEX).id(ID).source(SOURCE).create(true)).actionGet(); + updateExpectedDocStatusCounter(response); + + MatcherAssert.assertThat(response.getResult(), equalTo(DocWriteResponse.Result.CREATED)); + assertDocStatusStats(); + } + { // Testing Version Conflict + final String docId = "version_conflict"; + + updateExpectedDocStatusCounter(client().index(new IndexRequest(INDEX).id(docId).source(SOURCE)).actionGet()); + updateExpectedDocStatusCounter( + expectThrows( + VersionConflictEngineException.class, + () -> client().index(new IndexRequest(INDEX).id(docId).source(SOURCE).create(true)).actionGet() + ) + ); + assertDocStatusStats(); + } + } + { // Testing Delete + final String INDEX = "delete_index"; + final String ID = "id"; + { // Testing Deletion + IndexResponse response = client().index(new IndexRequest(INDEX).id(ID).source(SOURCE)).actionGet(); + updateExpectedDocStatusCounter(response); + + DeleteResponse deleteResponse = client().delete(new DeleteRequest(INDEX, ID)).actionGet(); + updateExpectedDocStatusCounter(deleteResponse); + + MatcherAssert.assertThat(response.getSeqNo(), greaterThanOrEqualTo(0L)); + MatcherAssert.assertThat(deleteResponse.getResult(), equalTo(DocWriteResponse.Result.DELETED)); + assertDocStatusStats(); + } + { // Testing Non-Existing Doc + updateExpectedDocStatusCounter(client().delete(new DeleteRequest(INDEX, "does_not_exist")).actionGet()); + assertDocStatusStats(); + } + { // Testing Version Conflict + final String docId = "version_conflict"; + + updateExpectedDocStatusCounter(client().index(new IndexRequest(INDEX).id(docId).source(SOURCE)).actionGet()); + updateExpectedDocStatusCounter( + expectThrows( + VersionConflictEngineException.class, + () -> client().delete(new DeleteRequest(INDEX, docId).setIfSeqNo(2L).setIfPrimaryTerm(99L)).actionGet() + ) + ); + + assertDocStatusStats(); + } + } + { // Testing Update + final String INDEX = "update_index"; + final String ID = "id"; + { // Testing Not Found + updateExpectedDocStatusCounter( + expectThrows( + DocumentMissingException.class, + () -> client().update(new UpdateRequest(INDEX, ID).doc(SOURCE)).actionGet() + ) + ); + assertDocStatusStats(); + } + { // Testing NoOp Update + updateExpectedDocStatusCounter(client().index(new IndexRequest(INDEX).id(ID).source(SOURCE)).actionGet()); + + UpdateResponse response = client().update(new UpdateRequest(INDEX, ID).doc(SOURCE)).actionGet(); + updateExpectedDocStatusCounter(response); + + MatcherAssert.assertThat(response.getResult(), equalTo(DocWriteResponse.Result.NOOP)); + assertDocStatusStats(); + } + { // Testing Update + final String UPDATED_VALUE = "updated_value"; + UpdateResponse response = client().update(new UpdateRequest(INDEX, ID).doc(singletonMap(FIELD, UPDATED_VALUE))).actionGet(); + updateExpectedDocStatusCounter(response); + + MatcherAssert.assertThat(response.getResult(), equalTo(DocWriteResponse.Result.UPDATED)); + assertDocStatusStats(); + } + { // Testing Missing Alias + updateExpectedDocStatusCounter( + expectThrows( + IndexNotFoundException.class, + () -> client().update(new UpdateRequest(INDEX, ID).setRequireAlias(true).doc(new IndexRequest().source(SOURCE))) + .actionGet() + ) + ); + assertDocStatusStats(); + } + { // Testing Version Conflict + final String docId = "version_conflict"; + + updateExpectedDocStatusCounter(client().index(new IndexRequest(INDEX).id(docId).source(SOURCE)).actionGet()); + updateExpectedDocStatusCounter( + expectThrows( + VersionConflictEngineException.class, + () -> client().update(new UpdateRequest(INDEX, docId).doc(SOURCE).setIfSeqNo(2L).setIfPrimaryTerm(99L)).actionGet() + ) + ); + assertDocStatusStats(); + } + } + } + + private void assertDocStatusStats() { + DocStatusStats docStatusStats = client().admin() + .cluster() + .prepareNodesStats() + .execute() + .actionGet() + .getNodes() + .get(0) + .getIndices() + .getIndexing() + .getTotal() + .getDocStatusStats(); + + assertTrue( + Arrays.equals( + docStatusStats.getDocStatusCounter(), + expectedDocStatusStats.getDocStatusCounter(), + Comparator.comparingLong(AtomicLong::longValue) + ) + ); + } + + private void updateExpectedDocStatusCounter(DocWriteResponse r) { + expectedDocStatusStats.inc(r.status()); + } + + private void updateExpectedDocStatusCounter(Exception e) { + expectedDocStatusStats.inc(ExceptionsHelper.status(e)); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/operateAllIndices/DestructiveOperationsIT.java b/server/src/internalClusterTest/java/org/opensearch/operateAllIndices/DestructiveOperationsIT.java index 7a19f3c832312..4732456cb092e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/operateAllIndices/DestructiveOperationsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/operateAllIndices/DestructiveOperationsIT.java @@ -32,7 +32,6 @@ package org.opensearch.operateAllIndices; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.action.support.DestructiveOperations; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; @@ -109,8 +108,8 @@ public void testCloseIndexDefaultBehaviour() throws Exception { } ClusterState state = client().admin().cluster().prepareState().get().getState(); - for (ObjectObjectCursor indexMetadataObjectObjectCursor : state.getMetadata().indices()) { - assertEquals(IndexMetadata.State.CLOSE, indexMetadataObjectObjectCursor.value.getState()); + for (final IndexMetadata indexMetadataObjectObjectCursor : state.getMetadata().indices().values()) { + assertEquals(IndexMetadata.State.CLOSE, indexMetadataObjectObjectCursor.getState()); } } @@ -141,8 +140,8 @@ public void testOpenIndexDefaultBehaviour() throws Exception { } ClusterState state = client().admin().cluster().prepareState().get().getState(); - for (ObjectObjectCursor indexMetadataObjectObjectCursor : state.getMetadata().indices()) { - assertEquals(IndexMetadata.State.OPEN, indexMetadataObjectObjectCursor.value.getState()); + for (final IndexMetadata indexMetadataObjectObjectCursor : state.getMetadata().indices().values()) { + assertEquals(IndexMetadata.State.OPEN, indexMetadataObjectObjectCursor.getState()); } } } diff --git a/server/src/internalClusterTest/java/org/opensearch/persistent/PersistentTasksExecutorIT.java b/server/src/internalClusterTest/java/org/opensearch/persistent/PersistentTasksExecutorIT.java index 9ea80ae7dbd89..312b98a8dd918 100644 --- a/server/src/internalClusterTest/java/org/opensearch/persistent/PersistentTasksExecutorIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/persistent/PersistentTasksExecutorIT.java @@ -38,6 +38,7 @@ import org.opensearch.common.UUIDs; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.tasks.TaskId; import org.opensearch.persistent.PersistentTasksCustomMetadata.PersistentTask; import org.opensearch.persistent.PersistentTasksService.WaitForPersistentTaskListener; import org.opensearch.persistent.TestPersistentTasksPlugin.State; @@ -45,7 +46,6 @@ import org.opensearch.persistent.TestPersistentTasksPlugin.TestPersistentTasksExecutor; import org.opensearch.persistent.TestPersistentTasksPlugin.TestTasksRequestBuilder; import org.opensearch.plugins.Plugin; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.test.OpenSearchIntegTestCase; import org.junit.After; @@ -224,7 +224,7 @@ public void testPersistentActionWithNoAvailableNode() throws Exception { public void testPersistentActionWithNonClusterStateCondition() throws Exception { PersistentTasksClusterService persistentTasksClusterService = internalCluster().getInstance( PersistentTasksClusterService.class, - internalCluster().getMasterName() + internalCluster().getClusterManagerName() ); // Speed up rechecks to a rate that is quicker than what settings would allow persistentTasksClusterService.setRecheckInterval(TimeValue.timeValueMillis(1)); @@ -384,7 +384,7 @@ public void testCreatePersistentTaskWithDuplicateId() throws Exception { public void testUnassignRunningPersistentTask() throws Exception { PersistentTasksClusterService persistentTasksClusterService = internalCluster().getInstance( PersistentTasksClusterService.class, - internalCluster().getMasterName() + internalCluster().getClusterManagerName() ); // Speed up rechecks to a rate that is quicker than what settings would allow persistentTasksClusterService.setRecheckInterval(TimeValue.timeValueMillis(1)); @@ -403,7 +403,7 @@ public void testUnassignRunningPersistentTask() throws Exception { PlainActionFuture> unassignmentFuture = new PlainActionFuture<>(); - // Disallow re-assignment after it is unallocated to verify master and node state + // Disallow re-assignment after it is unallocated to verify cluster-manager and node state TestPersistentTasksExecutor.setNonClusterStateCondition(false); persistentTasksClusterService.unassignPersistentTask(taskId, task.getAllocationId() + 1, "unassignment test", unassignmentFuture); diff --git a/server/src/internalClusterTest/java/org/opensearch/persistent/decider/EnableAssignmentDeciderIT.java b/server/src/internalClusterTest/java/org/opensearch/persistent/decider/EnableAssignmentDeciderIT.java index 5d75482567731..a749ad54042ab 100644 --- a/server/src/internalClusterTest/java/org/opensearch/persistent/decider/EnableAssignmentDeciderIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/persistent/decider/EnableAssignmentDeciderIT.java @@ -32,11 +32,11 @@ package org.opensearch.persistent.decider; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.admin.cluster.state.ClusterStateResponse; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; import org.opensearch.persistent.PersistentTasksCustomMetadata; import org.opensearch.persistent.PersistentTasksService; import org.opensearch.persistent.TestPersistentTasksPlugin; @@ -87,7 +87,7 @@ public void testEnableAssignmentAfterRestart() throws Exception { } latch.await(); - ClusterService clusterService = internalCluster().clusterService(internalCluster().getMasterName()); + ClusterService clusterService = internalCluster().clusterService(internalCluster().getClusterManagerName()); PersistentTasksCustomMetadata tasks = clusterService.state().getMetadata().custom(PersistentTasksCustomMetadata.TYPE); assertEquals(numberOfTasks, tasks.tasks().stream().filter(t -> TestPersistentTasksExecutor.NAME.equals(t.getTaskName())).count()); diff --git a/server/src/internalClusterTest/java/org/opensearch/recovery/FullRollingRestartIT.java b/server/src/internalClusterTest/java/org/opensearch/recovery/FullRollingRestartIT.java index 15d1f3a0559a8..f636185fd4649 100644 --- a/server/src/internalClusterTest/java/org/opensearch/recovery/FullRollingRestartIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/recovery/FullRollingRestartIT.java @@ -196,10 +196,10 @@ public void testFullRollingRestart() throws Exception { public void testNoRebalanceOnRollingRestart() throws Exception { // see https://github.com/elastic/elasticsearch/issues/14387 - internalCluster().startMasterOnlyNode(Settings.EMPTY); + internalCluster().startClusterManagerOnlyNode(Settings.EMPTY); internalCluster().startDataOnlyNodes(3); /** - * We start 3 nodes and a dedicated master. Restart on of the data-nodes and ensure that we got no relocations. + * We start 3 nodes and a dedicated cluster-manager. Restart on of the data-nodes and ensure that we got no relocations. * Yet we have 6 shards 0 replica so that means if the restarting node comes back both other nodes are subject * to relocating to the restarting node since all had 2 shards and now one node has nothing allocated. * We have a fix for this to wait until we have allocated unallocated shards now so this shouldn't happen. diff --git a/server/src/internalClusterTest/java/org/opensearch/recovery/RecoveryWhileUnderLoadIT.java b/server/src/internalClusterTest/java/org/opensearch/recovery/RecoveryWhileUnderLoadIT.java index 26b3e9ae336dc..30d5af58df545 100644 --- a/server/src/internalClusterTest/java/org/opensearch/recovery/RecoveryWhileUnderLoadIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/recovery/RecoveryWhileUnderLoadIT.java @@ -46,11 +46,11 @@ import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.shard.DocsStats; -import org.opensearch.index.shard.ShardId; import org.opensearch.index.translog.Translog; import org.opensearch.plugins.Plugin; import org.opensearch.search.sort.SortOrder; diff --git a/server/src/internalClusterTest/java/org/opensearch/recovery/RelocationIT.java b/server/src/internalClusterTest/java/org/opensearch/recovery/RelocationIT.java index 06475f1e7ac9d..1f0b4fdf370fe 100644 --- a/server/src/internalClusterTest/java/org/opensearch/recovery/RelocationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/recovery/RelocationIT.java @@ -32,10 +32,8 @@ package org.opensearch.recovery; -import com.carrotsearch.hppc.cursors.ObjectCursor; import org.apache.lucene.index.IndexFileNames; import org.apache.lucene.tests.util.English; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.cluster.reroute.ClusterRerouteResponse; import org.opensearch.action.admin.indices.stats.ShardStats; @@ -53,9 +51,11 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.Priority; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.env.NodeEnvironment; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; @@ -65,18 +65,17 @@ import org.opensearch.index.shard.IndexEventListener; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardState; -import org.opensearch.index.shard.ShardId; +import org.opensearch.indices.recovery.FileChunkRequest; import org.opensearch.indices.recovery.PeerRecoveryTargetService; -import org.opensearch.indices.recovery.RecoveryFileChunkRequest; import org.opensearch.plugins.Plugin; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.test.BackgroundIndexer; +import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.MockIndexEventListener; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalSettingsPlugin; -import org.opensearch.test.MockIndexEventListener; import org.opensearch.test.transport.MockTransportService; import org.opensearch.test.transport.StubbableTransport; import org.opensearch.transport.Transport; @@ -376,12 +375,12 @@ public void indexShardStateChanged( List builders1 = new ArrayList<>(); for (int numDocs = randomIntBetween(10, 30); numDocs > 0; numDocs--) { - builders1.add(client().prepareIndex("test").setSource("{}", XContentType.JSON)); + builders1.add(client().prepareIndex("test").setSource("{}", MediaTypeRegistry.JSON)); } List builders2 = new ArrayList<>(); for (int numDocs = randomIntBetween(10, 30); numDocs > 0; numDocs--) { - builders2.add(client().prepareIndex("test").setSource("{}", XContentType.JSON)); + builders2.add(client().prepareIndex("test").setSource("{}", MediaTypeRegistry.JSON)); } logger.info("--> START relocate the shard from {} to {}", nodes[fromNode], nodes[toNode]); @@ -441,7 +440,7 @@ public void testCancellationCleansTempFiles() throws Exception { List requests = new ArrayList<>(); int numDocs = scaledRandomIntBetween(25, 250); for (int i = 0; i < numDocs; i++) { - requests.add(client().prepareIndex(indexName).setSource("{}", XContentType.JSON)); + requests.add(client().prepareIndex(indexName).setSource("{}", MediaTypeRegistry.JSON)); } indexRandom(true, requests); assertFalse(client().admin().cluster().prepareHealth().setWaitForNodes("3").setWaitForGreenStatus().get().isTimedOut()); @@ -771,8 +770,8 @@ public void testRelocationEstablishedPeerRecoveryRetentionLeases() throws Except private void assertActiveCopiesEstablishedPeerRecoveryRetentionLeases() throws Exception { assertBusy(() -> { - for (ObjectCursor it : client().admin().cluster().prepareState().get().getState().metadata().indices().keys()) { - Map> byShardId = Stream.of(client().admin().indices().prepareStats(it.value).get().getShards()) + for (final String it : client().admin().cluster().prepareState().get().getState().metadata().indices().keySet()) { + Map> byShardId = Stream.of(client().admin().indices().prepareStats(it).get().getShards()) .collect(Collectors.groupingBy(l -> l.getShardRouting().shardId())); for (List shardStats : byShardId.values()) { Set expectedLeaseIds = shardStats.stream() @@ -809,7 +808,7 @@ public void sendRequest( TransportRequestOptions options ) throws IOException { if (action.equals(PeerRecoveryTargetService.Actions.FILE_CHUNK)) { - RecoveryFileChunkRequest chunkRequest = (RecoveryFileChunkRequest) request; + FileChunkRequest chunkRequest = (FileChunkRequest) request; if (chunkRequest.name().startsWith(IndexFileNames.SEGMENTS)) { // corrupting the segments_N files in order to make sure future recovery re-send files logger.debug("corrupting [{}] to {}. file name: [{}]", action, connection.getNode(), chunkRequest.name()); diff --git a/server/src/internalClusterTest/java/org/opensearch/recovery/SimpleRecoveryIT.java b/server/src/internalClusterTest/java/org/opensearch/recovery/SimpleRecoveryIT.java index 4ebb840c600d2..85f90738b19ce 100644 --- a/server/src/internalClusterTest/java/org/opensearch/recovery/SimpleRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/recovery/SimpleRecoveryIT.java @@ -36,7 +36,7 @@ import org.opensearch.action.admin.indices.refresh.RefreshResponse; import org.opensearch.action.get.GetResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.test.OpenSearchIntegTestCase; import static org.opensearch.client.Requests.flushRequest; @@ -44,7 +44,6 @@ import static org.opensearch.client.Requests.indexRequest; import static org.opensearch.client.Requests.refreshRequest; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; - import static org.hamcrest.Matchers.equalTo; public class SimpleRecoveryIT extends OpenSearchIntegTestCase { @@ -67,12 +66,12 @@ public void testSimpleRecovery() throws Exception { NumShards numShards = getNumShards("test"); - client().index(indexRequest("test").id("1").source(source("1", "test"), XContentType.JSON)).actionGet(); + client().index(indexRequest("test").id("1").source(source("1", "test"), MediaTypeRegistry.JSON)).actionGet(); FlushResponse flushResponse = client().admin().indices().flush(flushRequest("test")).actionGet(); assertThat(flushResponse.getTotalShards(), equalTo(numShards.totalNumShards)); assertThat(flushResponse.getSuccessfulShards(), equalTo(numShards.numPrimaries)); assertThat(flushResponse.getFailedShards(), equalTo(0)); - client().index(indexRequest("test").id("2").source(source("2", "test"), XContentType.JSON)).actionGet(); + client().index(indexRequest("test").id("2").source(source("2", "test"), MediaTypeRegistry.JSON)).actionGet(); RefreshResponse refreshResponse = client().admin().indices().refresh(refreshRequest("test")).actionGet(); assertThat(refreshResponse.getTotalShards(), equalTo(numShards.totalNumShards)); assertThat(refreshResponse.getSuccessfulShards(), equalTo(numShards.numPrimaries)); diff --git a/server/src/internalClusterTest/java/org/opensearch/recovery/TruncatedRecoveryIT.java b/server/src/internalClusterTest/java/org/opensearch/recovery/TruncatedRecoveryIT.java index 1708454faf7b3..5f0922615a557 100644 --- a/server/src/internalClusterTest/java/org/opensearch/recovery/TruncatedRecoveryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/recovery/TruncatedRecoveryIT.java @@ -39,11 +39,11 @@ import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.index.query.QueryBuilders; +import org.opensearch.indices.recovery.FileChunkRequest; import org.opensearch.indices.recovery.PeerRecoveryTargetService; -import org.opensearch.indices.recovery.RecoveryFileChunkRequest; import org.opensearch.node.RecoverySettingsChunkSizePlugin; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; @@ -146,7 +146,7 @@ public void testCancelRecoveryAndResume() throws Exception { internalCluster().getInstance(TransportService.class, unluckyNode.getNode().getName()), (connection, requestId, action, request, options) -> { if (action.equals(PeerRecoveryTargetService.Actions.FILE_CHUNK)) { - RecoveryFileChunkRequest req = (RecoveryFileChunkRequest) request; + FileChunkRequest req = (FileChunkRequest) request; logger.info("file chunk [{}] lastChunk: {}", req, req.lastChunk()); if ((req.name().endsWith("cfs") || req.name().endsWith("fdt")) && req.lastChunk() && truncate.get()) { latch.countDown(); diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/AbstractRemoteStoreMockRepositoryIntegTestCase.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/AbstractRemoteStoreMockRepositoryIntegTestCase.java new file mode 100644 index 0000000000000..9d4d8aa24bd51 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/AbstractRemoteStoreMockRepositoryIntegTestCase.java @@ -0,0 +1,185 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.index.IndexResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.UUIDs; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.util.FileSystemUtils; +import org.opensearch.index.IndexModule; +import org.opensearch.index.IndexSettings; +import org.opensearch.index.store.RemoteSegmentStoreDirectory; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.snapshots.AbstractSnapshotIntegTestCase; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.Locale; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.opensearch.indices.IndicesService.CLUSTER_REPLICATION_TYPE_SETTING; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_CLUSTER_STATE_REPOSITORY_NAME_ATTRIBUTE_KEY; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_SEGMENT_REPOSITORY_NAME_ATTRIBUTE_KEY; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_TRANSLOG_REPOSITORY_NAME_ATTRIBUTE_KEY; + +public abstract class AbstractRemoteStoreMockRepositoryIntegTestCase extends AbstractSnapshotIntegTestCase { + + protected static final String REPOSITORY_NAME = "my-segment-repo-1"; + protected static final String TRANSLOG_REPOSITORY_NAME = "my-translog-repo-1"; + protected static final String INDEX_NAME = "remote-store-test-idx-1"; + + @Override + public Settings indexSettings() { + return remoteStoreIndexSettings(0); + } + + protected Settings remoteStoreIndexSettings(int numberOfReplicas) { + return Settings.builder() + .put(super.indexSettings()) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "300s") + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numberOfReplicas) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + } + + public Settings buildRemoteStoreNodeAttributes(Path repoLocation, double ioFailureRate, String skipExceptionBlobList, long maxFailure) { + String segmentRepoTypeAttributeKey = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, + REPOSITORY_NAME + ); + String translogRepoTypeAttributeKey = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, + TRANSLOG_REPOSITORY_NAME + ); + String segmentRepoSettingsAttributeKeyPrefix = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, + REPOSITORY_NAME + ); + String translogRepoSettingsAttributeKeyPrefix = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, + TRANSLOG_REPOSITORY_NAME + ); + String stateRepoTypeAttributeKey = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, + REPOSITORY_NAME + ); + String stateRepoSettingsAttributeKeyPrefix = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, + REPOSITORY_NAME + ); + + return Settings.builder() + .put("node.attr." + REMOTE_STORE_SEGMENT_REPOSITORY_NAME_ATTRIBUTE_KEY, REPOSITORY_NAME) + .put(segmentRepoTypeAttributeKey, "mock") + .put(segmentRepoSettingsAttributeKeyPrefix + "location", repoLocation) + .put(segmentRepoSettingsAttributeKeyPrefix + "random_control_io_exception_rate", ioFailureRate) + .put(segmentRepoSettingsAttributeKeyPrefix + "skip_exception_on_verification_file", true) + .put(segmentRepoSettingsAttributeKeyPrefix + "skip_exception_on_list_blobs", true) + .put(segmentRepoSettingsAttributeKeyPrefix + "skip_exception_on_blobs", skipExceptionBlobList) + .put(segmentRepoSettingsAttributeKeyPrefix + "max_failure_number", maxFailure) + .put("node.attr." + REMOTE_STORE_TRANSLOG_REPOSITORY_NAME_ATTRIBUTE_KEY, TRANSLOG_REPOSITORY_NAME) + .put(translogRepoTypeAttributeKey, "mock") + .put(translogRepoSettingsAttributeKeyPrefix + "location", repoLocation) + .put("node.attr." + REMOTE_STORE_CLUSTER_STATE_REPOSITORY_NAME_ATTRIBUTE_KEY, REPOSITORY_NAME) + .put(stateRepoTypeAttributeKey, "mock") + .put(stateRepoSettingsAttributeKeyPrefix + "location", repoLocation) + .build(); + } + + protected void cleanupRepo() { + logger.info("--> Cleanup the repository={}", REPOSITORY_NAME); + clusterAdmin().prepareCleanupRepository(REPOSITORY_NAME).execute().actionGet(); + logger.info("--> Cleanup the repository={}", TRANSLOG_REPOSITORY_NAME); + clusterAdmin().prepareCleanupRepository(TRANSLOG_REPOSITORY_NAME).execute().actionGet(); + } + + protected String setup(Path repoLocation, double ioFailureRate, String skipExceptionBlobList, long maxFailure) { + // The random_control_io_exception_rate setting ensures that 10-25% of all operations to remote store results in + /// IOException. skip_exception_on_verification_file & skip_exception_on_list_blobs settings ensures that the + // repository creation can happen without failure. + Settings.Builder settings = Settings.builder() + .put(buildRemoteStoreNodeAttributes(repoLocation, ioFailureRate, skipExceptionBlobList, maxFailure)); + + if (randomBoolean()) { + settings.put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.SEGMENT); + } + + disableRepoConsistencyCheck("Remote Store Creates System Repository"); + + internalCluster().startClusterManagerOnlyNode(settings.build()); + String dataNodeName = internalCluster().startDataOnlyNode(settings.build()); + createIndex(INDEX_NAME); + logger.info("--> Created index={}", INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + logger.info("--> Cluster is yellow with no initializing shards"); + ensureGreen(INDEX_NAME); + logger.info("--> Cluster is green"); + return dataNodeName; + } + + /** + * Gets all segment files which starts with "_". For instance, _0.cfe, _o.cfs etc. + * + * @param location the path to location where segment files are being searched. + * @return set of file names of all segment file or empty set if there was IOException thrown. + */ + protected Set getSegmentFiles(Path location) { + try { + return Arrays.stream(FileSystemUtils.files(location)) + .filter(path -> path.getFileName().toString().startsWith("_")) + .map(path -> path.getFileName().toString()) + .map(this::getLocalSegmentFilename) + .collect(Collectors.toSet()); + } catch (IOException exception) { + logger.error("Exception occurred while getting segment files", exception); + } + return Collections.emptySet(); + } + + private String getLocalSegmentFilename(String remoteFilename) { + return remoteFilename.split(RemoteSegmentStoreDirectory.SEGMENT_NAME_UUID_SEPARATOR)[0]; + } + + private IndexResponse indexSingleDoc() { + return client().prepareIndex(INDEX_NAME) + .setId(UUIDs.randomBase64UUID()) + .setSource(randomAlphaOfLength(5), randomAlphaOfLength(5)) + .get(); + } + + protected void indexData(int numberOfIterations, boolean invokeFlush) { + logger.info("--> Indexing data for {} iterations with flush={}", numberOfIterations, invokeFlush); + for (int i = 0; i < numberOfIterations; i++) { + int numberOfOperations = randomIntBetween(1, 5); + logger.info("--> Indexing {} operations in iteration #{}", numberOfOperations, i); + for (int j = 0; j < numberOfOperations; j++) { + indexSingleDoc(); + } + if (invokeFlush) { + flush(INDEX_NAME); + } else { + refresh(INDEX_NAME); + } + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/BaseRemoteStoreRestoreIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/BaseRemoteStoreRestoreIT.java new file mode 100644 index 0000000000000..ad3e99dd274ce --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/BaseRemoteStoreRestoreIT.java @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreRequest; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.support.PlainActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.transport.MockTransportService; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; + +public class BaseRemoteStoreRestoreIT extends RemoteStoreBaseIntegTestCase { + static final String INDEX_NAME = "remote-store-test-idx-1"; + static final String INDEX_NAMES = "test-remote-store-1,test-remote-store-2,remote-store-test-index-1,remote-store-test-index-2"; + static final String INDEX_NAMES_WILDCARD = "test-remote-store-*,remote-store-test-index-*"; + static final String TOTAL_OPERATIONS = "total-operations"; + static final String REFRESHED_OR_FLUSHED_OPERATIONS = "refreshed-or-flushed-operations"; + static final String MAX_SEQ_NO_TOTAL = "max-seq-no-total"; + + @Override + public Settings indexSettings() { + return remoteStoreIndexSettings(0); + } + + public Settings indexSettings(int shards, int replicas) { + return remoteStoreIndexSettings(replicas, shards); + } + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class); + } + + protected void restore(String... indices) { + boolean restoreAllShards = randomBoolean(); + if (restoreAllShards) { + assertAcked(client().admin().indices().prepareClose(indices)); + } + client().admin() + .cluster() + .restoreRemoteStore( + new RestoreRemoteStoreRequest().indices(indices).restoreAllShards(restoreAllShards), + PlainActionFuture.newFuture() + ); + } + + protected void verifyRestoredData(Map indexStats, String indexName) throws Exception { + ensureYellowAndNoInitializingShards(indexName); + ensureGreen(indexName); + // This is to ensure that shards that were already assigned will get latest count + refresh(indexName); + assertBusy( + () -> assertHitCount(client().prepareSearch(indexName).setSize(0).get(), indexStats.get(TOTAL_OPERATIONS)), + 30, + TimeUnit.SECONDS + ); + IndexResponse response = indexSingleDoc(indexName); + if (indexStats.containsKey(MAX_SEQ_NO_TOTAL + "-shard-" + response.getShardId().id())) { + assertEquals(indexStats.get(MAX_SEQ_NO_TOTAL + "-shard-" + response.getShardId().id()) + 1, response.getSeqNo()); + } + refresh(indexName); + assertBusy( + () -> assertHitCount(client().prepareSearch(indexName).setSize(0).get(), indexStats.get(TOTAL_OPERATIONS) + 1), + 30, + TimeUnit.SECONDS + ); + } + + public void prepareCluster(int numClusterManagerNodes, int numDataOnlyNodes, String indices, int replicaCount, int shardCount) { + prepareCluster(numClusterManagerNodes, numDataOnlyNodes, indices, replicaCount, shardCount, Settings.EMPTY); + } + + public void prepareCluster( + int numClusterManagerNodes, + int numDataOnlyNodes, + String indices, + int replicaCount, + int shardCount, + Settings settings + ) { + prepareCluster(numClusterManagerNodes, numDataOnlyNodes, settings); + for (String index : indices.split(",")) { + createIndex(index, remoteStoreIndexSettings(replicaCount, shardCount)); + ensureYellowAndNoInitializingShards(index); + ensureGreen(index); + } + } + + public void prepareCluster(int numClusterManagerNodes, int numDataOnlyNodes, Settings settings) { + internalCluster().startClusterManagerOnlyNodes(numClusterManagerNodes, settings); + internalCluster().startDataOnlyNodes(numDataOnlyNodes, settings); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexClusterDefaultDocRepIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexClusterDefaultDocRepIT.java new file mode 100644 index 0000000000000..e1ab101fddf55 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexClusterDefaultDocRepIT.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.IndexSettings; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.Locale; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; +import static org.opensearch.indices.IndicesService.CLUSTER_REPLICATION_TYPE_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.containsString; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST) +public class CreateRemoteIndexClusterDefaultDocRepIT extends CreateRemoteIndexIT { + + @Override + protected Settings nodeSettings(int nodeOriginal) { + Settings settings = super.nodeSettings(nodeOriginal); + Settings.Builder builder = Settings.builder() + .put(settings) + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.DOCUMENT); + return builder.build(); + } + + @Override + public void testDefaultRemoteStoreNoUserOverride() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + String.format( + Locale.ROOT, + "To enable %s, %s should be set to %s", + SETTING_REMOTE_STORE_ENABLED, + SETTING_REPLICATION_TYPE, + ReplicationType.SEGMENT + ) + ) + ); + } + + public void testDefaultRemoteStoreNoUserOverrideExceptReplicationTypeSegment() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + REPOSITORY_NAME, + REPOSITORY_2_NAME, + ReplicationType.SEGMENT.toString(), + IndexSettings.DEFAULT_REMOTE_TRANSLOG_BUFFER_INTERVAL + ); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexIT.java new file mode 100644 index 0000000000000..d427a4db84ba2 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexIT.java @@ -0,0 +1,262 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.index.IndexSettings; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.Before; + +import java.util.Locale; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_SEGMENT_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; +import static org.opensearch.index.IndexSettings.INDEX_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.containsString; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST) +public class CreateRemoteIndexIT extends RemoteStoreBaseIntegTestCase { + + @Before + public void setup() throws Exception { + internalCluster().startNodes(2); + } + + public void testDefaultRemoteStoreNoUserOverride() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + REPOSITORY_NAME, + REPOSITORY_2_NAME, + ReplicationType.SEGMENT.toString(), + IndexSettings.DEFAULT_REMOTE_TRANSLOG_BUFFER_INTERVAL + ); + } + + public void testRemoteStoreDisabledByUser() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(SETTING_REMOTE_STORE_ENABLED, false) + .build(); + + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + String.format( + Locale.ROOT, + "Validation Failed: 1: private index setting [%s] can not be set explicitly;", + SETTING_REMOTE_STORE_ENABLED + ) + ) + ); + } + + public void testRemoteStoreEnabledByUserWithoutRemoteRepoAndSegmentReplicationIllegalArgumentException() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .build(); + + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString("To enable index.remote_store.enabled, index.replication.type should be set to SEGMENT") + ); + } + + public void testRemoteStoreEnabledByUserWithoutRemoteRepoIllegalArgumentException() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .build(); + + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + String.format( + Locale.ROOT, + "Validation Failed: 1: private index setting [%s] can not be set explicitly;", + SETTING_REMOTE_STORE_ENABLED + ) + ) + ); + } + + public void testReplicationTypeDocumentByUser() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT) + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + String.format( + Locale.ROOT, + "To enable %s, %s should be set to %s", + SETTING_REMOTE_STORE_ENABLED, + SETTING_REPLICATION_TYPE, + ReplicationType.SEGMENT + ) + ) + ); + } + + public void testRemoteStoreSegmentRepoWithoutRemoteEnabledAndSegmentReplicationIllegalArgumentException() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, "my-custom-repo") + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + String.format( + Locale.ROOT, + "Settings %s can only be set/enabled when %s is set to true", + SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, + SETTING_REMOTE_STORE_ENABLED + ) + ) + ); + } + + public void testRemoteStoreEnabledByUserWithRemoteRepoIllegalArgumentException() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, "my-custom-repo") + .build(); + + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + String.format( + Locale.ROOT, + "Validation Failed: 1: private index setting [%s] can not be set explicitly;2: private index setting [%s] can not be set explicitly;", + SETTING_REMOTE_STORE_ENABLED, + SETTING_REMOTE_SEGMENT_STORE_REPOSITORY + ) + ) + ); + } + + public void testRemoteStoreOverrideOnlyTranslogRepoIllegalArgumentException() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, "my-custom-repo") + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + String.format( + Locale.ROOT, + "Settings %s can only be set/enabled when %s is set to true", + SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, + SETTING_REMOTE_STORE_ENABLED + ) + ) + ); + } + + public void testRemoteStoreOverrideTranslogRepoCorrectly() throws Exception { + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, "my-custom-repo") + .put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, "my-custom-repo") + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + String.format( + Locale.ROOT, + "Validation Failed: 1: private index setting [%s] can not be set explicitly;2: private index setting [%s] can not be set explicitly;3: private index setting [%s] can not be set explicitly;", + SETTING_REMOTE_STORE_ENABLED, + SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, + SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY + ) + ) + ); + } + + protected void verifyRemoteStoreIndexSettings( + Settings indexSettings, + String isRemoteSegmentEnabled, + String remoteSegmentRepo, + String remoteTranslogRepo, + String replicationType, + TimeValue translogBufferInterval + ) { + assertEquals(replicationType, indexSettings.get(SETTING_REPLICATION_TYPE)); + assertEquals(isRemoteSegmentEnabled, indexSettings.get(SETTING_REMOTE_STORE_ENABLED)); + assertEquals(remoteSegmentRepo, indexSettings.get(SETTING_REMOTE_SEGMENT_STORE_REPOSITORY)); + assertEquals(remoteTranslogRepo, indexSettings.get(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY)); + assertEquals(translogBufferInterval, INDEX_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.get(indexSettings)); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/PrimaryTermValidationIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/PrimaryTermValidationIT.java new file mode 100644 index 0000000000000..e14a4062f7775 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/PrimaryTermValidationIT.java @@ -0,0 +1,170 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.opensearch.action.admin.indices.refresh.RefreshResponse; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.cluster.coordination.FollowersChecker; +import org.opensearch.cluster.coordination.LeaderChecker; +import org.opensearch.cluster.health.ClusterHealthStatus; +import org.opensearch.cluster.health.ClusterIndexHealth; +import org.opensearch.common.UUIDs; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.index.shard.ShardNotFoundException; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.transport.MockTransportService; +import org.junit.Before; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; +import static org.hamcrest.Matchers.equalTo; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class PrimaryTermValidationIT extends RemoteStoreBaseIntegTestCase { + + private static final String INDEX_NAME = "remote-store-test-idx-1"; + protected Path absolutePath; + protected Path absolutePath2; + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class); + } + + @Before + public void setup() { + absolutePath = randomRepoPath().toAbsolutePath(); + absolutePath2 = randomRepoPath().toAbsolutePath(); + } + + public void testPrimaryTermValidation() throws Exception { + // Follower checker interval is lower compared to leader checker so that the cluster manager can remove the node + // with network partition faster. The follower check retry count is also kept 1. + Settings clusterSettings = Settings.builder() + .put(LeaderChecker.LEADER_CHECK_TIMEOUT_SETTING.getKey(), "1s") + .put(LeaderChecker.LEADER_CHECK_INTERVAL_SETTING.getKey(), "20s") + .put(LeaderChecker.LEADER_CHECK_RETRY_COUNT_SETTING.getKey(), 4) + .put(FollowersChecker.FOLLOWER_CHECK_TIMEOUT_SETTING.getKey(), "1s") + .put(FollowersChecker.FOLLOWER_CHECK_INTERVAL_SETTING.getKey(), "1s") + .put(FollowersChecker.FOLLOWER_CHECK_RETRY_COUNT_SETTING.getKey(), 1) + .put(remoteStoreClusterSettings(REPOSITORY_NAME, absolutePath, REPOSITORY_2_NAME, absolutePath2)) + .build(); + internalCluster().startClusterManagerOnlyNode(clusterSettings); + internalCluster().startDataOnlyNodes(2, clusterSettings); + + // Create index + createIndex(INDEX_NAME, remoteStoreIndexSettings(1)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + // Get the names of nodes to create network disruption + String primaryNode = primaryNodeName(INDEX_NAME); + String replicaNode = replicaNodeName(INDEX_NAME); + String clusterManagerNode = internalCluster().getClusterManagerName(); + logger.info("Node names : clusterManager={} primary={} replica={}", clusterManagerNode, primaryNode, replicaNode); + + // Index some docs and validate that both primary and replica node has it. Refresh is triggered to trigger segment replication + // to ensure replica is also upto date. + int numOfDocs = randomIntBetween(5, 10); + for (int i = 0; i < numOfDocs; i++) { + indexSameDoc(clusterManagerNode, INDEX_NAME); + } + refresh(INDEX_NAME); + assertBusy( + () -> assertHitCount(client(primaryNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), numOfDocs) + ); + assertBusy( + () -> assertHitCount(client(replicaNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), numOfDocs) + ); + + // Start network disruption - primary node will be isolated + Set nodesInOneSide = Stream.of(clusterManagerNode, replicaNode).collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(primaryNode).collect(Collectors.toCollection(HashSet::new)); + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.DISCONNECT + ); + internalCluster().setDisruptionScheme(networkDisruption); + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + // Ensure the node which is partitioned is removed from the cluster + assertBusy(() -> { + NodesInfoResponse response = client(clusterManagerNode).admin().cluster().prepareNodesInfo().get(); + assertThat(response.getNodes().size(), equalTo(2)); + }); + + // Ensure that the cluster manager has latest information about the index + assertBusy(() -> { + ClusterHealthResponse clusterHealthResponse = client(clusterManagerNode).admin() + .cluster() + .health(new ClusterHealthRequest()) + .actionGet(TimeValue.timeValueSeconds(1)); + assertTrue(clusterHealthResponse.getIndices().containsKey(INDEX_NAME)); + ClusterIndexHealth clusterIndexHealth = clusterHealthResponse.getIndices().get(INDEX_NAME); + assertEquals(ClusterHealthStatus.YELLOW, clusterHealthResponse.getStatus()); + assertEquals(1, clusterIndexHealth.getNumberOfShards()); + assertEquals(1, clusterIndexHealth.getActiveShards()); + assertEquals(1, clusterIndexHealth.getUnassignedShards()); + assertEquals(1, clusterIndexHealth.getUnassignedShards()); + assertEquals(1, clusterIndexHealth.getActivePrimaryShards()); + assertEquals(ClusterHealthStatus.YELLOW, clusterIndexHealth.getStatus()); + }); + + // Index data to the newly promoted primary + indexSameDoc(clusterManagerNode, INDEX_NAME); + RefreshResponse refreshResponse = client(clusterManagerNode).admin() + .indices() + .prepareRefresh(INDEX_NAME) + .setIndicesOptions(IndicesOptions.STRICT_EXPAND_OPEN_HIDDEN_FORBID_CLOSED) + .execute() + .actionGet(); + assertNoFailures(refreshResponse); + assertEquals(1, refreshResponse.getSuccessfulShards()); + assertHitCount(client(replicaNode).prepareSearch(INDEX_NAME).setSize(0).setPreference("_only_local").get(), numOfDocs + 1); + + // At this point we stop the disruption. Since the follower checker has already failed and cluster manager has removed the node + // from cluster, failed node needs to start discovery process by leader checker call. We stop the disruption to allow the failed + // node to + // communicate with the other node which it assumes has replica. + networkDisruption.stopDisrupting(); + + // When the index call is made to the stale primary, it makes the primary term validation call to the other node (which + // it assumes has the replica node). At this moment, the stale primary realises that it is no more the primary and the caller + // received the following exception. + ShardNotFoundException exception = assertThrows(ShardNotFoundException.class, () -> indexSameDoc(primaryNode, INDEX_NAME)); + assertTrue(exception.getMessage().contains("no such shard")); + internalCluster().clearDisruptionScheme(); + ensureStableCluster(3); + ensureGreen(INDEX_NAME); + } + + private IndexResponse indexSameDoc(String nodeName, String indexName) { + return client(nodeName).prepareIndex(indexName) + .setId(UUIDs.randomBase64UUID()) + .setSource("{\"foo\" : \"bar\"}", MediaTypeRegistry.JSON) + .get(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteIndexPrimaryRelocationIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteIndexPrimaryRelocationIT.java new file mode 100644 index 0000000000000..d8b7718a55377 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteIndexPrimaryRelocationIT.java @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.indices.recovery.IndexPrimaryRelocationIT; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.nio.file.Path; + +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteIndexPrimaryRelocationIT extends IndexPrimaryRelocationIT { + + protected static final String REPOSITORY_NAME = "test-remote-store-repo"; + + protected Path absolutePath; + + protected Settings nodeSettings(int nodeOrdinal) { + if (absolutePath == null) { + absolutePath = randomRepoPath().toAbsolutePath(); + } + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(remoteStoreClusterSettings(REPOSITORY_NAME, absolutePath)) + .build(); + } + + @Override + protected boolean addMockInternalEngine() { + return false; + } + + public Settings indexSettings() { + return Settings.builder() + .put(super.indexSettings()) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .build(); + } + + public void testPrimaryRelocationWhileIndexing() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + super.testPrimaryRelocationWhileIndexing(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteIndexRecoveryIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteIndexRecoveryIT.java new file mode 100644 index 0000000000000..c957f1b338bfe --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteIndexRecoveryIT.java @@ -0,0 +1,166 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.IndexModule; +import org.opensearch.index.IndexSettings; +import org.opensearch.indices.recovery.IndexRecoveryIT; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; + +import java.nio.file.Path; + +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteIndexRecoveryIT extends IndexRecoveryIT { + + protected static final String REPOSITORY_NAME = "test-remote-store-repo"; + + protected Path repositoryPath; + + @Before + public void setup() { + repositoryPath = randomRepoPath().toAbsolutePath(); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(remoteStoreClusterSettings(REPOSITORY_NAME, repositoryPath)) + .build(); + } + + @Override + public Settings indexSettings() { + return Settings.builder() + .put(super.indexSettings()) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "300s") + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + } + + @After + public void teardown() { + clusterAdmin().prepareCleanupRepository(REPOSITORY_NAME).get(); + } + + @Override + protected Matcher getMatcherForThrottling(long value) { + return Matchers.greaterThanOrEqualTo(value); + } + + @Override + protected int numDocs() { + return randomIntBetween(100, 200); + } + + @Override + public void testUsesFileBasedRecoveryIfRetentionLeaseMissing() { + // Retention lease based tests not applicable for remote store; + } + + @Override + public void testPeerRecoveryTrimsLocalTranslog() { + // Peer recovery usecase not valid for remote enabled indices + } + + @Override + public void testHistoryRetention() { + // History retention not applicable for remote store + } + + @Override + public void testUsesFileBasedRecoveryIfOperationsBasedRecoveryWouldBeUnreasonable() { + // History retention not applicable for remote store + } + + @Override + public void testUsesFileBasedRecoveryIfRetentionLeaseAheadOfGlobalCheckpoint() { + // History retention not applicable for remote store + } + + @Override + public void testRecoverLocallyUpToGlobalCheckpoint() { + // History retention not applicable for remote store + } + + @Override + public void testCancelNewShardRecoveryAndUsesExistingShardCopy() { + // History retention not applicable for remote store + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testReservesBytesDuringPeerRecoveryPhaseOne() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testAllocateEmptyPrimaryResetsGlobalCheckpoint() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testDoesNotCopyOperationsInSafeCommit() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testRepeatedRecovery() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testDisconnectsWhileRecovering() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testTransientErrorsDuringRecoveryAreRetried() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testDoNotInfinitelyWaitForMapping() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testDisconnectsDuringRecovery() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8919") + @Override + public void testReplicaRecovery() { + + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9580") + public void testRerouteRecovery() { + + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteRestoreSnapshotIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteRestoreSnapshotIT.java new file mode 100644 index 0000000000000..4d3f0481e1e6f --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteRestoreSnapshotIT.java @@ -0,0 +1,515 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.DocWriteResponse; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreRequest; +import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; +import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; +import org.opensearch.action.delete.DeleteResponse; +import org.opensearch.action.support.PlainActionFuture; +import org.opensearch.client.Client; +import org.opensearch.client.Requests; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.io.PathUtils; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.index.IndexSettings; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.snapshots.AbstractSnapshotIntegTestCase; +import org.opensearch.snapshots.SnapshotState; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_SEGMENT_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_ENABLED; +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteRestoreSnapshotIT extends AbstractSnapshotIntegTestCase { + private static final String BASE_REMOTE_REPO = "test-rs-repo" + TEST_REMOTE_STORE_REPO_SUFFIX; + private Path remoteRepoPath; + + @Before + public void setup() { + remoteRepoPath = randomRepoPath().toAbsolutePath(); + } + + @After + public void teardown() { + clusterAdmin().prepareCleanupRepository(BASE_REMOTE_REPO).get(); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(remoteStoreClusterSettings(BASE_REMOTE_REPO, remoteRepoPath)) + .build(); + } + + private Settings.Builder getIndexSettings(int numOfShards, int numOfReplicas) { + Settings.Builder settingsBuilder = Settings.builder() + .put(super.indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numOfShards) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numOfReplicas) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "300s"); + return settingsBuilder; + } + + private void indexDocuments(Client client, String indexName, int numOfDocs) { + indexDocuments(client, indexName, 0, numOfDocs); + } + + private void indexDocuments(Client client, String indexName, int fromId, int toId) { + for (int i = fromId; i < toId; i++) { + String id = Integer.toString(i); + client.prepareIndex(indexName).setId(id).setSource("text", "sometext").get(); + } + client.admin().indices().prepareFlush(indexName).get(); + } + + private void assertDocsPresentInIndex(Client client, String indexName, int numOfDocs) { + for (int i = 0; i < numOfDocs; i++) { + String id = Integer.toString(i); + logger.info("checking for index " + indexName + " with docId" + id); + assertTrue("doc with id" + id + " is not present for index " + indexName, client.prepareGet(indexName, id).get().isExists()); + } + } + + public void testRestoreOperationsShallowCopyEnabled() throws IOException, ExecutionException, InterruptedException { + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + String primary = internalCluster().startDataOnlyNode(); + String indexName1 = "testindex1"; + String indexName2 = "testindex2"; + String snapshotRepoName = "test-restore-snapshot-repo"; + String snapshotName1 = "test-restore-snapshot1"; + String snapshotName2 = "test-restore-snapshot2"; + Path absolutePath1 = randomRepoPath().toAbsolutePath(); + logger.info("Snapshot Path [{}]", absolutePath1); + String restoredIndexName1 = indexName1 + "-restored"; + String restoredIndexName1Seg = indexName1 + "-restored-seg"; + String restoredIndexName1Doc = indexName1 + "-restored-doc"; + String restoredIndexName2 = indexName2 + "-restored"; + + createRepository(snapshotRepoName, "fs", getRepositorySettings(absolutePath1, true)); + + Client client = client(); + Settings indexSettings = getIndexSettings(1, 0).build(); + createIndex(indexName1, indexSettings); + + Settings indexSettings2 = getIndexSettings(1, 0).build(); + createIndex(indexName2, indexSettings2); + + final int numDocsInIndex1 = 5; + final int numDocsInIndex2 = 6; + indexDocuments(client, indexName1, numDocsInIndex1); + indexDocuments(client, indexName2, numDocsInIndex2); + ensureGreen(indexName1, indexName2); + + internalCluster().startDataOnlyNode(); + logger.info("--> snapshot"); + CreateSnapshotResponse createSnapshotResponse = client.admin() + .cluster() + .prepareCreateSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(true) + .setIndices(indexName1, indexName2) + .get(); + assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0)); + assertThat( + createSnapshotResponse.getSnapshotInfo().successfulShards(), + equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()) + ); + assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS)); + + updateRepository(snapshotRepoName, "fs", getRepositorySettings(absolutePath1, false)); + CreateSnapshotResponse createSnapshotResponse2 = client.admin() + .cluster() + .prepareCreateSnapshot(snapshotRepoName, snapshotName2) + .setWaitForCompletion(true) + .setIndices(indexName1, indexName2) + .get(); + assertThat(createSnapshotResponse2.getSnapshotInfo().successfulShards(), greaterThan(0)); + assertThat( + createSnapshotResponse2.getSnapshotInfo().successfulShards(), + equalTo(createSnapshotResponse2.getSnapshotInfo().totalShards()) + ); + assertThat(createSnapshotResponse2.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS)); + + DeleteResponse deleteResponse = client().prepareDelete(indexName1, "0").execute().actionGet(); + assertEquals(deleteResponse.getResult(), DocWriteResponse.Result.DELETED); + indexDocuments(client, indexName1, numDocsInIndex1, numDocsInIndex1 + randomIntBetween(2, 5)); + ensureGreen(indexName1); + + RestoreSnapshotResponse restoreSnapshotResponse1 = client.admin() + .cluster() + .prepareRestoreSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(false) + .setIndices(indexName1) + .setRenamePattern(indexName1) + .setRenameReplacement(restoredIndexName1) + .get(); + RestoreSnapshotResponse restoreSnapshotResponse2 = client.admin() + .cluster() + .prepareRestoreSnapshot(snapshotRepoName, snapshotName2) + .setWaitForCompletion(false) + .setIndices(indexName2) + .setRenamePattern(indexName2) + .setRenameReplacement(restoredIndexName2) + .get(); + assertEquals(restoreSnapshotResponse1.status(), RestStatus.ACCEPTED); + assertEquals(restoreSnapshotResponse2.status(), RestStatus.ACCEPTED); + ensureGreen(restoredIndexName1, restoredIndexName2); + assertDocsPresentInIndex(client, restoredIndexName1, numDocsInIndex1); + assertDocsPresentInIndex(client, restoredIndexName2, numDocsInIndex2); + + // deleting data for restoredIndexName1 and restoring from remote store. + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primary)); + ensureRed(restoredIndexName1); + // Re-initialize client to make sure we are not using client from stopped node. + client = client(clusterManagerNode); + assertAcked(client.admin().indices().prepareClose(restoredIndexName1)); + client.admin() + .cluster() + .restoreRemoteStore( + new RestoreRemoteStoreRequest().indices(restoredIndexName1).restoreAllShards(true), + PlainActionFuture.newFuture() + ); + ensureYellowAndNoInitializingShards(restoredIndexName1); + ensureGreen(restoredIndexName1); + assertDocsPresentInIndex(client(), restoredIndexName1, numDocsInIndex1); + // indexing some new docs and validating + indexDocuments(client, restoredIndexName1, numDocsInIndex1, numDocsInIndex1 + 2); + ensureGreen(restoredIndexName1); + assertDocsPresentInIndex(client, restoredIndexName1, numDocsInIndex1 + 2); + + // restore index as seg rep enabled with remote store and remote translog disabled + RestoreSnapshotResponse restoreSnapshotResponse3 = client.admin() + .cluster() + .prepareRestoreSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(false) + .setIgnoreIndexSettings(IndexMetadata.SETTING_REMOTE_STORE_ENABLED) + .setIndices(indexName1) + .setRenamePattern(indexName1) + .setRenameReplacement(restoredIndexName1Seg) + .get(); + assertEquals(restoreSnapshotResponse3.status(), RestStatus.ACCEPTED); + ensureGreen(restoredIndexName1Seg); + + GetIndexResponse getIndexResponse = client.admin() + .indices() + .getIndex(new GetIndexRequest().indices(restoredIndexName1Seg).includeDefaults(true)) + .get(); + indexSettings = getIndexResponse.settings().get(restoredIndexName1Seg); + assertNull(indexSettings.get(SETTING_REMOTE_STORE_ENABLED)); + assertNull(indexSettings.get(SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, null)); + assertEquals(ReplicationType.SEGMENT.toString(), indexSettings.get(IndexMetadata.SETTING_REPLICATION_TYPE)); + assertDocsPresentInIndex(client, restoredIndexName1Seg, numDocsInIndex1); + // indexing some new docs and validating + indexDocuments(client, restoredIndexName1Seg, numDocsInIndex1, numDocsInIndex1 + 2); + ensureGreen(restoredIndexName1Seg); + assertDocsPresentInIndex(client, restoredIndexName1Seg, numDocsInIndex1 + 2); + + // restore index as doc rep based from shallow copy snapshot + RestoreSnapshotResponse restoreSnapshotResponse4 = client.admin() + .cluster() + .prepareRestoreSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(false) + .setIgnoreIndexSettings(IndexMetadata.SETTING_REMOTE_STORE_ENABLED, IndexMetadata.SETTING_REPLICATION_TYPE) + .setIndices(indexName1) + .setRenamePattern(indexName1) + .setRenameReplacement(restoredIndexName1Doc) + .get(); + assertEquals(restoreSnapshotResponse4.status(), RestStatus.ACCEPTED); + ensureGreen(restoredIndexName1Doc); + + getIndexResponse = client.admin() + .indices() + .getIndex(new GetIndexRequest().indices(restoredIndexName1Doc).includeDefaults(true)) + .get(); + indexSettings = getIndexResponse.settings().get(restoredIndexName1Doc); + assertNull(indexSettings.get(SETTING_REMOTE_STORE_ENABLED)); + assertNull(indexSettings.get(SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, null)); + assertNull(indexSettings.get(IndexMetadata.SETTING_REPLICATION_TYPE)); + assertDocsPresentInIndex(client, restoredIndexName1Doc, numDocsInIndex1); + // indexing some new docs and validating + indexDocuments(client, restoredIndexName1Doc, numDocsInIndex1, numDocsInIndex1 + 2); + ensureGreen(restoredIndexName1Doc); + assertDocsPresentInIndex(client, restoredIndexName1Doc, numDocsInIndex1 + 2); + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9326") + public void testRestoreInSameRemoteStoreEnabledIndex() throws IOException { + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + String primary = internalCluster().startDataOnlyNode(); + String indexName1 = "testindex1"; + String indexName2 = "testindex2"; + String snapshotRepoName = "test-restore-snapshot-repo"; + String snapshotName1 = "test-restore-snapshot1"; + String snapshotName2 = "test-restore-snapshot2"; + Path absolutePath1 = randomRepoPath().toAbsolutePath(); + logger.info("Snapshot Path [{}]", absolutePath1); + String restoredIndexName2 = indexName2 + "-restored"; + + boolean enableShallowCopy = randomBoolean(); + createRepository(snapshotRepoName, "fs", getRepositorySettings(absolutePath1, enableShallowCopy)); + + Client client = client(); + Settings indexSettings = getIndexSettings(1, 0).build(); + createIndex(indexName1, indexSettings); + + Settings indexSettings2 = getIndexSettings(1, 0).build(); + createIndex(indexName2, indexSettings2); + + final int numDocsInIndex1 = 5; + final int numDocsInIndex2 = 6; + indexDocuments(client, indexName1, numDocsInIndex1); + indexDocuments(client, indexName2, numDocsInIndex2); + ensureGreen(indexName1, indexName2); + + internalCluster().startDataOnlyNode(); + logger.info("--> snapshot"); + CreateSnapshotResponse createSnapshotResponse = client.admin() + .cluster() + .prepareCreateSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(true) + .setIndices(indexName1, indexName2) + .get(); + assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0)); + assertThat( + createSnapshotResponse.getSnapshotInfo().successfulShards(), + equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()) + ); + assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS)); + + updateRepository(snapshotRepoName, "fs", getRepositorySettings(absolutePath1, false)); + CreateSnapshotResponse createSnapshotResponse2 = client.admin() + .cluster() + .prepareCreateSnapshot(snapshotRepoName, snapshotName2) + .setWaitForCompletion(true) + .setIndices(indexName1, indexName2) + .get(); + assertThat(createSnapshotResponse2.getSnapshotInfo().successfulShards(), greaterThan(0)); + assertThat( + createSnapshotResponse2.getSnapshotInfo().successfulShards(), + equalTo(createSnapshotResponse2.getSnapshotInfo().totalShards()) + ); + assertThat(createSnapshotResponse2.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS)); + + DeleteResponse deleteResponse = client().prepareDelete(indexName1, "0").execute().actionGet(); + assertEquals(deleteResponse.getResult(), DocWriteResponse.Result.DELETED); + indexDocuments(client, indexName1, numDocsInIndex1, numDocsInIndex1 + randomIntBetween(2, 5)); + ensureGreen(indexName1); + + assertAcked(client().admin().indices().prepareClose(indexName1)); + + RestoreSnapshotResponse restoreSnapshotResponse1 = client.admin() + .cluster() + .prepareRestoreSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(false) + .setIndices(indexName1) + .get(); + RestoreSnapshotResponse restoreSnapshotResponse2 = client.admin() + .cluster() + .prepareRestoreSnapshot(snapshotRepoName, snapshotName2) + .setWaitForCompletion(false) + .setIndices(indexName2) + .setRenamePattern(indexName2) + .setRenameReplacement(restoredIndexName2) + .get(); + assertEquals(restoreSnapshotResponse1.status(), RestStatus.ACCEPTED); + assertEquals(restoreSnapshotResponse2.status(), RestStatus.ACCEPTED); + ensureGreen(indexName1, restoredIndexName2); + assertDocsPresentInIndex(client, indexName1, numDocsInIndex1); + assertDocsPresentInIndex(client, restoredIndexName2, numDocsInIndex2); + + // deleting data for restoredIndexName1 and restoring from remote store. + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primary)); + ensureRed(indexName1); + // Re-initialize client to make sure we are not using client from stopped node. + client = client(clusterManagerNode); + assertAcked(client.admin().indices().prepareClose(indexName1)); + client.admin() + .cluster() + .restoreRemoteStore(new RestoreRemoteStoreRequest().indices(indexName1).restoreAllShards(true), PlainActionFuture.newFuture()); + ensureYellowAndNoInitializingShards(indexName1); + ensureGreen(indexName1); + assertDocsPresentInIndex(client(), indexName1, numDocsInIndex1); + // indexing some new docs and validating + indexDocuments(client, indexName1, numDocsInIndex1, numDocsInIndex1 + 2); + ensureGreen(indexName1); + assertDocsPresentInIndex(client, indexName1, numDocsInIndex1 + 2); + } + + public void testRestoreShallowCopySnapshotWithDifferentRepo() throws IOException { + String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); + String primary = internalCluster().startDataOnlyNode(); + String indexName1 = "testindex1"; + String indexName2 = "testindex2"; + String snapshotRepoName = "test-restore-snapshot-repo"; + String remoteStoreRepo2Name = "test-rs-repo-2" + TEST_REMOTE_STORE_REPO_SUFFIX; + String snapshotName1 = "test-restore-snapshot1"; + Path absolutePath1 = randomRepoPath().toAbsolutePath(); + Path absolutePath3 = randomRepoPath().toAbsolutePath(); + String restoredIndexName1 = indexName1 + "-restored"; + + createRepository(snapshotRepoName, "fs", getRepositorySettings(absolutePath1, false)); + createRepository(remoteStoreRepo2Name, "fs", absolutePath3); + + Client client = client(); + Settings indexSettings = getIndexSettings(1, 0).build(); + createIndex(indexName1, indexSettings); + + Settings indexSettings2 = getIndexSettings(1, 0).build(); + createIndex(indexName2, indexSettings2); + + final int numDocsInIndex1 = 5; + final int numDocsInIndex2 = 6; + indexDocuments(client, indexName1, numDocsInIndex1); + indexDocuments(client, indexName2, numDocsInIndex2); + ensureGreen(indexName1, indexName2); + + internalCluster().startDataOnlyNode(); + + logger.info("--> snapshot"); + CreateSnapshotResponse createSnapshotResponse = client.admin() + .cluster() + .prepareCreateSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(true) + .setIndices(indexName1, indexName2) + .get(); + assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0)); + assertThat( + createSnapshotResponse.getSnapshotInfo().successfulShards(), + equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()) + ); + assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS)); + + Settings remoteStoreIndexSettings = Settings.builder() + .put(IndexMetadata.SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, remoteStoreRepo2Name) + .build(); + // restore index as a remote store index with different remote store repo + RestoreSnapshotResponse restoreSnapshotResponse = client.admin() + .cluster() + .prepareRestoreSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(false) + .setIndexSettings(remoteStoreIndexSettings) + .setIndices(indexName1) + .setRenamePattern(indexName1) + .setRenameReplacement(restoredIndexName1) + .get(); + assertEquals(restoreSnapshotResponse.status(), RestStatus.ACCEPTED); + ensureGreen(restoredIndexName1); + assertDocsPresentInIndex(client(), restoredIndexName1, numDocsInIndex1); + + // deleting data for restoredIndexName1 and restoring from remote store. + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primary)); + // Re-initialize client to make sure we are not using client from stopped node. + client = client(clusterManagerNode); + assertAcked(client.admin().indices().prepareClose(restoredIndexName1)); + client.admin() + .cluster() + .restoreRemoteStore( + new RestoreRemoteStoreRequest().indices(restoredIndexName1).restoreAllShards(true), + PlainActionFuture.newFuture() + ); + ensureYellowAndNoInitializingShards(restoredIndexName1); + ensureGreen(restoredIndexName1); + // indexing some new docs and validating + assertDocsPresentInIndex(client, restoredIndexName1, numDocsInIndex1); + indexDocuments(client, restoredIndexName1, numDocsInIndex1, numDocsInIndex1 + 2); + ensureGreen(restoredIndexName1); + assertDocsPresentInIndex(client, restoredIndexName1, numDocsInIndex1 + 2); + } + + public void testRestoreShallowSnapshotRepository() throws ExecutionException, InterruptedException { + String indexName1 = "testindex1"; + String snapshotRepoName = "test-restore-snapshot-repo"; + String remoteStoreRepoNameUpdated = "test-rs-repo-updated" + TEST_REMOTE_STORE_REPO_SUFFIX; + String snapshotName1 = "test-restore-snapshot1"; + Path absolutePath1 = randomRepoPath().toAbsolutePath(); + Path absolutePath2 = randomRepoPath().toAbsolutePath(); + String[] pathTokens = absolutePath1.toString().split("/"); + String basePath = pathTokens[pathTokens.length - 1]; + Arrays.copyOf(pathTokens, pathTokens.length - 1); + Path location = PathUtils.get(String.join("/", pathTokens)); + pathTokens = absolutePath2.toString().split("/"); + String basePath2 = pathTokens[pathTokens.length - 1]; + Arrays.copyOf(pathTokens, pathTokens.length - 1); + Path location2 = PathUtils.get(String.join("/", pathTokens)); + logger.info("Path 1 [{}]", absolutePath1); + logger.info("Path 2 [{}]", absolutePath2); + String restoredIndexName1 = indexName1 + "-restored"; + + createRepository(snapshotRepoName, "fs", getRepositorySettings(location, basePath, true)); + + Client client = client(); + Settings indexSettings = Settings.builder() + .put(super.indexSettings()) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "300s") + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build(); + createIndex(indexName1, indexSettings); + + int numDocsInIndex1 = randomIntBetween(2, 5); + indexDocuments(client, indexName1, numDocsInIndex1); + + ensureGreen(indexName1); + + logger.info("--> snapshot"); + CreateSnapshotResponse createSnapshotResponse = client.admin() + .cluster() + .prepareCreateSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(true) + .setIndices(indexName1) + .get(); + assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0)); + assertThat( + createSnapshotResponse.getSnapshotInfo().successfulShards(), + equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()) + ); + assertThat(createSnapshotResponse.getSnapshotInfo().state(), equalTo(SnapshotState.SUCCESS)); + + client().admin().indices().close(Requests.closeIndexRequest(indexName1)).get(); + createRepository(remoteStoreRepoNameUpdated, "fs", remoteRepoPath); + RestoreSnapshotResponse restoreSnapshotResponse2 = client.admin() + .cluster() + .prepareRestoreSnapshot(snapshotRepoName, snapshotName1) + .setWaitForCompletion(true) + .setIndices(indexName1) + .setRenamePattern(indexName1) + .setRenameReplacement(restoredIndexName1) + .setSourceRemoteStoreRepository(remoteStoreRepoNameUpdated) + .get(); + + assertTrue(restoreSnapshotResponse2.getRestoreInfo().failedShards() == 0); + ensureGreen(restoredIndexName1); + assertDocsPresentInIndex(client, restoredIndexName1, numDocsInIndex1); + + // indexing some new docs and validating + indexDocuments(client, restoredIndexName1, numDocsInIndex1, numDocsInIndex1 + 2); + ensureGreen(restoredIndexName1); + assertDocsPresentInIndex(client, restoredIndexName1, numDocsInIndex1 + 2); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreBackpressureIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreBackpressureIT.java new file mode 100644 index 0000000000000..3462054c23630 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreBackpressureIT.java @@ -0,0 +1,159 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStats; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsResponse; +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.index.remote.RemoteSegmentTransferTracker; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.snapshots.mockstore.MockRepository; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.opensearch.index.remote.RemoteStorePressureSettings.MIN_CONSECUTIVE_FAILURES_LIMIT; +import static org.opensearch.index.remote.RemoteStorePressureSettings.REMOTE_REFRESH_SEGMENT_PRESSURE_ENABLED; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteStoreBackpressureIT extends AbstractRemoteStoreMockRepositoryIntegTestCase { + public void testWritesRejectedDueToConsecutiveFailureBreach() throws Exception { + // Here the doc size of the request remains same throughout the test. After initial indexing, all remote store interactions + // fail leading to consecutive failure limit getting exceeded and leading to rejections. + validateBackpressure(ByteSizeUnit.KB.toIntBytes(1), 10, ByteSizeUnit.KB.toIntBytes(1), 15, "failure_streak_count"); + } + + public void testWritesRejectedDueToBytesLagBreach() throws Exception { + // Initially indexing happens with doc size of 2 bytes, then all remote store interactions start failing. Now, the + // indexing happens with doc size of 1KB leading to bytes lag limit getting exceeded and leading to rejections. + validateBackpressure(ByteSizeUnit.BYTES.toIntBytes(2), 30, ByteSizeUnit.KB.toIntBytes(1), 15, "bytes_lag"); + } + + public void testWritesRejectedDueToTimeLagBreach() throws Exception { + // Initially indexing happens with doc size of 1KB, then all remote store interactions start failing. Now, the + // indexing happens with doc size of 1 byte leading to time lag limit getting exceeded and leading to rejections. + validateBackpressure(ByteSizeUnit.KB.toIntBytes(1), 20, ByteSizeUnit.BYTES.toIntBytes(1), 15, "time_lag"); + } + + private void validateBackpressure( + int initialDocSize, + int initialDocsToIndex, + int onFailureDocSize, + int onFailureDocsToIndex, + String breachMode + ) throws Exception { + Path location = randomRepoPath().toAbsolutePath(); + String dataNodeName = setup(location, 0d, "metadata", Long.MAX_VALUE); + + Settings request = Settings.builder() + .put(REMOTE_REFRESH_SEGMENT_PRESSURE_ENABLED.getKey(), true) + .put(MIN_CONSECUTIVE_FAILURES_LIMIT.getKey(), 10) + .build(); + ClusterUpdateSettingsResponse clusterUpdateResponse = client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(request) + .get(); + assertEquals(clusterUpdateResponse.getPersistentSettings().get(REMOTE_REFRESH_SEGMENT_PRESSURE_ENABLED.getKey()), "true"); + assertEquals(clusterUpdateResponse.getPersistentSettings().get(MIN_CONSECUTIVE_FAILURES_LIMIT.getKey()), "10"); + + logger.info("--> Indexing data"); + + String jsonString = generateString(initialDocSize); + BytesReference initialSource = new BytesArray(jsonString); + indexDocAndRefresh(initialSource, initialDocsToIndex); + + ((MockRepository) internalCluster().getInstance(RepositoriesService.class, dataNodeName).repository(REPOSITORY_NAME)) + .setRandomControlIOExceptionRate(1d); + + jsonString = generateString(onFailureDocSize); + BytesReference onFailureSource = new BytesArray(jsonString); + OpenSearchRejectedExecutionException ex = assertThrows( + OpenSearchRejectedExecutionException.class, + () -> indexDocAndRefresh(onFailureSource, onFailureDocsToIndex) + ); + assertTrue(ex.getMessage().contains("rejected execution on primary shard")); + assertTrue(ex.getMessage().contains(breachMode)); + + RemoteSegmentTransferTracker.Stats stats = stats(); + assertTrue(stats.bytesLag > 0); + assertTrue(stats.refreshTimeLagMs > 0); + assertTrue(stats.localRefreshNumber - stats.remoteRefreshNumber > 0); + assertTrue(stats.rejectionCount > 0); + + ((MockRepository) internalCluster().getInstance(RepositoriesService.class, dataNodeName).repository(REPOSITORY_NAME)) + .setRandomControlIOExceptionRate(0d); + + assertBusy(() -> { + RemoteSegmentTransferTracker.Stats finalStats = stats(); + assertEquals(0, finalStats.bytesLag); + assertEquals(0, finalStats.refreshTimeLagMs); + assertEquals(0, finalStats.localRefreshNumber - finalStats.remoteRefreshNumber); + }, 30, TimeUnit.SECONDS); + + long rejectionCount = stats.rejectionCount; + stats = stats(); + indexDocAndRefresh(initialSource, initialDocsToIndex); + assertEquals(rejectionCount, stats.rejectionCount); + cleanupRepo(); + } + + private RemoteSegmentTransferTracker.Stats stats() { + String shardId = "0"; + RemoteStoreStatsResponse response = client().admin().cluster().prepareRemoteStoreStats(INDEX_NAME, shardId).get(); + final String indexShardId = String.format(Locale.ROOT, "[%s][%s]", INDEX_NAME, shardId); + List matches = Arrays.stream(response.getRemoteStoreStats()) + .filter(stat -> indexShardId.equals(stat.getSegmentStats().shardId.toString())) + .collect(Collectors.toList()); + assertEquals(1, matches.size()); + return matches.get(0).getSegmentStats(); + } + + private void indexDocAndRefresh(BytesReference source, int iterations) { + for (int i = 0; i < iterations; i++) { + client().prepareIndex(INDEX_NAME).setSource(source, MediaTypeRegistry.JSON).get(); + refresh(INDEX_NAME); + } + } + + /** + * Generates string of given sizeInBytes + * + * @param sizeInBytes size of the string + * @return the generated string + */ + private String generateString(int sizeInBytes) { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + int i = 0; + // Based on local tests, 1 char is occupying 1 byte + while (sb.length() < sizeInBytes) { + String key = "field" + i; + String value = "value" + i; + sb.append("\"").append(key).append("\":\"").append(value).append("\","); + i++; + } + if (sb.length() > 1 && sb.charAt(sb.length() - 1) == ',') { + sb.setLength(sb.length() - 1); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreBaseIntegTestCase.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreBaseIntegTestCase.java new file mode 100644 index 0000000000000..e2ef5f85abc74 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreBaseIntegTestCase.java @@ -0,0 +1,377 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.bulk.BulkItemResponse; +import org.opensearch.action.bulk.BulkRequest; +import org.opensearch.action.bulk.BulkResponse; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.metadata.RepositoriesMetadata; +import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.UUIDs; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.index.IndexModule; +import org.opensearch.index.IndexSettings; +import org.opensearch.index.mapper.MapperService; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.repositories.blobstore.BlobStoreRepository; +import org.opensearch.repositories.fs.FsRepository; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.After; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_CLUSTER_STATE_REPOSITORY_NAME_ATTRIBUTE_KEY; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_SEGMENT_REPOSITORY_NAME_ATTRIBUTE_KEY; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_TRANSLOG_REPOSITORY_NAME_ATTRIBUTE_KEY; + +public class RemoteStoreBaseIntegTestCase extends OpenSearchIntegTestCase { + protected static final String REPOSITORY_NAME = "test-remote-store-repo"; + protected static final String REPOSITORY_2_NAME = "test-remote-store-repo-2"; + protected static final int SHARD_COUNT = 1; + protected static final int REPLICA_COUNT = 1; + protected static final String TOTAL_OPERATIONS = "total-operations"; + protected static final String REFRESHED_OR_FLUSHED_OPERATIONS = "refreshed-or-flushed-operations"; + protected static final String MAX_SEQ_NO_TOTAL = "max-seq-no-total"; + protected static final String MAX_SEQ_NO_REFRESHED_OR_FLUSHED = "max-seq-no-refreshed-or-flushed"; + + protected Path segmentRepoPath; + protected Path translogRepoPath; + protected boolean clusterSettingsSuppliedByTest = false; + private final List documentKeys = List.of( + randomAlphaOfLength(5), + randomAlphaOfLength(5), + randomAlphaOfLength(5), + randomAlphaOfLength(5), + randomAlphaOfLength(5) + ); + + protected Map indexData(int numberOfIterations, boolean invokeFlush, String index) { + long totalOperations = 0; + long refreshedOrFlushedOperations = 0; + long maxSeqNo = -1; + long maxSeqNoRefreshedOrFlushed = -1; + int shardId = 0; + Map indexingStats = new HashMap<>(); + for (int i = 0; i < numberOfIterations; i++) { + if (invokeFlush) { + flushAndRefresh(index); + } else { + refresh(index); + } + maxSeqNoRefreshedOrFlushed = maxSeqNo; + indexingStats.put(MAX_SEQ_NO_REFRESHED_OR_FLUSHED + "-shard-" + shardId, maxSeqNoRefreshedOrFlushed); + refreshedOrFlushedOperations = totalOperations; + int numberOfOperations = randomIntBetween(20, 50); + int numberOfBulk = randomIntBetween(1, 5); + for (int j = 0; j < numberOfBulk; j++) { + BulkResponse res = indexBulk(index, numberOfOperations); + for (BulkItemResponse singleResp : res.getItems()) { + indexingStats.put( + MAX_SEQ_NO_TOTAL + "-shard-" + singleResp.getResponse().getShardId().id(), + singleResp.getResponse().getSeqNo() + ); + maxSeqNo = singleResp.getResponse().getSeqNo(); + } + totalOperations += numberOfOperations; + } + } + + indexingStats.put(TOTAL_OPERATIONS, totalOperations); + indexingStats.put(REFRESHED_OR_FLUSHED_OPERATIONS, refreshedOrFlushedOperations); + indexingStats.put(MAX_SEQ_NO_TOTAL, maxSeqNo); + indexingStats.put(MAX_SEQ_NO_REFRESHED_OR_FLUSHED, maxSeqNoRefreshedOrFlushed); + return indexingStats; + } + + @Override + protected boolean addMockInternalEngine() { + return false; + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + if (segmentRepoPath == null || translogRepoPath == null) { + segmentRepoPath = randomRepoPath().toAbsolutePath(); + translogRepoPath = randomRepoPath().toAbsolutePath(); + } + if (clusterSettingsSuppliedByTest) { + return Settings.builder().put(super.nodeSettings(nodeOrdinal)).build(); + } else { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(remoteStoreClusterSettings(REPOSITORY_NAME, segmentRepoPath, REPOSITORY_2_NAME, translogRepoPath)) + .build(); + } + } + + public Settings indexSettings() { + return defaultIndexSettings(); + } + + protected IndexResponse indexSingleDoc(String indexName) { + return indexSingleDoc(indexName, false); + } + + protected IndexResponse indexSingleDoc(String indexName, boolean forceRefresh) { + IndexRequestBuilder indexRequestBuilder = client().prepareIndex(indexName) + .setId(UUIDs.randomBase64UUID()) + .setSource(documentKeys.get(randomIntBetween(0, documentKeys.size() - 1)), randomAlphaOfLength(5)); + if (forceRefresh) { + indexRequestBuilder.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + } + return indexRequestBuilder.get(); + } + + protected BulkResponse indexBulk(String indexName, int numDocs) { + BulkRequest bulkRequest = new BulkRequest(); + for (int i = 0; i < numDocs; i++) { + final IndexRequest request = client().prepareIndex(indexName) + .setId(UUIDs.randomBase64UUID()) + .setSource(documentKeys.get(randomIntBetween(0, documentKeys.size() - 1)), randomAlphaOfLength(5)) + .request(); + bulkRequest.add(request); + } + return client().bulk(bulkRequest).actionGet(); + } + + public static Settings remoteStoreClusterSettings(String name, Path path) { + return remoteStoreClusterSettings(name, path, name, path); + } + + public static Settings remoteStoreClusterSettings( + String segmentRepoName, + Path segmentRepoPath, + String segmentRepoType, + String translogRepoName, + Path translogRepoPath, + String translogRepoType + ) { + Settings.Builder settingsBuilder = Settings.builder(); + settingsBuilder.put( + buildRemoteStoreNodeAttributes( + segmentRepoName, + segmentRepoPath, + segmentRepoType, + translogRepoName, + translogRepoPath, + translogRepoType, + false + ) + ); + return settingsBuilder.build(); + } + + public static Settings remoteStoreClusterSettings( + String segmentRepoName, + Path segmentRepoPath, + String translogRepoName, + Path translogRepoPath + ) { + Settings.Builder settingsBuilder = Settings.builder(); + settingsBuilder.put(buildRemoteStoreNodeAttributes(segmentRepoName, segmentRepoPath, translogRepoName, translogRepoPath, false)); + return settingsBuilder.build(); + } + + public static Settings buildRemoteStoreNodeAttributes( + String segmentRepoName, + Path segmentRepoPath, + String translogRepoName, + Path translogRepoPath, + boolean withRateLimiterAttributes + ) { + return buildRemoteStoreNodeAttributes( + segmentRepoName, + segmentRepoPath, + FsRepository.TYPE, + translogRepoName, + translogRepoPath, + FsRepository.TYPE, + withRateLimiterAttributes + ); + } + + public static Settings buildRemoteStoreNodeAttributes( + String segmentRepoName, + Path segmentRepoPath, + String segmentRepoType, + String translogRepoName, + Path translogRepoPath, + String translogRepoType, + boolean withRateLimiterAttributes + ) { + String segmentRepoTypeAttributeKey = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, + segmentRepoName + ); + String segmentRepoSettingsAttributeKeyPrefix = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, + segmentRepoName + ); + String translogRepoTypeAttributeKey = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, + translogRepoName + ); + String translogRepoSettingsAttributeKeyPrefix = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, + translogRepoName + ); + String stateRepoTypeAttributeKey = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, + segmentRepoName + ); + String stateRepoSettingsAttributeKeyPrefix = String.format( + Locale.getDefault(), + "node.attr." + REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, + segmentRepoName + ); + + Settings.Builder settings = Settings.builder() + .put("node.attr." + REMOTE_STORE_SEGMENT_REPOSITORY_NAME_ATTRIBUTE_KEY, segmentRepoName) + .put(segmentRepoTypeAttributeKey, segmentRepoType) + .put(segmentRepoSettingsAttributeKeyPrefix + "location", segmentRepoPath) + .put("node.attr." + REMOTE_STORE_TRANSLOG_REPOSITORY_NAME_ATTRIBUTE_KEY, translogRepoName) + .put(translogRepoTypeAttributeKey, translogRepoType) + .put(translogRepoSettingsAttributeKeyPrefix + "location", translogRepoPath) + .put("node.attr." + REMOTE_STORE_CLUSTER_STATE_REPOSITORY_NAME_ATTRIBUTE_KEY, segmentRepoName) + .put(stateRepoTypeAttributeKey, segmentRepoType) + .put(stateRepoSettingsAttributeKeyPrefix + "location", segmentRepoPath); + + if (withRateLimiterAttributes) { + settings.put(segmentRepoSettingsAttributeKeyPrefix + "compress", randomBoolean()) + .put(segmentRepoSettingsAttributeKeyPrefix + "chunk_size", 200, ByteSizeUnit.BYTES); + } + + return settings.build(); + } + + private Settings defaultIndexSettings() { + return Settings.builder() + .put(super.indexSettings()) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, SHARD_COUNT) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, REPLICA_COUNT) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "300s") + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + } + + protected Settings remoteStoreIndexSettings(int numberOfReplicas, int numberOfShards) { + return Settings.builder() + .put(defaultIndexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, numberOfShards) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numberOfReplicas) + .build(); + } + + protected Settings remoteStoreIndexSettings(int numberOfReplicas) { + return remoteStoreIndexSettings(numberOfReplicas, 1); + } + + protected Settings remoteStoreIndexSettings(int numberOfReplicas, long totalFieldLimit, int refresh) { + return Settings.builder() + .put(remoteStoreIndexSettings(numberOfReplicas)) + .put(MapperService.INDEX_MAPPING_TOTAL_FIELDS_LIMIT_SETTING.getKey(), totalFieldLimit) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), String.valueOf(refresh)) + .build(); + } + + @After + public void teardown() { + clusterSettingsSuppliedByTest = false; + assertRemoteStoreRepositoryOnAllNodes(REPOSITORY_NAME); + assertRemoteStoreRepositoryOnAllNodes(REPOSITORY_2_NAME); + clusterAdmin().prepareCleanupRepository(REPOSITORY_NAME).get(); + clusterAdmin().prepareCleanupRepository(REPOSITORY_2_NAME).get(); + } + + public RepositoryMetadata buildRepositoryMetadata(DiscoveryNode node, String name) { + Map nodeAttributes = node.getAttributes(); + String type = nodeAttributes.get(String.format(Locale.getDefault(), REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, name)); + + String settingsAttributeKeyPrefix = String.format(Locale.getDefault(), REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, name); + Map settingsMap = node.getAttributes() + .keySet() + .stream() + .filter(key -> key.startsWith(settingsAttributeKeyPrefix)) + .collect(Collectors.toMap(key -> key.replace(settingsAttributeKeyPrefix, ""), key -> node.getAttributes().get(key))); + + Settings.Builder settings = Settings.builder(); + settingsMap.entrySet().forEach(entry -> settings.put(entry.getKey(), entry.getValue())); + settings.put(BlobStoreRepository.SYSTEM_REPOSITORY_SETTING.getKey(), true); + + return new RepositoryMetadata(name, type, settings.build()); + } + + public void assertRemoteStoreRepositoryOnAllNodes(String repositoryName) { + RepositoriesMetadata repositories = internalCluster().getInstance(ClusterService.class, internalCluster().getNodeNames()[0]) + .state() + .metadata() + .custom(RepositoriesMetadata.TYPE); + RepositoryMetadata actualRepository = repositories.repository(repositoryName); + + final RepositoriesService repositoriesService = internalCluster().getClusterManagerNodeInstance(RepositoriesService.class); + final BlobStoreRepository repository = (BlobStoreRepository) repositoriesService.repository(repositoryName); + + for (String nodeName : internalCluster().getNodeNames()) { + ClusterService clusterService = internalCluster().getInstance(ClusterService.class, nodeName); + DiscoveryNode node = clusterService.localNode(); + RepositoryMetadata expectedRepository = buildRepositoryMetadata(node, repositoryName); + + // Validated that all the restricted settings are entact on all the nodes. + repository.getRestrictedSystemRepositorySettings() + .stream() + .forEach(setting -> assertEquals(setting.get(actualRepository.settings()), setting.get(expectedRepository.settings()))); + } + } + + public static int getFileCount(Path path) throws Exception { + final AtomicInteger filesExisting = new AtomicInteger(0); + Files.walkFileTree(path, new SimpleFileVisitor<>() { + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException impossible) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + filesExisting.incrementAndGet(); + return FileVisitResult.CONTINUE; + } + }); + + return filesExisting.get(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreClusterStateRestoreIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreClusterStateRestoreIT.java new file mode 100644 index 0000000000000..5e92bb195680b --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreClusterStateRestoreIT.java @@ -0,0 +1,288 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreResponse; +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.opensearch.action.support.PlainActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.gateway.remote.RemoteClusterStateService; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutionException; + +import static org.opensearch.gateway.remote.RemoteClusterStateService.REMOTE_CLUSTER_STATE_ENABLED_SETTING; +import static org.opensearch.indices.ShardLimitValidator.SETTING_CLUSTER_MAX_SHARDS_PER_NODE; +import static org.opensearch.indices.ShardLimitValidator.SETTING_MAX_SHARDS_PER_CLUSTER_KEY; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteStoreClusterStateRestoreIT extends BaseRemoteStoreRestoreIT { + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder().put(super.nodeSettings(nodeOrdinal)).put(REMOTE_CLUSTER_STATE_ENABLED_SETTING.getKey(), true).build(); + } + + private void addNewNodes(int dataNodeCount, int clusterManagerNodeCount) { + internalCluster().startNodes(dataNodeCount + clusterManagerNodeCount); + } + + private Map initialTestSetup(int shardCount, int replicaCount, int dataNodeCount, int clusterManagerNodeCount) { + prepareCluster(clusterManagerNodeCount, dataNodeCount, INDEX_NAME, replicaCount, shardCount); + Map indexStats = indexData(1, false, INDEX_NAME); + assertEquals(shardCount * (replicaCount + 1), getNumShards(INDEX_NAME).totalNumShards); + ensureGreen(INDEX_NAME); + return indexStats; + } + + private void resetCluster(int dataNodeCount, int clusterManagerNodeCount) { + internalCluster().stopAllNodes(); + addNewNodes(dataNodeCount, clusterManagerNodeCount); + } + + private void restoreAndValidate(String clusterUUID, Map indexStats) throws Exception { + restoreAndValidate(clusterUUID, indexStats, true); + } + + private void restoreAndValidate(String clusterUUID, Map indexStats, boolean validate) throws Exception { + // TODO once auto restore is merged, the remote cluster state will be restored + + if (validate) { + // Step - 4 validation restore is successful. + ensureGreen(INDEX_NAME); + verifyRestoredData(indexStats, INDEX_NAME); + } + } + + private void restoreAndValidateFails( + String clusterUUID, + PlainActionFuture actionListener, + Class clazz, + String errorSubString + ) { + + try { + restoreAndValidate(clusterUUID, null, false); + } catch (Exception e) { + assertTrue( + String.format(Locale.ROOT, "%s %s", clazz, e), + clazz.isAssignableFrom(e.getClass()) + || clazz.isAssignableFrom(e.getCause().getClass()) + || (e.getCause().getCause() != null && clazz.isAssignableFrom(e.getCause().getCause().getClass())) + ); + assertTrue( + String.format(Locale.ROOT, "Error message mismatch. Expected: [%s]. Actual: [%s]", errorSubString, e.getMessage()), + e.getMessage().contains(errorSubString) + ); + } + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9834") + public void testFullClusterRestore() throws Exception { + int shardCount = randomIntBetween(1, 2); + int replicaCount = 1; + int dataNodeCount = shardCount * (replicaCount + 1); + int clusterManagerNodeCount = 1; + + // Step - 1 index some data to generate files in remote directory + Map indexStats = initialTestSetup(shardCount, replicaCount, dataNodeCount, 1); + String prevClusterUUID = clusterService().state().metadata().clusterUUID(); + + // Step - 2 Replace all nodes in the cluster with new nodes. This ensures new cluster state doesn't have previous index metadata + resetCluster(dataNodeCount, clusterManagerNodeCount); + + String newClusterUUID = clusterService().state().metadata().clusterUUID(); + assert !Objects.equals(newClusterUUID, prevClusterUUID) : "cluster restart not successful. cluster uuid is same"; + + // Step - 3 Trigger full cluster restore and validate + restoreAndValidate(prevClusterUUID, indexStats); + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9834") + public void testFullClusterRestoreMultipleIndices() throws Exception { + int shardCount = randomIntBetween(1, 2); + int replicaCount = 1; + int dataNodeCount = shardCount * (replicaCount + 1); + int clusterManagerNodeCount = 1; + + // Step - 1 index some data to generate files in remote directory + Map indexStats = initialTestSetup(shardCount, replicaCount, dataNodeCount, clusterManagerNodeCount); + + String secondIndexName = INDEX_NAME + "-2"; + createIndex(secondIndexName, remoteStoreIndexSettings(replicaCount, shardCount + 1)); + Map indexStats2 = indexData(1, false, secondIndexName); + assertEquals((shardCount + 1) * (replicaCount + 1), getNumShards(secondIndexName).totalNumShards); + ensureGreen(secondIndexName); + + String prevClusterUUID = clusterService().state().metadata().clusterUUID(); + + // Step - 2 Replace all nodes in the cluster with new nodes. This ensures new cluster state doesn't have previous index metadata + resetCluster(dataNodeCount, clusterManagerNodeCount); + + String newClusterUUID = clusterService().state().metadata().clusterUUID(); + assert !Objects.equals(newClusterUUID, prevClusterUUID) : "cluster restart not successful. cluster uuid is same"; + + // Step - 3 Trigger full cluster restore + restoreAndValidate(prevClusterUUID, indexStats); + ensureGreen(secondIndexName); + verifyRestoredData(indexStats2, secondIndexName); + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9834") + public void testFullClusterRestoreFailureValidationFailures() throws Exception { + int shardCount = randomIntBetween(1, 2); + int replicaCount = 1; + int dataNodeCount = shardCount * (replicaCount + 1); + int clusterManagerNodeCount = 1; + + // index some data to generate files in remote directory + Map indexStats = initialTestSetup(shardCount, replicaCount, dataNodeCount, clusterManagerNodeCount); + String prevClusterUUID = clusterService().state().metadata().clusterUUID(); + + // Start of Test - 1 + // Test - 1 Trigger full cluster restore and validate it fails due to incorrect cluster UUID + PlainActionFuture future = PlainActionFuture.newFuture(); + restoreAndValidateFails("randomUUID", future, IllegalStateException.class, "Remote Cluster State not found - randomUUID"); + // End of Test - 1 + + // Start of Test - 3 + // Test - 2 Trigger full cluster restore and validate it fails due to cluster UUID same as current cluster UUID + future = PlainActionFuture.newFuture(); + restoreAndValidateFails( + clusterService().state().metadata().clusterUUID(), + future, + IllegalArgumentException.class, + "clusterUUID to restore from should be different from current cluster UUID" + ); + // End of Test - 2 + + // Start of Test - 3 + // Step - 2 Replace all nodes in the cluster with new nodes. This ensures new cluster state doesn't have previous index metadata + // Restarting cluster with just 1 data node helps with applying cluster settings + resetCluster(1, clusterManagerNodeCount); + String newClusterUUID = clusterService().state().metadata().clusterUUID(); + assert !Objects.equals(newClusterUUID, prevClusterUUID) : "cluster restart not successful. cluster uuid is same"; + + reduceShardLimits(1, 1); + + // Step - 4 Trigger full cluster restore and validate it fails + future = PlainActionFuture.newFuture(); + restoreAndValidateFails( + prevClusterUUID, + future, + IllegalArgumentException.class, + "this action would add [2] total shards, but this cluster currently has [0]/[1] maximum shards open" + ); + resetShardLimits(); + // End of Test - 3 + + // Start of Test - 4 + // Test -4 Reset cluster and trigger full restore with same name index in the cluster + // Test -4 Add required nodes for this test after last reset. + addNewNodes(dataNodeCount - 1, 0); + + newClusterUUID = clusterService().state().metadata().clusterUUID(); + assert !Objects.equals(newClusterUUID, prevClusterUUID) : "cluster restart not successful. cluster uuid is same"; + + // Test -4 Step - 2 Create a new index with same name + createIndex(INDEX_NAME, remoteStoreIndexSettings(0, 1)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + future = PlainActionFuture.newFuture(); + + // Test -4 Step - 3 Trigger full cluster restore and validate fails + restoreAndValidateFails( + prevClusterUUID, + future, + IllegalStateException.class, + "cannot restore index [remote-store-test-idx-1] because an open index with same name/uuid already exists in the cluster" + ); + + // Test -4 Step - 4 validation restore is successful. + ensureGreen(INDEX_NAME); + // End of Test - 4 + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9834") + public void testFullClusterRestoreManifestFilePointsToInvalidIndexMetadataPathThrowsException() throws Exception { + int shardCount = randomIntBetween(1, 2); + int replicaCount = 1; + int dataNodeCount = shardCount * (replicaCount + 1); + int clusterManagerNodeCount = 1; + + // Step - 1 index some data to generate files in remote directory + initialTestSetup(shardCount, replicaCount, dataNodeCount, clusterManagerNodeCount); + + String prevClusterUUID = clusterService().state().metadata().clusterUUID(); + + // Step - 2 Replace all nodes in the cluster with new nodes. This ensures new cluster state doesn't have previous index metadata + resetCluster(dataNodeCount, clusterManagerNodeCount); + + String newClusterUUID = clusterService().state().metadata().clusterUUID(); + assert !Objects.equals(newClusterUUID, prevClusterUUID) : "cluster restart not successful. cluster uuid is same"; + + // Step - 4 Delete index metadata file in remote + try { + Files.move( + segmentRepoPath.resolve( + RemoteClusterStateService.encodeString(clusterService().state().getClusterName().value()) + + "/cluster-state/" + + prevClusterUUID + + "/index" + ), + segmentRepoPath.resolve("cluster-state/") + ); + } catch (IOException e) { + throw new RuntimeException(e); + } + + // Step - 5 Trigger full cluster restore and validate fails + PlainActionFuture future = PlainActionFuture.newFuture(); + restoreAndValidateFails(prevClusterUUID, future, IllegalStateException.class, "asdsa"); + } + + private void reduceShardLimits(int maxShardsPerNode, int maxShardsPerCluster) { + // Step 3 - Reduce shard limits to hit shard limit with less no of shards + try { + client().admin() + .cluster() + .updateSettings( + new ClusterUpdateSettingsRequest().transientSettings( + Settings.builder() + .put(SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey(), maxShardsPerNode) + .put(SETTING_MAX_SHARDS_PER_CLUSTER_KEY, maxShardsPerCluster) + ) + ) + .get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + private void resetShardLimits() { + // Step - 5 Reset the cluster settings + ClusterUpdateSettingsRequest resetRequest = new ClusterUpdateSettingsRequest(); + resetRequest.transientSettings( + Settings.builder().putNull(SETTING_CLUSTER_MAX_SHARDS_PER_NODE.getKey()).putNull(SETTING_MAX_SHARDS_PER_CLUSTER_KEY) + ); + + try { + client().admin().cluster().updateSettings(resetRequest).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreForceMergeIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreForceMergeIT.java new file mode 100644 index 0000000000000..0bcde4b44c734 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreForceMergeIT.java @@ -0,0 +1,143 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreRequest; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.action.support.PlainActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteStoreForceMergeIT extends RemoteStoreBaseIntegTestCase { + + private static final String INDEX_NAME = "remote-store-test-idx-1"; + private static final String TOTAL_OPERATIONS = "total-operations"; + private static final String MAX_SEQ_NO_TOTAL = "max-seq-no-total"; + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class); + } + + @Override + public Settings indexSettings() { + return remoteStoreIndexSettings(0); + } + + private Map indexData(int numberOfIterations, boolean invokeFlush, boolean flushAfterMerge, long deletedDocs) { + long totalOperations = 0; + long maxSeqNo = -1; + List indexResponseList = new ArrayList<>(); + for (int i = 0; i < numberOfIterations; i++) { + int numberOfOperations = randomIntBetween(20, 50); + for (int j = 0; j < numberOfOperations; j++) { + IndexResponse response = indexSingleDoc(INDEX_NAME); + maxSeqNo = response.getSeqNo(); + indexResponseList.add(response); + } + totalOperations += numberOfOperations; + if (invokeFlush) { + flush(INDEX_NAME); + } else { + refresh(INDEX_NAME); + } + } + if (deletedDocs == -1) { + deletedDocs = totalOperations; + } + int length = indexResponseList.size(); + for (int j = 0; j < deletedDocs; j++) { + maxSeqNo = client().prepareDelete().setIndex(INDEX_NAME).setId(indexResponseList.get(length - j - 1).getId()).get().getSeqNo(); + } + client().admin().indices().prepareForceMerge(INDEX_NAME).setMaxNumSegments(1).setFlush(flushAfterMerge).get(); + refresh(INDEX_NAME); + assertHitCount(client().prepareSearch(INDEX_NAME).setSize(0).get(), totalOperations - deletedDocs); + Map indexingStats = new HashMap<>(); + indexingStats.put(TOTAL_OPERATIONS, totalOperations); + indexingStats.put(MAX_SEQ_NO_TOTAL, maxSeqNo); + return indexingStats; + } + + private void verifyRestoredData(Map indexStats, long deletedDocs) { + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + assertHitCount(client().prepareSearch(INDEX_NAME).setSize(0).get(), indexStats.get(TOTAL_OPERATIONS) - deletedDocs); + IndexResponse response = indexSingleDoc(INDEX_NAME); + assertEquals(indexStats.get(MAX_SEQ_NO_TOTAL) + 1, response.getSeqNo()); + refresh(INDEX_NAME); + assertHitCount(client().prepareSearch(INDEX_NAME).setSize(0).get(), indexStats.get(TOTAL_OPERATIONS) + 1 - deletedDocs); + } + + private void testRestoreWithMergeFlow(int numberOfIterations, boolean invokeFlush, boolean flushAfterMerge, long deletedDocs) + throws IOException { + internalCluster().startNodes(3); + createIndex(INDEX_NAME, remoteStoreIndexSettings(0)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + Map indexStats = indexData(numberOfIterations, invokeFlush, flushAfterMerge, deletedDocs); + + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNodeName(INDEX_NAME))); + + boolean restoreAllShards = randomBoolean(); + if (restoreAllShards) { + assertAcked(client().admin().indices().prepareClose(INDEX_NAME)); + } + client().admin() + .cluster() + .restoreRemoteStore( + new RestoreRemoteStoreRequest().indices(INDEX_NAME).restoreAllShards(restoreAllShards), + PlainActionFuture.newFuture() + ); + ensureGreen(INDEX_NAME); + + if (deletedDocs == -1) { + verifyRestoredData(indexStats, indexStats.get(TOTAL_OPERATIONS)); + } else { + verifyRestoredData(indexStats, deletedDocs); + } + } + + // Following integ tests use randomBoolean to control the number of integ tests. If we use the separate + // values for each of the flags, number of integ tests become 16 in comparison to current 2. + // We have run all the 16 tests on local and they run fine. + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9294") + public void testRestoreForceMergeSingleIteration() throws IOException { + boolean invokeFLush = randomBoolean(); + boolean flushAfterMerge = randomBoolean(); + testRestoreWithMergeFlow(1, invokeFLush, flushAfterMerge, randomIntBetween(0, 10)); + } + + public void testRestoreForceMergeMultipleIterations() throws IOException { + boolean invokeFLush = randomBoolean(); + boolean flushAfterMerge = randomBoolean(); + testRestoreWithMergeFlow(randomIntBetween(2, 5), invokeFLush, flushAfterMerge, randomIntBetween(0, 10)); + } + + public void testRestoreForceMergeMultipleIterationsDeleteAll() throws IOException { + boolean invokeFLush = randomBoolean(); + boolean flushAfterMerge = randomBoolean(); + testRestoreWithMergeFlow(randomIntBetween(2, 3), invokeFLush, flushAfterMerge, -1); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreIT.java new file mode 100644 index 0000000000000..1fb5c2052aded --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreIT.java @@ -0,0 +1,512 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; +import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; +import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; +import org.opensearch.action.admin.indices.recovery.RecoveryResponse; +import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.routing.RecoverySource; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.concurrent.BufferedAsyncIOProcessor; +import org.opensearch.core.index.Index; +import org.opensearch.index.IndexService; +import org.opensearch.index.IndexSettings; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.index.translog.Translog.Durability; +import org.opensearch.indices.IndicesService; +import org.opensearch.indices.recovery.RecoverySettings; +import org.opensearch.indices.recovery.RecoveryState; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.transport.MockTransportService; +import org.hamcrest.MatcherAssert; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.index.shard.RemoteStoreRefreshListener.LAST_N_METADATA_FILES_TO_KEEP; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.hamcrest.Matchers.comparesEqualTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.oneOf; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteStoreIT extends RemoteStoreBaseIntegTestCase { + + protected final String INDEX_NAME = "remote-store-test-idx-1"; + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class); + } + + @Override + public Settings indexSettings() { + return remoteStoreIndexSettings(0); + } + + private void testPeerRecovery(int numberOfIterations, boolean invokeFlush) throws Exception { + internalCluster().startNodes(3); + createIndex(INDEX_NAME, remoteStoreIndexSettings(0)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + Map indexStats = indexData(numberOfIterations, invokeFlush, INDEX_NAME); + + client().admin() + .indices() + .prepareUpdateSettings(INDEX_NAME) + .setSettings(Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)) + .get(); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + refresh(INDEX_NAME); + String replicaNodeName = replicaNodeName(INDEX_NAME); + assertBusy( + () -> assertHitCount(client(replicaNodeName).prepareSearch(INDEX_NAME).setSize(0).get(), indexStats.get(TOTAL_OPERATIONS)), + 30, + TimeUnit.SECONDS + ); + + RecoveryResponse recoveryResponse = client(replicaNodeName).admin().indices().prepareRecoveries().get(); + + Optional recoverySource = recoveryResponse.shardRecoveryStates() + .get(INDEX_NAME) + .stream() + .filter(rs -> rs.getRecoverySource().getType() == RecoverySource.Type.PEER) + .findFirst(); + assertFalse(recoverySource.isEmpty()); + // segments_N file is copied to new replica + assertEquals(1, recoverySource.get().getIndex().recoveredFileCount()); + + IndexResponse response = indexSingleDoc(INDEX_NAME); + assertEquals(indexStats.get(MAX_SEQ_NO_TOTAL) + 1, response.getSeqNo()); + refresh(INDEX_NAME); + assertBusy( + () -> assertHitCount(client(replicaNodeName).prepareSearch(INDEX_NAME).setSize(0).get(), indexStats.get(TOTAL_OPERATIONS) + 1), + 30, + TimeUnit.SECONDS + ); + } + + public void testPeerRecoveryWithRemoteStoreAndRemoteTranslogNoDataFlush() throws Exception { + testPeerRecovery(1, true); + } + + public void testPeerRecoveryWithRemoteStoreAndRemoteTranslogFlush() throws Exception { + testPeerRecovery(randomIntBetween(2, 5), true); + } + + public void testPeerRecoveryWithLowActivityTimeout() throws Exception { + ClusterUpdateSettingsRequest req = new ClusterUpdateSettingsRequest().persistentSettings( + Settings.builder() + .put(RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING.getKey(), "20kb") + .put(RecoverySettings.INDICES_RECOVERY_ACTIVITY_TIMEOUT_SETTING.getKey(), "1s") + ); + internalCluster().client().admin().cluster().updateSettings(req).get(); + testPeerRecovery(randomIntBetween(2, 5), true); + } + + public void testPeerRecoveryWithRemoteStoreAndRemoteTranslogNoDataRefresh() throws Exception { + testPeerRecovery(1, false); + } + + public void testPeerRecoveryWithRemoteStoreAndRemoteTranslogRefresh() throws Exception { + testPeerRecovery(randomIntBetween(2, 5), false); + } + + private void verifyRemoteStoreCleanup() throws Exception { + internalCluster().startNodes(3); + createIndex(INDEX_NAME, remoteStoreIndexSettings(1)); + + indexData(5, randomBoolean(), INDEX_NAME); + String indexUUID = client().admin() + .indices() + .prepareGetSettings(INDEX_NAME) + .get() + .getSetting(INDEX_NAME, IndexMetadata.SETTING_INDEX_UUID); + Path indexPath = Path.of(String.valueOf(segmentRepoPath), indexUUID); + assertTrue(getFileCount(indexPath) > 0); + assertAcked(client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).get()); + // Delete is async. Give time for it + assertBusy(() -> { + try { + assertThat(getFileCount(indexPath), comparesEqualTo(0)); + } catch (Exception e) {} + }, 30, TimeUnit.SECONDS); + } + + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/9327") + public void testRemoteTranslogCleanup() throws Exception { + verifyRemoteStoreCleanup(); + } + + public void testStaleCommitDeletionWithInvokeFlush() throws Exception { + internalCluster().startNode(); + createIndex(INDEX_NAME, remoteStoreIndexSettings(1, 10000l, -1)); + int numberOfIterations = randomIntBetween(5, 15); + indexData(numberOfIterations, true, INDEX_NAME); + String indexUUID = client().admin() + .indices() + .prepareGetSettings(INDEX_NAME) + .get() + .getSetting(INDEX_NAME, IndexMetadata.SETTING_INDEX_UUID); + Path indexPath = Path.of(String.valueOf(segmentRepoPath), indexUUID, "/0/segments/metadata"); + // Delete is async. + assertBusy(() -> { + int actualFileCount = getFileCount(indexPath); + if (numberOfIterations <= LAST_N_METADATA_FILES_TO_KEEP) { + MatcherAssert.assertThat(actualFileCount, is(oneOf(numberOfIterations - 1, numberOfIterations, numberOfIterations + 1))); + } else { + // As delete is async its possible that the file gets created before the deletion or after + // deletion. + MatcherAssert.assertThat( + actualFileCount, + is(oneOf(LAST_N_METADATA_FILES_TO_KEEP - 1, LAST_N_METADATA_FILES_TO_KEEP, LAST_N_METADATA_FILES_TO_KEEP + 1)) + ); + } + }, 30, TimeUnit.SECONDS); + } + + public void testStaleCommitDeletionWithoutInvokeFlush() throws Exception { + internalCluster().startNode(); + createIndex(INDEX_NAME, remoteStoreIndexSettings(1, 10000l, -1)); + int numberOfIterations = randomIntBetween(5, 15); + indexData(numberOfIterations, false, INDEX_NAME); + String indexUUID = client().admin() + .indices() + .prepareGetSettings(INDEX_NAME) + .get() + .getSetting(INDEX_NAME, IndexMetadata.SETTING_INDEX_UUID); + Path indexPath = Path.of(String.valueOf(segmentRepoPath), indexUUID, "/0/segments/metadata"); + int actualFileCount = getFileCount(indexPath); + // We also allow (numberOfIterations + 1) as index creation also triggers refresh. + MatcherAssert.assertThat(actualFileCount, is(oneOf(numberOfIterations - 1, numberOfIterations, numberOfIterations + 1))); + } + + /** + * Tests that when the index setting is not passed during index creation, the buffer interval picked up is the cluster + * default. + */ + public void testDefaultBufferInterval() throws ExecutionException, InterruptedException { + internalCluster().startClusterManagerOnlyNode(); + String clusterManagerName = internalCluster().getClusterManagerName(); + String dataNode = internalCluster().startDataOnlyNodes(1).get(0); + createIndex(INDEX_NAME); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + assertClusterRemoteBufferInterval(IndexSettings.DEFAULT_REMOTE_TRANSLOG_BUFFER_INTERVAL, dataNode); + + IndexShard indexShard = getIndexShard(dataNode); + assertTrue(indexShard.getTranslogSyncProcessor() instanceof BufferedAsyncIOProcessor); + assertBufferInterval(IndexSettings.DEFAULT_REMOTE_TRANSLOG_BUFFER_INTERVAL, indexShard); + + // Next, we change the default buffer interval and the same should reflect in the buffer interval of the index created + TimeValue clusterBufferInterval = TimeValue.timeValueSeconds(randomIntBetween(100, 200)); + client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(CLUSTER_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.getKey(), clusterBufferInterval)) + .get(); + assertBufferInterval(clusterBufferInterval, indexShard); + clearClusterBufferIntervalSetting(clusterManagerName); + } + + /** + * This tests multiple cases where the index setting is passed during the index creation with multiple combinations + * with and without cluster default. + */ + public void testOverriddenBufferInterval() throws ExecutionException, InterruptedException { + internalCluster().startClusterManagerOnlyNode(); + String clusterManagerName = internalCluster().getClusterManagerName(); + String dataNode = internalCluster().startDataOnlyNodes(1).get(0); + + TimeValue bufferInterval = TimeValue.timeValueSeconds(randomIntBetween(0, 100)); + Settings indexSettings = Settings.builder() + .put(indexSettings()) + .put(IndexSettings.INDEX_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.getKey(), bufferInterval) + .build(); + createIndex(INDEX_NAME, indexSettings); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + IndexShard indexShard = getIndexShard(dataNode); + assertTrue(indexShard.getTranslogSyncProcessor() instanceof BufferedAsyncIOProcessor); + assertBufferInterval(bufferInterval, indexShard); + + // Set the cluster default with a different value, validate that the buffer interval is still the overridden value + TimeValue clusterBufferInterval = TimeValue.timeValueSeconds(randomIntBetween(100, 200)); + client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put(CLUSTER_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.getKey(), clusterBufferInterval)) + .get(); + assertBufferInterval(bufferInterval, indexShard); + + // Set the index setting (index.remote_store.translog.buffer_interval) with a different value and validate that + // the buffer interval is updated + bufferInterval = TimeValue.timeValueSeconds(bufferInterval.seconds() + randomIntBetween(1, 100)); + client(clusterManagerName).admin() + .indices() + .updateSettings( + new UpdateSettingsRequest(INDEX_NAME).settings( + Settings.builder().put(IndexSettings.INDEX_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.getKey(), bufferInterval) + ) + ) + .get(); + assertBufferInterval(bufferInterval, indexShard); + + // Set the index setting (index.remote_store.translog.buffer_interval) with null and validate the buffer interval + // which will be the cluster default now. + client(clusterManagerName).admin() + .indices() + .updateSettings( + new UpdateSettingsRequest(INDEX_NAME).settings( + Settings.builder().putNull(IndexSettings.INDEX_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.getKey()) + ) + ) + .get(); + assertBufferInterval(clusterBufferInterval, indexShard); + clearClusterBufferIntervalSetting(clusterManagerName); + } + + /** + * This tests validation which kicks in during index creation failing creation if the value is less than minimum allowed value. + */ + public void testOverriddenBufferIntervalValidation() { + internalCluster().startClusterManagerOnlyNode(); + TimeValue bufferInterval = TimeValue.timeValueSeconds(-1); + Settings indexSettings = Settings.builder() + .put(indexSettings()) + .put(IndexSettings.INDEX_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.getKey(), bufferInterval) + .build(); + IllegalArgumentException exceptionDuringCreateIndex = assertThrows( + IllegalArgumentException.class, + () -> createIndex(INDEX_NAME, indexSettings) + ); + assertEquals( + "failed to parse value [-1] for setting [index.remote_store.translog.buffer_interval], must be >= [0ms]", + exceptionDuringCreateIndex.getMessage() + ); + } + + /** + * This tests validation of the cluster setting when being set. + */ + public void testClusterBufferIntervalValidation() { + String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings( + Settings.builder().put(CLUSTER_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.getKey(), TimeValue.timeValueSeconds(-1)) + ) + .get() + ); + assertEquals( + "failed to parse value [-1] for setting [cluster.remote_store.translog.buffer_interval], must be >= [0ms]", + exception.getMessage() + ); + } + + public void testRequestDurabilityWhenRestrictSettingExplicitFalse() throws ExecutionException, InterruptedException { + // Explicit node settings and request durability + testRestrictSettingFalse(true, Durability.REQUEST); + } + + public void testAsyncDurabilityWhenRestrictSettingExplicitFalse() throws ExecutionException, InterruptedException { + // Explicit node settings and async durability + testRestrictSettingFalse(true, Durability.ASYNC); + } + + public void testRequestDurabilityWhenRestrictSettingImplicitFalse() throws ExecutionException, InterruptedException { + // No node settings and request durability + testRestrictSettingFalse(false, Durability.REQUEST); + } + + public void testAsyncDurabilityWhenRestrictSettingImplicitFalse() throws ExecutionException, InterruptedException { + // No node settings and async durability + testRestrictSettingFalse(false, Durability.ASYNC); + } + + private void testRestrictSettingFalse(boolean setRestrictFalse, Durability durability) throws ExecutionException, InterruptedException { + String clusterManagerName; + if (setRestrictFalse) { + clusterManagerName = internalCluster().startClusterManagerOnlyNode( + Settings.builder().put(IndicesService.CLUSTER_REMOTE_INDEX_RESTRICT_ASYNC_DURABILITY_SETTING.getKey(), false).build() + ); + } else { + clusterManagerName = internalCluster().startClusterManagerOnlyNode(); + } + String dataNode = internalCluster().startDataOnlyNodes(1).get(0); + Settings indexSettings = Settings.builder() + .put(indexSettings()) + .put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), durability) + .build(); + createIndex(INDEX_NAME, indexSettings); + IndexShard indexShard = getIndexShard(dataNode); + assertEquals(durability, indexShard.indexSettings().getTranslogDurability()); + + durability = randomFrom(Durability.values()); + client(clusterManagerName).admin() + .indices() + .updateSettings( + new UpdateSettingsRequest(INDEX_NAME).settings( + Settings.builder().put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), durability) + ) + ) + .get(); + assertEquals(durability, indexShard.indexSettings().getTranslogDurability()); + } + + public void testAsyncDurabilityThrowsExceptionWhenRestrictSettingTrue() throws ExecutionException, InterruptedException { + String expectedExceptionMsg = + "index setting [index.translog.durability=async] is not allowed as cluster setting [cluster.remote_store.index.restrict.async-durability=true]"; + String clusterManagerName = internalCluster().startClusterManagerOnlyNode( + Settings.builder().put(IndicesService.CLUSTER_REMOTE_INDEX_RESTRICT_ASYNC_DURABILITY_SETTING.getKey(), true).build() + ); + String dataNode = internalCluster().startDataOnlyNodes(1).get(0); + + // Case 1 - Test create index fails + Settings indexSettings = Settings.builder() + .put(indexSettings()) + .put(IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.getKey(), Durability.ASYNC) + .build(); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> createIndex(INDEX_NAME, indexSettings)); + assertEquals(expectedExceptionMsg, exception.getMessage()); + + // Case 2 - Test update index fails + createIndex(INDEX_NAME); + IndexShard indexShard = getIndexShard(dataNode); + assertEquals(Durability.REQUEST, indexShard.indexSettings().getTranslogDurability()); + exception = assertThrows( + IllegalArgumentException.class, + () -> client(clusterManagerName).admin() + .indices() + .updateSettings(new UpdateSettingsRequest(INDEX_NAME).settings(indexSettings)) + .actionGet() + ); + assertEquals(expectedExceptionMsg, exception.getMessage()); + } + + private IndexShard getIndexShard(String dataNode) throws ExecutionException, InterruptedException { + String clusterManagerName = internalCluster().getClusterManagerName(); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, dataNode); + GetIndexResponse getIndexResponse = client(clusterManagerName).admin().indices().getIndex(new GetIndexRequest()).get(); + String uuid = getIndexResponse.getSettings().get(INDEX_NAME).get(IndexMetadata.SETTING_INDEX_UUID); + IndexService indexService = indicesService.indexService(new Index(INDEX_NAME, uuid)); + return indexService.getShard(0); + } + + private void assertClusterRemoteBufferInterval(TimeValue expectedBufferInterval, String dataNode) { + IndicesService indicesService = internalCluster().getInstance(IndicesService.class, dataNode); + assertEquals(expectedBufferInterval, indicesService.getClusterRemoteTranslogBufferInterval()); + } + + private void assertBufferInterval(TimeValue expectedBufferInterval, IndexShard indexShard) { + assertEquals( + expectedBufferInterval, + ((BufferedAsyncIOProcessor) indexShard.getTranslogSyncProcessor()).getBufferIntervalSupplier().get() + ); + } + + private void clearClusterBufferIntervalSetting(String clusterManagerName) { + client(clusterManagerName).admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().putNull(CLUSTER_REMOTE_TRANSLOG_BUFFER_INTERVAL_SETTING.getKey())) + .get(); + } + + public void testRestoreSnapshotToIndexWithSameNameDifferentUUID() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + List dataNodes = internalCluster().startDataOnlyNodes(2); + + Path absolutePath = randomRepoPath().toAbsolutePath(); + assertAcked( + clusterAdmin().preparePutRepository("test-repo").setType("fs").setSettings(Settings.builder().put("location", absolutePath)) + ); + + logger.info("--> Create index and ingest 50 docs"); + createIndex(INDEX_NAME, remoteStoreIndexSettings(1)); + indexBulk(INDEX_NAME, 50); + flushAndRefresh(INDEX_NAME); + + String originalIndexUUID = client().admin() + .indices() + .prepareGetSettings(INDEX_NAME) + .get() + .getSetting(INDEX_NAME, IndexMetadata.SETTING_INDEX_UUID); + assertNotNull(originalIndexUUID); + assertNotEquals(IndexMetadata.INDEX_UUID_NA_VALUE, originalIndexUUID); + + ensureGreen(); + + logger.info("--> take a snapshot"); + client().admin().cluster().prepareCreateSnapshot("test-repo", "test-snap").setIndices(INDEX_NAME).setWaitForCompletion(true).get(); + + logger.info("--> wipe all indices"); + cluster().wipeIndices(INDEX_NAME); + + logger.info("--> Create index with the same name, different UUID"); + assertAcked( + prepareCreate(INDEX_NAME).setSettings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 1)) + ); + + ensureGreen(TimeValue.timeValueSeconds(30), INDEX_NAME); + + String newIndexUUID = client().admin() + .indices() + .prepareGetSettings(INDEX_NAME) + .get() + .getSetting(INDEX_NAME, IndexMetadata.SETTING_INDEX_UUID); + assertNotNull(newIndexUUID); + assertNotEquals(IndexMetadata.INDEX_UUID_NA_VALUE, newIndexUUID); + assertNotEquals(newIndexUUID, originalIndexUUID); + + logger.info("--> close index"); + client().admin().indices().prepareClose(INDEX_NAME).get(); + + logger.info("--> restore all indices from the snapshot"); + RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap") + .setWaitForCompletion(true) + .execute() + .actionGet(); + assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0)); + + flushAndRefresh(INDEX_NAME); + + ensureGreen(INDEX_NAME); + assertBusy(() -> { + assertHitCount(client(dataNodes.get(0)).prepareSearch(INDEX_NAME).setSize(0).get(), 50); + assertHitCount(client(dataNodes.get(1)).prepareSearch(INDEX_NAME).setSize(0).get(), 50); + }); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRefreshListenerIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRefreshListenerIT.java new file mode 100644 index 0000000000000..88760b7bbfad2 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRefreshListenerIT.java @@ -0,0 +1,71 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.opensearch.action.admin.indices.stats.IndicesStatsRequest; +import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static org.opensearch.index.remote.RemoteStorePressureSettings.REMOTE_REFRESH_SEGMENT_PRESSURE_ENABLED; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteStoreRefreshListenerIT extends AbstractRemoteStoreMockRepositoryIntegTestCase { + + public void testRemoteRefreshRetryOnFailure() throws Exception { + + Path location = randomRepoPath().toAbsolutePath(); + setup(location, randomDoubleBetween(0.1, 0.15, true), "metadata", 10L); + + // Here we are having flush/refresh after each iteration of indexing. However, the refresh will not always succeed + // due to IOExceptions that are thrown while doing uploadBlobs. + indexData(randomIntBetween(5, 10), randomBoolean()); + logger.info("--> Indexed data"); + + // TODO - Once the segments stats api is available, we need to verify that there were failed upload attempts. + IndicesStatsResponse response = client().admin().indices().stats(new IndicesStatsRequest()).get(); + assertEquals(1, response.getShards().length); + + String indexUuid = response.getShards()[0].getShardRouting().index().getUUID(); + Path segmentDataRepoPath = location.resolve(String.format(Locale.ROOT, "%s/0/segments/data", indexUuid)); + String segmentDataLocalPath = String.format(Locale.ROOT, "%s/indices/%s/0/index", response.getShards()[0].getDataPath(), indexUuid); + + logger.info("--> Verify that the segment files are same on local and repository eventually"); + // This can take time as the retry interval is exponential and maxed at 30s + assertBusy(() -> { + Set filesInLocal = getSegmentFiles(location.getRoot().resolve(segmentDataLocalPath)); + Set filesInRepo = getSegmentFiles(segmentDataRepoPath); + List sortedFilesInLocal = new ArrayList<>(filesInLocal), sortedFilesInRepo = new ArrayList<>(filesInRepo); + Collections.sort(sortedFilesInLocal); + Collections.sort(sortedFilesInRepo); + logger.info("Local files = {}, Repo files = {}", sortedFilesInLocal, sortedFilesInRepo); + assertTrue(filesInRepo.containsAll(filesInLocal)); + }, 90, TimeUnit.SECONDS); + cleanupRepo(); + } + + public void testRemoteRefreshSegmentPressureSettingChanged() { + Settings request = Settings.builder().put(REMOTE_REFRESH_SEGMENT_PRESSURE_ENABLED.getKey(), true).build(); + ClusterUpdateSettingsResponse response = client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get(); + assertEquals(response.getPersistentSettings().get(REMOTE_REFRESH_SEGMENT_PRESSURE_ENABLED.getKey()), "true"); + + request = Settings.builder().put(REMOTE_REFRESH_SEGMENT_PRESSURE_ENABLED.getKey(), false).build(); + response = client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get(); + assertEquals(response.getPersistentSettings().get(REMOTE_REFRESH_SEGMENT_PRESSURE_ENABLED.getKey()), "false"); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRepositoryRegistrationIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRepositoryRegistrationIT.java new file mode 100644 index 0000000000000..002a149f0c286 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRepositoryRegistrationIT.java @@ -0,0 +1,165 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.client.Client; +import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.plugins.Plugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.transport.MockTransportService; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteStoreRepositoryRegistrationIT extends RemoteStoreBaseIntegTestCase { + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class); + } + + public void testSingleNodeClusterRepositoryRegistration() throws Exception { + internalCluster().startNode(); + } + + public void testMultiNodeClusterRepositoryRegistration() throws Exception { + internalCluster().startNodes(3); + } + + public void testMultiNodeClusterRepositoryRegistrationWithMultipleClusterManager() throws Exception { + internalCluster().startClusterManagerOnlyNodes(3); + internalCluster().startNodes(3); + } + + public void testMultiNodeClusterActiveClusterManagerShutDown() throws Exception { + internalCluster().startNodes(3); + internalCluster().stopCurrentClusterManagerNode(); + ensureStableCluster(2); + } + + public void testMultiNodeClusterActiveMClusterManagerRestart() throws Exception { + internalCluster().startNodes(3); + String clusterManagerNodeName = internalCluster().getClusterManagerName(); + internalCluster().restartNode(clusterManagerNodeName); + ensureStableCluster(3); + } + + public void testMultiNodeClusterRandomNodeRestart() throws Exception { + internalCluster().startNodes(3); + internalCluster().restartRandomDataNode(); + ensureStableCluster(3); + } + + public void testMultiNodeClusterActiveClusterManagerRecoverNetworkIsolation() { + internalCluster().startClusterManagerOnlyNodes(3); + String dataNode = internalCluster().startNode(); + + NetworkDisruption partition = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); + internalCluster().setDisruptionScheme(partition); + + partition.startDisrupting(); + ensureStableCluster(3, dataNode); + partition.stopDisrupting(); + + ensureStableCluster(4); + + internalCluster().clearDisruptionScheme(); + } + + public void testMultiNodeClusterRandomNodeRecoverNetworkIsolation() { + Set nodesInOneSide = internalCluster().startNodes(3).stream().collect(Collectors.toCollection(HashSet::new)); + Set nodesInAnotherSide = internalCluster().startNodes(3).stream().collect(Collectors.toCollection(HashSet::new)); + ensureStableCluster(6); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInAnotherSide), + NetworkDisruption.DISCONNECT + ); + internalCluster().setDisruptionScheme(networkDisruption); + + networkDisruption.startDisrupting(); + ensureStableCluster(3, nodesInOneSide.stream().findAny().get()); + networkDisruption.stopDisrupting(); + + ensureStableCluster(6); + + internalCluster().clearDisruptionScheme(); + } + + public void testMultiNodeClusterRandomNodeRecoverNetworkIsolationPostNonRestrictedSettingsUpdate() { + Set nodesInOneSide = internalCluster().startNodes(3).stream().collect(Collectors.toCollection(HashSet::new)); + Set nodesInAnotherSide = internalCluster().startNodes(3).stream().collect(Collectors.toCollection(HashSet::new)); + ensureStableCluster(6); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInAnotherSide), + NetworkDisruption.DISCONNECT + ); + internalCluster().setDisruptionScheme(networkDisruption); + + networkDisruption.startDisrupting(); + + final Client client = client(nodesInOneSide.iterator().next()); + RepositoryMetadata repositoryMetadata = client.admin() + .cluster() + .prepareGetRepositories(REPOSITORY_NAME) + .get() + .repositories() + .get(0); + Settings.Builder updatedSettings = Settings.builder().put(repositoryMetadata.settings()).put("chunk_size", new ByteSizeValue(20)); + updatedSettings.remove("system_repository"); + + client.admin() + .cluster() + .preparePutRepository(repositoryMetadata.name()) + .setType(repositoryMetadata.type()) + .setSettings(updatedSettings) + .get(); + + ensureStableCluster(3, nodesInOneSide.stream().findAny().get()); + networkDisruption.stopDisrupting(); + + ensureStableCluster(6); + + internalCluster().clearDisruptionScheme(); + } + + public void testNodeRestartPostNonRestrictedSettingsUpdate() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + internalCluster().startNodes(3); + + final Client client = client(); + RepositoryMetadata repositoryMetadata = client.admin() + .cluster() + .prepareGetRepositories(REPOSITORY_NAME) + .get() + .repositories() + .get(0); + Settings.Builder updatedSettings = Settings.builder().put(repositoryMetadata.settings()).put("chunk_size", new ByteSizeValue(20)); + updatedSettings.remove("system_repository"); + + client.admin() + .cluster() + .preparePutRepository(repositoryMetadata.name()) + .setType(repositoryMetadata.type()) + .setSettings(updatedSettings) + .get(); + + internalCluster().restartRandomDataNode(); + + ensureStableCluster(4); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRestoreIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRestoreIT.java new file mode 100644 index 0000000000000..7626e3dba6424 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreRestoreIT.java @@ -0,0 +1,465 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreRequest; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreResponse; +import org.opensearch.action.support.PlainActionFuture; +import org.opensearch.cluster.health.ClusterHealthStatus; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.repositories.Repository; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.hamcrest.Matchers.greaterThan; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, numDataNodes = 0) +public class RemoteStoreRestoreIT extends BaseRemoteStoreRestoreIT { + + /** + * Simulates all data restored using Remote Translog Store. + * @throws IOException IO Exception. + */ + public void testRemoteTranslogRestoreWithNoDataPostCommit() throws Exception { + testRestoreFlow(1, true, randomIntBetween(1, 5)); + } + + /** + * Simulates all data restored using Remote Translog Store. + * @throws IOException IO Exception. + */ + public void testRemoteTranslogRestoreWithNoDataPostRefresh() throws Exception { + testRestoreFlow(1, false, randomIntBetween(1, 5)); + } + + /** + * Simulates refreshed data restored using Remote Segment Store + * and unrefreshed data restored using Remote Translog Store. + * @throws IOException IO Exception. + */ + public void testRemoteTranslogRestoreWithRefreshedData() throws Exception { + testRestoreFlow(randomIntBetween(2, 5), false, randomIntBetween(1, 5)); + } + + /** + * Simulates refreshed data restored using Remote Segment Store + * and unrefreshed data restored using Remote Translog Store. + * @throws IOException IO Exception. + */ + public void testRemoteTranslogRestoreWithCommittedData() throws Exception { + testRestoreFlow(randomIntBetween(2, 5), true, randomIntBetween(1, 5)); + } + + /** + * Simulates all data restored using Remote Translog Store. + * @throws IOException IO Exception. + */ + public void testRTSRestoreWithNoDataPostCommitPrimaryReplicaDown() throws Exception { + testRestoreFlowBothPrimaryReplicasDown(1, true, randomIntBetween(1, 5)); + } + + /** + * Simulates all data restored using Remote Translog Store. + * @throws IOException IO Exception. + */ + public void testRTSRestoreWithNoDataPostRefreshPrimaryReplicaDown() throws Exception { + testRestoreFlowBothPrimaryReplicasDown(1, false, randomIntBetween(1, 5)); + } + + /** + * Simulates refreshed data restored using Remote Segment Store + * and unrefreshed data restored using Remote Translog Store. + * @throws IOException IO Exception. + */ + public void testRTSRestoreWithRefreshedDataPrimaryReplicaDown() throws Exception { + testRestoreFlowBothPrimaryReplicasDown(randomIntBetween(2, 5), false, randomIntBetween(1, 5)); + } + + /** + * Simulates refreshed data restored using Remote Segment Store + * and unrefreshed data restored using Remote Translog Store. + * @throws IOException IO Exception. + */ + public void testRTSRestoreWithCommittedDataPrimaryReplicaDown() throws Exception { + testRestoreFlowBothPrimaryReplicasDown(randomIntBetween(2, 5), true, randomIntBetween(1, 5)); + } + + private void restoreAndVerify(int shardCount, int replicaCount, Map indexStats) throws Exception { + restore(INDEX_NAME); + ensureGreen(INDEX_NAME); + // This is required to get updated number from already active shards which were not restored + assertEquals(shardCount * (1 + replicaCount), getNumShards(INDEX_NAME).totalNumShards); + assertEquals(replicaCount, getNumShards(INDEX_NAME).numReplicas); + verifyRestoredData(indexStats, INDEX_NAME); + } + + /** + * Helper function to test restoring an index with no replication from remote store. Only primary node is dropped. + * @param numberOfIterations Number of times a refresh/flush should be invoked, followed by indexing some data. + * @param invokeFlush If true, a flush is invoked. Otherwise, a refresh is invoked. + * @throws IOException IO Exception. + */ + private void testRestoreFlow(int numberOfIterations, boolean invokeFlush, int shardCount) throws Exception { + prepareCluster(1, 3, INDEX_NAME, 0, shardCount); + Map indexStats = indexData(numberOfIterations, invokeFlush, INDEX_NAME); + assertEquals(shardCount, getNumShards(INDEX_NAME).totalNumShards); + + assertHitCount(client().prepareSearch(INDEX_NAME).setSize(0).get(), indexStats.get(REFRESHED_OR_FLUSHED_OPERATIONS)); + + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNodeName(INDEX_NAME))); + ensureRed(INDEX_NAME); + + restoreAndVerify(shardCount, 0, indexStats); + } + + /** + * Helper function to test restoring an index having replicas from remote store when all the nodes housing the primary/replica drop. + * @param numberOfIterations Number of times a refresh/flush should be invoked, followed by indexing some data. + * @param invokeFlush If true, a flush is invoked. Otherwise, a refresh is invoked. + * @throws IOException IO Exception. + */ + private void testRestoreFlowBothPrimaryReplicasDown(int numberOfIterations, boolean invokeFlush, int shardCount) throws Exception { + prepareCluster(1, 2, INDEX_NAME, 1, shardCount); + Map indexStats = indexData(numberOfIterations, invokeFlush, INDEX_NAME); + assertEquals(shardCount * 2, getNumShards(INDEX_NAME).totalNumShards); + + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(replicaNodeName(INDEX_NAME))); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNodeName(INDEX_NAME))); + ensureRed(INDEX_NAME); + internalCluster().startDataOnlyNodes(2); + + restoreAndVerify(shardCount, 1, indexStats); + } + + /** + * Helper function to test restoring multiple indices from remote store when all the nodes housing the primary/replica drop. + * @param numberOfIterations Number of times a refresh/flush should be invoked, followed by indexing some data. + * @param invokeFlush If true, a flush is invoked. Otherwise, a refresh is invoked. + * @throws IOException IO Exception. + */ + private void testRestoreFlowMultipleIndices(int numberOfIterations, boolean invokeFlush, int shardCount) throws Exception { + prepareCluster(1, 3, INDEX_NAMES, 1, shardCount); + String[] indices = INDEX_NAMES.split(","); + Map> indicesStats = new HashMap<>(); + for (String index : indices) { + Map indexStats = indexData(numberOfIterations, invokeFlush, index); + indicesStats.put(index, indexStats); + assertEquals(shardCount * 2, getNumShards(index).totalNumShards); + } + + for (String index : indices) { + ClusterHealthStatus indexHealth = ensureRed(index); + if (ClusterHealthStatus.RED.equals(indexHealth)) { + continue; + } + + if (ClusterHealthStatus.GREEN.equals(indexHealth)) { + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(replicaNodeName(index))); + } + + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNodeName(index))); + } + + ensureRed(indices); + internalCluster().startDataOnlyNodes(3); + + boolean restoreAllShards = randomBoolean(); + if (restoreAllShards) { + assertAcked(client().admin().indices().prepareClose(indices)); + } + client().admin() + .cluster() + .restoreRemoteStore( + new RestoreRemoteStoreRequest().indices(INDEX_NAMES_WILDCARD.split(",")).restoreAllShards(restoreAllShards), + PlainActionFuture.newFuture() + ); + ensureGreen(indices); + for (String index : indices) { + assertEquals(shardCount * 2, getNumShards(index).totalNumShards); + verifyRestoredData(indicesStats.get(index), index); + } + } + + public void testRestoreFlowAllShardsNoRedIndex() throws InterruptedException { + int shardCount = randomIntBetween(1, 5); + prepareCluster(1, 3, INDEX_NAME, 0, shardCount); + indexData(randomIntBetween(2, 5), true, INDEX_NAME); + assertEquals(shardCount, getNumShards(INDEX_NAME).totalNumShards); + + PlainActionFuture future = PlainActionFuture.newFuture(); + client().admin().cluster().restoreRemoteStore(new RestoreRemoteStoreRequest().indices(INDEX_NAME).restoreAllShards(true), future); + try { + future.get(); + } catch (ExecutionException e) { + // If the request goes to co-ordinator, e.getCause() can be RemoteTransportException + assertTrue(e.getCause() instanceof IllegalStateException || e.getCause().getCause() instanceof IllegalStateException); + } + } + + public void testRestoreFlowNoRedIndex() throws Exception { + int shardCount = randomIntBetween(1, 5); + prepareCluster(1, 3, INDEX_NAME, 0, shardCount); + Map indexStats = indexData(randomIntBetween(2, 5), true, INDEX_NAME); + assertEquals(shardCount, getNumShards(INDEX_NAME).totalNumShards); + + client().admin() + .cluster() + .restoreRemoteStore(new RestoreRemoteStoreRequest().indices(INDEX_NAME).restoreAllShards(false), PlainActionFuture.newFuture()); + + ensureGreen(INDEX_NAME); + assertEquals(shardCount, getNumShards(INDEX_NAME).totalNumShards); + verifyRestoredData(indexStats, INDEX_NAME); + } + + /** + * Simulates refreshed data restored using Remote Segment Store + * and unrefreshed data restored using Remote Translog Store + * for multiple indices matching a wildcard name pattern. + * @throws IOException IO Exception. + */ + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8480") + public void testRTSRestoreWithCommittedDataMultipleIndicesPatterns() throws Exception { + testRestoreFlowMultipleIndices(2, true, randomIntBetween(1, 5)); + } + + /** + * Simulates refreshed data restored using Remote Segment Store + * and unrefreshed data restored using Remote Translog Store, + * with all remote-enabled red indices considered for the restore by default. + * @throws IOException IO Exception. + */ + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8480") + public void testRTSRestoreWithCommittedDataDefaultAllIndices() throws Exception { + int shardCount = randomIntBetween(1, 5); + prepareCluster(1, 3, INDEX_NAMES, 1, shardCount); + String[] indices = INDEX_NAMES.split(","); + Map> indicesStats = new HashMap<>(); + for (String index : indices) { + Map indexStats = indexData(2, true, index); + indicesStats.put(index, indexStats); + assertEquals(shardCount, getNumShards(index).totalNumShards); + } + + for (String index : indices) { + if (ClusterHealthStatus.RED.equals(ensureRed(index))) { + continue; + } + + if (ClusterHealthStatus.GREEN.equals(ensureRed(index))) { + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(replicaNodeName(index))); + } + + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNodeName(index))); + } + + ensureRed(indices); + internalCluster().startDataOnlyNodes(3); + + restore(indices); + ensureGreen(indices); + + for (String index : indices) { + assertEquals(shardCount, getNumShards(index).totalNumShards); + verifyRestoredData(indicesStats.get(index), index); + } + } + + /** + * Simulates refreshed data restored using Remote Segment Store + * and unrefreshed data restored using Remote Translog Store, + * with only some of the remote-enabled red indices requested for the restore. + * @throws IOException IO Exception. + */ + public void testRTSRestoreWithCommittedDataNotAllRedRemoteIndices() throws Exception { + int shardCount = randomIntBetween(1, 5); + prepareCluster(1, 3, INDEX_NAMES, 0, shardCount); + String[] indices = INDEX_NAMES.split(","); + Map> indicesStats = new HashMap<>(); + for (String index : indices) { + Map indexStats = indexData(2, true, index); + indicesStats.put(index, indexStats); + assertEquals(shardCount, getNumShards(index).totalNumShards); + } + + for (String index : indices) { + if (ClusterHealthStatus.RED.equals(ensureRed(index))) { + continue; + } + + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNodeName(index))); + } + + ensureRed(indices); + internalCluster().startDataOnlyNodes(3); + + boolean restoreAllShards = randomBoolean(); + if (restoreAllShards) { + assertAcked(client().admin().indices().prepareClose(indices[0], indices[1])); + } + client().admin() + .cluster() + .restoreRemoteStore( + new RestoreRemoteStoreRequest().indices(indices[0], indices[1]).restoreAllShards(restoreAllShards), + PlainActionFuture.newFuture() + ); + ensureGreen(indices[0], indices[1]); + assertEquals(shardCount, getNumShards(indices[0]).totalNumShards); + verifyRestoredData(indicesStats.get(indices[0]), indices[0]); + assertEquals(shardCount, getNumShards(indices[1]).totalNumShards); + verifyRestoredData(indicesStats.get(indices[1]), indices[1]); + ensureRed(indices[2], indices[3]); + } + + /** + * Simulates refreshed data restored using Remote Segment Store + * and unrefreshed data restored using Remote Translog Store, + * with all remote-enabled red indices being considered for the restore + * except those matching the specified exclusion pattern. + * @throws IOException IO Exception. + */ + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8480") + public void testRTSRestoreWithCommittedDataExcludeIndicesPatterns() throws Exception { + int shardCount = randomIntBetween(1, 5); + prepareCluster(1, 3, INDEX_NAMES, 1, shardCount); + String[] indices = INDEX_NAMES.split(","); + Map> indicesStats = new HashMap<>(); + for (String index : indices) { + Map indexStats = indexData(2, true, index); + indicesStats.put(index, indexStats); + assertEquals(shardCount, getNumShards(index).totalNumShards); + } + + for (String index : indices) { + if (ClusterHealthStatus.RED.equals(ensureRed(index))) { + continue; + } + + if (ClusterHealthStatus.GREEN.equals(ensureRed(index))) { + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(replicaNodeName(index))); + } + + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNodeName(index))); + } + + ensureRed(indices); + internalCluster().startDataOnlyNodes(3); + + boolean restoreAllShards = randomBoolean(); + if (restoreAllShards) { + assertAcked(client().admin().indices().prepareClose(indices[0], indices[1])); + } + client().admin() + .cluster() + .restoreRemoteStore( + new RestoreRemoteStoreRequest().indices("*", "-remote-store-test-index-*").restoreAllShards(restoreAllShards), + PlainActionFuture.newFuture() + ); + ensureGreen(indices[0], indices[1]); + assertEquals(shardCount, getNumShards(indices[0]).totalNumShards); + verifyRestoredData(indicesStats.get(indices[0]), indices[0]); + assertEquals(shardCount, getNumShards(indices[1]).totalNumShards); + verifyRestoredData(indicesStats.get(indices[1]), indices[1]); + ensureRed(indices[2], indices[3]); + } + + /** + * Simulates no-op restore from remote store, + * when the index has no data. + * @throws IOException IO Exception. + */ + public void testRTSRestoreDataOnlyInTranslog() throws Exception { + testRestoreFlow(0, true, randomIntBetween(1, 5)); + } + + public void testRateLimitedRemoteDownloads() throws Exception { + clusterSettingsSuppliedByTest = true; + int shardCount = randomIntBetween(1, 3); + Path segmentRepoPath = randomRepoPath(); + Path tlogRepoPath = randomRepoPath(); + prepareCluster( + 1, + 3, + INDEX_NAME, + 0, + shardCount, + buildRemoteStoreNodeAttributes(REPOSITORY_NAME, segmentRepoPath, REPOSITORY_2_NAME, tlogRepoPath, true) + ); + + // validate inplace repository metadata update + ClusterService clusterService = internalCluster().getInstance(ClusterService.class); + DiscoveryNode node = clusterService.localNode(); + String settingsAttributeKeyPrefix = String.format( + Locale.getDefault(), + REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, + REPOSITORY_NAME + ); + Map settingsMap = node.getAttributes() + .keySet() + .stream() + .filter(key -> key.startsWith(settingsAttributeKeyPrefix)) + .collect(Collectors.toMap(key -> key.replace(settingsAttributeKeyPrefix, ""), key -> node.getAttributes().get(key))); + Settings.Builder settings = Settings.builder(); + settingsMap.entrySet().forEach(entry -> settings.put(entry.getKey(), entry.getValue())); + settings.put("location", segmentRepoPath).put("max_remote_download_bytes_per_sec", 4, ByteSizeUnit.KB); + + assertAcked(client().admin().cluster().preparePutRepository(REPOSITORY_NAME).setType("fs").setSettings(settings).get()); + + for (RepositoriesService repositoriesService : internalCluster().getDataNodeInstances(RepositoriesService.class)) { + Repository segmentRepo = repositoriesService.repository(REPOSITORY_NAME); + assertEquals("4096b", segmentRepo.getMetadata().settings().get("max_remote_download_bytes_per_sec")); + } + + Map indexStats = indexData(5, false, INDEX_NAME); + assertEquals(shardCount, getNumShards(INDEX_NAME).totalNumShards); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNodeName(INDEX_NAME))); + ensureRed(INDEX_NAME); + restore(INDEX_NAME); + assertBusy(() -> { + long downloadPauseTime = 0L; + for (RepositoriesService repositoriesService : internalCluster().getDataNodeInstances(RepositoriesService.class)) { + downloadPauseTime += repositoriesService.repository(REPOSITORY_NAME).getRemoteDownloadThrottleTimeInNanos(); + } + assertThat(downloadPauseTime, greaterThan(TimeValue.timeValueSeconds(randomIntBetween(3, 5)).nanos())); + }, 30, TimeUnit.SECONDS); + // Waiting for extended period for green state so that rate limit does not cause flakiness + ensureGreen(TimeValue.timeValueSeconds(120), INDEX_NAME); + // This is required to get updated number from already active shards which were not restored + assertEquals(shardCount, getNumShards(INDEX_NAME).totalNumShards); + assertEquals(0, getNumShards(INDEX_NAME).numReplicas); + verifyRestoredData(indexStats, INDEX_NAME); + + // revert repo metadata to pass asserts on repo metadata vs. node attrs during teardown + // https://github.com/opensearch-project/OpenSearch/pull/9569#discussion_r1345668700 + settings.remove("max_remote_download_bytes_per_sec"); + assertAcked(client().admin().cluster().preparePutRepository(REPOSITORY_NAME).setType("fs").setSettings(settings).get()); + for (RepositoriesService repositoriesService : internalCluster().getDataNodeInstances(RepositoriesService.class)) { + Repository segmentRepo = repositoriesService.repository(REPOSITORY_NAME); + assertNull(segmentRepo.getMetadata().settings().get("max_remote_download_bytes_per_sec")); + } + } + + // TODO: Restore flow - index aliases +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreStatsFromNodesStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreStatsFromNodesStatsIT.java new file mode 100644 index 0000000000000..6e796bdae5a4a --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreStatsFromNodesStatsIT.java @@ -0,0 +1,209 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsResponse; +import org.opensearch.action.admin.indices.stats.CommonStatsFlags; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.remote.RemoteSegmentStats; +import org.opensearch.index.translog.RemoteTranslogStats; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.Before; + +import java.util.concurrent.TimeUnit; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteStoreStatsFromNodesStatsIT extends RemoteStoreBaseIntegTestCase { + private static final String INDEX_NAME = "remote-index-1"; + private static final int DATA_NODE_COUNT = 2; + private static final int CLUSTER_MANAGER_NODE_COUNT = 3; + + @Before + public void setup() { + setupCustomCluster(); + } + + private void setupCustomCluster() { + internalCluster().startClusterManagerOnlyNodes(CLUSTER_MANAGER_NODE_COUNT); + internalCluster().startDataOnlyNodes(DATA_NODE_COUNT); + ensureStableCluster(DATA_NODE_COUNT + CLUSTER_MANAGER_NODE_COUNT); + } + + /** + * - Creates two indices with single primary shard, pinned to a single node. + * - Index documents in both of them and forces a fresh for both + * - Polls the _remotestore/stats API for individual index level stats + * - Adds up requisite fields from the API output, repeats this for the 2nd index + * - Polls _nodes/stats and verifies that the total values at node level adds up + * to the values capture in the previous step + */ + public void testNodesStatsParityWithOnlyPrimaryShards() { + String[] dataNodes = internalCluster().getDataNodeNames().toArray(String[]::new); + String randomDataNode = dataNodes[randomIntBetween(0, dataNodes.length - 1)]; + String firstIndex = INDEX_NAME + "1"; + String secondIndex = INDEX_NAME + "2"; + + // Create first index + createIndex( + firstIndex, + Settings.builder().put(remoteStoreIndexSettings(0, 1)).put("index.routing.allocation.require._name", randomDataNode).build() + ); + ensureGreen(firstIndex); + indexSingleDoc(firstIndex, true); + + // Create second index + createIndex( + secondIndex, + Settings.builder().put(remoteStoreIndexSettings(0, 1)).put("index.routing.allocation.require._name", randomDataNode).build() + ); + ensureGreen(secondIndex); + indexSingleDoc(secondIndex, true); + + assertNodeStatsParityOnNode(randomDataNode, firstIndex, secondIndex); + } + + /** + * - Creates two indices with single primary shard and single replica + * - Index documents in both of them and forces a fresh for both + * - Polls the _remotestore/stats API for individual index level stats + * - Adds up requisite fields from the API output for both indices + * - Polls _nodes/stats and verifies that the total values at node level adds up + * to the values capture in the previous step + * - Repeats the above 3 steps for the second node + */ + public void testNodesStatsParityWithReplicaShards() throws Exception { + String firstIndex = INDEX_NAME + "1"; + String secondIndex = INDEX_NAME + "2"; + + createIndex(firstIndex, Settings.builder().put(remoteStoreIndexSettings(1, 1)).build()); + ensureGreen(firstIndex); + indexSingleDoc(firstIndex, true); + + // Create second index + createIndex(secondIndex, Settings.builder().put(remoteStoreIndexSettings(1, 1)).build()); + ensureGreen(secondIndex); + indexSingleDoc(secondIndex, true); + + assertBusy(() -> assertNodeStatsParityAcrossNodes(firstIndex, secondIndex), 15, TimeUnit.SECONDS); + } + + /** + * Ensures that node stats shows 0 values for dedicated cluster manager nodes + * since cluster manager nodes does not participate in indexing + */ + public void testZeroRemoteStatsOnNodesStatsForClusterManager() { + createIndex(INDEX_NAME, remoteStoreIndexSettings(0)); + ensureGreen(INDEX_NAME); + indexSingleDoc(INDEX_NAME); + refresh(INDEX_NAME); + + NodesStatsResponse nodesStatsResponseForClusterManager = client().admin() + .cluster() + .prepareNodesStats(internalCluster().getClusterManagerName()) + .setIndices(new CommonStatsFlags().set(CommonStatsFlags.Flag.Segments, true).set(CommonStatsFlags.Flag.Translog, true)) + .get(); + + assertTrue( + nodesStatsResponseForClusterManager.getNodes().get(0).getNode().isClusterManagerNode() + && !nodesStatsResponseForClusterManager.getNodes().get(0).getNode().isDataNode() + ); + assertZeroRemoteSegmentStats( + nodesStatsResponseForClusterManager.getNodes().get(0).getIndices().getSegments().getRemoteSegmentStats() + ); + assertZeroRemoteTranslogStats( + nodesStatsResponseForClusterManager.getNodes().get(0).getIndices().getTranslog().getRemoteTranslogStats() + ); + + NodesStatsResponse nodesStatsResponseForDataNode = client().admin() + .cluster() + .prepareNodesStats(primaryNodeName(INDEX_NAME)) + .setIndices(new CommonStatsFlags().set(CommonStatsFlags.Flag.Segments, true).set(CommonStatsFlags.Flag.Translog, true)) + .get(); + + assertTrue(nodesStatsResponseForDataNode.getNodes().get(0).getNode().isDataNode()); + RemoteSegmentStats remoteSegmentStats = nodesStatsResponseForDataNode.getNodes() + .get(0) + .getIndices() + .getSegments() + .getRemoteSegmentStats(); + assertTrue(remoteSegmentStats.getUploadBytesStarted() > 0); + assertTrue(remoteSegmentStats.getUploadBytesSucceeded() > 0); + + RemoteTranslogStats remoteTranslogStats = nodesStatsResponseForDataNode.getNodes() + .get(0) + .getIndices() + .getTranslog() + .getRemoteTranslogStats(); + assertTrue(remoteTranslogStats.getUploadBytesStarted() > 0); + assertTrue(remoteTranslogStats.getUploadBytesSucceeded() > 0); + } + + private void assertZeroRemoteSegmentStats(RemoteSegmentStats remoteSegmentStats) { + // Compare with fresh object because all values default to 0 in default fresh object + assertEquals(new RemoteSegmentStats(), remoteSegmentStats); + } + + private void assertZeroRemoteTranslogStats(RemoteTranslogStats remoteTranslogStats) { + // Compare with fresh object because all values default to 0 in default fresh object + assertEquals(new RemoteTranslogStats(), remoteTranslogStats); + } + + private static void assertNodeStatsParityAcrossNodes(String... indices) { + for (String dataNode : internalCluster().getDataNodeNames()) { + assertNodeStatsParityOnNode(dataNode, indices); + } + } + + private static void assertNodeStatsParityOnNode(String dataNode, String... indices) { + RemoteSegmentStats remoteSegmentStatsCumulative = new RemoteSegmentStats(); + RemoteTranslogStats remoteTranslogStatsCumulative = new RemoteTranslogStats(); + for (String index : indices) { + // Fetch _remotestore/stats + RemoteStoreStatsResponse remoteStoreStats = client(dataNode).admin() + .cluster() + .prepareRemoteStoreStats(index, "0") + .setLocal(true) + .get(); + remoteSegmentStatsCumulative.add(new RemoteSegmentStats(remoteStoreStats.getRemoteStoreStats()[0].getSegmentStats())); + remoteTranslogStatsCumulative.add(new RemoteTranslogStats(remoteStoreStats.getRemoteStoreStats()[0].getTranslogStats())); + } + + // Fetch _nodes/stats + NodesStatsResponse nodesStatsResponse = client().admin() + .cluster() + .prepareNodesStats(dataNode) + .setIndices(new CommonStatsFlags().set(CommonStatsFlags.Flag.Segments, true).set(CommonStatsFlags.Flag.Translog, true)) + .get(); + + // assert segment stats + RemoteSegmentStats remoteSegmentStatsFromNodesStats = nodesStatsResponse.getNodes() + .get(0) + .getIndices() + .getSegments() + .getRemoteSegmentStats(); + assertEquals(remoteSegmentStatsCumulative, remoteSegmentStatsFromNodesStats); + // Ensure that total upload time has non-zero value if there has been segments uploaded from the node + if (remoteSegmentStatsCumulative.getUploadBytesStarted() > 0) { + assertTrue(remoteSegmentStatsCumulative.getTotalUploadTime() > 0); + } + // Ensure that total download time has non-zero value if there has been segments downloaded to the node + if (remoteSegmentStatsCumulative.getDownloadBytesStarted() > 0) { + assertTrue(remoteSegmentStatsCumulative.getTotalDownloadTime() > 0); + } + + // assert translog stats + RemoteTranslogStats remoteTranslogStatsFromNodesStats = nodesStatsResponse.getNodes() + .get(0) + .getIndices() + .getTranslog() + .getRemoteTranslogStats(); + assertEquals(remoteTranslogStatsCumulative, remoteTranslogStatsFromNodesStats); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreStatsIT.java new file mode 100644 index 0000000000000..463e960755bd5 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/RemoteStoreStatsIT.java @@ -0,0 +1,701 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreRequest; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStats; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsRequestBuilder; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsResponse; +import org.opensearch.action.support.PlainActionFuture; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.ShardRoutingState; +import org.opensearch.cluster.routing.allocation.command.MoveAllocationCommand; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.IndexSettings; +import org.opensearch.index.remote.RemoteSegmentTransferTracker; +import org.opensearch.index.remote.RemoteTranslogTransferTracker; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteStoreStatsIT extends RemoteStoreBaseIntegTestCase { + + private static final String INDEX_NAME = "remote-store-test-idx-1"; + + @Before + public void setup() { + internalCluster().startNodes(3); + } + + public void testStatsResponseFromAllNodes() { + + // Step 1 - We create cluster, create an index, and then index documents into. We also do multiple refreshes/flushes + // during this time frame. This ensures that the segment upload has started. + createIndex(INDEX_NAME, remoteStoreIndexSettings(0)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + indexDocs(); + + // Step 2 - We find all the nodes that are present in the cluster. We make the remote store stats api call from + // each of the node in the cluster and check that the response is coming as expected. + ClusterState state = getClusterState(); + List nodes = StreamSupport.stream(state.nodes().getNodes().values().spliterator(), false) + .map(x -> x.getName()) + .collect(Collectors.toList()); + String shardId = "0"; + for (String node : nodes) { + RemoteStoreStatsResponse response = client(node).admin().cluster().prepareRemoteStoreStats(INDEX_NAME, shardId).get(); + assertTrue(response.getSuccessfulShards() > 0); + assertTrue(response.getRemoteStoreStats() != null && response.getRemoteStoreStats().length != 0); + final String indexShardId = String.format(Locale.ROOT, "[%s][%s]", INDEX_NAME, shardId); + List matches = Arrays.stream(response.getRemoteStoreStats()) + .filter(stat -> indexShardId.equals(stat.getSegmentStats().shardId.toString())) + .collect(Collectors.toList()); + assertEquals(1, matches.size()); + + RemoteSegmentTransferTracker.Stats segmentStats = matches.get(0).getSegmentStats(); + validateSegmentUploadStats(segmentStats); + assertEquals(0, segmentStats.directoryFileTransferTrackerStats.transferredBytesStarted); + + RemoteTranslogTransferTracker.Stats translogStats = matches.get(0).getTranslogStats(); + assertNonZeroTranslogUploadStatsNoFailures(translogStats); + assertZeroTranslogDownloadStats(translogStats); + } + + // Step 3 - Enable replicas on the existing indices and ensure that download + // stats are being populated as well + changeReplicaCountAndEnsureGreen(1); + for (String node : nodes) { + RemoteStoreStatsResponse response = client(node).admin().cluster().prepareRemoteStoreStats(INDEX_NAME, shardId).get(); + assertTrue(response.getSuccessfulShards() > 0); + assertTrue(response.getRemoteStoreStats() != null && response.getRemoteStoreStats().length != 0); + final String indexShardId = String.format(Locale.ROOT, "[%s][%s]", INDEX_NAME, shardId); + List matches = Arrays.stream(response.getRemoteStoreStats()) + .filter(stat -> indexShardId.equals(stat.getSegmentStats().shardId.toString())) + .collect(Collectors.toList()); + assertEquals(2, matches.size()); + for (RemoteStoreStats stat : matches) { + ShardRouting routing = stat.getShardRouting(); + validateShardRouting(routing); + RemoteSegmentTransferTracker.Stats segmentStats = stat.getSegmentStats(); + RemoteTranslogTransferTracker.Stats translogStats = stat.getTranslogStats(); + if (routing.primary()) { + validateSegmentUploadStats(segmentStats); + assertEquals(0, segmentStats.directoryFileTransferTrackerStats.transferredBytesStarted); + + assertNonZeroTranslogUploadStatsNoFailures(translogStats); + assertZeroTranslogDownloadStats(translogStats); + } else { + validateSegmentDownloadStats(segmentStats); + assertEquals(0, segmentStats.totalUploadsStarted); + + assertZeroTranslogUploadStats(translogStats); + assertZeroTranslogDownloadStats(translogStats); + } + } + } + } + + public void testStatsResponseAllShards() { + + // Step 1 - We create cluster, create an index, and then index documents into. We also do multiple refreshes/flushes + // during this time frame. This ensures that the segment upload has started. + createIndex(INDEX_NAME, remoteStoreIndexSettings(0, 3)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + indexDocs(); + + // Step 2 - We find all the nodes that are present in the cluster. We make the remote store stats api call from + // each of the node in the cluster and check that the response is coming as expected. + ClusterState state = getClusterState(); + String node = StreamSupport.stream(state.nodes().getDataNodes().values().spliterator(), false) + .map(x -> x.getName()) + .findFirst() + .get(); + RemoteStoreStatsRequestBuilder remoteStoreStatsRequestBuilder = client(node).admin() + .cluster() + .prepareRemoteStoreStats(INDEX_NAME, null); + RemoteStoreStatsResponse response = remoteStoreStatsRequestBuilder.get(); + assertEquals(3, response.getSuccessfulShards()); + assertTrue(response.getRemoteStoreStats() != null && response.getRemoteStoreStats().length == 3); + + RemoteSegmentTransferTracker.Stats segmentStats = response.getRemoteStoreStats()[0].getSegmentStats(); + validateSegmentUploadStats(segmentStats); + assertEquals(0, segmentStats.directoryFileTransferTrackerStats.transferredBytesStarted); + + RemoteTranslogTransferTracker.Stats translogStats = response.getRemoteStoreStats()[0].getTranslogStats(); + assertNonZeroTranslogUploadStatsNoFailures(translogStats); + assertZeroTranslogDownloadStats(translogStats); + + // Step 3 - Enable replicas on the existing indices and ensure that download + // stats are being populated as well + changeReplicaCountAndEnsureGreen(1); + response = client(node).admin().cluster().prepareRemoteStoreStats(INDEX_NAME, null).get(); + assertEquals(6, response.getSuccessfulShards()); + assertTrue(response.getRemoteStoreStats() != null && response.getRemoteStoreStats().length == 6); + for (RemoteStoreStats stat : response.getRemoteStoreStats()) { + ShardRouting routing = stat.getShardRouting(); + validateShardRouting(routing); + segmentStats = stat.getSegmentStats(); + translogStats = stat.getTranslogStats(); + if (routing.primary()) { + validateSegmentUploadStats(segmentStats); + assertEquals(0, segmentStats.directoryFileTransferTrackerStats.transferredBytesStarted); + + assertNonZeroTranslogUploadStatsNoFailures(translogStats); + assertZeroTranslogDownloadStats(translogStats); + } else { + validateSegmentDownloadStats(segmentStats); + assertEquals(0, segmentStats.totalUploadsStarted); + + assertZeroTranslogUploadStats(translogStats); + assertZeroTranslogDownloadStats(translogStats); + } + } + + } + + public void testStatsResponseFromLocalNode() { + + // Step 1 - We create cluster, create an index, and then index documents into. We also do multiple refreshes/flushes + // during this time frame. This ensures that the segment upload has started. + createIndex(INDEX_NAME, remoteStoreIndexSettings(0, 3)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + indexDocs(); + + // Step 2 - We find a data node in the cluster. We make the remote store stats api call from + // each of the data node in the cluster and check that only local shards are returned. + ClusterState state = getClusterState(); + List nodes = StreamSupport.stream(state.nodes().getDataNodes().values().spliterator(), false) + .map(x -> x.getName()) + .collect(Collectors.toList()); + for (String node : nodes) { + RemoteStoreStatsRequestBuilder remoteStoreStatsRequestBuilder = client(node).admin() + .cluster() + .prepareRemoteStoreStats(INDEX_NAME, null); + remoteStoreStatsRequestBuilder.setLocal(true); + RemoteStoreStatsResponse response = remoteStoreStatsRequestBuilder.get(); + assertEquals(1, response.getSuccessfulShards()); + assertTrue(response.getRemoteStoreStats() != null && response.getRemoteStoreStats().length == 1); + RemoteSegmentTransferTracker.Stats segmentStats = response.getRemoteStoreStats()[0].getSegmentStats(); + validateSegmentUploadStats(segmentStats); + assertEquals(0, segmentStats.directoryFileTransferTrackerStats.transferredBytesStarted); + + RemoteTranslogTransferTracker.Stats translogStats = response.getRemoteStoreStats()[0].getTranslogStats(); + assertNonZeroTranslogUploadStatsNoFailures(translogStats); + assertZeroTranslogDownloadStats(translogStats); + } + changeReplicaCountAndEnsureGreen(1); + for (String node : nodes) { + RemoteStoreStatsRequestBuilder remoteStoreStatsRequestBuilder = client(node).admin() + .cluster() + .prepareRemoteStoreStats(INDEX_NAME, null); + remoteStoreStatsRequestBuilder.setLocal(true); + RemoteStoreStatsResponse response = remoteStoreStatsRequestBuilder.get(); + assertTrue(response.getSuccessfulShards() > 0); + assertTrue(response.getRemoteStoreStats() != null && response.getRemoteStoreStats().length != 0); + for (RemoteStoreStats stat : response.getRemoteStoreStats()) { + ShardRouting routing = stat.getShardRouting(); + validateShardRouting(routing); + RemoteSegmentTransferTracker.Stats segmentStats = stat.getSegmentStats(); + RemoteTranslogTransferTracker.Stats translogStats = stat.getTranslogStats(); + if (routing.primary()) { + validateSegmentUploadStats(segmentStats); + assertEquals(0, segmentStats.directoryFileTransferTrackerStats.transferredBytesStarted); + + assertNonZeroTranslogUploadStatsNoFailures(translogStats); + assertZeroTranslogDownloadStats(translogStats); + } else { + validateSegmentDownloadStats(segmentStats); + assertEquals(0, segmentStats.totalUploadsStarted); + + assertZeroTranslogUploadStats(translogStats); + assertZeroTranslogDownloadStats(translogStats); + } + } + } + } + + public void testDownloadStatsCorrectnessSinglePrimarySingleReplica() throws Exception { + // Scenario: + // - Create index with single primary and single replica shard + // - Disable Refresh Interval for the index + // - Index documents + // - Trigger refresh and flush + // - Assert that download stats == upload stats + // - Repeat this step for random times (between 5 and 10) + + // Create index with 1 pri and 1 replica and refresh interval disabled + createIndex( + INDEX_NAME, + Settings.builder().put(remoteStoreIndexSettings(1, 1)).put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), -1).build() + ); + ensureGreen(INDEX_NAME); + + // Manually invoke a refresh + refresh(INDEX_NAME); + + // Get zero state values + // Extract and assert zero state primary stats + RemoteStoreStatsResponse zeroStateResponse = client().admin().cluster().prepareRemoteStoreStats(INDEX_NAME, "0").get(); + RemoteSegmentTransferTracker.Stats zeroStatePrimaryStats = Arrays.stream(zeroStateResponse.getRemoteStoreStats()) + .filter(remoteStoreStats -> remoteStoreStats.getShardRouting().primary()) + .collect(Collectors.toList()) + .get(0) + .getSegmentStats(); + assertTrue( + zeroStatePrimaryStats.totalUploadsStarted == zeroStatePrimaryStats.totalUploadsSucceeded + && zeroStatePrimaryStats.totalUploadsSucceeded == 1 + ); + assertTrue( + zeroStatePrimaryStats.uploadBytesStarted == zeroStatePrimaryStats.uploadBytesSucceeded + && zeroStatePrimaryStats.uploadBytesSucceeded > 0 + ); + assertTrue(zeroStatePrimaryStats.totalUploadsFailed == 0 && zeroStatePrimaryStats.uploadBytesFailed == 0); + + // Extract and assert zero state replica stats + RemoteSegmentTransferTracker.Stats zeroStateReplicaStats = Arrays.stream(zeroStateResponse.getRemoteStoreStats()) + .filter(remoteStoreStats -> !remoteStoreStats.getShardRouting().primary()) + .collect(Collectors.toList()) + .get(0) + .getSegmentStats(); + assertTrue( + zeroStateReplicaStats.directoryFileTransferTrackerStats.transferredBytesStarted == 0 + && zeroStateReplicaStats.directoryFileTransferTrackerStats.transferredBytesSucceeded == 0 + ); + + // Index documents + for (int i = 1; i <= randomIntBetween(5, 10); i++) { + indexSingleDoc(INDEX_NAME); + // Running Flush & Refresh manually + flushAndRefresh(INDEX_NAME); + ensureGreen(INDEX_NAME); + + // Poll for RemoteStore Stats + assertBusy(() -> { + RemoteStoreStatsResponse response = client().admin().cluster().prepareRemoteStoreStats(INDEX_NAME, "0").get(); + // Iterate through the response and extract the relevant segment upload and download stats + List primaryStatsList = Arrays.stream(response.getRemoteStoreStats()) + .filter(remoteStoreStats -> remoteStoreStats.getShardRouting().primary()) + .collect(Collectors.toList()); + assertEquals(1, primaryStatsList.size()); + List replicaStatsList = Arrays.stream(response.getRemoteStoreStats()) + .filter(remoteStoreStats -> !remoteStoreStats.getShardRouting().primary()) + .collect(Collectors.toList()); + assertEquals(1, replicaStatsList.size()); + RemoteSegmentTransferTracker.Stats primaryStats = primaryStatsList.get(0).getSegmentStats(); + RemoteSegmentTransferTracker.Stats replicaStats = replicaStatsList.get(0).getSegmentStats(); + // Assert Upload syncs - zero state uploads == download syncs + assertTrue(primaryStats.totalUploadsStarted > 0); + assertTrue(primaryStats.totalUploadsSucceeded > 0); + assertTrue( + replicaStats.directoryFileTransferTrackerStats.transferredBytesStarted > 0 + && primaryStats.uploadBytesStarted + - zeroStatePrimaryStats.uploadBytesStarted >= replicaStats.directoryFileTransferTrackerStats.transferredBytesStarted + ); + assertTrue( + replicaStats.directoryFileTransferTrackerStats.transferredBytesSucceeded > 0 + && primaryStats.uploadBytesSucceeded + - zeroStatePrimaryStats.uploadBytesSucceeded >= replicaStats.directoryFileTransferTrackerStats.transferredBytesSucceeded + ); + // Assert zero failures + assertEquals(0, primaryStats.uploadBytesFailed); + assertEquals(0, replicaStats.directoryFileTransferTrackerStats.transferredBytesFailed); + }, 60, TimeUnit.SECONDS); + } + } + + public void testDownloadStatsCorrectnessSinglePrimaryMultipleReplicaShards() throws Exception { + // Scenario: + // - Create index with single primary and N-1 replica shards (N = no of data nodes) + // - Disable Refresh Interval for the index + // - Index documents + // - Trigger refresh and flush + // - Assert that download stats == upload stats + // - Repeat this step for random times (between 5 and 10) + + // Create index + int dataNodeCount = client().admin().cluster().prepareHealth().get().getNumberOfDataNodes(); + createIndex( + INDEX_NAME, + Settings.builder() + .put(remoteStoreIndexSettings(dataNodeCount - 1, 1)) + .put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), -1) + .build() + ); + ensureGreen(INDEX_NAME); + + // Manually invoke a refresh + refresh(INDEX_NAME); + + // Get zero state values + // Extract and assert zero state primary stats + RemoteStoreStatsResponse zeroStateResponse = client().admin().cluster().prepareRemoteStoreStats(INDEX_NAME, "0").get(); + RemoteSegmentTransferTracker.Stats zeroStatePrimaryStats = Arrays.stream(zeroStateResponse.getRemoteStoreStats()) + .filter(remoteStoreStats -> remoteStoreStats.getShardRouting().primary()) + .collect(Collectors.toList()) + .get(0) + .getSegmentStats(); + assertTrue( + zeroStatePrimaryStats.totalUploadsStarted == zeroStatePrimaryStats.totalUploadsSucceeded + && zeroStatePrimaryStats.totalUploadsSucceeded == 1 + ); + assertTrue( + zeroStatePrimaryStats.uploadBytesStarted == zeroStatePrimaryStats.uploadBytesSucceeded + && zeroStatePrimaryStats.uploadBytesSucceeded > 0 + ); + assertTrue(zeroStatePrimaryStats.totalUploadsFailed == 0 && zeroStatePrimaryStats.uploadBytesFailed == 0); + + // Extract and assert zero state replica stats + List zeroStateReplicaStats = Arrays.stream(zeroStateResponse.getRemoteStoreStats()) + .filter(remoteStoreStats -> !remoteStoreStats.getShardRouting().primary()) + .collect(Collectors.toList()); + zeroStateReplicaStats.forEach(stats -> { + assertTrue( + stats.getSegmentStats().directoryFileTransferTrackerStats.transferredBytesStarted == 0 + && stats.getSegmentStats().directoryFileTransferTrackerStats.transferredBytesSucceeded == 0 + ); + }); + + int currentNodesInCluster = client().admin().cluster().prepareHealth().get().getNumberOfDataNodes(); + for (int i = 0; i < randomIntBetween(5, 10); i++) { + indexSingleDoc(INDEX_NAME); + // Running Flush & Refresh manually + flushAndRefresh(INDEX_NAME); + + assertBusy(() -> { + RemoteStoreStatsResponse response = client().admin().cluster().prepareRemoteStoreStats(INDEX_NAME, "0").get(); + assertEquals(currentNodesInCluster, response.getSuccessfulShards()); + long uploadsStarted = 0, uploadsSucceeded = 0, uploadsFailed = 0; + long uploadBytesStarted = 0, uploadBytesSucceeded = 0, uploadBytesFailed = 0; + List downloadBytesStarted = new ArrayList<>(), downloadBytesSucceeded = new ArrayList<>(), downloadBytesFailed = + new ArrayList<>(); + + // Assert that stats for primary shard and replica shard set are equal + for (RemoteStoreStats eachStatsObject : response.getRemoteStoreStats()) { + RemoteSegmentTransferTracker.Stats stats = eachStatsObject.getSegmentStats(); + if (eachStatsObject.getShardRouting().primary()) { + uploadBytesStarted = stats.uploadBytesStarted; + uploadBytesSucceeded = stats.uploadBytesSucceeded; + uploadBytesFailed = stats.uploadBytesFailed; + } else { + downloadBytesStarted.add(stats.directoryFileTransferTrackerStats.transferredBytesStarted); + downloadBytesSucceeded.add(stats.directoryFileTransferTrackerStats.transferredBytesSucceeded); + downloadBytesFailed.add(stats.directoryFileTransferTrackerStats.transferredBytesFailed); + } + } + + assertEquals(0, uploadsFailed); + assertEquals(0, uploadBytesFailed); + for (int j = 0; j < response.getSuccessfulShards() - 1; j++) { + assertTrue(uploadBytesStarted - zeroStatePrimaryStats.uploadBytesStarted > downloadBytesStarted.get(j)); + assertTrue(uploadBytesSucceeded - zeroStatePrimaryStats.uploadBytesSucceeded > downloadBytesSucceeded.get(j)); + assertEquals(0, (long) downloadBytesFailed.get(j)); + } + }, 60, TimeUnit.SECONDS); + } + } + + public void testStatsOnShardRelocation() { + // Scenario: + // - Create index with single primary and single replica shard + // - Index documents + // - Reroute replica shard to one of the remaining nodes + // - Assert that remote store stats reflects the new node ID + + // Create index + createIndex(INDEX_NAME, remoteStoreIndexSettings(1, 1)); + ensureGreen(INDEX_NAME); + // Index docs + indexDocs(); + + // Fetch current set of nodes in the cluster + List currentNodesInCluster = getClusterState().nodes() + .getDataNodes() + .values() + .stream() + .map(DiscoveryNode::getId) + .collect(Collectors.toList()); + DiscoveryNode[] discoveryNodesForIndex = client().admin().cluster().prepareSearchShards(INDEX_NAME).get().getNodes(); + + // Fetch nodes with shard copies of the created index + List nodeIdsWithShardCopies = new ArrayList<>(); + Arrays.stream(discoveryNodesForIndex).forEach(eachNode -> nodeIdsWithShardCopies.add(eachNode.getId())); + + // Fetch nodes which does not have any copies of the index + List nodeIdsWithoutShardCopy = currentNodesInCluster.stream() + .filter(eachNode -> !nodeIdsWithShardCopies.contains(eachNode)) + .collect(Collectors.toList()); + assertEquals(1, nodeIdsWithoutShardCopy.size()); + + // Manually reroute shard to a node which does not have any shard copy at present + ShardRouting replicaShardRouting = getClusterState().routingTable() + .index(INDEX_NAME) + .shard(0) + .assignedShards() + .stream() + .filter(shard -> !shard.primary()) + .collect(Collectors.toList()) + .get(0); + String sourceNode = replicaShardRouting.currentNodeId(); + String destinationNode = nodeIdsWithoutShardCopy.get(0); + relocateShard(0, sourceNode, destinationNode); + RemoteStoreStats[] allShardsStats = client().admin().cluster().prepareRemoteStoreStats(INDEX_NAME, "0").get().getRemoteStoreStats(); + RemoteStoreStats replicaShardStat = Arrays.stream(allShardsStats) + .filter(eachStat -> !eachStat.getShardRouting().primary()) + .collect(Collectors.toList()) + .get(0); + + // Assert that remote store stats reflect the new shard state + assertEquals(ShardRoutingState.STARTED, replicaShardStat.getShardRouting().state()); + assertEquals(destinationNode, replicaShardStat.getShardRouting().currentNodeId()); + } + + public void testStatsOnShardUnassigned() throws IOException { + // Scenario: + // - Create index with single primary and two replica shard + // - Index documents + // - Stop one data node + // - Assert: + // a. Total shard Count in the response object is equal to the previous node count + // b. Successful shard count in the response object is equal to the new node count + createIndex(INDEX_NAME, remoteStoreIndexSettings(2, 1)); + ensureGreen(INDEX_NAME); + indexDocs(); + ClusterHealthResponse clusterHealthResponse = client().admin().cluster().prepareHealth().get(); + int dataNodeCountBeforeStop = clusterHealthResponse.getNumberOfDataNodes(); + int nodeCount = clusterHealthResponse.getNumberOfNodes(); + String nodeToBeStopped = randomBoolean() ? primaryNodeName(INDEX_NAME) : replicaNodeName(INDEX_NAME); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodeToBeStopped)); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureStableCluster(nodeCount - 1); + RemoteStoreStatsResponse response = client().admin().cluster().prepareRemoteStoreStats(INDEX_NAME, "0").get(); + int dataNodeCountAfterStop = client().admin().cluster().prepareHealth().get().getNumberOfDataNodes(); + assertEquals(dataNodeCountBeforeStop, response.getTotalShards()); + assertEquals(dataNodeCountAfterStop, response.getSuccessfulShards()); + // Indexing docs to ensure that the primary has started + indexSingleDoc(INDEX_NAME); + } + + public void testStatsOnRemoteStoreRestore() throws IOException { + // Creating an index with primary shard count == total nodes in cluster and 0 replicas + int dataNodeCount = client().admin().cluster().prepareHealth().get().getNumberOfDataNodes(); + createIndex(INDEX_NAME, remoteStoreIndexSettings(0, dataNodeCount)); + ensureGreen(INDEX_NAME); + + // Index some docs to ensure segments being uploaded to remote store + indexDocs(); + refresh(INDEX_NAME); + + // Stop one data node to force the index into a red state + internalCluster().stopRandomDataNode(); + ensureRed(INDEX_NAME); + + // Start another data node to fulfil the previously launched capacity + internalCluster().startDataOnlyNode(); + + // Restore index from remote store + assertAcked(client().admin().indices().prepareClose(INDEX_NAME)); + client().admin() + .cluster() + .restoreRemoteStore(new RestoreRemoteStoreRequest().indices(INDEX_NAME).restoreAllShards(true), PlainActionFuture.newFuture()); + + // Ensure that the index is green + ensureGreen(INDEX_NAME); + + // Index some more docs to force segment uploads to remote store + indexDocs(); + + RemoteStoreStatsResponse remoteStoreStatsResponse = client().admin().cluster().prepareRemoteStoreStats(INDEX_NAME, "0").get(); + Arrays.stream(remoteStoreStatsResponse.getRemoteStoreStats()).forEach(statObject -> { + RemoteSegmentTransferTracker.Stats segmentStats = statObject.getSegmentStats(); + // Assert that we have both upload and download stats for the index + assertTrue( + segmentStats.totalUploadsStarted > 0 && segmentStats.totalUploadsSucceeded > 0 && segmentStats.totalUploadsFailed == 0 + ); + assertTrue( + segmentStats.directoryFileTransferTrackerStats.transferredBytesStarted > 0 + && segmentStats.directoryFileTransferTrackerStats.transferredBytesSucceeded > 0 + ); + + RemoteTranslogTransferTracker.Stats translogStats = statObject.getTranslogStats(); + assertNonZeroTranslogUploadStatsNoFailures(translogStats); + assertNonZeroTranslogDownloadStats(translogStats); + }); + } + + public void testNonZeroPrimaryStatsOnNewlyCreatedIndexWithZeroDocs() throws Exception { + // Create an index with one primary and one replica shard + createIndex(INDEX_NAME, remoteStoreIndexSettings(1, 1)); + ensureGreen(INDEX_NAME); + refresh(INDEX_NAME); + + // Ensure that the index has 0 documents in it + assertEquals(0, client().admin().indices().prepareStats(INDEX_NAME).get().getTotal().docs.getCount()); + + // Assert that within 5 seconds the download and upload stats moves to a non-zero value + assertBusy(() -> { + RemoteStoreStats[] remoteStoreStats = client().admin() + .cluster() + .prepareRemoteStoreStats(INDEX_NAME, "0") + .get() + .getRemoteStoreStats(); + Arrays.stream(remoteStoreStats).forEach(statObject -> { + RemoteSegmentTransferTracker.Stats segmentStats = statObject.getSegmentStats(); + if (statObject.getShardRouting().primary()) { + assertTrue( + segmentStats.totalUploadsSucceeded == 1 + && segmentStats.totalUploadsStarted == segmentStats.totalUploadsSucceeded + && segmentStats.totalUploadsFailed == 0 + ); + } else { + assertTrue( + segmentStats.directoryFileTransferTrackerStats.transferredBytesStarted == 0 + && segmentStats.directoryFileTransferTrackerStats.transferredBytesSucceeded == 0 + ); + } + + RemoteTranslogTransferTracker.Stats translogStats = statObject.getTranslogStats(); + assertZeroTranslogUploadStats(translogStats); + assertZeroTranslogDownloadStats(translogStats); + }); + }, 5, TimeUnit.SECONDS); + } + + private void indexDocs() { + for (int i = 0; i < randomIntBetween(5, 10); i++) { + if (randomBoolean()) { + flush(INDEX_NAME); + } else { + refresh(INDEX_NAME); + } + int numberOfOperations = randomIntBetween(10, 30); + indexBulk(INDEX_NAME, numberOfOperations); + } + } + + private void changeReplicaCountAndEnsureGreen(int replicaCount) { + assertAcked( + client().admin() + .indices() + .prepareUpdateSettings(INDEX_NAME) + .setSettings(Settings.builder().put(SETTING_NUMBER_OF_REPLICAS, replicaCount)) + ); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + } + + private void relocateShard(int shardId, String sourceNode, String destNode) { + assertAcked(client().admin().cluster().prepareReroute().add(new MoveAllocationCommand(INDEX_NAME, shardId, sourceNode, destNode))); + ensureGreen(INDEX_NAME); + } + + private void validateSegmentUploadStats(RemoteSegmentTransferTracker.Stats stats) { + assertEquals(0, stats.refreshTimeLagMs); + assertEquals(stats.localRefreshNumber, stats.remoteRefreshNumber); + assertTrue(stats.uploadBytesStarted > 0); + assertEquals(0, stats.uploadBytesFailed); + assertTrue(stats.uploadBytesSucceeded > 0); + assertTrue(stats.totalUploadsStarted > 0); + assertEquals(0, stats.totalUploadsFailed); + assertTrue(stats.totalUploadsSucceeded > 0); + assertEquals(0, stats.rejectionCount); + assertEquals(0, stats.consecutiveFailuresCount); + assertEquals(0, stats.bytesLag); + assertTrue(stats.uploadBytesMovingAverage > 0); + assertTrue(stats.uploadBytesPerSecMovingAverage > 0); + assertTrue(stats.uploadTimeMovingAverage > 0); + } + + private void validateSegmentDownloadStats(RemoteSegmentTransferTracker.Stats stats) { + assertTrue(stats.directoryFileTransferTrackerStats.lastTransferTimestampMs > 0); + assertTrue(stats.directoryFileTransferTrackerStats.transferredBytesStarted > 0); + assertTrue(stats.directoryFileTransferTrackerStats.transferredBytesSucceeded > 0); + assertEquals(stats.directoryFileTransferTrackerStats.transferredBytesFailed, 0); + assertTrue(stats.directoryFileTransferTrackerStats.lastSuccessfulTransferInBytes > 0); + assertTrue(stats.directoryFileTransferTrackerStats.transferredBytesMovingAverage > 0); + assertTrue(stats.directoryFileTransferTrackerStats.transferredBytesPerSecMovingAverage > 0); + } + + private void assertNonZeroTranslogUploadStatsNoFailures(RemoteTranslogTransferTracker.Stats stats) { + assertTrue(stats.uploadBytesStarted > 0); + assertTrue(stats.totalUploadsStarted > 0); + assertEquals(0, stats.uploadBytesFailed); + assertEquals(0, stats.totalUploadsFailed); + assertTrue(stats.uploadBytesSucceeded > 0); + assertTrue(stats.totalUploadsSucceeded > 0); + assertTrue(stats.totalUploadTimeInMillis > 0); + assertTrue(stats.lastSuccessfulUploadTimestamp > 0); + } + + private void assertZeroTranslogUploadStats(RemoteTranslogTransferTracker.Stats stats) { + assertEquals(0, stats.uploadBytesStarted); + assertEquals(0, stats.totalUploadsStarted); + assertEquals(0, stats.uploadBytesFailed); + assertEquals(0, stats.totalUploadsFailed); + assertEquals(0, stats.uploadBytesSucceeded); + assertEquals(0, stats.totalUploadsSucceeded); + assertEquals(0, stats.totalUploadTimeInMillis); + assertEquals(0, stats.lastSuccessfulUploadTimestamp); + } + + private void assertNonZeroTranslogDownloadStats(RemoteTranslogTransferTracker.Stats stats) { + assertTrue(stats.downloadBytesSucceeded > 0); + assertTrue(stats.totalDownloadsSucceeded > 0); + // TODO: Need to simulate a delay for this assertion to avoid flakiness + // assertTrue(stats.totalDownloadTimeInMillis > 0); + assertTrue(stats.lastSuccessfulDownloadTimestamp > 0); + } + + private void assertZeroTranslogDownloadStats(RemoteTranslogTransferTracker.Stats stats) { + assertEquals(0, stats.downloadBytesSucceeded); + assertEquals(0, stats.totalDownloadsSucceeded); + assertEquals(0, stats.totalDownloadTimeInMillis); + assertEquals(0, stats.lastSuccessfulDownloadTimestamp); + } + + // Validate if the shardRouting obtained from cluster state contains the exact same routing object + // parameters as obtained from the remote store stats API + private void validateShardRouting(ShardRouting routing) { + Stream currentRoutingTable = getClusterState().routingTable() + .getIndicesRouting() + .get(INDEX_NAME) + .shard(routing.id()) + .assignedShards() + .stream(); + assertTrue( + currentRoutingTable.anyMatch( + r -> (r.currentNodeId().equals(routing.currentNodeId()) + && r.state().equals(routing.state()) + && r.primary() == routing.primary()) + ) + ); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/ReplicaToPrimaryPromotionIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/ReplicaToPrimaryPromotionIT.java new file mode 100644 index 0000000000000..4e3f01b8f257f --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/ReplicaToPrimaryPromotionIT.java @@ -0,0 +1,184 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import com.carrotsearch.randomizedtesting.RandomizedTest; + +import org.opensearch.action.admin.indices.close.CloseIndexResponse; +import org.opensearch.action.index.IndexResponse; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.IndexShardRoutingTable; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.test.BackgroundIndexer; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.Before; + +import java.util.Locale; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class ReplicaToPrimaryPromotionIT extends RemoteStoreBaseIntegTestCase { + private int shard_count = 5; + + @Before + public void setup() { + internalCluster().startClusterManagerOnlyNode(); + } + + @Override + public Settings indexSettings() { + return Settings.builder().put(super.indexSettings()).put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, shard_count).build(); + } + + public void testPromoteReplicaToPrimary() throws Exception { + internalCluster().startNode(); + internalCluster().startNode(); + final String indexName = randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + shard_count = scaledRandomIntBetween(1, 5); + createIndex(indexName); + int numOfDocs = 0; + int numIter = scaledRandomIntBetween(0, 10); + for (int i = 0; i < numIter; i++) { + final int numOfDoc = scaledRandomIntBetween(0, 200); + logger.info("num of docs in iter {} {}", numOfDoc, i); + if (numOfDoc > 0) { + try ( + BackgroundIndexer indexer = new BackgroundIndexer( + indexName, + "_doc", + client(), + numOfDoc, + RandomizedTest.scaledRandomIntBetween(2, 5), + false, + null + ) + ) { + indexer.setUseAutoGeneratedIDs(true); + indexer.start(numOfDoc); + waitForIndexed(numOfDoc, indexer); + numOfDocs += numOfDoc; + indexer.stopAndAwaitStopped(); + if (random().nextBoolean()) { + // 90% refresh + 10% flush + if (random().nextInt(10) != 0) { + refresh(indexName); + } else { + flush(indexName); + } + } + } + } + } + + ensureGreen(indexName); + + // sometimes test with a closed index + final IndexMetadata.State indexState = randomFrom(IndexMetadata.State.OPEN, IndexMetadata.State.CLOSE); + if (indexState == IndexMetadata.State.CLOSE) { + CloseIndexResponse closeIndexResponse = client().admin().indices().prepareClose(indexName).get(); + assertThat("close index not acked - " + closeIndexResponse, closeIndexResponse.isAcknowledged(), equalTo(true)); + ensureGreen(indexName); + } + + // pick up a data node that contains a random primary shard + ClusterState state = client(internalCluster().getClusterManagerName()).admin().cluster().prepareState().get().getState(); + final int numShards = state.metadata().index(indexName).getNumberOfShards(); + final ShardRouting primaryShard = state.routingTable().index(indexName).shard(randomIntBetween(0, numShards - 1)).primaryShard(); + final DiscoveryNode randomNode = state.nodes().resolveNode(primaryShard.currentNodeId()); + + // stop the random data node, all remaining shards are promoted to primaries + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(randomNode.getName())); + ensureYellowAndNoInitializingShards(indexName); + + state = client(internalCluster().getClusterManagerName()).admin().cluster().prepareState().get().getState(); + for (IndexShardRoutingTable shardRoutingTable : state.routingTable().index(indexName)) { + for (ShardRouting shardRouting : shardRoutingTable.activeShards()) { + assertThat(shardRouting + " should be promoted as a primary", shardRouting.primary(), is(true)); + } + } + + if (indexState == IndexMetadata.State.CLOSE) { + assertAcked(client().admin().indices().prepareOpen(indexName)); + ensureYellowAndNoInitializingShards(indexName); + } + refresh(indexName); + assertHitCount(client().prepareSearch(indexName).setSize(0).get(), numOfDocs); + } + + public void testFailoverWhileIndexing() throws Exception { + internalCluster().startNode(); + internalCluster().startNode(); + final String indexName = randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + shard_count = scaledRandomIntBetween(1, 5); + createIndex(indexName); + ensureGreen(indexName); + int docCount = scaledRandomIntBetween(20, 50); + final int indexDocAfterFailover = scaledRandomIntBetween(20, 50); + AtomicInteger numAutoGenDocs = new AtomicInteger(); + CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean finished = new AtomicBoolean(false); + Thread indexingThread = new Thread(() -> { + int docsAfterFailover = 0; + while (finished.get() == false && numAutoGenDocs.get() < docCount) { + IndexResponse indexResponse = internalCluster().clusterManagerClient() + .prepareIndex(indexName) + .setSource("field", numAutoGenDocs.get()) + .get(); + + if (indexResponse.status() == RestStatus.CREATED || indexResponse.status() == RestStatus.OK) { + numAutoGenDocs.incrementAndGet(); + if (numAutoGenDocs.get() == docCount / 2) { + if (random().nextInt(3) == 0) { + refresh(indexName); + } else if (random().nextInt(2) == 0) { + flush(indexName); + } + // Node is killed on this + latch.countDown(); + } else if (numAutoGenDocs.get() > docCount / 2) { + docsAfterFailover++; + if (docsAfterFailover == indexDocAfterFailover) { + finished.set(true); + } + } + } + } + logger.debug("Done indexing"); + }); + indexingThread.start(); + latch.await(); + + ClusterState state = client(internalCluster().getClusterManagerName()).admin().cluster().prepareState().get().getState(); + final int numShards = state.metadata().index(indexName).getNumberOfShards(); + final ShardRouting primaryShard = state.routingTable().index(indexName).shard(randomIntBetween(0, numShards - 1)).primaryShard(); + final DiscoveryNode randomNode = state.nodes().resolveNode(primaryShard.currentNodeId()); + + // stop the random data node, all remaining shards are promoted to primaries + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(randomNode.getName())); + ensureYellowAndNoInitializingShards(indexName); + indexingThread.join(); + refresh(indexName); + assertHitCount( + client(internalCluster().getClusterManagerName()).prepareSearch(indexName).setSize(0).setTrackTotalHits(true).get(), + numAutoGenDocs.get() + ); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/SegmentReplicationUsingRemoteStoreIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/SegmentReplicationUsingRemoteStoreIT.java new file mode 100644 index 0000000000000..23864c35ad154 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/SegmentReplicationUsingRemoteStoreIT.java @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.common.settings.Settings; +import org.opensearch.indices.replication.SegmentReplicationIT; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.After; +import org.junit.Before; + +import java.nio.file.Path; + +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; + +/** + * This class runs Segment Replication Integ test suite with remote store enabled. + */ +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationUsingRemoteStoreIT extends SegmentReplicationIT { + + private static final String REPOSITORY_NAME = "test-remote-store-repo"; + protected Path absolutePath; + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + if (absolutePath == null) { + absolutePath = randomRepoPath().toAbsolutePath(); + } + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(remoteStoreClusterSettings(REPOSITORY_NAME, absolutePath)) + .build(); + } + + protected boolean segmentReplicationWithRemoteEnabled() { + return true; + } + + @Before + public void setup() { + internalCluster().startClusterManagerOnlyNode(); + } + + @After + public void teardown() { + clusterAdmin().prepareCleanupRepository(REPOSITORY_NAME).get(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/SegmentReplicationWithRemoteStorePressureIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/SegmentReplicationWithRemoteStorePressureIT.java new file mode 100644 index 0000000000000..6cfc76b7e3223 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/SegmentReplicationWithRemoteStorePressureIT.java @@ -0,0 +1,53 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.common.settings.Settings; +import org.opensearch.index.SegmentReplicationPressureIT; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.After; +import org.junit.Before; + +import java.nio.file.Path; + +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; + +/** + * This class executes the SegmentReplicationPressureIT suite with remote store integration enabled. + */ +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationWithRemoteStorePressureIT extends SegmentReplicationPressureIT { + + private static final String REPOSITORY_NAME = "test-remote-store-repo"; + protected Path absolutePath; + + @Override + protected boolean segmentReplicationWithRemoteEnabled() { + return true; + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(remoteStoreClusterSettings(REPOSITORY_NAME, absolutePath)) + .build(); + } + + @Before + public void setup() { + absolutePath = randomRepoPath().toAbsolutePath(); + internalCluster().startClusterManagerOnlyNode(); + } + + @After + public void teardown() { + clusterAdmin().prepareCleanupRepository(REPOSITORY_NAME).get(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/RemoteStoreMultipartFileCorruptionIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/RemoteStoreMultipartFileCorruptionIT.java new file mode 100644 index 0000000000000..7112b266840ac --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/RemoteStoreMultipartFileCorruptionIT.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore.multipart; + +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.index.IndexModule; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.plugins.Plugin; +import org.opensearch.remotestore.RemoteStoreBaseIntegTestCase; +import org.opensearch.remotestore.multipart.mocks.MockFsRepositoryPlugin; + +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class RemoteStoreMultipartFileCorruptionIT extends RemoteStoreBaseIntegTestCase { + + private static final String INDEX_NAME = "remote-store-test-idx-1"; + + @Override + protected Collection> nodePlugins() { + return Stream.concat(super.nodePlugins().stream(), Stream.of(MockFsRepositoryPlugin.class)).collect(Collectors.toList()); + } + + protected Settings remoteStoreIndexSettings() { + return Settings.builder() + .put(super.indexSettings()) + .put("index.refresh_interval", "300s") + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + } + + public void testLocalFileCorruptionDuringUpload() { + internalCluster().startDataOnlyNodes(1); + createIndex(INDEX_NAME, remoteStoreIndexSettings()); + ensureYellowAndNoInitializingShards(INDEX_NAME); + ensureGreen(INDEX_NAME); + + indexSingleDoc(INDEX_NAME); + + client().admin() + .indices() + .prepareRefresh(INDEX_NAME) + .setIndicesOptions(IndicesOptions.STRICT_EXPAND_OPEN_HIDDEN_FORBID_CLOSED) + .execute() + .actionGet(); + + // ensuring red cluster meaning shard has failed and is unassigned + ensureRed(INDEX_NAME); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/RemoteStoreMultipartIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/RemoteStoreMultipartIT.java new file mode 100644 index 0000000000000..3dfde6f472525 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/RemoteStoreMultipartIT.java @@ -0,0 +1,155 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore.multipart; + +import org.opensearch.client.Client; +import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.plugins.Plugin; +import org.opensearch.remotestore.RemoteStoreIT; +import org.opensearch.remotestore.multipart.mocks.MockFsRepositoryPlugin; +import org.opensearch.repositories.RepositoriesService; +import org.opensearch.repositories.blobstore.BlobStoreRepository; +import org.junit.Before; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; + +public class RemoteStoreMultipartIT extends RemoteStoreIT { + + Path repositoryLocation; + boolean compress; + boolean overrideBuildRepositoryMetadata; + + @Override + protected Collection> nodePlugins() { + return Stream.concat(super.nodePlugins().stream(), Stream.of(MockFsRepositoryPlugin.class)).collect(Collectors.toList()); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put( + remoteStoreClusterSettings( + REPOSITORY_NAME, + segmentRepoPath, + MockFsRepositoryPlugin.TYPE, + REPOSITORY_2_NAME, + translogRepoPath, + MockFsRepositoryPlugin.TYPE + ) + ) + .build(); + } + + @Before + public void setup() { + clusterSettingsSuppliedByTest = true; + overrideBuildRepositoryMetadata = false; + repositoryLocation = randomRepoPath(); + compress = randomBoolean(); + } + + @Override + public RepositoryMetadata buildRepositoryMetadata(DiscoveryNode node, String name) { + if (overrideBuildRepositoryMetadata) { + Map nodeAttributes = node.getAttributes(); + String type = nodeAttributes.get(String.format(Locale.getDefault(), REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, name)); + + String settingsAttributeKeyPrefix = String.format( + Locale.getDefault(), + REMOTE_STORE_REPOSITORY_SETTINGS_ATTRIBUTE_KEY_PREFIX, + name + ); + Map settingsMap = node.getAttributes() + .keySet() + .stream() + .filter(key -> key.startsWith(settingsAttributeKeyPrefix)) + .collect(Collectors.toMap(key -> key.replace(settingsAttributeKeyPrefix, ""), key -> node.getAttributes().get(key))); + + Settings.Builder settings = Settings.builder(); + settingsMap.entrySet().forEach(entry -> settings.put(entry.getKey(), entry.getValue())); + settings.put(BlobStoreRepository.SYSTEM_REPOSITORY_SETTING.getKey(), true); + + if (name.equals(REPOSITORY_NAME)) { + settings.put("location", repositoryLocation) + .put("compress", compress) + .put("max_remote_upload_bytes_per_sec", "1kb") + .put("chunk_size", 100, ByteSizeUnit.BYTES); + return new RepositoryMetadata(name, MockFsRepositoryPlugin.TYPE, settings.build()); + } + + return new RepositoryMetadata(name, type, settings.build()); + } else { + return super.buildRepositoryMetadata(node, name); + } + + } + + public void testRateLimitedRemoteUploads() throws Exception { + clusterSettingsSuppliedByTest = true; + overrideBuildRepositoryMetadata = true; + Settings.Builder clusterSettings = Settings.builder() + .put(remoteStoreClusterSettings(REPOSITORY_NAME, repositoryLocation, REPOSITORY_2_NAME, repositoryLocation)); + clusterSettings.put( + String.format(Locale.getDefault(), "node.attr." + REMOTE_STORE_REPOSITORY_TYPE_ATTRIBUTE_KEY_FORMAT, REPOSITORY_NAME), + MockFsRepositoryPlugin.TYPE + ); + internalCluster().startNode(clusterSettings.build()); + Client client = client(); + logger.info("--> updating repository"); + assertAcked( + client.admin() + .cluster() + .preparePutRepository(REPOSITORY_NAME) + .setType(MockFsRepositoryPlugin.TYPE) + .setSettings( + Settings.builder() + .put("location", repositoryLocation) + .put("compress", compress) + .put("max_remote_upload_bytes_per_sec", "1kb") + .put("chunk_size", 100, ByteSizeUnit.BYTES) + ) + ); + + createIndex(INDEX_NAME, remoteStoreIndexSettings(0)); + ensureGreen(); + + logger.info("--> indexing some data"); + for (int i = 0; i < 10; i++) { + index(INDEX_NAME, "_doc", Integer.toString(i), "foo", "bar" + i); + } + refresh(); + // check if throttling is active + assertBusy(() -> { + long uploadPauseTime = 0L; + for (RepositoriesService repositoriesService : internalCluster().getDataNodeInstances(RepositoriesService.class)) { + uploadPauseTime += repositoriesService.repository(REPOSITORY_NAME).getRemoteUploadThrottleTimeInNanos(); + } + assertThat(uploadPauseTime, greaterThan(TimeValue.timeValueSeconds(randomIntBetween(5, 10)).nanos())); + }, 30, TimeUnit.SECONDS); + + assertThat(client.prepareSearch(INDEX_NAME).setSize(0).get().getHits().getTotalHits().value, equalTo(10L)); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsAsyncBlobContainer.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsAsyncBlobContainer.java new file mode 100644 index 0000000000000..36987ac2d4991 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsAsyncBlobContainer.java @@ -0,0 +1,149 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore.multipart.mocks; + +import org.apache.lucene.index.CorruptIndexException; +import org.opensearch.common.StreamContext; +import org.opensearch.common.blobstore.AsyncMultiStreamBlobContainer; +import org.opensearch.common.blobstore.BlobPath; +import org.opensearch.common.blobstore.fs.FsBlobContainer; +import org.opensearch.common.blobstore.fs.FsBlobStore; +import org.opensearch.common.blobstore.stream.read.ReadContext; +import org.opensearch.common.blobstore.stream.write.WriteContext; +import org.opensearch.common.io.InputStreamContainer; +import org.opensearch.core.action.ActionListener; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +public class MockFsAsyncBlobContainer extends FsBlobContainer implements AsyncMultiStreamBlobContainer { + + private static final int TRANSFER_TIMEOUT_MILLIS = 30000; + + private final boolean triggerDataIntegrityFailure; + + public MockFsAsyncBlobContainer(FsBlobStore blobStore, BlobPath blobPath, Path path, boolean triggerDataIntegrityFailure) { + super(blobStore, blobPath, path); + this.triggerDataIntegrityFailure = triggerDataIntegrityFailure; + } + + @Override + public void asyncBlobUpload(WriteContext writeContext, ActionListener completionListener) throws IOException { + + int nParts = 10; + long partSize = writeContext.getFileSize() / nParts; + StreamContext streamContext = writeContext.getStreamProvider(partSize); + final Path file = path.resolve(writeContext.getFileName()); + byte[] buffer = new byte[(int) writeContext.getFileSize()]; + AtomicLong totalContentRead = new AtomicLong(); + CountDownLatch latch = new CountDownLatch(streamContext.getNumberOfParts()); + for (int partIdx = 0; partIdx < streamContext.getNumberOfParts(); partIdx++) { + int finalPartIdx = partIdx; + Thread thread = new Thread(() -> { + try { + InputStreamContainer inputStreamContainer = streamContext.provideStream(finalPartIdx); + InputStream inputStream = inputStreamContainer.getInputStream(); + long remainingContentLength = inputStreamContainer.getContentLength(); + long offset = partSize * finalPartIdx; + while (remainingContentLength > 0) { + int readContentLength = inputStream.read(buffer, (int) offset, (int) remainingContentLength); + totalContentRead.addAndGet(readContentLength); + remainingContentLength -= readContentLength; + offset += readContentLength; + } + inputStream.close(); + } catch (IOException e) { + completionListener.onFailure(e); + } finally { + latch.countDown(); + } + }); + thread.start(); + } + try { + if (!latch.await(TRANSFER_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)) { + throw new IOException("Timed out waiting for file transfer to complete for " + writeContext.getFileName()); + } + } catch (InterruptedException e) { + throw new IOException("Await interrupted on CountDownLatch, transfer failed for " + writeContext.getFileName()); + } + try (OutputStream outputStream = Files.newOutputStream(file, StandardOpenOption.CREATE_NEW)) { + outputStream.write(buffer); + } + if (writeContext.getFileSize() != totalContentRead.get()) { + throw new IOException( + "Incorrect content length read for file " + + writeContext.getFileName() + + ", actual file size: " + + writeContext.getFileSize() + + ", bytes read: " + + totalContentRead.get() + ); + } + + try { + // bulks need to succeed for segment files to be generated + if (isSegmentFile(writeContext.getFileName()) && triggerDataIntegrityFailure) { + completionListener.onFailure( + new RuntimeException( + new CorruptIndexException( + "Data integrity check failure for file: " + writeContext.getFileName(), + writeContext.getFileName() + ) + ) + ); + } else { + writeContext.getUploadFinalizer().accept(true); + completionListener.onResponse(null); + } + } catch (Exception e) { + completionListener.onFailure(e); + } + + } + + @Override + public void readBlobAsync(String blobName, ActionListener listener) { + new Thread(() -> { + try { + long contentLength = listBlobs().get(blobName).length(); + long partSize = contentLength / 10; + int numberOfParts = (int) ((contentLength % partSize) == 0 ? contentLength / partSize : (contentLength / partSize) + 1); + List blobPartStreams = new ArrayList<>(); + for (int partNumber = 0; partNumber < numberOfParts; partNumber++) { + long offset = partNumber * partSize; + InputStreamContainer blobPartStream = new InputStreamContainer(readBlob(blobName, offset, partSize), partSize, offset); + blobPartStreams.add(() -> CompletableFuture.completedFuture(blobPartStream)); + } + ReadContext blobReadContext = new ReadContext(contentLength, blobPartStreams, null); + listener.onResponse(blobReadContext); + } catch (Exception e) { + listener.onFailure(e); + } + }).start(); + } + + public boolean remoteIntegrityCheckSupported() { + return true; + } + + private boolean isSegmentFile(String filename) { + return !filename.endsWith(".tlog") && !filename.endsWith(".ckp"); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsBlobStore.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsBlobStore.java new file mode 100644 index 0000000000000..77b0cac922014 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsBlobStore.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore.multipart.mocks; + +import org.opensearch.OpenSearchException; +import org.opensearch.common.blobstore.BlobContainer; +import org.opensearch.common.blobstore.BlobPath; +import org.opensearch.common.blobstore.fs.FsBlobStore; + +import java.io.IOException; +import java.nio.file.Path; + +public class MockFsBlobStore extends FsBlobStore { + + private final boolean triggerDataIntegrityFailure; + + public MockFsBlobStore(int bufferSizeInBytes, Path path, boolean readonly, boolean triggerDataIntegrityFailure) throws IOException { + super(bufferSizeInBytes, path, readonly); + this.triggerDataIntegrityFailure = triggerDataIntegrityFailure; + } + + @Override + public BlobContainer blobContainer(BlobPath path) { + try { + return new MockFsAsyncBlobContainer(this, path, buildAndCreate(path), triggerDataIntegrityFailure); + } catch (IOException ex) { + throw new OpenSearchException("failed to create blob container", ex); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsRepository.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsRepository.java new file mode 100644 index 0000000000000..15a9853477081 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsRepository.java @@ -0,0 +1,46 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore.multipart.mocks; + +import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.blobstore.BlobStore; +import org.opensearch.common.blobstore.fs.FsBlobStore; +import org.opensearch.common.settings.Setting; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.env.Environment; +import org.opensearch.indices.recovery.RecoverySettings; +import org.opensearch.repositories.fs.FsRepository; + +public class MockFsRepository extends FsRepository { + + public static Setting TRIGGER_DATA_INTEGRITY_FAILURE = Setting.boolSetting( + "mock_fs_repository.trigger_data_integrity_failure", + false + ); + + private final boolean triggerDataIntegrityFailure; + + public MockFsRepository( + RepositoryMetadata metadata, + Environment environment, + NamedXContentRegistry namedXContentRegistry, + ClusterService clusterService, + RecoverySettings recoverySettings + ) { + super(metadata, environment, namedXContentRegistry, clusterService, recoverySettings); + triggerDataIntegrityFailure = TRIGGER_DATA_INTEGRITY_FAILURE.get(metadata.settings()); + } + + @Override + protected BlobStore createBlobStore() throws Exception { + FsBlobStore fsBlobStore = (FsBlobStore) super.createBlobStore(); + return new MockFsBlobStore(fsBlobStore.bufferSizeInBytes(), fsBlobStore.path(), isReadOnly(), triggerDataIntegrityFailure); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsRepositoryPlugin.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsRepositoryPlugin.java new file mode 100644 index 0000000000000..ffd53adf4e29e --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/multipart/mocks/MockFsRepositoryPlugin.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore.multipart.mocks; + +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.env.Environment; +import org.opensearch.indices.recovery.RecoverySettings; +import org.opensearch.plugins.Plugin; +import org.opensearch.plugins.RepositoryPlugin; +import org.opensearch.repositories.Repository; + +import java.util.Collections; +import java.util.Map; + +public class MockFsRepositoryPlugin extends Plugin implements RepositoryPlugin { + + public static final String TYPE = "fs_multipart_repository"; + + @Override + public Map getRepositories( + Environment env, + NamedXContentRegistry namedXContentRegistry, + ClusterService clusterService, + RecoverySettings recoverySettings + ) { + return Collections.singletonMap( + "fs_multipart_repository", + metadata -> new MockFsRepository(metadata, env, namedXContentRegistry, clusterService, recoverySettings) + ); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/repositories/RepositoriesServiceIT.java b/server/src/internalClusterTest/java/org/opensearch/repositories/RepositoriesServiceIT.java index 78cf55e2f223c..b8415f4b41815 100644 --- a/server/src/internalClusterTest/java/org/opensearch/repositories/RepositoriesServiceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/repositories/RepositoriesServiceIT.java @@ -39,8 +39,8 @@ import org.opensearch.plugins.Plugin; import org.opensearch.repositories.fs.FsRepository; import org.opensearch.snapshots.mockstore.MockRepository; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Collection; import java.util.Collections; @@ -65,7 +65,9 @@ public void testUpdateRepository() { final String repositoryName = "test-repo"; final Client client = client(); - final RepositoriesService repositoriesService = cluster.getDataOrMasterNodeInstances(RepositoriesService.class).iterator().next(); + final RepositoriesService repositoriesService = cluster.getDataOrClusterManagerNodeInstances(RepositoriesService.class) + .iterator() + .next(); final Settings.Builder repoSettings = Settings.builder().put("location", randomRepoPath()); @@ -106,4 +108,16 @@ public void testUpdateRepository() { final Repository updatedRepository = repositoriesService.repository(repositoryName); assertThat(updatedRepository, updated ? not(sameInstance(originalRepository)) : sameInstance(originalRepository)); } + + public void testSystemRepositoryCantBeCreated() { + internalCluster(); + final String repositoryName = "test-repo"; + final Client client = client(); + final Settings.Builder repoSettings = Settings.builder().put("system_repository", true).put("location", randomRepoPath()); + + assertThrows( + RepositoryException.class, + () -> client.admin().cluster().preparePutRepository(repositoryName).setType(FsRepository.TYPE).setSettings(repoSettings).get() + ); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/repositories/blobstore/BlobStoreRepositoryCleanupIT.java b/server/src/internalClusterTest/java/org/opensearch/repositories/blobstore/BlobStoreRepositoryCleanupIT.java index 7208a608e1ea1..29aab140231c7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/repositories/blobstore/BlobStoreRepositoryCleanupIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/repositories/blobstore/BlobStoreRepositoryCleanupIT.java @@ -51,12 +51,12 @@ @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) public class BlobStoreRepositoryCleanupIT extends AbstractSnapshotIntegTestCase { - public void testMasterFailoverDuringCleanup() throws Exception { + public void testClusterManagerFailoverDuringCleanup() throws Exception { startBlockedCleanup("test-repo"); - final int nodeCount = internalCluster().numDataAndMasterNodes(); - logger.info("--> stopping master node"); - internalCluster().stopCurrentMasterNode(); + final int nodeCount = internalCluster().numDataAndClusterManagerNodes(); + logger.info("--> stopping cluster-manager node"); + internalCluster().stopCurrentClusterManagerNode(); ensureStableCluster(nodeCount - 1); @@ -67,7 +67,7 @@ public void testMasterFailoverDuringCleanup() throws Exception { } public void testRepeatCleanupsDontRemove() throws Exception { - final String masterNode = startBlockedCleanup("test-repo"); + final String clusterManagerNode = startBlockedCleanup("test-repo"); logger.info("--> sending another cleanup"); assertFutureThrows(client().admin().cluster().prepareCleanupRepository("test-repo").execute(), IllegalStateException.class); @@ -81,8 +81,8 @@ public void testRepeatCleanupsDontRemove() throws Exception { .custom(RepositoryCleanupInProgress.TYPE); assertTrue(cleanup.hasCleanupInProgress()); - logger.info("--> unblocking master node"); - unblockNode("test-repo", masterNode); + logger.info("--> unblocking cluster-manager node"); + unblockNode("test-repo", clusterManagerNode); logger.info("--> wait for cleanup to finish and disappear from cluster state"); awaitClusterState( @@ -91,8 +91,8 @@ public void testRepeatCleanupsDontRemove() throws Exception { } private String startBlockedCleanup(String repoName) throws Exception { - logger.info("--> starting two master nodes and one data node"); - internalCluster().startMasterOnlyNodes(2); + logger.info("--> starting two cluster-manager nodes and one data node"); + internalCluster().startClusterManagerOnlyNodes(2); internalCluster().startDataOnlyNodes(1); createRepository(repoName, "mock"); @@ -100,7 +100,10 @@ private String startBlockedCleanup(String repoName) throws Exception { logger.info("--> snapshot"); client().admin().cluster().prepareCreateSnapshot(repoName, "test-snap").setWaitForCompletion(true).get(); - final RepositoriesService service = internalCluster().getInstance(RepositoriesService.class, internalCluster().getMasterName()); + final RepositoriesService service = internalCluster().getInstance( + RepositoriesService.class, + internalCluster().getClusterManagerName() + ); final BlobStoreRepository repository = (BlobStoreRepository) service.repository(repoName); logger.info("--> creating a garbage data blob"); @@ -117,17 +120,17 @@ private String startBlockedCleanup(String repoName) throws Exception { ); garbageFuture.get(); - final String masterNode = blockMasterFromFinalizingSnapshotOnIndexFile(repoName); + final String clusterManagerNode = blockClusterManagerFromFinalizingSnapshotOnIndexFile(repoName); logger.info("--> starting repository cleanup"); client().admin().cluster().prepareCleanupRepository(repoName).execute(); - logger.info("--> waiting for block to kick in on " + masterNode); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(60)); + logger.info("--> waiting for block to kick in on " + clusterManagerNode); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(60)); awaitClusterState( state -> state.custom(RepositoryCleanupInProgress.TYPE, RepositoryCleanupInProgress.EMPTY).hasCleanupInProgress() ); - return masterNode; + return clusterManagerNode; } public void testCleanupOldIndexN() throws ExecutionException, InterruptedException { @@ -146,7 +149,10 @@ public void testCleanupOldIndexN() throws ExecutionException, InterruptedExcepti assertThat(createSnapshotResponse.getSnapshotInfo().state(), is(SnapshotState.SUCCESS)); } - final RepositoriesService service = internalCluster().getInstance(RepositoriesService.class, internalCluster().getMasterName()); + final RepositoriesService service = internalCluster().getInstance( + RepositoriesService.class, + internalCluster().getClusterManagerName() + ); final BlobStoreRepository repository = (BlobStoreRepository) service.repository(repoName); logger.info("--> write two outdated index-N blobs"); diff --git a/server/src/internalClusterTest/java/org/opensearch/repositories/fs/FsBlobStoreRepositoryIT.java b/server/src/internalClusterTest/java/org/opensearch/repositories/fs/FsBlobStoreRepositoryIT.java index 75deb731881ec..9057ef900efbd 100644 --- a/server/src/internalClusterTest/java/org/opensearch/repositories/fs/FsBlobStoreRepositoryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/repositories/fs/FsBlobStoreRepositoryIT.java @@ -35,11 +35,11 @@ import org.opensearch.common.blobstore.BlobContainer; import org.opensearch.common.blobstore.BlobPath; import org.opensearch.common.blobstore.fs.FsBlobStore; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.repositories.blobstore.OpenSearchBlobStoreRepositoryIntegTestCase; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/routing/AliasRoutingIT.java b/server/src/internalClusterTest/java/org/opensearch/routing/AliasRoutingIT.java index 274133c2c8239..299c2da21c222 100644 --- a/server/src/internalClusterTest/java/org/opensearch/routing/AliasRoutingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/routing/AliasRoutingIT.java @@ -44,7 +44,6 @@ import org.opensearch.test.OpenSearchIntegTestCase; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; - import static org.hamcrest.Matchers.equalTo; /** diff --git a/server/src/internalClusterTest/java/org/opensearch/routing/PartitionedRoutingIT.java b/server/src/internalClusterTest/java/org/opensearch/routing/PartitionedRoutingIT.java index a64e857f089f0..64df858a18c9d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/routing/PartitionedRoutingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/routing/PartitionedRoutingIT.java @@ -38,13 +38,14 @@ import org.opensearch.common.settings.Settings; import org.opensearch.index.query.QueryBuilders; import org.opensearch.test.OpenSearchIntegTestCase; -import org.mockito.internal.util.collections.Sets; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.mockito.internal.util.collections.Sets; + public class PartitionedRoutingIT extends OpenSearchIntegTestCase { public void testVariousPartitionSizes() throws Exception { @@ -133,7 +134,7 @@ public void testShrinking() throws Exception { .nodes() .getDataNodes() .values() - .toArray(DiscoveryNode.class)[0].getName() + .toArray(new DiscoveryNode[0])[0].getName() ) .put("index.blocks.write", true) ) diff --git a/server/src/internalClusterTest/java/org/opensearch/routing/SimpleRoutingIT.java b/server/src/internalClusterTest/java/org/opensearch/routing/SimpleRoutingIT.java index 8909b9deece9b..80e82fa387c96 100644 --- a/server/src/internalClusterTest/java/org/opensearch/routing/SimpleRoutingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/routing/SimpleRoutingIT.java @@ -54,9 +54,9 @@ import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchIntegTestCase; import static org.hamcrest.Matchers.containsString; diff --git a/server/src/internalClusterTest/java/org/opensearch/script/ScriptCacheIT.java b/server/src/internalClusterTest/java/org/opensearch/script/ScriptCacheIT.java index b4823bb482bfa..2fbaf4ea5a4d3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/script/ScriptCacheIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/script/ScriptCacheIT.java @@ -8,22 +8,26 @@ package org.opensearch.script; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.rest.RestStatus; import org.opensearch.index.MockEngineFactoryPlugin; import org.opensearch.index.mapper.MockFieldFilterPlugin; import org.opensearch.node.NodeMocksPlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.search.MockSearchService; import org.opensearch.test.MockHttpTransport; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.TestGeoShapeFieldMapperPlugin; import org.opensearch.test.store.MockFSIndexStore; import org.opensearch.test.transport.MockTransportService; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -31,9 +35,26 @@ import java.util.concurrent.ExecutionException; import java.util.function.Function; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.apache.logging.log4j.core.util.Throwables.getRootCause; -public class ScriptCacheIT extends OpenSearchIntegTestCase { +public class ScriptCacheIT extends ParameterizedOpenSearchIntegTestCase { + public ScriptCacheIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } protected Settings nodeSettings(int nodeOrdinal) { Settings.Builder builder = Settings.builder() diff --git a/server/src/internalClusterTest/java/org/opensearch/script/StoredScriptsIT.java b/server/src/internalClusterTest/java/org/opensearch/script/StoredScriptsIT.java index 0d3a7154559cb..71b48a8357e80 100644 --- a/server/src/internalClusterTest/java/org/opensearch/script/StoredScriptsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/script/StoredScriptsIT.java @@ -31,9 +31,9 @@ package org.opensearch.script; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.plugins.Plugin; import org.opensearch.test.OpenSearchIntegTestCase; @@ -69,7 +69,7 @@ public void testBasics() { .cluster() .preparePutStoredScript() .setId("foobar") - .setContent(new BytesArray("{\"script\": {\"lang\": \"" + LANG + "\", \"source\": \"1\"} }"), XContentType.JSON) + .setContent(new BytesArray("{\"script\": {\"lang\": \"" + LANG + "\", \"source\": \"1\"} }"), MediaTypeRegistry.JSON) ); String script = client().admin().cluster().prepareGetStoredScript("foobar").get().getSource().getSource(); assertNotNull(script); @@ -81,7 +81,12 @@ public void testBasics() { IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().admin().cluster().preparePutStoredScript().setId("id#").setContent(new BytesArray("{}"), XContentType.JSON).get() + () -> client().admin() + .cluster() + .preparePutStoredScript() + .setId("id#") + .setContent(new BytesArray("{}"), MediaTypeRegistry.JSON) + .get() ); assertEquals("Validation Failed: 1: id cannot contain '#' for stored script;", e.getMessage()); } @@ -95,7 +100,7 @@ public void testMaxScriptSize() { .setId("foobar") .setContent( new BytesArray("{\"script\": { \"lang\": \"" + LANG + "\"," + " \"source\":\"0123456789abcdef\"} }"), - XContentType.JSON + MediaTypeRegistry.JSON ) .get() ); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/SearchCancellationIT.java b/server/src/internalClusterTest/java/org/opensearch/search/SearchCancellationIT.java index da5698918cf99..18b4625761c51 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/SearchCancellationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/SearchCancellationIT.java @@ -32,11 +32,10 @@ package org.opensearch.search; -import org.apache.logging.log4j.LogManager; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.junit.After; +import org.apache.logging.log4j.LogManager; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.node.tasks.cancel.CancelTasksResponse; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.opensearch.action.bulk.BulkRequestBuilder; @@ -48,28 +47,33 @@ import org.opensearch.action.search.SearchScrollAction; import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.action.support.WriteRequest; -import org.opensearch.common.Strings; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.Strings; +import org.opensearch.core.tasks.TaskCancelledException; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.PluginsService; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.lookup.LeafFieldsLookup; -import org.opensearch.tasks.TaskCancelledException; import org.opensearch.tasks.TaskInfo; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.transport.TransportException; +import org.junit.After; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; @@ -77,6 +81,7 @@ import static org.opensearch.action.search.TransportSearchAction.SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING_KEY; import static org.opensearch.index.query.QueryBuilders.scriptQuery; import static org.opensearch.search.SearchCancellationIT.ScriptedBlockPlugin.SCRIPT_NAME; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.SearchService.NO_TIMEOUT; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFailures; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; @@ -86,7 +91,28 @@ import static org.hamcrest.Matchers.notNullValue; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) -public class SearchCancellationIT extends OpenSearchIntegTestCase { +public class SearchCancellationIT extends ParameterizedOpenSearchIntegTestCase { + + private TimeValue requestCancellationTimeout = TimeValue.timeValueSeconds(1); + private TimeValue clusterCancellationTimeout = TimeValue.timeValueMillis(1500); + private TimeValue keepAlive = TimeValue.timeValueSeconds(5); + + public SearchCancellationIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -225,7 +251,7 @@ public void testCancellationDuringQueryPhase() throws Exception { awaitForBlock(plugins); cancelSearch(SearchAction.NAME); disableBlocks(plugins); - logger.info("Segments {}", Strings.toString(client().admin().indices().prepareSegments("test").get())); + logger.info("Segments {}", Strings.toString(MediaTypeRegistry.JSON, client().admin().indices().prepareSegments("test").get())); ensureSearchWasCancelled(searchResponse); } @@ -233,15 +259,13 @@ public void testCancellationDuringQueryPhaseUsingRequestParameter() throws Excep List plugins = initBlockFactory(); indexTestData(); - TimeValue cancellationTimeout = new TimeValue(2, TimeUnit.SECONDS); ActionFuture searchResponse = client().prepareSearch("test") - .setCancelAfterTimeInterval(cancellationTimeout) + .setCancelAfterTimeInterval(requestCancellationTimeout) .setAllowPartialSearchResults(randomBoolean()) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap()))) .execute(); awaitForBlock(plugins); - // sleep for cancellation timeout to ensure scheduled cancellation task is actually executed - Thread.sleep(cancellationTimeout.getMillis()); + sleepForAtLeast(requestCancellationTimeout.getMillis()); // unblock the search thread disableBlocks(plugins); ensureSearchWasCancelled(searchResponse); @@ -251,19 +275,19 @@ public void testCancellationDuringQueryPhaseUsingClusterSetting() throws Excepti List plugins = initBlockFactory(); indexTestData(); - TimeValue cancellationTimeout = new TimeValue(2, TimeUnit.SECONDS); client().admin() .cluster() .prepareUpdateSettings() - .setPersistentSettings(Settings.builder().put(SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING_KEY, cancellationTimeout).build()) + .setPersistentSettings( + Settings.builder().put(SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING_KEY, clusterCancellationTimeout).build() + ) .get(); ActionFuture searchResponse = client().prepareSearch("test") .setAllowPartialSearchResults(randomBoolean()) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap()))) .execute(); awaitForBlock(plugins); - // sleep for cluster cancellation timeout to ensure scheduled cancellation task is actually executed - Thread.sleep(cancellationTimeout.getMillis()); + sleepForAtLeast(clusterCancellationTimeout.getMillis()); // unblock the search thread disableBlocks(plugins); ensureSearchWasCancelled(searchResponse); @@ -281,21 +305,19 @@ public void testCancellationDuringFetchPhase() throws Exception { awaitForBlock(plugins); cancelSearch(SearchAction.NAME); disableBlocks(plugins); - logger.info("Segments {}", Strings.toString(client().admin().indices().prepareSegments("test").get())); + logger.info("Segments {}", Strings.toString(MediaTypeRegistry.JSON, client().admin().indices().prepareSegments("test").get())); ensureSearchWasCancelled(searchResponse); } public void testCancellationDuringFetchPhaseUsingRequestParameter() throws Exception { List plugins = initBlockFactory(); indexTestData(); - TimeValue cancellationTimeout = new TimeValue(2, TimeUnit.SECONDS); ActionFuture searchResponse = client().prepareSearch("test") - .setCancelAfterTimeInterval(cancellationTimeout) + .setCancelAfterTimeInterval(requestCancellationTimeout) .addScriptField("test_field", new Script(ScriptType.INLINE, "mockscript", SCRIPT_NAME, Collections.emptyMap())) .execute(); awaitForBlock(plugins); - // sleep for request cancellation timeout to ensure scheduled cancellation task is actually executed - Thread.sleep(cancellationTimeout.getMillis()); + sleepForAtLeast(requestCancellationTimeout.getMillis()); // unblock the search thread disableBlocks(plugins); ensureSearchWasCancelled(searchResponse); @@ -307,7 +329,7 @@ public void testCancellationOfScrollSearches() throws Exception { logger.info("Executing search"); ActionFuture searchResponse = client().prepareSearch("test") - .setScroll(TimeValue.timeValueSeconds(10)) + .setScroll(keepAlive) .setSize(5) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SCRIPT_NAME, Collections.emptyMap()))) .execute(); @@ -326,16 +348,16 @@ public void testCancellationOfScrollSearches() throws Exception { public void testCancellationOfFirstScrollSearchRequestUsingRequestParameter() throws Exception { List plugins = initBlockFactory(); indexTestData(); - TimeValue cancellationTimeout = new TimeValue(2, TimeUnit.SECONDS); ActionFuture searchResponse = client().prepareSearch("test") - .setScroll(TimeValue.timeValueSeconds(10)) - .setCancelAfterTimeInterval(cancellationTimeout) + .setScroll(keepAlive) + .setCancelAfterTimeInterval(requestCancellationTimeout) .setSize(5) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SCRIPT_NAME, Collections.emptyMap()))) .execute(); awaitForBlock(plugins); - Thread.sleep(cancellationTimeout.getMillis()); + sleepForAtLeast(requestCancellationTimeout.getMillis()); + // unblock the search thread disableBlocks(plugins); SearchResponse response = ensureSearchWasCancelled(searchResponse); if (response != null) { @@ -354,7 +376,6 @@ public void testCancellationOfScrollSearchesOnFollowupRequests() throws Exceptio disableBlocks(plugins); logger.info("Executing search"); - TimeValue keepAlive = TimeValue.timeValueSeconds(5); SearchResponse searchResponse = client().prepareSearch("test") .setScroll(keepAlive) .setSize(2) @@ -394,11 +415,9 @@ public void testNoCancellationOfScrollSearchOnFollowUpRequest() throws Exception // Disable block so the first request would pass disableBlocks(plugins); - TimeValue keepAlive = TimeValue.timeValueSeconds(5); - TimeValue cancellationTimeout = TimeValue.timeValueSeconds(2); SearchResponse searchResponse = client().prepareSearch("test") .setScroll(keepAlive) - .setCancelAfterTimeInterval(cancellationTimeout) + .setCancelAfterTimeInterval(requestCancellationTimeout) .setSize(2) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap()))) .get(); @@ -418,8 +437,8 @@ public void testNoCancellationOfScrollSearchOnFollowUpRequest() throws Exception .execute(); awaitForBlock(plugins); - // sleep for cancellation timeout to ensure there is no scheduled task for cancellation - Thread.sleep(cancellationTimeout.getMillis()); + sleepForAtLeast(requestCancellationTimeout.getMillis()); + // unblock the search thread disableBlocks(plugins); // wait for response and ensure there is no failure @@ -432,11 +451,12 @@ public void testNoCancellationOfScrollSearchOnFollowUpRequest() throws Exception public void testDisableCancellationAtRequestLevel() throws Exception { List plugins = initBlockFactory(); indexTestData(); - TimeValue cancellationTimeout = new TimeValue(2, TimeUnit.SECONDS); client().admin() .cluster() .prepareUpdateSettings() - .setPersistentSettings(Settings.builder().put(SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING_KEY, cancellationTimeout).build()) + .setPersistentSettings( + Settings.builder().put(SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING_KEY, clusterCancellationTimeout).build() + ) .get(); ActionFuture searchResponse = client().prepareSearch("test") .setAllowPartialSearchResults(randomBoolean()) @@ -444,8 +464,7 @@ public void testDisableCancellationAtRequestLevel() throws Exception { .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap()))) .execute(); awaitForBlock(plugins); - // sleep for cancellation timeout to ensure there is no scheduled task for cancellation - Thread.sleep(cancellationTimeout.getMillis()); + sleepForAtLeast(clusterCancellationTimeout.getMillis()); // unblock the search thread disableBlocks(plugins); // ensure search was successful since cancellation was disabled at request level @@ -455,7 +474,6 @@ public void testDisableCancellationAtRequestLevel() throws Exception { public void testDisableCancellationAtClusterLevel() throws Exception { List plugins = initBlockFactory(); indexTestData(); - TimeValue cancellationTimeout = new TimeValue(2, TimeUnit.SECONDS); client().admin() .cluster() .prepareUpdateSettings() @@ -466,8 +484,7 @@ public void testDisableCancellationAtClusterLevel() throws Exception { .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap()))) .execute(); awaitForBlock(plugins); - // sleep for cancellation timeout to ensure there is no scheduled task for cancellation - Thread.sleep(cancellationTimeout.getMillis()); + sleepForAtLeast(clusterCancellationTimeout.getMillis()); // unblock the search thread disableBlocks(plugins); // ensure search was successful since cancellation was disabled at request level @@ -501,11 +518,12 @@ public void testCancelMultiSearch() throws Exception { public void testMSearchChildRequestCancellationWithClusterLevelTimeout() throws Exception { List plugins = initBlockFactory(); indexTestData(); - TimeValue cancellationTimeout = new TimeValue(2, TimeUnit.SECONDS); client().admin() .cluster() .prepareUpdateSettings() - .setPersistentSettings(Settings.builder().put(SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING_KEY, cancellationTimeout).build()) + .setPersistentSettings( + Settings.builder().put(SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING_KEY, clusterCancellationTimeout).build() + ) .get(); ActionFuture mSearchResponse = client().prepareMultiSearch() .setMaxConcurrentSearchRequests(2) @@ -526,8 +544,7 @@ public void testMSearchChildRequestCancellationWithClusterLevelTimeout() throws ) .execute(); awaitForBlock(plugins); - // sleep for cluster cancellation timeout to ensure scheduled cancellation task is actually executed - Thread.sleep(cancellationTimeout.getMillis()); + sleepForAtLeast(clusterCancellationTimeout.getMillis()); // unblock the search thread disableBlocks(plugins); // both child requests are expected to fail @@ -544,8 +561,6 @@ public void testMSearchChildRequestCancellationWithClusterLevelTimeout() throws public void testMSearchChildReqCancellationWithHybridTimeout() throws Exception { List plugins = initBlockFactory(); indexTestData(); - TimeValue reqCancellationTimeout = new TimeValue(2, TimeUnit.SECONDS); - TimeValue clusterCancellationTimeout = new TimeValue(3, TimeUnit.SECONDS); client().admin() .cluster() .prepareUpdateSettings() @@ -558,7 +573,7 @@ public void testMSearchChildReqCancellationWithHybridTimeout() throws Exception .add( client().prepareSearch("test") .setAllowPartialSearchResults(randomBoolean()) - .setCancelAfterTimeInterval(reqCancellationTimeout) + .setCancelAfterTimeInterval(requestCancellationTimeout) .setQuery( scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedBlockPlugin.SCRIPT_NAME, Collections.emptyMap())) ) @@ -581,8 +596,7 @@ public void testMSearchChildReqCancellationWithHybridTimeout() throws Exception ) .execute(); awaitForBlock(plugins); - // sleep for cluster cancellation timeout to ensure scheduled cancellation task is actually executed - Thread.sleep(Math.max(reqCancellationTimeout.getMillis(), clusterCancellationTimeout.getMillis())); + sleepForAtLeast(Math.max(requestCancellationTimeout.getMillis(), clusterCancellationTimeout.getMillis())); // unblock the search thread disableBlocks(plugins); // only first and last child request are expected to fail @@ -592,6 +606,16 @@ public void testMSearchChildReqCancellationWithHybridTimeout() throws Exception ensureMSearchWasCancelled(mSearchResponse, expectedFailedRequests); } + /** + * Sleeps for the specified number of milliseconds plus a 100ms buffer to account for system timer/scheduler inaccuracies. + * + * @param milliseconds The minimum time to sleep + * @throws InterruptedException if interrupted during sleep + */ + private static void sleepForAtLeast(long milliseconds) throws InterruptedException { + Thread.sleep(milliseconds + 100L); + } + public static class ScriptedBlockPlugin extends MockScriptPlugin { static final String SCRIPT_NAME = "search_block"; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/SearchTimeoutIT.java b/server/src/internalClusterTest/java/org/opensearch/search/SearchTimeoutIT.java index 049dcb50024ba..94816346e6c9e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/SearchTimeoutIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/SearchTimeoutIT.java @@ -32,16 +32,21 @@ package org.opensearch.search; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -50,11 +55,27 @@ import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.index.query.QueryBuilders.scriptQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.SearchTimeoutIT.ScriptedTimeoutPlugin.SCRIPT_NAME; -import static org.hamcrest.Matchers.equalTo; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) -public class SearchTimeoutIT extends OpenSearchIntegTestCase { +public class SearchTimeoutIT extends ParameterizedOpenSearchIntegTestCase { + public SearchTimeoutIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -67,17 +88,36 @@ protected Settings nodeSettings(int nodeOrdinal) { } public void testSimpleTimeout() throws Exception { - for (int i = 0; i < 32; i++) { + final int numDocs = 1000; + for (int i = 0; i < numDocs; i++) { + client().prepareIndex("test").setId(Integer.toString(i)).setSource("field", "value").get(); + } + refresh("test"); + + SearchResponse searchResponse = client().prepareSearch("test") + .setTimeout(new TimeValue(5, TimeUnit.MILLISECONDS)) + .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SCRIPT_NAME, Collections.emptyMap()))) + .setAllowPartialSearchResults(true) + .get(); + assertTrue(searchResponse.isTimedOut()); + assertEquals(0, searchResponse.getFailedShards()); + } + + public void testSimpleDoesNotTimeout() throws Exception { + final int numDocs = 10; + for (int i = 0; i < numDocs; i++) { client().prepareIndex("test").setId(Integer.toString(i)).setSource("field", "value").get(); } refresh("test"); SearchResponse searchResponse = client().prepareSearch("test") - .setTimeout(new TimeValue(10, TimeUnit.MILLISECONDS)) + .setTimeout(new TimeValue(10000, TimeUnit.SECONDS)) .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", SCRIPT_NAME, Collections.emptyMap()))) .setAllowPartialSearchResults(true) .get(); - assertThat(searchResponse.isTimedOut(), equalTo(true)); + assertFalse(searchResponse.isTimedOut()); + assertEquals(0, searchResponse.getFailedShards()); + assertEquals(numDocs, searchResponse.getHits().getTotalHits().value); } public void testPartialResultsIntolerantTimeout() throws Exception { @@ -91,7 +131,7 @@ public void testPartialResultsIntolerantTimeout() throws Exception { .setAllowPartialSearchResults(false) // this line causes timeouts to report failures .get() ); - assertTrue(ex.toString().contains("Time exceeded")); + assertTrue(ex.toString().contains("QueryPhaseExecutionException[Time exceeded]")); } public static class ScriptedTimeoutPlugin extends MockScriptPlugin { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/SearchWeightedRoutingIT.java b/server/src/internalClusterTest/java/org/opensearch/search/SearchWeightedRoutingIT.java new file mode 100644 index 0000000000000..0975585e2984d --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/search/SearchWeightedRoutingIT.java @@ -0,0 +1,1463 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search; + +import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; +import org.opensearch.action.admin.cluster.node.stats.NodeStats; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingResponse; +import org.opensearch.action.get.MultiGetRequest; +import org.opensearch.action.get.MultiGetResponse; +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.SearchPhaseName; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.PreferenceBasedSearchNotAllowedException; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.ShardRoutingState; +import org.opensearch.cluster.routing.WeightedRouting; +import org.opensearch.cluster.routing.WeightedRoutingStats; +import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.index.search.stats.SearchStats; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.aggregations.Aggregations; +import org.opensearch.search.aggregations.bucket.terms.Terms; +import org.opensearch.snapshots.mockstore.MockRepository; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.disruption.NetworkDisruption; +import org.opensearch.test.transport.MockTransportService; +import org.junit.Assert; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.opensearch.action.search.TransportSearchAction.SEARCH_REQUEST_STATS_ENABLED_KEY; +import static org.opensearch.search.aggregations.AggregationBuilders.terms; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThanOrEqualTo; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0, minNumDataNodes = 3) +public class SearchWeightedRoutingIT extends OpenSearchIntegTestCase { + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(MockTransportService.TestPlugin.class, MockRepository.Plugin.class); + } + + public void testSearchWithWRRShardRouting() throws IOException { + Settings commonSettings = Settings.builder() + .put(AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING.getKey() + "zone" + ".values", "a,b,c") + .put(AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.getKey(), "zone") + .put("cluster.routing.weighted.fail_open", false) + .put(SEARCH_REQUEST_STATS_ENABLED_KEY, true) + .build(); + + logger.info("--> starting 6 nodes on different zones"); + List nodes = internalCluster().startNodes( + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build(), + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + + String A_0 = nodes.get(0); + String B_0 = nodes.get(1); + String B_1 = nodes.get(2); + String A_1 = nodes.get(3); + String C_0 = nodes.get(4); + String C_1 = nodes.get(5); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("6").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + assertAcked( + prepareCreate("test").setSettings(Settings.builder().put("index.number_of_shards", 10).put("index.number_of_replicas", 2)) + ); + ensureGreen(); + logger.info("--> creating indices for test"); + for (int i = 0; i < 100; i++) { + client().prepareIndex("test_" + i).setId("" + i).setSource("field_" + i, "value_" + i).get(); + } + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertEquals(response.isAcknowledged(), true); + + Set hitNodes = new HashSet<>(); + // making search requests + for (int i = 0; i < 50; i++) { + SearchResponse searchResponse = internalCluster().client(randomFrom(A_0, A_1, B_0, B_1)) + .prepareSearch() + .setQuery(QueryBuilders.matchAllQuery()) + .get(); + assertEquals(searchResponse.getFailedShards(), 0); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } + // search should not go to nodes in zone c with weight zero in case + // shard copies are available in other zones + assertThat(hitNodes.size(), lessThanOrEqualTo(4)); + DiscoveryNodes dataNodes = internalCluster().clusterService().state().nodes(); + List nodeIdsFromZoneWithWeightZero = new ArrayList<>(); + for (DiscoveryNode node : dataNodes) { + if (node.getAttributes().get("zone").equals("c")) { + nodeIdsFromZoneWithWeightZero.add(node.getId()); + } + } + for (String nodeId : nodeIdsFromZoneWithWeightZero) { + assertFalse(hitNodes.contains(nodeId)); + } + + NodesStatsResponse nodeStats = client().admin().cluster().prepareNodesStats().execute().actionGet(); + for (NodeStats stat : nodeStats.getNodes()) { + SearchStats.Stats searchStats = stat.getIndices().getSearch().getTotal(); + if (stat.getNode().getAttributes().get("zone").equals("c")) { + assertEquals(0, searchStats.getQueryCount()); + assertEquals(0, searchStats.getFetchCount()); + + } else { + Assert.assertTrue(searchStats.getQueryCount() > 0L); + Assert.assertTrue(searchStats.getFetchCount() > 0L); + } + } + + logger.info("--> deleted shard routing weights for weighted round robin"); + + ClusterDeleteWeightedRoutingResponse deleteResponse = client().admin().cluster().prepareDeleteWeightedRouting().setVersion(0).get(); + assertEquals(deleteResponse.isAcknowledged(), true); + + hitNodes = new HashSet<>(); + // making search requests + for (int i = 0; i < 100; i++) { + SearchResponse searchResponse = internalCluster().client(randomFrom(A_0, A_1, B_0, B_1)) + .prepareSearch() + .setQuery(QueryBuilders.matchAllQuery()) + .get(); + assertEquals(searchResponse.getFailedShards(), 0); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } + + // Check shard routing requests hit data nodes in zone c + for (String nodeId : nodeIdsFromZoneWithWeightZero) { + assertFalse(!hitNodes.contains(nodeId)); + } + nodeStats = client().admin().cluster().prepareNodesStats().execute().actionGet(); + int num = 0; + int coordNumber = 0; + + for (NodeStats stat : nodeStats.getNodes()) { + SearchStats.Stats searchStats = stat.getIndices().getSearch().getTotal(); + if (searchStats.getRequestStatsLongHolder() + .getRequestStatsHolder() + .get(SearchPhaseName.QUERY.getName()) + .getTimeInMillis() > 0) { + assertThat( + searchStats.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.QUERY.getName()).getTotal(), + greaterThan(0L) + ); + assertThat( + searchStats.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.FETCH.getName()).getTimeInMillis(), + greaterThan(0L) + ); + assertThat( + searchStats.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.FETCH.getName()).getTotal(), + greaterThan(0L) + ); + assertThat( + searchStats.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.EXPAND.getName()).getTotal(), + greaterThan(0L) + ); + coordNumber += 1; + } + Assert.assertTrue(searchStats.getQueryCount() > 0L); + Assert.assertTrue(searchStats.getFetchCount() > 0L); + num++; + } + assertThat(coordNumber, greaterThan(0)); + assertThat(num, greaterThan(0)); + } + + private Map> setupCluster(int nodeCountPerAZ, Settings commonSettings) { + + logger.info("--> starting a dedicated cluster manager node"); + internalCluster().startClusterManagerOnlyNode(Settings.builder().put(commonSettings).build()); + + logger.info("--> starting 1 nodes on zones 'a' & 'b' & 'c'"); + Map> nodeMap = new HashMap<>(); + List nodes_in_zone_a = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "a").build() + ); + nodeMap.put("a", nodes_in_zone_a); + List nodes_in_zone_b = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "b").build() + ); + nodeMap.put("b", nodes_in_zone_b); + + List nodes_in_zone_c = internalCluster().startDataOnlyNodes( + nodeCountPerAZ, + Settings.builder().put(commonSettings).put("node.attr.zone", "c").build() + ); + nodeMap.put("c", nodes_in_zone_c); + + logger.info("--> waiting for nodes to form a cluster"); + ClusterHealthResponse health = client().admin().cluster().prepareHealth().setWaitForNodes("4").execute().actionGet(); + assertThat(health.isTimedOut(), equalTo(false)); + + ensureGreen(); + return nodeMap; + + } + + private void setUpIndexing(int numShards, int numReplicas) { + assertAcked( + prepareCreate("test").setSettings( + Settings.builder().put("index.number_of_shards", numShards).put("index.number_of_replicas", numReplicas) + ) + ); + ensureGreen(); + + logger.info("--> creating indices for test"); + for (int i = 0; i < 100; i++) { + client().prepareIndex("test").setId("" + i).setSource("field_" + i, "value_" + i).get(); + } + refresh("test"); + } + + private void setShardRoutingWeights(Map weights) { + WeightedRouting weightedRouting = new WeightedRouting("zone", weights); + + ClusterPutWeightedRoutingResponse response = client().admin() + .cluster() + .prepareWeightedRouting() + .setWeightedRouting(weightedRouting) + .setVersion(-1) + .get(); + assertEquals(response.isAcknowledged(), true); + } + + /** + * Shard routing request fail without fail-open if there are no healthy nodes in active az to serve request + * This is tested by setting up a 3 node cluster with one data node per az. + * Weighted shard routing weight is set as 0 for az-c. + * Data nodes in zone a and b are stopped, + * assertions are put to check that search requests fail. + * @throws Exception throws Exception + */ + public void testShardRoutingByStoppingDataNodes_FailOpenDisabled() throws Exception { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", false) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 1; + int numReplicas = 2; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + logger.info("--> data nodes in zone a and b are stopped"); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodeMap.get("a").get(0))); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodeMap.get("b").get(0))); + ensureStableCluster(2); + + Set hitNodes = new HashSet<>(); + + // Make Search Requests + Future[] responses = new Future[50]; + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().smartClient().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).execute(); + } + int failedCount = 0; + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + assertEquals(0, searchResponse.getFailedShards()); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } catch (Exception t) { + failedCount++; + } + } + + Assert.assertTrue(failedCount > 0); + logger.info("--> failed request count is [()]", failedCount); + assertNoSearchInAZ("c"); + } + + /** + * Shard routing request with fail open enabled is served by data nodes in az with weight set as 0, + * in case shard copies are not available in other azs (with fail open enabled) + * This is tested by setting up a 3 node cluster with one data node per az. + * Weighted shard routing weight is set as 0 for az-c. + * Data nodes in zone a and b are stopped, + * assertions are put to make sure shard search requests do not fail. + * @throws IOException throws exception + */ + public void testShardRoutingByStoppingDataNodes_FailOpenEnabled() throws Exception { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 1; + int numReplicas = 2; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + logger.info("--> data nodes in zone a and b are stopped"); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodeMap.get("a").get(0))); + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(nodeMap.get("b").get(0))); + ensureStableCluster(2); + + Set hitNodes = new HashSet<>(); + + // Make Search Requests + Future[] responses = new Future[50]; + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().smartClient().prepareSearch("test").setQuery(QueryBuilders.matchAllQuery()).execute(); + } + int failedCount = 0; + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + assertEquals(0, searchResponse.getFailedShards()); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } catch (Exception t) { + failedCount++; + } + } + + Assert.assertTrue(failedCount == 0); + assertSearchInAZ("c"); + } + + /** + * Shard routing request with fail open disabled is not served by data nodes in az with weight set as 0, + * in case shard copies are not available in other azs. + * This is tested by setting up a 3 node cluster with one data node per az. + * Weighted shard routing weight is set as 0 for az-c. + * Indices are created with one replica copy and network disruption is introduced, + * which makes data node in zone-a unresponsive. + * Since there are two copies of a shard, there can be few shards for which copy doesn't exist in zone b. + * Assertions are put to make sure such shard search requests are not served by data node in zone c. + * @throws IOException throws exception + */ + public void testShardRoutingWithNetworkDisruption_FailOpenDisabled() throws Exception { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", false) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("b").get(0), nodeMap.get("c").get(0)) + .collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Set hitNodes = new HashSet<>(); + Future[] responses = new Future[50]; + logger.info("--> making search requests"); + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch("test") + .setQuery(QueryBuilders.matchAllQuery()) + .execute(); + } + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + int failedShardCount = 0; + + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + failedShardCount += searchResponse.getFailedShards(); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } catch (Exception t) { + fail("search should not fail"); + } + } + Assert.assertTrue(failedShardCount > 0); + // assert that no search request goes to az with weight zero + assertNoSearchInAZ("c"); + } + + /** + * Shard routing request is served by data nodes in az with weight set as 0, + * in case shard copies are not available in other azs.(with fail open enabled) + * This is tested by setting up a 3 node cluster with one data node per az. + * Weighted shard routing weight is set as 0 for az-c. + * Indices are created with one replica copy and network disruption is introduced, + * which makes data node in zone-a unresponsive. + * Since there are two copies of a shard, there can be few shards for which copy doesn't exist in zone b. + * Assertions are put to make sure such shard search requests are served by data node in zone c. + * @throws IOException throws exception + */ + public void testShardRoutingWithNetworkDisruption_FailOpenEnabled() throws Exception { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("b").get(0)).collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Set hitNodes = new HashSet<>(); + Future[] responses = new Future[50]; + logger.info("--> making search requests"); + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch("test") + .setSize(100) + .setQuery(QueryBuilders.matchAllQuery()) + .execute(); + } + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + assertEquals(searchResponse.getFailedShards(), 0); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } catch (Exception t) { + fail("search should not fail"); + } + } + + assertSearchInAZ("b"); + assertSearchInAZ("c"); + assertNoSearchInAZ("a"); + } + + public void testStrictWeightedRoutingWithCustomString_FailOpenEnabled() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .put("cluster.routing.weighted.strict", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("b").get(0)).collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Set hitNodes = new HashSet<>(); + Future[] responses = new Future[50]; + String customPreference = randomAlphaOfLength(10); + logger.info("--> making search requests"); + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch("test") + .setPreference(customPreference) + .setSize(100) + .setQuery(QueryBuilders.matchAllQuery()) + .execute(); + } + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + logger.info("--> shards should fail due to network disruption"); + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + assertEquals(searchResponse.getFailedShards(), 0); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } catch (Exception t) { + fail("search should not fail"); + } + } + + try { + assertSearchInAZ("b"); + } catch (AssertionError ae) { + assertSearchInAZ("c"); + } + assertNoSearchInAZ("a"); + } + + public void testStrictWeightedRoutingWithCustomString_FailOpenDisabled() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", false) + .put("cluster.routing.weighted.strict", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("b").get(0)).collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Set hitNodes = new HashSet<>(); + Future[] responses = new Future[50]; + String customPreference = randomAlphaOfLength(10); + logger.info("--> making search requests"); + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch("test") + .setPreference(customPreference) + .setSize(100) + .setQuery(QueryBuilders.matchAllQuery()) + .execute(); + } + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + logger.info("--> shards should fail due to network disruption"); + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + assertNotEquals(searchResponse.getFailedShards(), 0); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } catch (Exception t) { + fail("search should not fail"); + } + } + + DiscoveryNodes dataNodes = internalCluster().clusterService().state().nodes(); + Set expectedHotNodes = new HashSet<>(); + for (DiscoveryNode node : dataNodes) { + if (node.getAttributes().getOrDefault("zone", "").equals("b")) { + expectedHotNodes.add(node.getId()); + } + } + + assertEquals(expectedHotNodes, hitNodes); + + assertSearchInAZ("b"); + assertNoSearchInAZ("c"); + assertNoSearchInAZ("a"); + } + + /** + * Should failopen shards even if failopen enabled with custom search preference. + */ + public void testStrictWeightedRoutingWithShardPrefNetworkDisruption_FailOpenEnabled() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .put("cluster.routing.weighted.strict", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("c").get(0)).collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Future[] responses = new Future[50]; + DiscoveryNodes dataNodes = internalCluster().clusterService().state().nodes(); + ShardId shardId = internalCluster().clusterService() + .state() + .getRoutingTable() + .index("test") + .randomAllActiveShardsIt() + .getShardRoutings() + .stream() + .filter(shard -> { + return dataNodes.get(shard.currentNodeId()).getAttributes().getOrDefault("zone", "").equals("c"); + }) + .findFirst() + .get() + .shardId(); + + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().client(nodeMap.get("c").get(0)) + .prepareSearch("test") + .setPreference(String.format(Locale.ROOT, "_shards:%s", shardId.getId())) + .setSize(100) + .setQuery(QueryBuilders.matchAllQuery()) + .execute(); + } + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + assertEquals(searchResponse.getFailedShards(), 0); + } catch (Exception t) { + fail("search should not fail"); + } + } + + assertNoSearchInAZ("a"); + try { + assertSearchInAZ("c"); + } catch (AssertionError ae) { + assertSearchInAZ("b"); + } + } + + public void testStrictWeightedRoutingWithShardPref() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .put("cluster.routing.weighted.strict", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + DiscoveryNodes dataNodes = internalCluster().clusterService().state().nodes(); + ShardId shardId = internalCluster().clusterService() + .state() + .getRoutingTable() + .index("test") + .randomAllActiveShardsIt() + .getShardRoutings() + .stream() + .filter(shard -> { + return dataNodes.get(shard.currentNodeId()).getAttributes().getOrDefault("zone", "").equals("c"); + }) + .findFirst() + .get() + .shardId(); + + Future[] responses = new Future[50]; + logger.info("--> making search requests"); + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch("test") + .setPreference(String.format(Locale.ROOT, "_shards:%s", shardId.getId())) + .setSize(100) + .setQuery(QueryBuilders.matchAllQuery()) + .execute(); + } + + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + assertEquals(searchResponse.getFailedShards(), 0); + assertNotEquals(searchResponse.getHits().getTotalHits().value, 0); + } catch (Exception t) { + fail("search should not fail"); + } + } + assertNoSearchInAZ("c"); + } + + private void assertNoSearchInAZ(String az) { + final Map dataNodes = internalCluster().clusterService().state().nodes().getDataNodes(); + String dataNodeId = null; + + for (Iterator it = dataNodes.values().iterator(); it.hasNext();) { + DiscoveryNode node = it.next(); + if (node.getAttributes().get("zone").equals(az)) { + dataNodeId = node.getId(); + break; + } + } + + NodesStatsResponse nodeStats = client().admin().cluster().prepareNodesStats().execute().actionGet(); + for (NodeStats stat : nodeStats.getNodes()) { + SearchStats.Stats searchStats = stat.getIndices().getSearch().getTotal(); + if (stat.getNode().isDataNode()) { + if (stat.getNode().getId().equals(dataNodeId)) { + assertEquals(0, searchStats.getQueryCount()); + assertEquals(0, searchStats.getFetchCount()); + } + } + } + } + + private void assertSearchInAZ(String az) { + final Map dataNodes = internalCluster().clusterService().state().nodes().getDataNodes(); + String dataNodeId = null; + + for (Iterator it = dataNodes.values().iterator(); it.hasNext();) { + DiscoveryNode node = it.next(); + if (node.getAttributes().get("zone").equals(az)) { + dataNodeId = node.getId(); + break; + } + } + + NodesStatsResponse nodeStats = client().admin().cluster().prepareNodesStats().execute().actionGet(); + for (NodeStats stat : nodeStats.getNodes()) { + SearchStats.Stats searchStats = stat.getIndices().getSearch().getTotal(); + if (stat.getNode().isDataNode()) { + if (stat.getNode().getId().equals(dataNodeId)) { + Assert.assertTrue(searchStats.getFetchCount() > 0L); + Assert.assertTrue(searchStats.getQueryCount() > 0L); + } + } + } + } + + /** + * Shard routing request is served by data nodes in az with weight set as 0, + * in case shard copies are not available in other azs. + * This is tested by setting up a 3 node cluster with one data node per az. + * Weighted shard routing weight is set as 0 for az-c. + * Indices are created with one replica copy and network disruption is introduced, + * which makes node in zone-a unresponsive. + * Since there are two copies of a shard, there can be few shards for which copy doesn't exist in zone b. + * Assertions are put to make sure such shard search requests are served by data node in zone c. + * @throws IOException throws exception + */ + public void testSearchAggregationWithNetworkDisruption_FailOpenEnabled() throws Exception { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + assertAcked( + prepareCreate("index").setMapping("f", "type=keyword") + .setSettings(Settings.builder().put("index" + ".number_of_shards", 10).put("index" + ".number_of_replicas", 1)) + ); + + int numDocs = 10; + List docs = new ArrayList<>(); + for (int i = 0; i < numDocs; ++i) { + docs.add(client().prepareIndex("index").setSource("f", Integer.toString(i / 3))); + } + indexRandom(true, docs); + ensureGreen(); + refresh("index"); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("b").get(0), nodeMap.get("c").get(0)) + .collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Set hitNodes = new HashSet<>(); + Future[] responses = new Future[51]; + int size = 17; + logger.info("--> making search requests"); + for (int i = 0; i < 50; i++) { + responses[i] = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch("index") + .setSize(20) + .addAggregation(terms("f").field("f")) + .execute(); + } + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + for (int i = 0; i < 50; i++) { + try { + SearchResponse searchResponse = responses[i].get(); + Aggregations aggregations = searchResponse.getAggregations(); + assertNotNull(aggregations); + Terms terms = aggregations.get("f"); + assertEquals(0, searchResponse.getFailedShards()); + assertEquals(Math.min(numDocs, 3L), terms.getBucketByKey("0").getDocCount()); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } catch (Exception t) { + fail("search should not fail"); + } + } + assertSearchInAZ("b"); + assertSearchInAZ("c"); + assertNoSearchInAZ("a"); + + assertBusy( + () -> assertThat(client().admin().indices().prepareStats().get().getTotal().getSearch().getOpenContexts(), equalTo(0L)), + 60, + TimeUnit.SECONDS + ); + } + + /** + * MultiGet with fail open enabled. No request failure on network disruption + * @throws IOException throws exception + */ + public void testMultiGetWithNetworkDisruption_FailOpenEnabled() throws Exception { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("b").get(0), nodeMap.get("c").get(0)) + .collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Future[] responses = new Future[50]; + logger.info("--> making search requests"); + int index1, index2; + for (int i = 0; i < 50; i++) { + index1 = randomIntBetween(0, 9); + index2 = randomIntBetween(0, 9); + responses[i] = client().prepareMultiGet() + .add(new MultiGetRequest.Item("test", "" + index1)) + .add(new MultiGetRequest.Item("test", "" + index2)) + .execute(); + } + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + for (int i = 0; i < 50; i++) { + try { + MultiGetResponse multiGetResponse = responses[i].get(); + assertThat(multiGetResponse.getResponses().length, equalTo(2)); + assertThat(multiGetResponse.getResponses()[0].isFailed(), equalTo(false)); + assertThat(multiGetResponse.getResponses()[1].isFailed(), equalTo(false)); + } catch (Exception t) { + fail("search should not fail"); + } + } + + assertBusy( + () -> assertThat(client().admin().indices().prepareStats().get().getTotal().getSearch().getOpenContexts(), equalTo(0L)), + 60, + TimeUnit.SECONDS + ); + } + + /** + * MultiGet with fail open disabled. Assert that some requests do fail. + * @throws IOException throws exception + */ + public void testMultiGetWithNetworkDisruption_FailOpenDisabled() throws Exception { + + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", false) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("b").get(0), nodeMap.get("c").get(0)) + .collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Future[] responses = new Future[50]; + logger.info("--> making search requests"); + int index1, index2; + for (int i = 0; i < 50; i++) { + index1 = randomIntBetween(0, 9); + index2 = randomIntBetween(0, 9); + responses[i] = client().prepareMultiGet() + .add(new MultiGetRequest.Item("test", "" + index1)) + .add(new MultiGetRequest.Item("test", "" + index2)) + .execute(); + } + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + int failedCount = 0; + for (int i = 0; i < 50; i++) { + try { + MultiGetResponse multiGetResponse = responses[i].get(); + assertThat(multiGetResponse.getResponses().length, equalTo(2)); + if (multiGetResponse.getResponses()[0].isFailed() || multiGetResponse.getResponses()[1].isFailed()) { + failedCount++; + } + } catch (Exception t) { + fail("search should not fail"); + } + } + + Assert.assertTrue(failedCount > 0); + + assertBusy( + () -> assertThat(client().admin().indices().prepareStats().get().getTotal().getSearch().getOpenContexts(), equalTo(0L)), + 60, + TimeUnit.SECONDS + ); + } + + /** + * Assert that preference based search is not allowed with strict weighted shard routing + */ + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8030") + public void testStrictWeightedRoutingWithCustomString() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .put("cluster.routing.weighted.strict", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + String nodeInZoneA = nodeMap.get("a").get(0); + String customPreference = randomAlphaOfLength(10); + + SearchResponse searchResponse = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch() + .setSize(20) + .setPreference(customPreference) + .get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + assertNoSearchInAZ("c"); + assertSearchInAZ("a"); + assertSearchInAZ("b"); + + // disable strict weighed routing + client().admin() + .cluster() + .prepareUpdateSettings() + .setTransientSettings(Settings.builder().put("cluster.routing.weighted.strict", false)) + .get(); + + // make search requests with custom string + internalCluster().client(nodeMap.get("a").get(0)) + .prepareSearch() + .setSize(20) + .setPreference(customPreference) + .setQuery(QueryBuilders.matchAllQuery()) + .get(); + // assert search on data nodes on az c (weighed away az) + try { + assertSearchInAZ("c"); + } catch (AssertionError ae) { + assertSearchInAZ("a"); + } + } + + /** + * Assert that preference based search works with non-strict weighted shard routing + */ + public void testPreferenceSearchWithWeightedRouting() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .put("cluster.routing.weighted.strict", false) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 2; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + String customPreference = randomAlphaOfLength(10); + String nodeInZoneA = nodeMap.get("a").get(0); + String nodeInZoneB = nodeMap.get("b").get(0); + String nodeInZoneC = nodeMap.get("c").get(0); + + Map nodeIDMap = new HashMap<>(); + DiscoveryNodes dataNodes = internalCluster().clusterService().state().nodes(); + for (DiscoveryNode node : dataNodes) { + nodeIDMap.put(node.getName(), node.getId()); + } + SearchResponse searchResponse = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch() + .setPreference(randomFrom("_local", "_prefer_nodes:" + "zone:a", customPreference)) + .get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + + searchResponse = internalCluster().client(nodeMap.get("a").get(0)) + .prepareSearch() + .setPreference( + "_only_nodes:" + nodeIDMap.get(nodeInZoneA) + "," + nodeIDMap.get(nodeInZoneB) + "," + nodeIDMap.get(nodeInZoneC) + ) + .get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + } + + public void testPreferenceSearchWithIgnoreWeightedRouting() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .put("cluster.routing.weighted.strict", false) + .put("cluster.routing.ignore_weighted_routing", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 2; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + + String customPreference = randomAlphaOfLength(10); + String nodeInZoneA = nodeMap.get("a").get(0); + String nodeInZoneB = nodeMap.get("b").get(0); + String nodeInZoneC = nodeMap.get("c").get(0); + + Map nodeIDMap = new HashMap<>(); + DiscoveryNodes dataNodes = internalCluster().clusterService().state().nodes(); + for (DiscoveryNode node : dataNodes) { + nodeIDMap.put(node.getName(), node.getId()); + } + + SearchResponse searchResponse = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch() + .setPreference(randomFrom("_local", "_prefer_nodes:" + "zone:a", customPreference)) + .get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + + searchResponse = internalCluster().client(nodeMap.get("a").get(0)) + .prepareSearch() + .setPreference( + "_only_nodes:" + nodeIDMap.get(nodeInZoneA) + "," + nodeIDMap.get(nodeInZoneB) + "," + nodeIDMap.get(nodeInZoneC) + ) + .get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + } + + /** + * Assert that preference based search with preference type is not allowed with strict weighted shard routing + */ + public void testStrictWeightedRouting() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .put("cluster.routing.weighted.strict", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + String nodeInZoneA = nodeMap.get("a").get(0); + + assertThrows( + PreferenceBasedSearchNotAllowedException.class, + () -> internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch() + .setSize(0) + .setPreference("_only_nodes:" + nodeInZoneA) + .get() + ); + + assertThrows( + PreferenceBasedSearchNotAllowedException.class, + () -> internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch() + .setSize(0) + .setPreference("_prefer_nodes:" + nodeInZoneA) + .get() + ); + } + + public void testStrictWeightedRoutingAllowedForSomeSearchPrefs() { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .put("cluster.routing.weighted.strict", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + String nodeInZoneA = nodeMap.get("a").get(0); + String customPreference = randomAlphaOfLength(10); + + SearchResponse searchResponse = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch() + .setSize(0) + .setPreference("_only_local:" + nodeInZoneA) + .get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + + searchResponse = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch() + .setSize(0) + .setPreference("_local:" + nodeInZoneA) + .get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + + searchResponse = internalCluster().client(nodeMap.get("b").get(0)).prepareSearch().setSize(0).setPreference("_shards:1").get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + + searchResponse = internalCluster().client(nodeMap.get("b").get(0)).prepareSearch().setSize(0).setPreference(customPreference).get(); + assertEquals(RestStatus.OK.getStatus(), searchResponse.status().getStatus()); + } + + /** + * Shard routing request is served by data nodes in az with weight set as 0, + * in case shard copies are not available in other azs.(with fail open enabled) + * This is tested by setting up a 3 node cluster with one data node per az. + * Weighted shard routing weight is set as 0 for az-c. + * Indices are created with one replica copy and network disruption is introduced, + * which makes data node in zone-a unresponsive. + * Since there are two copies of a shard, there can be few shards for which copy doesn't exist in zone b. + * Assertions are put to make sure such shard search requests are served by data node in zone c. + * Asserts on fail open stats which captures number of times fail open is executed + * @throws IOException throws exception + */ + public void testWeightedRoutingFailOpenStats() throws Exception { + Settings commonSettings = Settings.builder() + .put("cluster.routing.allocation.awareness.attributes", "zone") + .put("cluster.routing.allocation.awareness.force.zone.values", "a,b,c") + .put("cluster.routing.weighted.fail_open", true) + .build(); + + int nodeCountPerAZ = 1; + Map> nodeMap = setupCluster(nodeCountPerAZ, commonSettings); + + int numShards = 10; + int numReplicas = 1; + setUpIndexing(numShards, numReplicas); + + logger.info("--> setting shard routing weights for weighted round robin"); + Map weights = Map.of("a", 1.0, "b", 1.0, "c", 0.0); + setShardRoutingWeights(weights); + WeightedRoutingStats.getInstance().resetFailOpenCount(); + + logger.info("--> creating network partition disruption"); + final String clusterManagerNode1 = internalCluster().getClusterManagerName(); + Set nodesInOneSide = Stream.of(clusterManagerNode1, nodeMap.get("b").get(0)).collect(Collectors.toCollection(HashSet::new)); + Set nodesInOtherSide = Stream.of(nodeMap.get("a").get(0)).collect(Collectors.toCollection(HashSet::new)); + + NetworkDisruption networkDisruption = new NetworkDisruption( + new NetworkDisruption.TwoPartitions(nodesInOneSide, nodesInOtherSide), + NetworkDisruption.UNRESPONSIVE + ); + internalCluster().setDisruptionScheme(networkDisruption); + + DiscoveryNodes dataNodes = internalCluster().clusterService().state().nodes(); + + Map nodeIDMap = new HashMap<>(); + for (DiscoveryNode node : dataNodes) { + nodeIDMap.put(node.getName(), node.getId()); + } + + List shardInNodeA = internalCluster().clusterService() + .state() + .getRoutingNodes() + .node(nodeIDMap.get(nodeMap.get("a").get(0))) + .shardsWithState(ShardRoutingState.STARTED); + + List shardInNodeC = internalCluster().clusterService() + .state() + .getRoutingNodes() + .node(nodeIDMap.get(nodeMap.get("c").get(0))) + .shardsWithState(ShardRoutingState.STARTED); + + // fail open will be called for shards in zone-a data node with replica in zone-c data node + Set result = new HashSet<>(); + int failOpenShardCount = 0; + for (ShardRouting shardRouting : shardInNodeA) { + result.add(shardRouting.shardId()); + } + for (ShardRouting shardRouting : shardInNodeC) { + if (result.contains(shardRouting.shardId())) { + failOpenShardCount++; + } + } + + logger.info("--> network disruption is started"); + networkDisruption.startDisrupting(); + + Set hitNodes = new HashSet<>(); + logger.info("--> making search requests"); + + Future response = internalCluster().client(nodeMap.get("b").get(0)) + .prepareSearch("test") + .setSize(100) + .setQuery(QueryBuilders.matchAllQuery()) + .execute(); + + logger.info("--> network disruption is stopped"); + networkDisruption.stopDisrupting(); + + try { + SearchResponse searchResponse = response.get(); + assertEquals(searchResponse.getFailedShards(), 0); + for (int j = 0; j < searchResponse.getHits().getHits().length; j++) { + hitNodes.add(searchResponse.getHits().getAt(j).getShard().getNodeId()); + } + } catch (Exception t) { + fail("search should not fail"); + } + assertSearchInAZ("b"); + assertSearchInAZ("c"); + assertNoSearchInAZ("a"); + + NodesStatsResponse nodeStats = client().admin().cluster().prepareNodesStats().addMetric("weighted_routing").execute().actionGet(); + Map stats = nodeStats.getNodesMap(); + NodeStats nodeStatsC = stats.get(nodeIDMap.get(nodeMap.get("c").get(0))); + assertEquals(failOpenShardCount, nodeStatsC.getWeightedRoutingStats().getFailOpenCount()); + WeightedRoutingStats.getInstance().resetFailOpenCount(); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/SearchWithRejectionsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/SearchWithRejectionsIT.java index 30e6aa4cd31fc..24c72a66da6d0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/SearchWithRejectionsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/SearchWithRejectionsIT.java @@ -32,21 +32,45 @@ package org.opensearch.search; -import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.SearchType; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.hamcrest.Matchers.equalTo; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) -public class SearchWithRejectionsIT extends OpenSearchIntegTestCase { +public class SearchWithRejectionsIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchWithRejectionsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public Settings nodeSettings(int nodeOrdinal) { return Settings.builder() diff --git a/server/src/internalClusterTest/java/org/opensearch/search/StressSearchServiceReaperIT.java b/server/src/internalClusterTest/java/org/opensearch/search/StressSearchServiceReaperIT.java index 42e515cca9b6b..a61102b9db144 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/StressSearchServiceReaperIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/StressSearchServiceReaperIT.java @@ -31,23 +31,45 @@ package org.opensearch.search; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.tests.util.English; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.ExecutionException; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.OpenSearchIntegTestCase.Scope.SUITE; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; @ClusterScope(scope = SUITE) -public class StressSearchServiceReaperIT extends OpenSearchIntegTestCase { +public class StressSearchServiceReaperIT extends ParameterizedOpenSearchIntegTestCase { + public StressSearchServiceReaperIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Settings nodeSettings(int nodeOrdinal) { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/AggregationsIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/AggregationsIntegrationIT.java index 6778765599fe9..257786c1e9ce5 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/AggregationsIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/AggregationsIntegrationIT.java @@ -32,24 +32,62 @@ package org.opensearch.search.aggregations; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.search.aggregations.bucket.terms.IncludeExclude; +import org.opensearch.search.aggregations.bucket.terms.RareTermsAggregationBuilder; +import org.opensearch.search.aggregations.bucket.terms.SignificantTermsAggregationBuilder; +import org.opensearch.search.aggregations.bucket.terms.SignificantTermsAggregatorFactory; import org.opensearch.search.aggregations.bucket.terms.Terms; +import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder; +import org.opensearch.search.aggregations.bucket.terms.TermsAggregatorFactory; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class AggregationsIntegrationIT extends OpenSearchIntegTestCase { +public class AggregationsIntegrationIT extends ParameterizedOpenSearchIntegTestCase { static int numDocs; + private static final String LARGE_STRING = "a".repeat(2000); + private static final String LARGE_STRING_EXCEPTION_MESSAGE = "The length of regex [" + + LARGE_STRING.length() + + "] used in the request has exceeded the allowed maximum"; + + public AggregationsIntegrationIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(prepareCreate("index").setMapping("f", "type=keyword").get()); @@ -85,4 +123,51 @@ public void testScroll() { assertEquals(numDocs, total); } + public void testLargeRegExTermsAggregation() { + for (TermsAggregatorFactory.ExecutionMode executionMode : TermsAggregatorFactory.ExecutionMode.values()) { + TermsAggregationBuilder termsAggregation = terms("my_terms").field("f") + .includeExclude(getLargeStringInclude()) + .executionHint(executionMode.toString()); + runLargeStringAggregationTest(termsAggregation); + } + } + + public void testLargeRegExSignificantTermsAggregation() { + for (SignificantTermsAggregatorFactory.ExecutionMode executionMode : SignificantTermsAggregatorFactory.ExecutionMode.values()) { + SignificantTermsAggregationBuilder significantTerms = new SignificantTermsAggregationBuilder("my_terms").field("f") + .includeExclude(getLargeStringInclude()) + .executionHint(executionMode.toString()); + runLargeStringAggregationTest(significantTerms); + } + } + + public void testLargeRegExRareTermsAggregation() { + // currently this only supports "map" as an execution hint + RareTermsAggregationBuilder rareTerms = new RareTermsAggregationBuilder("my_terms").field("f") + .includeExclude(getLargeStringInclude()) + .maxDocCount(2); + runLargeStringAggregationTest(rareTerms); + } + + private IncludeExclude getLargeStringInclude() { + return new IncludeExclude(LARGE_STRING, null); + } + + private void runLargeStringAggregationTest(AggregationBuilder aggregation) { + boolean exceptionThrown = false; + IncludeExclude include = new IncludeExclude(LARGE_STRING, null); + try { + client().prepareSearch("index").addAggregation(aggregation).get(); + } catch (SearchPhaseExecutionException ex) { + exceptionThrown = true; + Throwable nestedException = ex.getCause(); + assertNotNull(nestedException); + assertTrue(nestedException instanceof OpenSearchException); + assertNotNull(nestedException.getCause()); + assertTrue(nestedException.getCause() instanceof IllegalArgumentException); + String actualExceptionMessage = nestedException.getCause().getMessage(); + assertTrue(actualExceptionMessage.startsWith(LARGE_STRING_EXCEPTION_MESSAGE)); + } + assertTrue("Exception should have been thrown", exceptionThrown); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/CombiIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/CombiIT.java index c5794c76e21de..3d3cf1943dfe3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/CombiIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/CombiIT.java @@ -32,20 +32,27 @@ package org.opensearch.search.aggregations; -import com.carrotsearch.hppc.IntIntHashMap; -import com.carrotsearch.hppc.IntIntMap; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.missing.Missing; import org.opensearch.search.aggregations.bucket.terms.Terms; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.missing; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -54,7 +61,24 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.core.IsNull.notNullValue; -public class CombiIT extends OpenSearchIntegTestCase { +public class CombiIT extends ParameterizedOpenSearchIntegTestCase { + + public CombiIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } /** * Making sure that if there are multiple aggregations, working on the same field, yet require different @@ -67,7 +91,7 @@ public void testMultipleAggsOnSameField_WithDifferentRequiredValueSourceType() t createIndex("idx"); IndexRequestBuilder[] builders = new IndexRequestBuilder[randomInt(30)]; - IntIntMap values = new IntIntHashMap(); + final Map values = new HashMap<>(); long missingValues = 0; for (int i = 0; i < builders.length; i++) { String name = "name_" + randomIntBetween(1, 10); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/EquivalenceIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/EquivalenceIT.java index d03b10ce092c9..2ffdf5fb32778 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/EquivalenceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/EquivalenceIT.java @@ -32,12 +32,15 @@ package org.opensearch.search.aggregations; -import com.carrotsearch.hppc.IntHashSet; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.RangeQueryBuilder; import org.opensearch.plugins.Plugin; @@ -53,20 +56,24 @@ import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.bucket.terms.TermsAggregatorFactory; import org.opensearch.search.aggregations.metrics.Sum; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.After; import org.junit.Before; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.extendedStats; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; @@ -87,7 +94,24 @@ * Additional tests that aim at testing more complex aggregation trees on larger random datasets, so that things like * the growth of dynamic arrays is tested. */ -public class EquivalenceIT extends OpenSearchIntegTestCase { +public class EquivalenceIT extends ParameterizedOpenSearchIntegTestCase { + + public EquivalenceIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -230,7 +254,7 @@ public void testDuelTerms() throws Exception { final int numDocs = scaledRandomIntBetween(1000, 2000); final int maxNumTerms = randomIntBetween(10, 5000); - final IntHashSet valuesSet = new IntHashSet(); + final Set valuesSet = new HashSet<>(); cluster().wipeIndices("idx"); prepareCreate("idx").setMapping( jsonBuilder().startObject() diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/FiltersAggsRewriteIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/FiltersAggsRewriteIT.java index 3b8431c50a3ee..5926ff9f0cad1 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/FiltersAggsRewriteIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/FiltersAggsRewriteIT.java @@ -33,11 +33,11 @@ package org.opensearch.search.aggregations; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.WrapperQueryBuilder; import org.opensearch.search.aggregations.bucket.filter.FiltersAggregationBuilder; import org.opensearch.search.aggregations.bucket.filter.FiltersAggregator; @@ -59,7 +59,7 @@ public void testWrapperQueryIsRewritten() throws IOException { XContentType xContentType = randomFrom(XContentType.values()); BytesReference bytesReference; - try (XContentBuilder builder = XContentFactory.contentBuilder(xContentType)) { + try (XContentBuilder builder = MediaTypeRegistry.contentBuilder(xContentType)) { builder.startObject(); { builder.startObject("terms"); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/MetadataIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/MetadataIT.java index f210af7c10fb3..1bc0cb36f5fe3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/MetadataIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/MetadataIT.java @@ -32,25 +32,49 @@ package org.opensearch.search.aggregations; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.pipeline.InternalBucketMetricValue; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.search.aggregations.PipelineAggregatorBuilders.maxBucket; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; -public class MetadataIT extends OpenSearchIntegTestCase { +public class MetadataIT extends ParameterizedOpenSearchIntegTestCase { + + public MetadataIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } public void testMetadataSetOnAggregationResult() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("name", "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/MissingValueIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/MissingValueIT.java index 7d3f06760882d..e6325987d330f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/MissingValueIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/MissingValueIT.java @@ -32,22 +32,29 @@ package org.opensearch.search.aggregations; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; import org.opensearch.search.aggregations.metrics.Cardinality; -import org.opensearch.search.aggregations.metrics.GeoBounds; import org.opensearch.search.aggregations.metrics.GeoCentroid; import org.opensearch.search.aggregations.metrics.Percentiles; import org.opensearch.search.aggregations.metrics.Stats; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.cardinality; import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; -import static org.opensearch.search.aggregations.AggregationBuilders.geoBounds; import static org.opensearch.search.aggregations.AggregationBuilders.geoCentroid; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.percentiles; @@ -58,7 +65,24 @@ import static org.hamcrest.Matchers.closeTo; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class MissingValueIT extends OpenSearchIntegTestCase { +public class MissingValueIT extends ParameterizedOpenSearchIntegTestCase { + + public MissingValueIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected int maximumNumberOfShards() { @@ -213,28 +237,6 @@ public void testStats() { assertEquals(4, stats.getAvg(), 0); } - public void testUnmappedGeoBounds() { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(geoBounds("bounds").field("non_existing_field").missing("2,1")) - .get(); - assertSearchResponse(response); - GeoBounds bounds = response.getAggregations().get("bounds"); - assertThat(bounds.bottomRight().lat(), closeTo(2.0, 1E-5)); - assertThat(bounds.bottomRight().lon(), closeTo(1.0, 1E-5)); - assertThat(bounds.topLeft().lat(), closeTo(2.0, 1E-5)); - assertThat(bounds.topLeft().lon(), closeTo(1.0, 1E-5)); - } - - public void testGeoBounds() { - SearchResponse response = client().prepareSearch("idx").addAggregation(geoBounds("bounds").field("location").missing("2,1")).get(); - assertSearchResponse(response); - GeoBounds bounds = response.getAggregations().get("bounds"); - assertThat(bounds.bottomRight().lat(), closeTo(1.0, 1E-5)); - assertThat(bounds.bottomRight().lon(), closeTo(2.0, 1E-5)); - assertThat(bounds.topLeft().lat(), closeTo(2.0, 1E-5)); - assertThat(bounds.topLeft().lon(), closeTo(1.0, 1E-5)); - } - public void testGeoCentroid() { SearchResponse response = client().prepareSearch("idx") .addAggregation(geoCentroid("centroid").field("location").missing("2,1")) diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/AdjacencyMatrixIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/AdjacencyMatrixIT.java index af3cc85ed40c0..cd0922606ec99 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/AdjacencyMatrixIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/AdjacencyMatrixIT.java @@ -32,12 +32,15 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.QueryBuilder; @@ -47,15 +50,19 @@ import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.metrics.Avg; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.adjacencyMatrix; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; @@ -68,11 +75,28 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class AdjacencyMatrixIT extends OpenSearchIntegTestCase { +public class AdjacencyMatrixIT extends ParameterizedOpenSearchIntegTestCase { static int numDocs, numSingleTag1Docs, numSingleTag2Docs, numTag1Docs, numTag2Docs, numMultiTagDocs; static final int MAX_NUM_FILTERS = 3; + public AdjacencyMatrixIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { createIndex("idx"); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/BooleanTermsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/BooleanTermsIT.java index fc5407c4cade8..7ab1a44ce220c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/BooleanTermsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/BooleanTermsIT.java @@ -31,26 +31,52 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class BooleanTermsIT extends OpenSearchIntegTestCase { +public class BooleanTermsIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "b_value"; private static final String MULTI_VALUED_FIELD_NAME = "b_values"; static int numSingleTrues, numSingleFalses, numMultiTrues, numMultiFalses; + public BooleanTermsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { createIndex("idx"); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateHistogramIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateHistogramIT.java index 782bcde39ce8d..4ce8af3e0f081 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateHistogramIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateHistogramIT.java @@ -31,16 +31,17 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; -import org.opensearch.bootstrap.JavaVersion; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; import org.opensearch.common.time.DateFormatters; import org.opensearch.common.time.DateMathParser; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.mapper.DateFieldMapper; import org.opensearch.index.query.MatchNoneQueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -51,13 +52,14 @@ import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; -import org.opensearch.search.aggregations.bucket.histogram.LongBounds; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.opensearch.search.aggregations.bucket.histogram.InternalDateHistogram; +import org.opensearch.search.aggregations.bucket.histogram.LongBounds; import org.opensearch.search.aggregations.metrics.Avg; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import org.junit.After; @@ -80,6 +82,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; @@ -95,7 +98,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class DateHistogramIT extends OpenSearchIntegTestCase { +public class DateHistogramIT extends ParameterizedOpenSearchIntegTestCase { static Map> expectedMultiSortBuckets; @@ -103,6 +106,23 @@ private ZonedDateTime date(int month, int day) { return ZonedDateTime.of(2012, month, day, 0, 0, 0, 0, ZoneOffset.UTC); } + public DateHistogramIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + private ZonedDateTime date(String date) { return DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(date)); } @@ -386,9 +406,6 @@ public void testSingleValued_timeZone_epoch() throws Exception { ZonedDateTime expectedKey = keyIterator.next(); String bucketKey = bucket.getKeyAsString(); String expectedBucketName = Long.toString(expectedKey.toInstant().toEpochMilli() / millisDivider); - if (JavaVersion.current().getVersion().get(0) == 8 && bucket.getKeyAsString().endsWith(".0")) { - expectedBucketName = expectedBucketName + ".0"; - } assertThat(bucketKey, equalTo(expectedBucketName)); assertThat(((ZonedDateTime) bucket.getKey()), equalTo(expectedKey)); assertThat(bucket.getDocCount(), equalTo(1L)); @@ -1313,16 +1330,15 @@ public void testSingleValueFieldWithExtendedBoundsOffset() throws Exception { } public void testSingleValueWithMultipleDateFormatsFromMapping() throws Exception { - String mappingJson = Strings.toString( - jsonBuilder().startObject() - .startObject("properties") - .startObject("date") - .field("type", "date") - .field("format", "strict_date_optional_time||dd-MM-yyyy") - .endObject() - .endObject() - .endObject() - ); + String mappingJson = jsonBuilder().startObject() + .startObject("properties") + .startObject("date") + .field("type", "date") + .field("format", "strict_date_optional_time||dd-MM-yyyy") + .endObject() + .endObject() + .endObject() + .toString(); prepareCreate("idx2").setMapping(mappingJson).get(); IndexRequestBuilder[] reqs = new IndexRequestBuilder[5]; for (int i = 0; i < reqs.length; i++) { @@ -1509,11 +1525,7 @@ public void testRewriteTimeZone_EpochMillisFormat() throws InterruptedException, assertSearchResponse(response); Histogram histo = response.getAggregations().get("histo"); assertThat(histo.getBuckets().size(), equalTo(1)); - if (JavaVersion.current().getVersion().get(0) == 8 && histo.getBuckets().get(0).getKeyAsString().endsWith(".0")) { - assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("1477954800000.0")); - } else { - assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("1477954800000")); - } + assertThat(histo.getBuckets().get(0).getKeyAsString(), equalTo("1477954800000")); assertThat(histo.getBuckets().get(0).getDocCount(), equalTo(1L)); response = client().prepareSearch(index) @@ -1743,6 +1755,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + cluster().wipeIndices("cache_test_idx"); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndKeyDesc() throws Exception { @@ -1860,6 +1873,7 @@ public void testDateNanosHistogram() throws Exception { assertEquals(1, buckets.get(0).getDocCount()); assertEquals(946771200000L, ((ZonedDateTime) buckets.get(1).getKey()).toEpochSecond() * 1000); assertEquals(1, buckets.get(1).getDocCount()); + cluster().wipeIndices("nanos"); } public void testDateKeyFormatting() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateHistogramOffsetIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateHistogramOffsetIT.java index 19e5bdb8916b8..04115f69172da 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateHistogramOffsetIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateHistogramOffsetIT.java @@ -31,24 +31,32 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; import org.opensearch.common.time.DateFormatters; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.mapper.DateFieldMapper; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.After; import org.junit.Before; import java.io.IOException; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.core.IsNull.notNullValue; @@ -61,11 +69,28 @@ */ @OpenSearchIntegTestCase.SuiteScopeTestCase @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) -public class DateHistogramOffsetIT extends OpenSearchIntegTestCase { +public class DateHistogramOffsetIT extends ParameterizedOpenSearchIntegTestCase { private static final String DATE_FORMAT = "yyyy-MM-dd:hh-mm-ss"; private static final DateFormatter FORMATTER = DateFormatter.forPattern(DATE_FORMAT); + public DateHistogramOffsetIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + private ZonedDateTime date(String date) { return DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(date)); } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateRangeIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateRangeIT.java index 470ee6a4d2cea..ae4243019ffb1 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateRangeIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DateRangeIT.java @@ -31,11 +31,14 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; @@ -46,6 +49,7 @@ import org.opensearch.search.aggregations.bucket.range.Range.Bucket; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.time.ZoneId; @@ -63,6 +67,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.dateRange; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; @@ -76,7 +81,24 @@ import static org.hamcrest.core.IsNull.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class DateRangeIT extends OpenSearchIntegTestCase { +public class DateRangeIT extends ParameterizedOpenSearchIntegTestCase { + + public DateRangeIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } private static IndexRequestBuilder indexDoc(int month, int day, int value) throws Exception { return client().prepareIndex("idx") @@ -1062,6 +1084,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } /** @@ -1124,6 +1147,7 @@ public void testRangeWithFormatStringValue() throws Exception { .get() ); assertThat(e.getDetailedMessage(), containsString("failed to parse date field [1000000] with format [strict_hour_minute_second]")); + internalCluster().wipeIndices(indexName); } /** @@ -1196,6 +1220,7 @@ public void testRangeWithFormatNumericValue() throws Exception { buckets = checkBuckets(searchResponse.getAggregations().get("date_range"), "date_range", 2); assertBucket(buckets.get(0), 2L, "1000000-3000000", 1000000L, 3000000L); assertBucket(buckets.get(1), 1L, "3000000-4000000", 3000000L, 4000000L); + internalCluster().wipeIndices(indexName); } private static List checkBuckets(Range dateRange, String expectedAggName, long expectedBucketsSize) { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DiversifiedSamplerIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DiversifiedSamplerIT.java index 5b01e7573908c..5e95073209c71 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DiversifiedSamplerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DiversifiedSamplerIT.java @@ -31,11 +31,15 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.indices.refresh.RefreshRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.SearchType; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.TermQueryBuilder; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.sampler.DiversifiedAggregationBuilder; import org.opensearch.search.aggregations.bucket.sampler.Sampler; import org.opensearch.search.aggregations.bucket.sampler.SamplerAggregator; @@ -43,14 +47,16 @@ import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket; import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder; import org.opensearch.search.aggregations.metrics.Max; -import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; import java.util.Collection; import java.util.List; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.max; import static org.opensearch.search.aggregations.AggregationBuilders.sampler; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -65,10 +71,27 @@ * Tests the Sampler aggregation */ @OpenSearchIntegTestCase.SuiteScopeTestCase -public class DiversifiedSamplerIT extends OpenSearchIntegTestCase { +public class DiversifiedSamplerIT extends ParameterizedOpenSearchIntegTestCase { public static final int NUM_SHARDS = 2; + public DiversifiedSamplerIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public String randomExecutionHint() { return randomBoolean() ? null : randomFrom(SamplerAggregator.ExecutionMode.values()).toString(); } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DoubleTermsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DoubleTermsIT.java index 3093c7490a2a5..88bb41923e53f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DoubleTermsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/DoubleTermsIT.java @@ -48,9 +48,9 @@ import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket; import org.opensearch.search.aggregations.metrics.Avg; +import org.opensearch.search.aggregations.metrics.ExtendedStats; import org.opensearch.search.aggregations.metrics.Max; import org.opensearch.search.aggregations.metrics.Stats; -import org.opensearch.search.aggregations.metrics.ExtendedStats; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.test.OpenSearchIntegTestCase; @@ -88,6 +88,10 @@ @OpenSearchIntegTestCase.SuiteScopeTestCase public class DoubleTermsIT extends AbstractTermsTestCase { + public DoubleTermsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -1106,5 +1110,6 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/FilterIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/FilterIT.java index 0845db4f332d7..7aa98803403e0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/FilterIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/FilterIT.java @@ -31,10 +31,14 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.search.aggregations.InternalAggregation; @@ -42,14 +46,18 @@ import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.metrics.Avg; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; @@ -60,10 +68,27 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class FilterIT extends OpenSearchIntegTestCase { +public class FilterIT extends ParameterizedOpenSearchIntegTestCase { static int numDocs, numTag1Docs; + public FilterIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { createIndex("idx"); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/FiltersIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/FiltersIT.java index a64fe61b29b8a..b6cf515df78ba 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/FiltersIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/FiltersIT.java @@ -32,10 +32,14 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.search.aggregations.InternalAggregation; @@ -44,6 +48,7 @@ import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.metrics.Avg; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; @@ -56,6 +61,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.filters; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; @@ -66,10 +72,27 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class FiltersIT extends OpenSearchIntegTestCase { +public class FiltersIT extends ParameterizedOpenSearchIntegTestCase { static int numDocs, numTag1Docs, numTag2Docs, numOtherDocs; + public FiltersIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { createIndex("idx"); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoDistanceIT.java index 603a141abcaec..bfacbc1c17a3c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoDistanceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoDistanceIT.java @@ -31,6 +31,8 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.Version; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; @@ -39,7 +41,8 @@ import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.DistanceUnit; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.histogram.Histogram; @@ -47,17 +50,20 @@ import org.opensearch.search.aggregations.bucket.range.Range.Bucket; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; import org.hamcrest.Matchers; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.geoDistance; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -70,7 +76,11 @@ import static org.hamcrest.core.IsNull.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class GeoDistanceIT extends OpenSearchIntegTestCase { +public class GeoDistanceIT extends ParameterizedOpenSearchIntegTestCase { + + public GeoDistanceIT(Settings dynamicSettings) { + super(dynamicSettings); + } @Override protected boolean forbidPrivateIndexSettings() { @@ -79,6 +89,19 @@ protected boolean forbidPrivateIndexSettings() { private Version version = VersionUtils.randomIndexCompatibleVersion(random()); + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + private IndexRequestBuilder indexCity(String idx, String name, String... latLons) throws Exception { XContentBuilder source = jsonBuilder().startObject().field("city", name); source.startArray("location"); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoHashGridIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoHashGridIT.java deleted file mode 100644 index 56d918feef9d8..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GeoHashGridIT.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.search.aggregations.bucket; - -import com.carrotsearch.hppc.ObjectIntHashMap; -import com.carrotsearch.hppc.ObjectIntMap; -import com.carrotsearch.hppc.cursors.ObjectIntCursor; -import org.opensearch.Version; -import org.opensearch.action.index.IndexRequestBuilder; -import org.opensearch.action.search.SearchResponse; -import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.query.GeoBoundingBoxQueryBuilder; -import org.opensearch.search.aggregations.AggregationBuilders; -import org.opensearch.search.aggregations.InternalAggregation; -import org.opensearch.search.aggregations.bucket.filter.Filter; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGrid; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGrid.Bucket; -import org.opensearch.test.OpenSearchIntegTestCase; -import org.opensearch.test.VersionUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; - -import static org.opensearch.geometry.utils.Geohash.PRECISION; -import static org.opensearch.geometry.utils.Geohash.stringEncode; -import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; -import static org.opensearch.search.aggregations.AggregationBuilders.geohashGrid; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; - -@OpenSearchIntegTestCase.SuiteScopeTestCase -public class GeoHashGridIT extends OpenSearchIntegTestCase { - - @Override - protected boolean forbidPrivateIndexSettings() { - return false; - } - - private Version version = VersionUtils.randomIndexCompatibleVersion(random()); - - static ObjectIntMap expectedDocCountsForGeoHash = null; - static ObjectIntMap multiValuedExpectedDocCountsForGeoHash = null; - static int numDocs = 100; - - static String smallestGeoHash = null; - - private static IndexRequestBuilder indexCity(String index, String name, List latLon) throws Exception { - XContentBuilder source = jsonBuilder().startObject().field("city", name); - if (latLon != null) { - source = source.field("location", latLon); - } - source = source.endObject(); - return client().prepareIndex(index).setSource(source); - } - - private static IndexRequestBuilder indexCity(String index, String name, String latLon) throws Exception { - return indexCity(index, name, Arrays.asList(latLon)); - } - - @Override - public void setupSuiteScopeCluster() throws Exception { - createIndex("idx_unmapped"); - - Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, version).build(); - - assertAcked(prepareCreate("idx").setSettings(settings).setMapping("location", "type=geo_point", "city", "type=keyword")); - - List cities = new ArrayList<>(); - Random random = random(); - expectedDocCountsForGeoHash = new ObjectIntHashMap<>(numDocs * 2); - for (int i = 0; i < numDocs; i++) { - // generate random point - double lat = (180d * random.nextDouble()) - 90d; - double lng = (360d * random.nextDouble()) - 180d; - String randomGeoHash = stringEncode(lng, lat, PRECISION); - // Index at the highest resolution - cities.add(indexCity("idx", randomGeoHash, lat + ", " + lng)); - expectedDocCountsForGeoHash.put(randomGeoHash, expectedDocCountsForGeoHash.getOrDefault(randomGeoHash, 0) + 1); - // Update expected doc counts for all resolutions.. - for (int precision = PRECISION - 1; precision > 0; precision--) { - String hash = stringEncode(lng, lat, precision); - if ((smallestGeoHash == null) || (hash.length() < smallestGeoHash.length())) { - smallestGeoHash = hash; - } - expectedDocCountsForGeoHash.put(hash, expectedDocCountsForGeoHash.getOrDefault(hash, 0) + 1); - } - } - indexRandom(true, cities); - - assertAcked( - prepareCreate("multi_valued_idx").setSettings(settings).setMapping("location", "type=geo_point", "city", "type=keyword") - ); - - cities = new ArrayList<>(); - multiValuedExpectedDocCountsForGeoHash = new ObjectIntHashMap<>(numDocs * 2); - for (int i = 0; i < numDocs; i++) { - final int numPoints = random.nextInt(4); - List points = new ArrayList<>(); - Set geoHashes = new HashSet<>(); - for (int j = 0; j < numPoints; ++j) { - double lat = (180d * random.nextDouble()) - 90d; - double lng = (360d * random.nextDouble()) - 180d; - points.add(lat + "," + lng); - // Update expected doc counts for all resolutions.. - for (int precision = PRECISION; precision > 0; precision--) { - final String geoHash = stringEncode(lng, lat, precision); - geoHashes.add(geoHash); - } - } - cities.add(indexCity("multi_valued_idx", Integer.toString(i), points)); - for (String hash : geoHashes) { - multiValuedExpectedDocCountsForGeoHash.put(hash, multiValuedExpectedDocCountsForGeoHash.getOrDefault(hash, 0) + 1); - } - } - indexRandom(true, cities); - - ensureSearchable(); - } - - public void testSimple() throws Exception { - for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) - .get(); - - assertSearchResponse(response); - - GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); - List buckets = geoGrid.getBuckets(); - Object[] propertiesKeys = (Object[]) ((InternalAggregation) geoGrid).getProperty("_key"); - Object[] propertiesDocCounts = (Object[]) ((InternalAggregation) geoGrid).getProperty("_count"); - for (int i = 0; i < buckets.size(); i++) { - GeoGrid.Bucket cell = buckets.get(i); - String geohash = cell.getKeyAsString(); - - long bucketCount = cell.getDocCount(); - int expectedBucketCount = expectedDocCountsForGeoHash.get(geohash); - assertNotSame(bucketCount, 0); - assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); - GeoPoint geoPoint = (GeoPoint) propertiesKeys[i]; - assertThat(stringEncode(geoPoint.lon(), geoPoint.lat(), precision), equalTo(geohash)); - assertThat((long) propertiesDocCounts[i], equalTo(bucketCount)); - } - } - } - - public void testMultivalued() throws Exception { - for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("multi_valued_idx") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) - .get(); - - assertSearchResponse(response); - - GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); - for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { - String geohash = cell.getKeyAsString(); - - long bucketCount = cell.getDocCount(); - int expectedBucketCount = multiValuedExpectedDocCountsForGeoHash.get(geohash); - assertNotSame(bucketCount, 0); - assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); - } - } - } - - public void testFiltered() throws Exception { - GeoBoundingBoxQueryBuilder bbox = new GeoBoundingBoxQueryBuilder("location"); - bbox.setCorners(smallestGeoHash).queryName("bbox"); - for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation( - AggregationBuilders.filter("filtered", bbox) - .subAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) - ) - .get(); - - assertSearchResponse(response); - - Filter filter = response.getAggregations().get("filtered"); - - GeoGrid geoGrid = filter.getAggregations().get("geohashgrid"); - for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { - String geohash = cell.getKeyAsString(); - long bucketCount = cell.getDocCount(); - int expectedBucketCount = expectedDocCountsForGeoHash.get(geohash); - assertNotSame(bucketCount, 0); - assertTrue("Buckets must be filtered", geohash.startsWith(smallestGeoHash)); - assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); - - } - } - } - - public void testUnmapped() throws Exception { - for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx_unmapped") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) - .get(); - - assertSearchResponse(response); - - GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); - assertThat(geoGrid.getBuckets().size(), equalTo(0)); - } - - } - - public void testPartiallyUnmapped() throws Exception { - for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx", "idx_unmapped") - .addAggregation(geohashGrid("geohashgrid").field("location").precision(precision)) - .get(); - - assertSearchResponse(response); - - GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); - for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { - String geohash = cell.getKeyAsString(); - - long bucketCount = cell.getDocCount(); - int expectedBucketCount = expectedDocCountsForGeoHash.get(geohash); - assertNotSame(bucketCount, 0); - assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); - } - } - } - - public void testTopMatch() throws Exception { - for (int precision = 1; precision <= PRECISION; precision++) { - SearchResponse response = client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(1).shardSize(100).precision(precision)) - .get(); - - assertSearchResponse(response); - - GeoGrid geoGrid = response.getAggregations().get("geohashgrid"); - // Check we only have one bucket with the best match for that resolution - assertThat(geoGrid.getBuckets().size(), equalTo(1)); - for (GeoGrid.Bucket cell : geoGrid.getBuckets()) { - String geohash = cell.getKeyAsString(); - long bucketCount = cell.getDocCount(); - int expectedBucketCount = 0; - for (ObjectIntCursor cursor : expectedDocCountsForGeoHash) { - if (cursor.key.length() == precision) { - expectedBucketCount = Math.max(expectedBucketCount, cursor.value); - } - } - assertNotSame(bucketCount, 0); - assertEquals("Geohash " + geohash + " has wrong doc count ", expectedBucketCount, bucketCount); - } - } - } - - public void testSizeIsZero() { - final int size = 0; - final int shardSize = 10000; - IllegalArgumentException exception = expectThrows( - IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)) - .get() - ); - assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [geohashgrid]")); - } - - public void testShardSizeIsZero() { - final int size = 100; - final int shardSize = 0; - IllegalArgumentException exception = expectThrows( - IllegalArgumentException.class, - () -> client().prepareSearch("idx") - .addAggregation(geohashGrid("geohashgrid").field("location").size(size).shardSize(shardSize)) - .get() - ); - assertThat(exception.getMessage(), containsString("[shardSize] must be greater than 0. Found [0] in [geohashgrid]")); - } - -} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GlobalIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GlobalIT.java index 46e5fb7332e89..be31a3afadad0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GlobalIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/GlobalIT.java @@ -31,19 +31,27 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.metrics.Stats; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.search.aggregations.AggregationBuilders.stats; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; @@ -53,10 +61,27 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class GlobalIT extends OpenSearchIntegTestCase { +public class GlobalIT extends ParameterizedOpenSearchIntegTestCase { static int numDocs; + public GlobalIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { createIndex("idx"); @@ -83,10 +108,19 @@ public void setupSuiteScopeCluster() throws Exception { ensureSearchable(); } - public void testWithStatsSubAggregator() throws Exception { + public void testWithStatsSubAggregatorAndProfileEnabled() throws Exception { + testWithStatsSubAggregator(true); + } + + public void testWithStatsSubAggregatorAndProfileDisabled() throws Exception { + testWithStatsSubAggregator(false); + } + + private void testWithStatsSubAggregator(boolean profileEnabled) throws Exception { SearchResponse response = client().prepareSearch("idx") .setQuery(QueryBuilders.termQuery("tag", "tag1")) .addAggregation(global("global").subAggregation(stats("value_stats").field("value"))) + .setProfile(profileEnabled) .get(); assertSearchResponse(response); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/HistogramIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/HistogramIT.java index dae788abe0d10..75f57d1cc4c0e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/HistogramIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/HistogramIT.java @@ -31,18 +31,21 @@ package org.opensearch.search.aggregations.bucket; -import com.carrotsearch.hppc.LongHashSet; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.aggregations.AggregationExecutionException; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.filter.Filter; import org.opensearch.search.aggregations.bucket.histogram.DoubleBounds; @@ -52,22 +55,26 @@ import org.opensearch.search.aggregations.metrics.Max; import org.opensearch.search.aggregations.metrics.Stats; import org.opensearch.search.aggregations.metrics.Sum; -import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import static java.util.Collections.emptyMap; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; @@ -84,7 +91,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class HistogramIT extends OpenSearchIntegTestCase { +public class HistogramIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; private static final String MULTI_VALUED_FIELD_NAME = "l_values"; @@ -95,6 +102,23 @@ public class HistogramIT extends OpenSearchIntegTestCase { static long[] valueCounts, valuesCounts; static Map> expectedMultiSortBuckets; + public HistogramIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -396,7 +420,7 @@ public void testSingleValuedFieldOrderedByCountAsc() throws Exception { assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); - LongHashSet buckets = new LongHashSet(); + final Set buckets = new HashSet<>(); List histoBuckets = new ArrayList<>(histo.getBuckets()); long previousCount = Long.MIN_VALUE; for (int i = 0; i < numValueBuckets; ++i) { @@ -423,7 +447,7 @@ public void testSingleValuedFieldOrderedByCountDesc() throws Exception { assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); - LongHashSet buckets = new LongHashSet(); + final Set buckets = new HashSet<>(); List histoBuckets = new ArrayList<>(histo.getBuckets()); long previousCount = Long.MAX_VALUE; for (int i = 0; i < numValueBuckets; ++i) { @@ -497,7 +521,7 @@ public void testSingleValuedFieldOrderedBySubAggregationAsc() throws Exception { assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); - LongHashSet visited = new LongHashSet(); + final Set visited = new HashSet<>(); double previousSum = Double.NEGATIVE_INFINITY; List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { @@ -539,7 +563,7 @@ public void testSingleValuedFieldOrderedBySubAggregationDesc() throws Exception assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); - LongHashSet visited = new LongHashSet(); + final Set visited = new HashSet<>(); double previousSum = Double.POSITIVE_INFINITY; List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { @@ -581,7 +605,7 @@ public void testSingleValuedFieldOrderedByMultiValuedSubAggregationDesc() throws assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); - LongHashSet visited = new LongHashSet(); + final Set visited = new HashSet<>(); double previousSum = Double.POSITIVE_INFINITY; List buckets = new ArrayList<>(histo.getBuckets()); @@ -625,7 +649,7 @@ public void testSingleValuedFieldOrderedBySubAggregationDescDeepOrderPath() thro assertThat(histo.getName(), equalTo("histo")); assertThat(histo.getBuckets().size(), equalTo(numValueBuckets)); - LongHashSet visited = new LongHashSet(); + final Set visited = new HashSet<>(); double prevMax = asc ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; List buckets = new ArrayList<>(histo.getBuckets()); for (int i = 0; i < numValueBuckets; ++i) { @@ -1143,6 +1167,7 @@ public void testDecimalIntervalAndOffset() throws Exception { assertEquals(1, buckets.get(0).getDocCount()); assertEquals(0.05, (double) buckets.get(1).getKey(), 0.01d); assertEquals(1, buckets.get(1).getDocCount()); + internalCluster().wipeIndices("decimal_values"); } /** @@ -1284,6 +1309,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } public void testSingleValuedFieldOrderedBySingleValueSubAggregationAscAndKeyDesc() throws Exception { @@ -1387,6 +1413,7 @@ public void testHardBounds() throws Exception { buckets = histogram.getBuckets(); assertEquals(1, buckets.size()); assertEquals(0.1, (double) buckets.get(0).getKey(), 0.01d); + internalCluster().wipeIndices("test"); } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/IpRangeIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/IpRangeIT.java index f8f666aaa3c1b..14a3685bd183e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/IpRangeIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/IpRangeIT.java @@ -31,9 +31,13 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.health.ClusterHealthStatus; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; @@ -41,6 +45,7 @@ import org.opensearch.search.aggregations.AggregationBuilders; import org.opensearch.search.aggregations.bucket.range.Range; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.Arrays; import java.util.Collection; @@ -48,13 +53,31 @@ import java.util.Map; import java.util.function.Function; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class IpRangeIT extends OpenSearchIntegTestCase { +public class IpRangeIT extends ParameterizedOpenSearchIntegTestCase { + + public IpRangeIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } public static class DummyScriptPlugin extends MockScriptPlugin { @Override diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/IpTermsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/IpTermsIT.java index cff51e74fdbd0..c712c97af5c71 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/IpTermsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/IpTermsIT.java @@ -32,6 +32,7 @@ package org.opensearch.search.aggregations.bucket; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; @@ -50,6 +51,10 @@ public class IpTermsIT extends AbstractTermsTestCase { + public IpTermsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/LongTermsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/LongTermsIT.java index 87968bd2117c6..41cf0529f78f5 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/LongTermsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/LongTermsIT.java @@ -48,9 +48,9 @@ import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket; import org.opensearch.search.aggregations.metrics.Avg; +import org.opensearch.search.aggregations.metrics.ExtendedStats; import org.opensearch.search.aggregations.metrics.Max; import org.opensearch.search.aggregations.metrics.Stats; -import org.opensearch.search.aggregations.metrics.ExtendedStats; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.test.OpenSearchIntegTestCase; @@ -86,6 +86,10 @@ @OpenSearchIntegTestCase.SuiteScopeTestCase public class LongTermsIT extends AbstractTermsTestCase { + public LongTermsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -1054,5 +1058,6 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/MinDocCountIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/MinDocCountIT.java index 08e696245209e..90dafc0d57887 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/MinDocCountIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/MinDocCountIT.java @@ -32,12 +32,12 @@ package org.opensearch.search.aggregations.bucket; -import com.carrotsearch.hppc.LongHashSet; -import com.carrotsearch.hppc.LongSet; import com.carrotsearch.randomizedtesting.generators.RandomStrings; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.index.query.QueryBuilder; @@ -82,6 +82,10 @@ public class MinDocCountIT extends AbstractTermsTestCase { private static final QueryBuilder QUERY = QueryBuilders.termQuery("match", true); private static int cardinality; + public MinDocCountIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -122,7 +126,7 @@ public void setupSuiteScopeCluster() throws Exception { cardinality = randomIntBetween(8, 30); final List indexRequests = new ArrayList<>(); final Set stringTerms = new HashSet<>(); - final LongSet longTerms = new LongHashSet(); + final Set longTerms = new HashSet(); for (int i = 0; i < cardinality; ++i) { String stringTerm; do { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/MultiTermsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/MultiTermsIT.java new file mode 100644 index 0000000000000..8d79f581a0ab4 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/MultiTermsIT.java @@ -0,0 +1,172 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.aggregations.bucket; + +import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.script.Script; +import org.opensearch.script.ScriptType; +import org.opensearch.search.aggregations.bucket.terms.BaseStringTermsTestCase; +import org.opensearch.search.aggregations.bucket.terms.StringTermsIT; +import org.opensearch.search.aggregations.bucket.terms.Terms; +import org.opensearch.search.aggregations.support.MultiTermsValuesSourceConfig; +import org.opensearch.search.aggregations.support.ValueType; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.util.Collections; + +import static java.util.Arrays.asList; +import static org.opensearch.search.aggregations.AggregationBuilders.multiTerms; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.core.IsNull.notNullValue; + +/** + * Extend {@link BaseStringTermsTestCase}. + */ +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class MultiTermsIT extends BaseStringTermsTestCase { + + public MultiTermsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + // the main purpose of this test is to make sure we're not allocating 2GB of memory per shard + public void testSizeIsZero() { + final int minDocCount = randomInt(1); + IllegalArgumentException exception = expectThrows( + IllegalArgumentException.class, + () -> client().prepareSearch("high_card_idx") + .addAggregation( + multiTerms("mterms").terms( + asList( + new MultiTermsValuesSourceConfig.Builder().setFieldName(SINGLE_VALUED_FIELD_NAME).build(), + new MultiTermsValuesSourceConfig.Builder().setFieldName(MULTI_VALUED_FIELD_NAME).build() + ) + ).minDocCount(minDocCount).size(0) + ) + .get() + ); + assertThat(exception.getMessage(), containsString("[size] must be greater than 0. Found [0] in [mterms]")); + } + + public void testSingleValuedFieldWithValueScript() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .addAggregation( + multiTerms("mterms").terms( + asList( + new MultiTermsValuesSourceConfig.Builder().setFieldName("i").build(), + new MultiTermsValuesSourceConfig.Builder().setFieldName(SINGLE_VALUED_FIELD_NAME) + .setScript( + new Script( + ScriptType.INLINE, + StringTermsIT.CustomScriptPlugin.NAME, + "'foo_' + _value", + Collections.emptyMap() + ) + ) + .build() + ) + ) + ) + .get(); + + assertSearchResponse(response); + + Terms terms = response.getAggregations().get("mterms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("mterms")); + assertThat(terms.getBuckets().size(), equalTo(5)); + + for (int i = 0; i < 5; i++) { + Terms.Bucket bucket = terms.getBucketByKey(i + "|foo_val" + i); + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo(i + "|foo_val" + i)); + assertThat(bucket.getDocCount(), equalTo(1L)); + } + } + + public void testSingleValuedFieldWithScript() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .addAggregation( + multiTerms("mterms").terms( + asList( + new MultiTermsValuesSourceConfig.Builder().setFieldName("i").build(), + new MultiTermsValuesSourceConfig.Builder().setScript( + new Script( + ScriptType.INLINE, + StringTermsIT.CustomScriptPlugin.NAME, + "doc['" + SINGLE_VALUED_FIELD_NAME + "'].value", + Collections.emptyMap() + ) + ).setUserValueTypeHint(ValueType.STRING).build() + ) + ) + ) + .get(); + + assertSearchResponse(response); + + Terms terms = response.getAggregations().get("mterms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("mterms")); + assertThat(terms.getBuckets().size(), equalTo(5)); + + for (int i = 0; i < 5; i++) { + Terms.Bucket bucket = terms.getBucketByKey(i + "|val" + i); + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo(i + "|val" + i)); + assertThat(bucket.getDocCount(), equalTo(1L)); + } + } + + public void testMultiValuedFieldWithValueScript() throws Exception { + SearchResponse response = client().prepareSearch("idx") + .addAggregation( + multiTerms("mterms").terms( + asList( + new MultiTermsValuesSourceConfig.Builder().setFieldName("tag").build(), + new MultiTermsValuesSourceConfig.Builder().setFieldName(MULTI_VALUED_FIELD_NAME) + .setScript( + new Script( + ScriptType.INLINE, + StringTermsIT.CustomScriptPlugin.NAME, + "_value.substring(0,3)", + Collections.emptyMap() + ) + ) + .build() + ) + ) + ) + .get(); + + assertSearchResponse(response); + + Terms terms = response.getAggregations().get("mterms"); + assertThat(terms, notNullValue()); + assertThat(terms.getName(), equalTo("mterms")); + assertThat(terms.getBuckets().size(), equalTo(2)); + + Terms.Bucket bucket = terms.getBucketByKey("more|val"); + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo("more|val")); + assertThat(bucket.getDocCount(), equalTo(3L)); + + bucket = terms.getBucketByKey("less|val"); + assertThat(bucket, notNullValue()); + assertThat(key(bucket), equalTo("less|val")); + assertThat(bucket.getDocCount(), equalTo(2L)); + } + + private MultiTermsValuesSourceConfig field(String name) { + return new MultiTermsValuesSourceConfig.Builder().setFieldName(name).build(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/NaNSortingIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/NaNSortingIT.java index 3a3e02c577096..1ef2c0e8db8c7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/NaNSortingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/NaNSortingIT.java @@ -32,23 +32,32 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; import org.opensearch.common.util.Comparators; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.Aggregation; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.metrics.Avg; import org.opensearch.search.aggregations.metrics.AvgAggregationBuilder; import org.opensearch.search.aggregations.metrics.ExtendedStats; import org.opensearch.search.aggregations.metrics.ExtendedStatsAggregationBuilder; -import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.support.ValuesSource; import org.opensearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.extendedStats; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; @@ -58,7 +67,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class NaNSortingIT extends OpenSearchIntegTestCase { +public class NaNSortingIT extends ParameterizedOpenSearchIntegTestCase { private enum SubAggregation { AVG("avg") { @@ -130,6 +139,23 @@ public String sortKey() { public abstract double getValue(Aggregation aggregation); } + public NaNSortingIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("string_value", "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/NestedIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/NestedIT.java index fcde2f628ecd7..b3009ffcf4f45 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/NestedIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/NestedIT.java @@ -31,17 +31,19 @@ package org.opensearch.search.aggregations.bucket; -import org.apache.lucene.search.join.ScoreMode; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.apache.lucene.search.join.ScoreMode; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.InnerHitBuilder; -import org.opensearch.rest.RestStatus; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.filter.Filter; @@ -55,10 +57,12 @@ import org.opensearch.search.aggregations.metrics.Stats; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.test.OpenSearchIntegTestCase; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; @@ -68,6 +72,7 @@ import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.nestedQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.max; @@ -87,12 +92,29 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class NestedIT extends OpenSearchIntegTestCase { +public class NestedIT extends ParameterizedOpenSearchIntegTestCase { private static int numParents; private static int[] numChildren; private static SubAggCollectionMode aggCollectionMode; + public NestedIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { @@ -461,7 +483,7 @@ public void testParentFilterResolvedCorrectly() throws Exception { "{\"dates\": {\"month\": {\"label\": \"2014-11\", \"end\": \"2014-11-30\", \"start\": \"2014-11-01\"}, " + "\"day\": \"2014-11-30\"}, \"comments\": [{\"cid\": 3,\"identifier\": \"29111\"}, {\"cid\": 4,\"tags\": [" + "{\"tid\" :44,\"name\": \"Roles\"}], \"identifier\": \"29101\"}]}", - XContentType.JSON + MediaTypeRegistry.JSON ) ); indexRequests.add( @@ -471,7 +493,7 @@ public void testParentFilterResolvedCorrectly() throws Exception { "{\"dates\": {\"month\": {\"label\": \"2014-12\", \"end\": \"2014-12-31\", \"start\": \"2014-12-01\"}, " + "\"day\": \"2014-12-03\"}, \"comments\": [{\"cid\": 1, \"identifier\": \"29111\"}, {\"cid\": 2,\"tags\": [" + "{\"tid\" : 22, \"name\": \"DataChannels\"}], \"identifier\": \"29101\"}]}", - XContentType.JSON + MediaTypeRegistry.JSON ) ); indexRandom(true, indexRequests); @@ -534,6 +556,7 @@ public void testParentFilterResolvedCorrectly() throws Exception { assertThat(nestedTags.getDocCount(), equalTo(0L)); // This must be 0 tags = nestedTags.getAggregations().get("tag"); assertThat(tags.getBuckets().size(), equalTo(0)); // and this must be empty + internalCluster().wipeIndices("idx2"); } public void testNestedSameDocIdProcessedMultipleTime() throws Exception { @@ -640,6 +663,7 @@ public void testNestedSameDocIdProcessedMultipleTime() throws Exception { assertThat(propertyId.getBucketByKey("1").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("2").getDocCount(), equalTo(1L)); assertThat(propertyId.getBucketByKey("3").getDocCount(), equalTo(1L)); + internalCluster().wipeIndices("idx4"); } public void testFilterAggInsideNestedAgg() throws Exception { @@ -802,6 +826,7 @@ public void testFilterAggInsideNestedAgg() throws Exception { assertThat(bucket.getDocCount(), equalTo(1L)); numStringParams = bucket.getAggregations().get("num_string_params"); assertThat(numStringParams.getDocCount(), equalTo(0L)); + internalCluster().wipeIndices("classes"); } public void testExtractInnerHitBuildersWithDuplicateHitName() throws Exception { @@ -826,6 +851,7 @@ public void testExtractInnerHitBuildersWithDuplicateHitName() throws Exception { RestStatus.BAD_REQUEST, containsString("[inner_hits] already contains an entry for key [ih1]") ); + internalCluster().wipeIndices("idxduplicatehitnames"); } public void testExtractInnerHitBuildersWithDuplicatePath() throws Exception { @@ -848,5 +874,6 @@ public void testExtractInnerHitBuildersWithDuplicatePath() throws Exception { RestStatus.BAD_REQUEST, containsString("[inner_hits] already contains an entry for key [property]") ); + internalCluster().wipeIndices("idxnullhitnames"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/RangeIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/RangeIT.java index c46d6dcd847e1..64ab6f1382ac3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/RangeIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/RangeIT.java @@ -31,10 +31,13 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; @@ -48,9 +51,11 @@ import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -60,6 +65,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.range; import static org.opensearch.search.aggregations.AggregationBuilders.sum; @@ -73,13 +79,30 @@ import static org.hamcrest.core.IsNull.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class RangeIT extends OpenSearchIntegTestCase { +public class RangeIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; private static final String MULTI_VALUED_FIELD_NAME = "l_values"; static int numDocs; + public RangeIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -1061,6 +1084,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } public void testFieldAlias() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ReverseNestedIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ReverseNestedIT.java index 67ae145aece11..2716db6b7e745 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ReverseNestedIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ReverseNestedIT.java @@ -31,22 +31,27 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.filter.Filter; import org.opensearch.search.aggregations.bucket.nested.Nested; import org.opensearch.search.aggregations.bucket.nested.ReverseNested; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.metrics.ValueCount; -import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; @@ -55,6 +60,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.count; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.nested; @@ -70,7 +76,24 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class ReverseNestedIT extends OpenSearchIntegTestCase { +public class ReverseNestedIT extends ParameterizedOpenSearchIntegTestCase { + + public ReverseNestedIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override public void setupSuiteScopeCluster() throws Exception { @@ -726,6 +749,7 @@ public void testSameParentDocHavingMultipleBuckets() throws Exception { ValueCount barCount = reverseToBar.getAggregations().get("sku_count"); assertThat(barCount.getValue(), equalTo(2L)); } + internalCluster().wipeIndices("idx3"); } public void testFieldAlias() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/SamplerIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/SamplerIT.java index 0bfeff9297ce8..7033c42c5d661 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/SamplerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/SamplerIT.java @@ -32,24 +32,31 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.indices.refresh.RefreshRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.SearchType; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.TermQueryBuilder; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.sampler.Sampler; -import org.opensearch.search.aggregations.bucket.sampler.SamplerAggregator; import org.opensearch.search.aggregations.bucket.sampler.SamplerAggregationBuilder; +import org.opensearch.search.aggregations.bucket.sampler.SamplerAggregator; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket; import org.opensearch.search.aggregations.metrics.Max; -import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.max; import static org.opensearch.search.aggregations.AggregationBuilders.sampler; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -64,7 +71,7 @@ * Tests the Sampler aggregation */ @OpenSearchIntegTestCase.SuiteScopeTestCase -public class SamplerIT extends OpenSearchIntegTestCase { +public class SamplerIT extends ParameterizedOpenSearchIntegTestCase { public static final int NUM_SHARDS = 2; @@ -72,6 +79,23 @@ public String randomExecutionHint() { return randomBoolean() ? null : randomFrom(SamplerAggregator.ExecutionMode.values()).toString(); } + public SamplerIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked( diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardReduceIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardReduceIT.java index 7352dc7170a21..66d761c56634e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardReduceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardReduceIT.java @@ -31,13 +31,16 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.geometry.utils.Geohash; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.opensearch.search.aggregations.bucket.filter.Filter; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGrid; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; import org.opensearch.search.aggregations.bucket.histogram.Histogram; @@ -46,13 +49,16 @@ import org.opensearch.search.aggregations.bucket.range.Range; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.opensearch.search.aggregations.AggregationBuilders.dateRange; import static org.opensearch.search.aggregations.AggregationBuilders.filter; -import static org.opensearch.search.aggregations.AggregationBuilders.geohashGrid; -import static org.opensearch.search.aggregations.AggregationBuilders.geotileGrid; import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.ipRange; @@ -71,7 +77,24 @@ * we can make sure that the reduce is properly propagated by checking that empty buckets were created. */ @OpenSearchIntegTestCase.SuiteScopeTestCase -public class ShardReduceIT extends OpenSearchIntegTestCase { +public class ShardReduceIT extends ParameterizedOpenSearchIntegTestCase { + + public ShardReduceIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } private IndexRequestBuilder indexDoc(String date, int value) throws Exception { return client().prepareIndex("idx") @@ -338,36 +361,4 @@ public void testDateHistogram() throws Exception { } - public void testGeoHashGrid() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) - .addAggregation( - geohashGrid("grid").field("location") - .subAggregation(dateHistogram("histo").field("date").dateHistogramInterval(DateHistogramInterval.DAY).minDocCount(0)) - ) - .get(); - - assertSearchResponse(response); - - GeoGrid grid = response.getAggregations().get("grid"); - Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); - assertThat(histo.getBuckets().size(), equalTo(4)); - } - - public void testGeoTileGrid() throws Exception { - SearchResponse response = client().prepareSearch("idx") - .setQuery(QueryBuilders.matchAllQuery()) - .addAggregation( - geotileGrid("grid").field("location") - .subAggregation(dateHistogram("histo").field("date").dateHistogramInterval(DateHistogramInterval.DAY).minDocCount(0)) - ) - .get(); - - assertSearchResponse(response); - - GeoGrid grid = response.getAggregations().get("grid"); - Histogram histo = grid.getBuckets().iterator().next().getAggregations().get("histo"); - assertThat(histo.getBuckets().size(), equalTo(4)); - } - } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardSizeTermsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardSizeTermsIT.java index 8f3d94c2eacdb..145830f02ee56 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardSizeTermsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/ShardSizeTermsIT.java @@ -32,9 +32,10 @@ package org.opensearch.search.aggregations.bucket; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; -import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.BucketOrder; +import org.opensearch.search.aggregations.bucket.terms.Terms; import java.util.HashMap; import java.util.List; @@ -45,6 +46,11 @@ import static org.hamcrest.Matchers.equalTo; public class ShardSizeTermsIT extends ShardSizeTestCase { + + public ShardSizeTermsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + public void testNoShardSizeString() throws Exception { createIdx("type=keyword"); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java index 9c334df1d806b..e914b87754865 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/SignificantTermsSignificanceScoreIT.java @@ -31,17 +31,21 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; +import org.opensearch.index.query.TermQueryBuilder; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.SearchPlugin; import org.opensearch.script.MockScriptPlugin; @@ -61,6 +65,7 @@ import org.opensearch.search.aggregations.bucket.terms.heuristic.ScriptHeuristic; import org.opensearch.search.aggregations.bucket.terms.heuristic.SignificanceHeuristic; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.search.aggregations.bucket.SharedSignificantTermsTestMethods; import java.io.IOException; @@ -77,6 +82,7 @@ import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.significantTerms; import static org.opensearch.search.aggregations.AggregationBuilders.significantText; @@ -89,12 +95,29 @@ import static org.hamcrest.Matchers.is; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) -public class SignificantTermsSignificanceScoreIT extends OpenSearchIntegTestCase { +public class SignificantTermsSignificanceScoreIT extends ParameterizedOpenSearchIntegTestCase { static final String INDEX_NAME = "testidx"; static final String TEXT_FIELD = "text"; static final String CLASS_FIELD = "class"; + public SignificantTermsSignificanceScoreIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Arrays.asList(TestScriptPlugin.class); @@ -206,14 +229,42 @@ public void testXContentResponse() throws Exception { + "\"score\":0.75," + "\"bg_count\":4" + "}]}}]}}"; - assertThat(Strings.toString(responseBuilder), equalTo(result)); + assertThat(responseBuilder.toString(), equalTo(result)); + + } + + public void testConsistencyWithDifferentShardCounts() throws Exception { + // The purpose of this test is to validate that the aggregation results do not change with shard count. + // bg_count for significant term agg is summed up across shards, so in this test we compare a 1 shard and 2 shard search request + String type = randomBoolean() ? "text" : "long"; + String settings = "{\"index.number_of_shards\": 1, \"index.number_of_replicas\": 0}"; + SharedSignificantTermsTestMethods.index01Docs(type, settings, this); + SearchRequestBuilder request = client().prepareSearch(INDEX_NAME) + .setQuery(new TermQueryBuilder(CLASS_FIELD, "0")) + .addAggregation((significantTerms("sig_terms").field(TEXT_FIELD))); + + SearchResponse response1 = request.get(); + + assertAcked(client().admin().indices().delete(new DeleteIndexRequest("*")).get()); + + settings = "{\"index.number_of_shards\": 2, \"index.number_of_replicas\": 0}"; + // We use a custom routing strategy here to ensure that each shard will have at least 1 bucket. + // If there are no buckets collected for a shard, then that will affect the scoring and bg_count and our assertion will not be + // valid. + SharedSignificantTermsTestMethods.index01DocsWithRouting(type, settings, this); + SearchResponse response2 = request.get(); + + assertEquals( + response1.getAggregations().asMap().get("sig_terms").toString(), + response2.getAggregations().asMap().get("sig_terms").toString() + ); } public void testPopularTermManyDeletedDocs() throws Exception { String settings = "{\"index.number_of_shards\": 1, \"index.number_of_replicas\": 0}"; assertAcked( - prepareCreate(INDEX_NAME).setSettings(settings, XContentType.JSON) + prepareCreate(INDEX_NAME).setSettings(settings, MediaTypeRegistry.JSON) .setMapping("text", "type=keyword", CLASS_FIELD, "type=keyword") ); String[] cat1v1 = { "constant", "one" }; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/TermsDocCountErrorIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/TermsDocCountErrorIT.java index 3d76b994ebac3..3fcf4b5d533d4 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/TermsDocCountErrorIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/TermsDocCountErrorIT.java @@ -32,23 +32,30 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket; import org.opensearch.search.aggregations.bucket.terms.TermsAggregatorFactory.ExecutionMode; -import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; @@ -60,7 +67,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class TermsDocCountErrorIT extends OpenSearchIntegTestCase { +public class TermsDocCountErrorIT extends ParameterizedOpenSearchIntegTestCase { private static final String STRING_FIELD_NAME = "s_value"; private static final String LONG_FIELD_NAME = "l_value"; @@ -72,6 +79,23 @@ public static String randomExecutionHint() { private static int numRoutingValues; + public TermsDocCountErrorIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping(STRING_FIELD_NAME, "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/TermsShardMinDocCountIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/TermsShardMinDocCountIT.java index 852c3760751b3..b0d8e7ea02e8f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/TermsShardMinDocCountIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/TermsShardMinDocCountIT.java @@ -31,23 +31,29 @@ package org.opensearch.search.aggregations.bucket; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.filter.InternalFilter; import org.opensearch.search.aggregations.bucket.terms.SignificantTerms; import org.opensearch.search.aggregations.bucket.terms.SignificantTermsAggregatorFactory; import org.opensearch.search.aggregations.bucket.terms.Terms; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.significantTerms; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -55,9 +61,27 @@ import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.equalTo; -public class TermsShardMinDocCountIT extends OpenSearchIntegTestCase { +public class TermsShardMinDocCountIT extends ParameterizedOpenSearchIntegTestCase { + private static final String index = "someindex"; + public TermsShardMinDocCountIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + private static String randomExecutionHint() { return randomBoolean() ? null : randomFrom(SignificantTermsAggregatorFactory.ExecutionMode.values()).toString(); } @@ -124,10 +148,10 @@ private void addTermsDocs(String term, int numInClass, int numNotInClass, List builders) { String sourceClass = "{\"text\": \"" + term + "\"}"; for (int i = 0; i < numDocs; i++) { - builders.add(client().prepareIndex(index).setSource(sourceClass, XContentType.JSON)); + builders.add(client().prepareIndex(index).setSource(sourceClass, MediaTypeRegistry.JSON)); } } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/terms/BaseStringTermsTestCase.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/terms/BaseStringTermsTestCase.java new file mode 100644 index 0000000000000..20caa4fd076fe --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/terms/BaseStringTermsTestCase.java @@ -0,0 +1,261 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.aggregations.bucket.terms; + +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; +import org.opensearch.index.fielddata.ScriptDocValues; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.aggregations.AggregationTestScriptsPlugin; +import org.opensearch.search.aggregations.bucket.AbstractTermsTestCase; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.SuiteScopeTestCase +public class BaseStringTermsTestCase extends AbstractTermsTestCase { + + protected static final String SINGLE_VALUED_FIELD_NAME = "s_value"; + protected static final String MULTI_VALUED_FIELD_NAME = "s_values"; + protected static Map> expectedMultiSortBuckets; + + public BaseStringTermsTestCase(Settings dynamicSettings) { + super(dynamicSettings); + } + + @Override + protected Collection> nodePlugins() { + return Collections.singleton(CustomScriptPlugin.class); + } + + @Before + public void randomizeOptimizations() { + TermsAggregatorFactory.COLLECT_SEGMENT_ORDS = randomBoolean(); + TermsAggregatorFactory.REMAP_GLOBAL_ORDS = randomBoolean(); + } + + @After + public void resetOptimizations() { + TermsAggregatorFactory.COLLECT_SEGMENT_ORDS = null; + TermsAggregatorFactory.REMAP_GLOBAL_ORDS = null; + } + + public static class CustomScriptPlugin extends AggregationTestScriptsPlugin { + + @Override + protected Map, Object>> pluginScripts() { + Map, Object>> scripts = super.pluginScripts(); + + scripts.put("'foo_' + _value", vars -> "foo_" + (String) vars.get("_value")); + scripts.put("_value.substring(0,3)", vars -> ((String) vars.get("_value")).substring(0, 3)); + + scripts.put("doc['" + MULTI_VALUED_FIELD_NAME + "']", vars -> { + Map doc = (Map) vars.get("doc"); + return doc.get(MULTI_VALUED_FIELD_NAME); + }); + + scripts.put("doc['" + SINGLE_VALUED_FIELD_NAME + "'].value", vars -> { + Map doc = (Map) vars.get("doc"); + ScriptDocValues.Strings value = (ScriptDocValues.Strings) doc.get(SINGLE_VALUED_FIELD_NAME); + return value.getValue(); + }); + + scripts.put("42", vars -> 42); + + return scripts; + } + + @Override + protected Map, Object>> nonDeterministicPluginScripts() { + Map, Object>> scripts = new HashMap<>(); + + scripts.put("Math.random()", vars -> randomDouble()); + + return scripts; + } + } + + @Override + public void setupSuiteScopeCluster() throws Exception { + assertAcked( + client().admin() + .indices() + .prepareCreate("idx") + .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword") + .get() + ); + List builders = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + builders.add( + client().prepareIndex("idx") + .setSource( + jsonBuilder().startObject() + .field(SINGLE_VALUED_FIELD_NAME, "val" + i) + .field("i", i) + .field("constant", 1) + .field("tag", i < 5 / 2 + 1 ? "more" : "less") + .startArray(MULTI_VALUED_FIELD_NAME) + .value("val" + i) + .value("val" + (i + 1)) + .endArray() + .endObject() + ) + ); + } + + getMultiSortDocs(builders); + + assertAcked( + client().admin() + .indices() + .prepareCreate("high_card_idx") + .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword") + .get() + ); + for (int i = 0; i < 100; i++) { + builders.add( + client().prepareIndex("high_card_idx") + .setSource( + jsonBuilder().startObject() + .field(SINGLE_VALUED_FIELD_NAME, "val" + Strings.padStart(i + "", 3, '0')) + .startArray(MULTI_VALUED_FIELD_NAME) + .value("val" + Strings.padStart(i + "", 3, '0')) + .value("val" + Strings.padStart((i + 1) + "", 3, '0')) + .endArray() + .endObject() + ) + ); + } + prepareCreate("empty_bucket_idx").setMapping(SINGLE_VALUED_FIELD_NAME, "type=integer").get(); + + for (int i = 0; i < 2; i++) { + builders.add( + client().prepareIndex("empty_bucket_idx") + .setId("" + i) + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, i * 2).endObject()) + ); + } + indexRandom(true, builders); + createIndex("idx_unmapped"); + ensureSearchable(); + } + + private void getMultiSortDocs(List builders) throws IOException { + expectedMultiSortBuckets = new HashMap<>(); + Map bucketProps = new HashMap<>(); + bucketProps.put("_term", "val1"); + bucketProps.put("_count", 3L); + bucketProps.put("avg_l", 1d); + bucketProps.put("sum_d", 6d); + expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); + bucketProps = new HashMap<>(); + bucketProps.put("_term", "val2"); + bucketProps.put("_count", 3L); + bucketProps.put("avg_l", 2d); + bucketProps.put("sum_d", 6d); + expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); + bucketProps = new HashMap<>(); + bucketProps.put("_term", "val3"); + bucketProps.put("_count", 2L); + bucketProps.put("avg_l", 3d); + bucketProps.put("sum_d", 3d); + expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); + bucketProps = new HashMap<>(); + bucketProps.put("_term", "val4"); + bucketProps.put("_count", 2L); + bucketProps.put("avg_l", 3d); + bucketProps.put("sum_d", 4d); + expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); + bucketProps = new HashMap<>(); + bucketProps.put("_term", "val5"); + bucketProps.put("_count", 2L); + bucketProps.put("avg_l", 5d); + bucketProps.put("sum_d", 3d); + expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); + bucketProps = new HashMap<>(); + bucketProps.put("_term", "val6"); + bucketProps.put("_count", 1L); + bucketProps.put("avg_l", 5d); + bucketProps.put("sum_d", 1d); + expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); + bucketProps = new HashMap<>(); + bucketProps.put("_term", "val7"); + bucketProps.put("_count", 1L); + bucketProps.put("avg_l", 5d); + bucketProps.put("sum_d", 1d); + expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); + + assertAcked( + client().admin() + .indices() + .prepareCreate("sort_idx") + .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword") + .get() + ); + for (int i = 1; i <= 3; i++) { + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val1").field("l", 1).field("d", i).endObject()) + ); + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val2").field("l", 2).field("d", i).endObject()) + ); + } + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val3").field("l", 3).field("d", 1).endObject()) + ); + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val3").field("l", 3).field("d", 2).endObject()) + ); + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val4").field("l", 3).field("d", 1).endObject()) + ); + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val4").field("l", 3).field("d", 3).endObject()) + ); + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val5").field("l", 5).field("d", 1).endObject()) + ); + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val5").field("l", 5).field("d", 2).endObject()) + ); + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val6").field("l", 5).field("d", 1).endObject()) + ); + builders.add( + client().prepareIndex("sort_idx") + .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val7").field("l", 5).field("d", 1).endObject()) + ); + } + + protected String key(Terms.Bucket bucket) { + return bucket.getKeyAsString(); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/terms/StringTermsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/terms/StringTermsIT.java index 3190bcb72fcbb..8c727d280ec52 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/terms/StringTermsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/bucket/terms/StringTermsIT.java @@ -32,25 +32,19 @@ package org.opensearch.search.aggregations.bucket.terms; import org.opensearch.OpenSearchException; -import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; -import org.opensearch.index.fielddata.ScriptDocValues; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.IndexFieldMapper; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.aggregations.AggregationExecutionException; -import org.opensearch.search.aggregations.AggregationTestScriptsPlugin; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.opensearch.search.aggregations.BucketOrder; -import org.opensearch.search.aggregations.bucket.AbstractTermsTestCase; import org.opensearch.search.aggregations.bucket.filter.Filter; import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket; import org.opensearch.search.aggregations.metrics.Avg; @@ -60,23 +54,13 @@ import org.opensearch.search.aggregations.support.ValueType; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.test.OpenSearchIntegTestCase; -import org.junit.After; -import org.junit.Before; -import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.function.Function; -import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.termQuery; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.extendedStats; @@ -93,227 +77,10 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class StringTermsIT extends AbstractTermsTestCase { +public class StringTermsIT extends BaseStringTermsTestCase { - private static final String SINGLE_VALUED_FIELD_NAME = "s_value"; - private static final String MULTI_VALUED_FIELD_NAME = "s_values"; - private static Map> expectedMultiSortBuckets; - - @Override - protected Collection> nodePlugins() { - return Collections.singleton(CustomScriptPlugin.class); - } - - @Before - public void randomizeOptimizations() { - TermsAggregatorFactory.COLLECT_SEGMENT_ORDS = randomBoolean(); - TermsAggregatorFactory.REMAP_GLOBAL_ORDS = randomBoolean(); - } - - @After - public void resetOptimizations() { - TermsAggregatorFactory.COLLECT_SEGMENT_ORDS = null; - TermsAggregatorFactory.REMAP_GLOBAL_ORDS = null; - } - - public static class CustomScriptPlugin extends AggregationTestScriptsPlugin { - - @Override - protected Map, Object>> pluginScripts() { - Map, Object>> scripts = super.pluginScripts(); - - scripts.put("'foo_' + _value", vars -> "foo_" + (String) vars.get("_value")); - scripts.put("_value.substring(0,3)", vars -> ((String) vars.get("_value")).substring(0, 3)); - - scripts.put("doc['" + MULTI_VALUED_FIELD_NAME + "']", vars -> { - Map doc = (Map) vars.get("doc"); - return doc.get(MULTI_VALUED_FIELD_NAME); - }); - - scripts.put("doc['" + SINGLE_VALUED_FIELD_NAME + "'].value", vars -> { - Map doc = (Map) vars.get("doc"); - ScriptDocValues.Strings value = (ScriptDocValues.Strings) doc.get(SINGLE_VALUED_FIELD_NAME); - return value.getValue(); - }); - - scripts.put("42", vars -> 42); - - return scripts; - } - - @Override - protected Map, Object>> nonDeterministicPluginScripts() { - Map, Object>> scripts = new HashMap<>(); - - scripts.put("Math.random()", vars -> StringTermsIT.randomDouble()); - - return scripts; - } - } - - @Override - public void setupSuiteScopeCluster() throws Exception { - assertAcked( - client().admin() - .indices() - .prepareCreate("idx") - .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword") - .get() - ); - List builders = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - builders.add( - client().prepareIndex("idx") - .setSource( - jsonBuilder().startObject() - .field(SINGLE_VALUED_FIELD_NAME, "val" + i) - .field("i", i) - .field("constant", 1) - .field("tag", i < 5 / 2 + 1 ? "more" : "less") - .startArray(MULTI_VALUED_FIELD_NAME) - .value("val" + i) - .value("val" + (i + 1)) - .endArray() - .endObject() - ) - ); - } - - getMultiSortDocs(builders); - - assertAcked( - client().admin() - .indices() - .prepareCreate("high_card_idx") - .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword") - .get() - ); - for (int i = 0; i < 100; i++) { - builders.add( - client().prepareIndex("high_card_idx") - .setSource( - jsonBuilder().startObject() - .field(SINGLE_VALUED_FIELD_NAME, "val" + Strings.padStart(i + "", 3, '0')) - .startArray(MULTI_VALUED_FIELD_NAME) - .value("val" + Strings.padStart(i + "", 3, '0')) - .value("val" + Strings.padStart((i + 1) + "", 3, '0')) - .endArray() - .endObject() - ) - ); - } - prepareCreate("empty_bucket_idx").setMapping(SINGLE_VALUED_FIELD_NAME, "type=integer").get(); - - for (int i = 0; i < 2; i++) { - builders.add( - client().prepareIndex("empty_bucket_idx") - .setId("" + i) - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, i * 2).endObject()) - ); - } - indexRandom(true, builders); - createIndex("idx_unmapped"); - ensureSearchable(); - } - - private void getMultiSortDocs(List builders) throws IOException { - expectedMultiSortBuckets = new HashMap<>(); - Map bucketProps = new HashMap<>(); - bucketProps.put("_term", "val1"); - bucketProps.put("_count", 3L); - bucketProps.put("avg_l", 1d); - bucketProps.put("sum_d", 6d); - expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); - bucketProps = new HashMap<>(); - bucketProps.put("_term", "val2"); - bucketProps.put("_count", 3L); - bucketProps.put("avg_l", 2d); - bucketProps.put("sum_d", 6d); - expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); - bucketProps = new HashMap<>(); - bucketProps.put("_term", "val3"); - bucketProps.put("_count", 2L); - bucketProps.put("avg_l", 3d); - bucketProps.put("sum_d", 3d); - expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); - bucketProps = new HashMap<>(); - bucketProps.put("_term", "val4"); - bucketProps.put("_count", 2L); - bucketProps.put("avg_l", 3d); - bucketProps.put("sum_d", 4d); - expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); - bucketProps = new HashMap<>(); - bucketProps.put("_term", "val5"); - bucketProps.put("_count", 2L); - bucketProps.put("avg_l", 5d); - bucketProps.put("sum_d", 3d); - expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); - bucketProps = new HashMap<>(); - bucketProps.put("_term", "val6"); - bucketProps.put("_count", 1L); - bucketProps.put("avg_l", 5d); - bucketProps.put("sum_d", 1d); - expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); - bucketProps = new HashMap<>(); - bucketProps.put("_term", "val7"); - bucketProps.put("_count", 1L); - bucketProps.put("avg_l", 5d); - bucketProps.put("sum_d", 1d); - expectedMultiSortBuckets.put((String) bucketProps.get("_term"), bucketProps); - - assertAcked( - client().admin() - .indices() - .prepareCreate("sort_idx") - .setMapping(SINGLE_VALUED_FIELD_NAME, "type=keyword", MULTI_VALUED_FIELD_NAME, "type=keyword", "tag", "type=keyword") - .get() - ); - for (int i = 1; i <= 3; i++) { - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val1").field("l", 1).field("d", i).endObject()) - ); - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val2").field("l", 2).field("d", i).endObject()) - ); - } - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val3").field("l", 3).field("d", 1).endObject()) - ); - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val3").field("l", 3).field("d", 2).endObject()) - ); - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val4").field("l", 3).field("d", 1).endObject()) - ); - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val4").field("l", 3).field("d", 3).endObject()) - ); - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val5").field("l", 5).field("d", 1).endObject()) - ); - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val5").field("l", 5).field("d", 2).endObject()) - ); - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val6").field("l", 5).field("d", 1).endObject()) - ); - builders.add( - client().prepareIndex("sort_idx") - .setSource(jsonBuilder().startObject().field(SINGLE_VALUED_FIELD_NAME, "val7").field("l", 5).field("d", 1).endObject()) - ); - } - - private String key(Terms.Bucket bucket) { - return bucket.getKeyAsString(); + public StringTermsIT(Settings dynamicSettings) { + super(dynamicSettings); } // the main purpose of this test is to make sure we're not allocating 2GB of memory per shard @@ -1364,6 +1131,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } public void testScriptWithValueType() throws Exception { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/CardinalityIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/CardinalityIT.java index 147f451c14de8..9ebec21367164 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/CardinalityIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/CardinalityIT.java @@ -32,9 +32,12 @@ package org.opensearch.search.aggregations.metrics; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; @@ -45,7 +48,9 @@ import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -55,6 +60,7 @@ import static java.util.Collections.emptyMap; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.cardinality; import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -65,7 +71,24 @@ import static org.hamcrest.Matchers.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class CardinalityIT extends OpenSearchIntegTestCase { +public class CardinalityIT extends ParameterizedOpenSearchIntegTestCase { + + public CardinalityIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -615,5 +638,6 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/CardinalityWithRequestBreakerIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/CardinalityWithRequestBreakerIT.java index e8d425596beb0..2bf5230c67b43 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/CardinalityWithRequestBreakerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/CardinalityWithRequestBreakerIT.java @@ -32,22 +32,46 @@ package org.opensearch.search.aggregations.metrics; -import org.opensearch.OpenSearchException; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ExceptionsHelper; +import org.opensearch.OpenSearchException; import org.opensearch.action.index.IndexRequestBuilder; -import org.opensearch.common.breaker.CircuitBreakingException; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.breaker.CircuitBreakingException; import org.opensearch.indices.breaker.HierarchyCircuitBreakerService; import org.opensearch.search.aggregations.Aggregator; import org.opensearch.search.aggregations.BucketOrder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; import java.util.stream.IntStream; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.cardinality; import static org.opensearch.search.aggregations.AggregationBuilders.terms; -public class CardinalityWithRequestBreakerIT extends OpenSearchIntegTestCase { +public class CardinalityWithRequestBreakerIT extends ParameterizedOpenSearchIntegTestCase { + + public CardinalityWithRequestBreakerIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } /** * Test that searches using cardinality aggregations returns all request breaker memory. @@ -62,7 +86,7 @@ public void testRequestBreaker() throws Exception { .mapToObj( i -> client().prepareIndex("test") .setId("id_" + i) - .setSource(org.opensearch.common.collect.Map.of("field0", randomAlphaOfLength(5), "field1", randomAlphaOfLength(5))) + .setSource(Map.of("field0", randomAlphaOfLength(5), "field1", randomAlphaOfLength(5))) ) .toArray(IndexRequestBuilder[]::new) ); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ExtendedStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ExtendedStatsIT.java index cd0a649659c6e..3d804b9aa626e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ExtendedStatsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ExtendedStatsIT.java @@ -37,6 +37,7 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.aggregations.AggregationTestScriptsPlugin; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.filter.Filter; import org.opensearch.search.aggregations.bucket.global.Global; @@ -44,7 +45,6 @@ import org.opensearch.search.aggregations.bucket.missing.Missing; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.metrics.ExtendedStats.Bounds; -import org.opensearch.search.aggregations.BucketOrder; import java.util.Collection; import java.util.Collections; @@ -70,6 +70,10 @@ public class ExtendedStatsIT extends AbstractNumericTestCase { + public ExtendedStatsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(AggregationTestScriptsPlugin.class); @@ -995,6 +999,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoBoundsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoBoundsIT.java deleted file mode 100644 index 3af3b9e5212f8..0000000000000 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoBoundsIT.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.search.aggregations.metrics; - -import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.geo.GeoPoint; -import org.opensearch.common.util.BigArray; -import org.opensearch.search.aggregations.InternalAggregation; -import org.opensearch.search.aggregations.bucket.global.Global; -import org.opensearch.search.aggregations.bucket.terms.Terms; -import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket; -import org.opensearch.test.OpenSearchIntegTestCase; - -import java.util.List; - -import static org.opensearch.index.query.QueryBuilders.matchAllQuery; -import static org.opensearch.search.aggregations.AggregationBuilders.geoBounds; -import static org.opensearch.search.aggregations.AggregationBuilders.global; -import static org.opensearch.search.aggregations.AggregationBuilders.terms; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.lessThanOrEqualTo; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.sameInstance; - -@OpenSearchIntegTestCase.SuiteScopeTestCase -public class GeoBoundsIT extends AbstractGeoTestCase { - private static final String aggName = "geoBounds"; - - public void testSingleValuedField() throws Exception { - SearchResponse response = client().prepareSearch(IDX_NAME) - .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) - .get(); - - assertSearchResponse(response); - - GeoBounds geoBounds = response.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - GeoPoint topLeft = geoBounds.topLeft(); - GeoPoint bottomRight = geoBounds.bottomRight(); - assertThat(topLeft.lat(), closeTo(singleTopLeft.lat(), GEOHASH_TOLERANCE)); - assertThat(topLeft.lon(), closeTo(singleTopLeft.lon(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lat(), closeTo(singleBottomRight.lat(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lon(), closeTo(singleBottomRight.lon(), GEOHASH_TOLERANCE)); - } - - public void testSingleValuedField_getProperty() throws Exception { - SearchResponse searchResponse = client().prepareSearch(IDX_NAME) - .setQuery(matchAllQuery()) - .addAggregation(global("global").subAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false))) - .get(); - - assertSearchResponse(searchResponse); - - Global global = searchResponse.getAggregations().get("global"); - assertThat(global, notNullValue()); - assertThat(global.getName(), equalTo("global")); - assertThat(global.getDocCount(), equalTo((long) numDocs)); - assertThat(global.getAggregations(), notNullValue()); - assertThat(global.getAggregations().asMap().size(), equalTo(1)); - - GeoBounds geobounds = global.getAggregations().get(aggName); - assertThat(geobounds, notNullValue()); - assertThat(geobounds.getName(), equalTo(aggName)); - assertThat((GeoBounds) ((InternalAggregation) global).getProperty(aggName), sameInstance(geobounds)); - GeoPoint topLeft = geobounds.topLeft(); - GeoPoint bottomRight = geobounds.bottomRight(); - assertThat(topLeft.lat(), closeTo(singleTopLeft.lat(), GEOHASH_TOLERANCE)); - assertThat(topLeft.lon(), closeTo(singleTopLeft.lon(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lat(), closeTo(singleBottomRight.lat(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lon(), closeTo(singleBottomRight.lon(), GEOHASH_TOLERANCE)); - assertThat((double) ((InternalAggregation) global).getProperty(aggName + ".top"), closeTo(singleTopLeft.lat(), GEOHASH_TOLERANCE)); - assertThat((double) ((InternalAggregation) global).getProperty(aggName + ".left"), closeTo(singleTopLeft.lon(), GEOHASH_TOLERANCE)); - assertThat( - (double) ((InternalAggregation) global).getProperty(aggName + ".bottom"), - closeTo(singleBottomRight.lat(), GEOHASH_TOLERANCE) - ); - assertThat( - (double) ((InternalAggregation) global).getProperty(aggName + ".right"), - closeTo(singleBottomRight.lon(), GEOHASH_TOLERANCE) - ); - } - - public void testMultiValuedField() throws Exception { - SearchResponse response = client().prepareSearch(IDX_NAME) - .addAggregation(geoBounds(aggName).field(MULTI_VALUED_FIELD_NAME).wrapLongitude(false)) - .get(); - - assertSearchResponse(response); - - GeoBounds geoBounds = response.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - GeoPoint topLeft = geoBounds.topLeft(); - GeoPoint bottomRight = geoBounds.bottomRight(); - assertThat(topLeft.lat(), closeTo(multiTopLeft.lat(), GEOHASH_TOLERANCE)); - assertThat(topLeft.lon(), closeTo(multiTopLeft.lon(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lat(), closeTo(multiBottomRight.lat(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lon(), closeTo(multiBottomRight.lon(), GEOHASH_TOLERANCE)); - } - - public void testUnmapped() throws Exception { - SearchResponse response = client().prepareSearch(UNMAPPED_IDX_NAME) - .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) - .get(); - - assertSearchResponse(response); - - GeoBounds geoBounds = response.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - GeoPoint topLeft = geoBounds.topLeft(); - GeoPoint bottomRight = geoBounds.bottomRight(); - assertThat(topLeft, equalTo(null)); - assertThat(bottomRight, equalTo(null)); - } - - public void testPartiallyUnmapped() throws Exception { - SearchResponse response = client().prepareSearch(IDX_NAME, UNMAPPED_IDX_NAME) - .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) - .get(); - - assertSearchResponse(response); - - GeoBounds geoBounds = response.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - GeoPoint topLeft = geoBounds.topLeft(); - GeoPoint bottomRight = geoBounds.bottomRight(); - assertThat(topLeft.lat(), closeTo(singleTopLeft.lat(), GEOHASH_TOLERANCE)); - assertThat(topLeft.lon(), closeTo(singleTopLeft.lon(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lat(), closeTo(singleBottomRight.lat(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lon(), closeTo(singleBottomRight.lon(), GEOHASH_TOLERANCE)); - } - - public void testEmptyAggregation() throws Exception { - SearchResponse searchResponse = client().prepareSearch(EMPTY_IDX_NAME) - .setQuery(matchAllQuery()) - .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) - .get(); - - assertThat(searchResponse.getHits().getTotalHits().value, equalTo(0L)); - GeoBounds geoBounds = searchResponse.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - GeoPoint topLeft = geoBounds.topLeft(); - GeoPoint bottomRight = geoBounds.bottomRight(); - assertThat(topLeft, equalTo(null)); - assertThat(bottomRight, equalTo(null)); - } - - public void testSingleValuedFieldNearDateLine() throws Exception { - SearchResponse response = client().prepareSearch(DATELINE_IDX_NAME) - .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) - .get(); - - assertSearchResponse(response); - - GeoPoint geoValuesTopLeft = new GeoPoint(38, -179); - GeoPoint geoValuesBottomRight = new GeoPoint(-24, 178); - - GeoBounds geoBounds = response.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - GeoPoint topLeft = geoBounds.topLeft(); - GeoPoint bottomRight = geoBounds.bottomRight(); - assertThat(topLeft.lat(), closeTo(geoValuesTopLeft.lat(), GEOHASH_TOLERANCE)); - assertThat(topLeft.lon(), closeTo(geoValuesTopLeft.lon(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lat(), closeTo(geoValuesBottomRight.lat(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lon(), closeTo(geoValuesBottomRight.lon(), GEOHASH_TOLERANCE)); - } - - public void testSingleValuedFieldNearDateLineWrapLongitude() throws Exception { - - GeoPoint geoValuesTopLeft = new GeoPoint(38, 170); - GeoPoint geoValuesBottomRight = new GeoPoint(-24, -175); - SearchResponse response = client().prepareSearch(DATELINE_IDX_NAME) - .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(true)) - .get(); - - assertSearchResponse(response); - - GeoBounds geoBounds = response.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - GeoPoint topLeft = geoBounds.topLeft(); - GeoPoint bottomRight = geoBounds.bottomRight(); - assertThat(topLeft.lat(), closeTo(geoValuesTopLeft.lat(), GEOHASH_TOLERANCE)); - assertThat(topLeft.lon(), closeTo(geoValuesTopLeft.lon(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lat(), closeTo(geoValuesBottomRight.lat(), GEOHASH_TOLERANCE)); - assertThat(bottomRight.lon(), closeTo(geoValuesBottomRight.lon(), GEOHASH_TOLERANCE)); - } - - /** - * This test forces the {@link GeoBoundsAggregator} to resize the {@link BigArray}s it uses to ensure they are resized correctly - */ - public void testSingleValuedFieldAsSubAggToHighCardTermsAgg() { - SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) - .addAggregation( - terms("terms").field(NUMBER_FIELD_NAME) - .subAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) - ) - .get(); - - assertSearchResponse(response); - - Terms terms = response.getAggregations().get("terms"); - assertThat(terms, notNullValue()); - assertThat(terms.getName(), equalTo("terms")); - List buckets = terms.getBuckets(); - assertThat(buckets.size(), equalTo(10)); - for (int i = 0; i < 10; i++) { - Bucket bucket = buckets.get(i); - assertThat(bucket, notNullValue()); - assertThat("InternalBucket " + bucket.getKey() + " has wrong number of documents", bucket.getDocCount(), equalTo(1L)); - GeoBounds geoBounds = bucket.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - assertThat(geoBounds.topLeft().getLat(), allOf(greaterThanOrEqualTo(-90.0), lessThanOrEqualTo(90.0))); - assertThat(geoBounds.topLeft().getLon(), allOf(greaterThanOrEqualTo(-180.0), lessThanOrEqualTo(180.0))); - assertThat(geoBounds.bottomRight().getLat(), allOf(greaterThanOrEqualTo(-90.0), lessThanOrEqualTo(90.0))); - assertThat(geoBounds.bottomRight().getLon(), allOf(greaterThanOrEqualTo(-180.0), lessThanOrEqualTo(180.0))); - } - } - - public void testSingleValuedFieldWithZeroLon() throws Exception { - SearchResponse response = client().prepareSearch(IDX_ZERO_NAME) - .addAggregation(geoBounds(aggName).field(SINGLE_VALUED_FIELD_NAME).wrapLongitude(false)) - .get(); - - assertSearchResponse(response); - - GeoBounds geoBounds = response.getAggregations().get(aggName); - assertThat(geoBounds, notNullValue()); - assertThat(geoBounds.getName(), equalTo(aggName)); - GeoPoint topLeft = geoBounds.topLeft(); - GeoPoint bottomRight = geoBounds.bottomRight(); - assertThat(topLeft.lat(), closeTo(1.0, GEOHASH_TOLERANCE)); - assertThat(topLeft.lon(), closeTo(0.0, GEOHASH_TOLERANCE)); - assertThat(bottomRight.lat(), closeTo(1.0, GEOHASH_TOLERANCE)); - assertThat(bottomRight.lon(), closeTo(0.0, GEOHASH_TOLERANCE)); - } -} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoCentroidIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoCentroidIT.java index 7cd8b3ed39051..78100d1778ecf 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoCentroidIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/GeoCentroidIT.java @@ -34,16 +34,13 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.common.geo.GeoPoint; +import org.opensearch.common.settings.Settings; import org.opensearch.search.aggregations.InternalAggregation; -import org.opensearch.search.aggregations.bucket.geogrid.GeoGrid; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.test.OpenSearchIntegTestCase; -import java.util.List; - import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.search.aggregations.AggregationBuilders.geoCentroid; -import static org.opensearch.search.aggregations.AggregationBuilders.geohashGrid; import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; import static org.hamcrest.Matchers.closeTo; @@ -58,6 +55,10 @@ public class GeoCentroidIT extends AbstractGeoTestCase { private static final String aggName = "geoCentroid"; + public GeoCentroidIT(Settings dynamicSettings) { + super(dynamicSettings); + } + public void testEmptyAggregation() throws Exception { SearchResponse response = client().prepareSearch(EMPTY_IDX_NAME) .setQuery(matchAllQuery()) @@ -168,33 +169,4 @@ public void testMultiValuedField() throws Exception { assertThat(centroid.lon(), closeTo(multiCentroid.lon(), GEOHASH_TOLERANCE)); assertEquals(2 * numDocs, geoCentroid.count()); } - - public void testSingleValueFieldAsSubAggToGeohashGrid() throws Exception { - SearchResponse response = client().prepareSearch(HIGH_CARD_IDX_NAME) - .addAggregation( - geohashGrid("geoGrid").field(SINGLE_VALUED_FIELD_NAME).subAggregation(geoCentroid(aggName).field(SINGLE_VALUED_FIELD_NAME)) - ) - .get(); - assertSearchResponse(response); - - GeoGrid grid = response.getAggregations().get("geoGrid"); - assertThat(grid, notNullValue()); - assertThat(grid.getName(), equalTo("geoGrid")); - List buckets = grid.getBuckets(); - for (GeoGrid.Bucket cell : buckets) { - String geohash = cell.getKeyAsString(); - GeoPoint expectedCentroid = expectedCentroidsForGeoHash.get(geohash); - GeoCentroid centroidAgg = cell.getAggregations().get(aggName); - assertThat( - "Geohash " + geohash + " has wrong centroid latitude ", - expectedCentroid.lat(), - closeTo(centroidAgg.centroid().lat(), GEOHASH_TOLERANCE) - ); - assertThat( - "Geohash " + geohash + " has wrong centroid longitude", - expectedCentroid.lon(), - closeTo(centroidAgg.centroid().lon(), GEOHASH_TOLERANCE) - ); - } - } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/HDRPercentileRanksIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/HDRPercentileRanksIT.java index 20fc6aaee20c9..7ca5130388eea 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/HDRPercentileRanksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/HDRPercentileRanksIT.java @@ -38,12 +38,12 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.aggregations.AggregationTestScriptsPlugin; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.filter.Filter; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.terms.Terms; -import org.opensearch.search.aggregations.BucketOrder; import java.util.Arrays; import java.util.Collection; @@ -53,7 +53,7 @@ import java.util.Map; import static java.util.Collections.emptyMap; -import static org.opensearch.common.util.CollectionUtils.iterableAsArrayList; +import static org.opensearch.core.common.util.CollectionUtils.iterableAsArrayList; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; import static org.opensearch.search.aggregations.AggregationBuilders.filter; @@ -72,6 +72,10 @@ public class HDRPercentileRanksIT extends AbstractNumericTestCase { + public HDRPercentileRanksIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(AggregationTestScriptsPlugin.class); @@ -716,6 +720,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/HDRPercentilesIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/HDRPercentilesIT.java index 2660dbe0a88ed..ec913b3e130f5 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/HDRPercentilesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/HDRPercentilesIT.java @@ -34,7 +34,7 @@ import org.apache.logging.log4j.LogManager; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.util.CollectionUtils; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; @@ -75,6 +75,10 @@ public class HDRPercentilesIT extends AbstractNumericTestCase { + public HDRPercentilesIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(AggregationTestScriptsPlugin.class); @@ -687,6 +691,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java index 226b4dbca18d9..b8447d682abae 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/MedianAbsoluteDeviationIT.java @@ -68,8 +68,8 @@ import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.range; import static org.opensearch.search.aggregations.AggregationBuilders.terms; -import static org.opensearch.search.aggregations.metrics.MedianAbsoluteDeviationAggregatorTests.IsCloseToRelative.closeToRelative; import static org.opensearch.search.aggregations.metrics.MedianAbsoluteDeviationAggregatorTests.ExactMedianAbsoluteDeviation.calculateMAD; +import static org.opensearch.search.aggregations.metrics.MedianAbsoluteDeviationAggregatorTests.IsCloseToRelative.closeToRelative; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; @@ -91,6 +91,10 @@ public class MedianAbsoluteDeviationIT extends AbstractNumericTestCase { private static double singleValueExactMAD; private static double multiValueExactMAD; + public MedianAbsoluteDeviationIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override public void setupSuiteScopeCluster() throws Exception { final Settings settings = Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0).build(); @@ -643,5 +647,6 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ScriptedMetricIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ScriptedMetricIT.java index 27dbc56cf3b79..ced2358ac3f78 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ScriptedMetricIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ScriptedMetricIT.java @@ -32,14 +32,17 @@ package org.opensearch.search.aggregations.metrics; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; @@ -53,12 +56,14 @@ import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.Before; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -69,6 +74,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.scriptedMetric; @@ -87,10 +93,27 @@ @ClusterScope(scope = Scope.SUITE) @OpenSearchIntegTestCase.SuiteScopeTestCase -public class ScriptedMetricIT extends OpenSearchIntegTestCase { +public class ScriptedMetricIT extends ParameterizedOpenSearchIntegTestCase { private static long numDocs; + public ScriptedMetricIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -332,7 +355,7 @@ public void setupSuiteScopeCluster() throws Exception { new BytesArray( "{\"script\": {\"lang\": \"" + MockScriptPlugin.NAME + "\"," + " \"source\": \"vars.multiplier = 3\"} }" ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -345,7 +368,7 @@ public void setupSuiteScopeCluster() throws Exception { new BytesArray( "{\"script\": {\"lang\": \"" + MockScriptPlugin.NAME + "\"," + " \"source\": \"state.list.add(vars.multiplier)\"} }" ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -361,7 +384,7 @@ public void setupSuiteScopeCluster() throws Exception { + "\"," + " \"source\": \"sum state values as a new aggregation\"} }" ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -377,7 +400,7 @@ public void setupSuiteScopeCluster() throws Exception { + "\"," + " \"source\": \"sum all states (lists) values as a new aggregation\"} }" ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); @@ -435,7 +458,9 @@ public void testMap() { assertThat(scriptedMetricAggregation.aggregation(), notNullValue()); assertThat(scriptedMetricAggregation.aggregation(), instanceOf(ArrayList.class)); List aggregationList = (List) scriptedMetricAggregation.aggregation(); - assertThat(aggregationList.size(), equalTo(getNumShards("idx").numPrimaries)); + // with script based aggregation, if it does not support reduce then aggregationList size + // will be numShards * slicesCount + assertThat(aggregationList.size(), greaterThanOrEqualTo(getNumShards("idx").numPrimaries)); int numShardsRun = 0; for (Object object : aggregationList) { assertThat(object, notNullValue()); @@ -483,7 +508,9 @@ public void testMapWithParams() { assertThat(scriptedMetricAggregation.aggregation(), notNullValue()); assertThat(scriptedMetricAggregation.aggregation(), instanceOf(ArrayList.class)); List aggregationList = (List) scriptedMetricAggregation.aggregation(); - assertThat(aggregationList.size(), equalTo(getNumShards("idx").numPrimaries)); + // with script based aggregation, if it does not support reduce then aggregationList size + // will be numShards * slicesCount + assertThat(aggregationList.size(), greaterThanOrEqualTo(getNumShards("idx").numPrimaries)); int numShardsRun = 0; for (Object object : aggregationList) { assertThat(object, notNullValue()); @@ -535,7 +562,9 @@ public void testInitMutatesParams() { assertThat(scriptedMetricAggregation.aggregation(), notNullValue()); assertThat(scriptedMetricAggregation.aggregation(), instanceOf(ArrayList.class)); List aggregationList = (List) scriptedMetricAggregation.aggregation(); - assertThat(aggregationList.size(), equalTo(getNumShards("idx").numPrimaries)); + // with script based aggregation, if it does not support reduce then aggregationList size + // will be numShards * slicesCount + assertThat(aggregationList.size(), greaterThanOrEqualTo(getNumShards("idx").numPrimaries)); long totalCount = 0; for (Object object : aggregationList) { assertThat(object, notNullValue()); @@ -588,7 +617,9 @@ public void testMapCombineWithParams() { assertThat(scriptedMetricAggregation.aggregation(), notNullValue()); assertThat(scriptedMetricAggregation.aggregation(), instanceOf(ArrayList.class)); List aggregationList = (List) scriptedMetricAggregation.aggregation(); - assertThat(aggregationList.size(), equalTo(getNumShards("idx").numPrimaries)); + // with script based aggregation, if it does not support reduce then aggregationList size + // will be numShards * slicesCount + assertThat(aggregationList.size(), greaterThanOrEqualTo(getNumShards("idx").numPrimaries)); long totalCount = 0; for (Object object : aggregationList) { assertThat(object, notNullValue()); @@ -651,7 +682,9 @@ public void testInitMapCombineWithParams() { assertThat(scriptedMetricAggregation.aggregation(), notNullValue()); assertThat(scriptedMetricAggregation.aggregation(), instanceOf(ArrayList.class)); List aggregationList = (List) scriptedMetricAggregation.aggregation(); - assertThat(aggregationList.size(), equalTo(getNumShards("idx").numPrimaries)); + // with script based aggregation, if it does not support reduce then aggregationList size + // will be numShards * slicesCount + assertThat(aggregationList.size(), greaterThanOrEqualTo(getNumShards("idx").numPrimaries)); long totalCount = 0; for (Object object : aggregationList) { assertThat(object, notNullValue()); @@ -1368,6 +1401,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } public void testConflictingAggAndScriptParams() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/StatsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/StatsIT.java index debdde8e13fe7..f957a74eeb9d0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/StatsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/StatsIT.java @@ -39,12 +39,12 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.aggregations.AggregationTestScriptsPlugin; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.filter.Filter; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.terms.Terms; -import org.opensearch.search.aggregations.BucketOrder; import java.util.Collection; import java.util.Collections; @@ -66,6 +66,10 @@ import static org.hamcrest.Matchers.sameInstance; public class StatsIT extends AbstractNumericTestCase { + public StatsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(AggregationTestScriptsPlugin.class); @@ -384,5 +388,6 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/SumIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/SumIT.java index fe236f04c19e8..382d656448114 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/SumIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/SumIT.java @@ -68,6 +68,10 @@ public class SumIT extends AbstractNumericTestCase { + public SumIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(MetricAggScriptPlugin.class); @@ -359,6 +363,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } public void testFieldAlias() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TDigestPercentileRanksIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TDigestPercentileRanksIT.java index adf027222d7d9..941d3a888db29 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TDigestPercentileRanksIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TDigestPercentileRanksIT.java @@ -34,17 +34,17 @@ import org.apache.logging.log4j.LogManager; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.util.CollectionUtils; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.aggregations.AggregationTestScriptsPlugin; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.bucket.filter.Filter; import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.terms.Terms; -import org.opensearch.search.aggregations.BucketOrder; import java.util.Arrays; import java.util.Collection; @@ -72,6 +72,10 @@ public class TDigestPercentileRanksIT extends AbstractNumericTestCase { + public TDigestPercentileRanksIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(AggregationTestScriptsPlugin.class); @@ -626,6 +630,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TDigestPercentilesIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TDigestPercentilesIT.java index fda15f9b90ea2..6457cf9307fa1 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TDigestPercentilesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TDigestPercentilesIT.java @@ -34,7 +34,7 @@ import org.apache.logging.log4j.LogManager; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.util.CollectionUtils; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; @@ -74,6 +74,10 @@ public class TDigestPercentilesIT extends AbstractNumericTestCase { + public TDigestPercentilesIT(Settings dynamicSettings) { + super(dynamicSettings); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(AggregationTestScriptsPlugin.class); @@ -597,5 +601,6 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TopHitsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TopHitsIT.java index c3240c5eef7c5..10e51079cf389 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TopHitsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/TopHitsIT.java @@ -31,6 +31,8 @@ package org.opensearch.search.aggregations.metrics; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.Explanation; import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.util.ArrayUtil; @@ -40,7 +42,8 @@ import org.opensearch.action.search.SearchType; import org.opensearch.common.document.DocumentField; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -67,8 +70,10 @@ import org.opensearch.search.sort.SortBuilders; import org.opensearch.search.sort.SortOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; @@ -83,6 +88,7 @@ import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.matchQuery; import static org.opensearch.index.query.QueryBuilders.nestedQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.max; @@ -105,11 +111,28 @@ import static org.hamcrest.Matchers.sameInstance; @OpenSearchIntegTestCase.SuiteScopeTestCase() -public class TopHitsIT extends OpenSearchIntegTestCase { +public class TopHitsIT extends ParameterizedOpenSearchIntegTestCase { private static final String TERMS_AGGS_FIELD = "terms"; private static final String SORT_FIELD = "sort"; + public TopHitsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -1386,7 +1409,7 @@ public void testWithRescore() { SearchResponse response = client().prepareSearch("idx") .addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) .addAggregation( - terms("terms").field(TERMS_AGGS_FIELD).subAggregation(topHits("hits").sort(SortBuilders.fieldSort("_type"))) + terms("terms").field(TERMS_AGGS_FIELD).subAggregation(topHits("hits").sort(SortBuilders.fieldSort("_index"))) ) .get(); Terms terms = response.getAggregations().get("terms"); @@ -1403,7 +1426,7 @@ public void testWithRescore() { .addRescorer(new QueryRescorerBuilder(new MatchAllQueryBuilder().boost(3.0f))) .addAggregation( terms("terms").field(TERMS_AGGS_FIELD) - .subAggregation(topHits("hits").sort(SortBuilders.scoreSort()).sort(SortBuilders.fieldSort("_type"))) + .subAggregation(topHits("hits").sort(SortBuilders.scoreSort()).sort(SortBuilders.fieldSort("_index"))) ) .get(); Terms terms = response.getAggregations().get("terms"); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ValueCountIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ValueCountIT.java index 82e667bccc576..833d1ce3bb4c3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ValueCountIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/metrics/ValueCountIT.java @@ -31,8 +31,11 @@ package org.opensearch.search.aggregations.metrics; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.plugins.Plugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; @@ -42,7 +45,9 @@ import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -51,6 +56,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.count; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.global; @@ -67,7 +73,25 @@ import static org.hamcrest.Matchers.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class ValueCountIT extends OpenSearchIntegTestCase { +public class ValueCountIT extends ParameterizedOpenSearchIntegTestCase { + + public ValueCountIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { createIndex("idx"); @@ -363,6 +387,7 @@ public void testScriptCaching() throws Exception { .getMissCount(), equalTo(2L) ); + internalCluster().wipeIndices("cache_test_idx"); } public void testOrderByEmptyAggregation() throws Exception { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/AvgBucketIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/AvgBucketIT.java index 6cd16a47e98d2..bec9203384026 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/AvgBucketIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/AvgBucketIT.java @@ -32,8 +32,12 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; @@ -42,11 +46,15 @@ import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -58,7 +66,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class AvgBucketIT extends OpenSearchIntegTestCase { +public class AvgBucketIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; @@ -69,6 +77,23 @@ public class AvgBucketIT extends OpenSearchIntegTestCase { static int numValueBuckets; static long[] valueCounts; + public AvgBucketIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("tag", "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketScriptIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketScriptIT.java index 5de4e5162247d..4c3129eb89e3b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketScriptIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketScriptIT.java @@ -32,12 +32,16 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; @@ -47,9 +51,11 @@ import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -58,6 +64,7 @@ import java.util.function.Function; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.dateRange; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; @@ -69,7 +76,7 @@ import static org.hamcrest.Matchers.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class BucketScriptIT extends OpenSearchIntegTestCase { +public class BucketScriptIT extends ParameterizedOpenSearchIntegTestCase { private static final String FIELD_1_NAME = "field1"; private static final String FIELD_2_NAME = "field2"; @@ -83,6 +90,23 @@ public class BucketScriptIT extends OpenSearchIntegTestCase { private static int maxNumber; private static long date; + public BucketScriptIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -551,7 +575,7 @@ public void testStoredScript() { // Script source is not interpreted but it references a pre-defined script from CustomScriptPlugin .setContent( new BytesArray("{ \"script\": {\"lang\": \"" + CustomScriptPlugin.NAME + "\"," + " \"source\": \"my_script\" } }"), - XContentType.JSON + MediaTypeRegistry.JSON ) ); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketSelectorIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketSelectorIT.java index 7674679378758..a7b28add7373a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketSelectorIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketSelectorIT.java @@ -32,11 +32,15 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; @@ -46,9 +50,11 @@ import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -57,6 +63,7 @@ import java.util.function.Function; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.PipelineAggregatorBuilders.bucketSelector; @@ -70,7 +77,7 @@ import static org.hamcrest.Matchers.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class BucketSelectorIT extends OpenSearchIntegTestCase { +public class BucketSelectorIT extends ParameterizedOpenSearchIntegTestCase { private static final String FIELD_1_NAME = "field1"; private static final String FIELD_2_NAME = "field2"; @@ -82,6 +89,23 @@ public class BucketSelectorIT extends OpenSearchIntegTestCase { private static int minNumber; private static int maxNumber; + public BucketSelectorIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); @@ -488,7 +512,7 @@ public void testStoredScript() { + "\", " + "\"source\": \"Double.isNaN(_value0) ? false : (_value0 + _value1 > 100)\" } }" ), - XContentType.JSON + MediaTypeRegistry.JSON ) ); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketSortIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketSortIT.java index d05740a5a0f36..2e4fd7a412118 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketSortIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/BucketSortIT.java @@ -32,11 +32,15 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.terms.Terms; @@ -44,15 +48,18 @@ import org.opensearch.search.sort.FieldSortBuilder; import org.opensearch.search.sort.SortOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; @@ -68,7 +75,7 @@ import static org.hamcrest.Matchers.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class BucketSortIT extends OpenSearchIntegTestCase { +public class BucketSortIT extends ParameterizedOpenSearchIntegTestCase { private static final String INDEX = "bucket-sort-it-data-index"; private static final String INDEX_WITH_GAPS = "bucket-sort-it-data-index-with-gaps"; @@ -78,6 +85,23 @@ public class BucketSortIT extends OpenSearchIntegTestCase { private static final String VALUE_1_FIELD = "value_1"; private static final String VALUE_2_FIELD = "value_2"; + public BucketSortIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { createIndex(INDEX, INDEX_WITH_GAPS); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/DateDerivativeIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/DateDerivativeIT.java index 2c7890fb7b1cb..b05ff7b4329cd 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/DateDerivativeIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/DateDerivativeIT.java @@ -32,10 +32,14 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; import org.opensearch.common.time.DateFormatters; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.InternalMultiBucketAggregation; import org.opensearch.search.aggregations.bucket.histogram.DateHistogramInterval; @@ -44,6 +48,7 @@ import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.support.AggregationPath; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matcher; import org.junit.After; @@ -55,9 +60,11 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.dateHistogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.PipelineAggregatorBuilders.derivative; @@ -69,13 +76,30 @@ import static org.hamcrest.core.IsNull.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class DateDerivativeIT extends OpenSearchIntegTestCase { +public class DateDerivativeIT extends ParameterizedOpenSearchIntegTestCase { // some index names used during these tests private static final String IDX_DST_START = "idx_dst_start"; private static final String IDX_DST_END = "idx_dst_end"; private static final String IDX_DST_KATHMANDU = "idx_dst_kathmandu"; + public DateDerivativeIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + private ZonedDateTime date(int month, int day) { return ZonedDateTime.of(2012, month, day, 0, 0, 0, 0, ZoneOffset.UTC); } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/DerivativeIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/DerivativeIT.java index c03ed8277a3b4..18484c8a60ed7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/DerivativeIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/DerivativeIT.java @@ -32,11 +32,15 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ExceptionsHelper; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.aggregations.InternalAggregation; import org.opensearch.search.aggregations.InternalMultiBucketAggregation; @@ -47,19 +51,23 @@ import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.opensearch.search.aggregations.support.AggregationPath; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; +import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.filters; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.stats; import static org.opensearch.search.aggregations.AggregationBuilders.sum; -import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.PipelineAggregatorBuilders.derivative; import static org.opensearch.search.aggregations.PipelineAggregatorBuilders.movingAvg; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; @@ -70,7 +78,7 @@ import static org.hamcrest.core.IsNull.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class DerivativeIT extends OpenSearchIntegTestCase { +public class DerivativeIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; @@ -92,6 +100,23 @@ public class DerivativeIT extends OpenSearchIntegTestCase { private static Double[] firstDerivValueCounts_empty_rnd; private static long numDocsEmptyIdx_rnd; + public DerivativeIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { createIndex("idx"); @@ -680,6 +705,7 @@ public void testAvgMovavgDerivNPE() throws Exception { .get(); assertSearchResponse(response); + internalCluster().wipeIndices("movavg_npe"); } private void checkBucketKeyAndDocCount( diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java index 85fe794b05fc6..299827e2413d4 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/ExtendedStatsBucketIT.java @@ -32,10 +32,14 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ExceptionsHelper; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; @@ -45,11 +49,15 @@ import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -61,7 +69,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class ExtendedStatsBucketIT extends OpenSearchIntegTestCase { +public class ExtendedStatsBucketIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; @@ -72,6 +80,23 @@ public class ExtendedStatsBucketIT extends OpenSearchIntegTestCase { static int numValueBuckets; static long[] valueCounts; + public ExtendedStatsBucketIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("tag", "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MaxBucketIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MaxBucketIT.java index 22890620d6b15..bc6828c7d0c31 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MaxBucketIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MaxBucketIT.java @@ -32,13 +32,17 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.WriteRequest; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.aggregations.AggregationBuilders; import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.PipelineAggregatorBuilders; @@ -54,12 +58,16 @@ import org.opensearch.search.aggregations.metrics.SumAggregationBuilder; import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.filter; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; @@ -72,7 +80,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class MaxBucketIT extends OpenSearchIntegTestCase { +public class MaxBucketIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; @@ -83,6 +91,23 @@ public class MaxBucketIT extends OpenSearchIntegTestCase { static int numValueBuckets; static long[] valueCounts; + public MaxBucketIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("tag", "type=keyword").get()); @@ -585,7 +610,8 @@ public void testFieldIsntWrittenOutTwice() throws Exception { groupByLicenseAgg.subAggregation(peakPipelineAggBuilder); SearchResponse response = client().prepareSearch("foo_*").setSize(0).addAggregation(groupByLicenseAgg).get(); - BytesReference bytes = XContentHelper.toXContent(response, XContentType.JSON, false); - XContentHelper.convertToMap(bytes, false, XContentType.JSON); + BytesReference bytes = org.opensearch.core.xcontent.XContentHelper.toXContent(response, MediaTypeRegistry.JSON, false); + XContentHelper.convertToMap(bytes, false, MediaTypeRegistry.JSON); + internalCluster().wipeIndices("foo_*"); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MinBucketIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MinBucketIT.java index b3929943f0d02..3c8d3e2064014 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MinBucketIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MinBucketIT.java @@ -32,21 +32,29 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; -import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.bucket.terms.IncludeExclude; +import org.opensearch.search.aggregations.bucket.terms.Terms; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; -import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -58,7 +66,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class MinBucketIT extends OpenSearchIntegTestCase { +public class MinBucketIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; @@ -69,6 +77,23 @@ public class MinBucketIT extends OpenSearchIntegTestCase { static int numValueBuckets; static long[] valueCounts; + public MinBucketIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("tag", "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MovAvgIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MovAvgIT.java index 91390edc7e872..0cf89778c6e99 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MovAvgIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/MovAvgIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.bulk.BulkRequestBuilder; import org.opensearch.action.index.IndexRequestBuilder; @@ -40,13 +42,15 @@ import org.opensearch.action.support.WriteRequest; import org.opensearch.client.Client; import org.opensearch.common.collect.EvictingQueue; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.opensearch.search.aggregations.metrics.Avg; import org.opensearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.opensearch.test.OpenSearchIntegTestCase; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; @@ -58,6 +62,7 @@ import java.util.Map; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.max; @@ -72,7 +77,7 @@ import static org.hamcrest.core.IsNull.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class MovAvgIT extends OpenSearchIntegTestCase { +public class MovAvgIT extends ParameterizedOpenSearchIntegTestCase { private static final String INTERVAL_FIELD = "l_value"; private static final String VALUE_FIELD = "v_value"; private static final String VALUE_FIELD2 = "v_value2"; @@ -128,6 +133,23 @@ public String toString() { } } + public MovAvgIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { prepareCreate("idx").setMapping( @@ -1342,6 +1364,7 @@ public void testPredictWithNonEmptyBuckets() throws Exception { assertThat(movAvgAgg, nullValue()); } } + internalCluster().wipeIndices("predict_non_empty"); } private void assertValidIterators(Iterator expectedBucketIter, Iterator expectedCountsIter, Iterator expectedValuesIter) { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/PercentilesBucketIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/PercentilesBucketIT.java index 1da079781dc63..580497715ed6d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/PercentilesBucketIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/PercentilesBucketIT.java @@ -32,10 +32,14 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ExceptionsHelper; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.terms.IncludeExclude; @@ -43,14 +47,17 @@ import org.opensearch.search.aggregations.metrics.Percentile; import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -62,7 +69,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class PercentilesBucketIT extends OpenSearchIntegTestCase { +public class PercentilesBucketIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; private static final double[] PERCENTS = { 0.0, 1.0, 25.0, 50.0, 75.0, 99.0, 100.0 }; @@ -73,6 +80,23 @@ public class PercentilesBucketIT extends OpenSearchIntegTestCase { static int numValueBuckets; static long[] valueCounts; + public PercentilesBucketIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("tag", "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/SerialDiffIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/SerialDiffIT.java index f5a5d025946ec..b4da63802bc50 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/SerialDiffIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/SerialDiffIT.java @@ -32,22 +32,30 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.collect.EvictingQueue; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; import org.opensearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.max; @@ -61,7 +69,7 @@ import static org.hamcrest.core.IsNull.nullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class SerialDiffIT extends OpenSearchIntegTestCase { +public class SerialDiffIT extends ParameterizedOpenSearchIntegTestCase { private static final String INTERVAL_FIELD = "l_value"; private static final String VALUE_FIELD = "v_value"; @@ -90,6 +98,23 @@ public String toString() { } } + public SerialDiffIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + private ValuesSourceAggregationBuilder> randomMetric(String name, String field) { int rand = randomIntBetween(0, 3); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/StatsBucketIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/StatsBucketIT.java index e9f34f6aa65d9..21fdd5e761e77 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/StatsBucketIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/StatsBucketIT.java @@ -32,8 +32,12 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; @@ -42,11 +46,15 @@ import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -58,10 +66,9 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class StatsBucketIT extends OpenSearchIntegTestCase { +public class StatsBucketIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; - static int numDocs; static int interval; static int minRandomValue; @@ -69,6 +76,23 @@ public class StatsBucketIT extends OpenSearchIntegTestCase { static int numValueBuckets; static long[] valueCounts; + public StatsBucketIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("tag", "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/SumBucketIT.java b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/SumBucketIT.java index 5bd962017c247..d4bd8f21b2a99 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/SumBucketIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/aggregations/pipeline/SumBucketIT.java @@ -32,8 +32,12 @@ package org.opensearch.search.aggregations.pipeline; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.BucketOrder; import org.opensearch.search.aggregations.bucket.histogram.Histogram; import org.opensearch.search.aggregations.bucket.histogram.Histogram.Bucket; @@ -42,11 +46,15 @@ import org.opensearch.search.aggregations.metrics.Sum; import org.opensearch.search.aggregations.pipeline.BucketHelpers.GapPolicy; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.sum; import static org.opensearch.search.aggregations.AggregationBuilders.terms; @@ -58,7 +66,7 @@ import static org.hamcrest.core.IsNull.notNullValue; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class SumBucketIT extends OpenSearchIntegTestCase { +public class SumBucketIT extends ParameterizedOpenSearchIntegTestCase { private static final String SINGLE_VALUED_FIELD_NAME = "l_value"; @@ -69,6 +77,23 @@ public class SumBucketIT extends OpenSearchIntegTestCase { static int numValueBuckets; static long[] valueCounts; + public SumBucketIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override public void setupSuiteScopeCluster() throws Exception { assertAcked(client().admin().indices().prepareCreate("idx").setMapping("tag", "type=keyword").get()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/backpressure/SearchBackpressureIT.java b/server/src/internalClusterTest/java/org/opensearch/search/backpressure/SearchBackpressureIT.java new file mode 100644 index 0000000000000..28ada82a1c56b --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/search/backpressure/SearchBackpressureIT.java @@ -0,0 +1,477 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.backpressure; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.ActionType; +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.opensearch.action.search.SearchShardTask; +import org.opensearch.action.search.SearchTask; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskCancelledException; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.plugins.ActionPlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.search.backpressure.settings.NodeDuressSettings; +import org.opensearch.search.backpressure.settings.SearchBackpressureSettings; +import org.opensearch.search.backpressure.settings.SearchShardTaskSettings; +import org.opensearch.search.backpressure.settings.SearchTaskSettings; +import org.opensearch.tasks.CancellableTask; +import org.opensearch.tasks.Task; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; +import org.hamcrest.MatcherAssert; +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) +public class SearchBackpressureIT extends ParameterizedOpenSearchIntegTestCase { + + private static final TimeValue TIMEOUT = new TimeValue(10, TimeUnit.SECONDS); + private static final int MOVING_AVERAGE_WINDOW_SIZE = 10; + + public SearchBackpressureIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + + @Override + protected Collection> nodePlugins() { + final List> plugins = new ArrayList<>(super.nodePlugins()); + plugins.add(TestPlugin.class); + return plugins; + } + + @Before + public final void setupNodeSettings() { + Settings request = Settings.builder() + .put(NodeDuressSettings.SETTING_CPU_THRESHOLD.getKey(), 0.0) + .put(NodeDuressSettings.SETTING_HEAP_THRESHOLD.getKey(), 0.0) + .put(NodeDuressSettings.SETTING_NUM_SUCCESSIVE_BREACHES.getKey(), 1) + .put(SearchTaskSettings.SETTING_TOTAL_HEAP_PERCENT_THRESHOLD.getKey(), 0.0) + .put(SearchShardTaskSettings.SETTING_TOTAL_HEAP_PERCENT_THRESHOLD.getKey(), 0.0) + .build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get()); + } + + @After + public final void cleanupNodeSettings() { + assertAcked( + client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().putNull("*")) + .setTransientSettings(Settings.builder().putNull("*")) + ); + } + + public void testCancellationSettingsChanged() { + Settings request = Settings.builder().put(SearchTaskSettings.SETTING_CANCELLATION_RATE.getKey(), "0.05").build(); + ClusterUpdateSettingsResponse response = client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get(); + assertEquals(response.getPersistentSettings().get(SearchTaskSettings.SETTING_CANCELLATION_RATE.getKey()), "0.05"); + + request = Settings.builder().put(SearchShardTaskSettings.SETTING_CANCELLATION_RATIO.getKey(), "0.7").build(); + response = client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get(); + assertEquals(response.getPersistentSettings().get(SearchShardTaskSettings.SETTING_CANCELLATION_RATIO.getKey()), "0.7"); + } + + public void testSearchTaskCancellationWithHighElapsedTime() throws InterruptedException { + Settings request = Settings.builder() + .put(SearchBackpressureSettings.SETTING_MODE.getKey(), "enforced") + .put(SearchTaskSettings.SETTING_ELAPSED_TIME_MILLIS_THRESHOLD.getKey(), 1000) + .build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get()); + + ExceptionCatchingListener listener = new ExceptionCatchingListener(); + client().execute( + TestTransportAction.ACTION, + new TestRequest<>( + RequestType.HIGH_ELAPSED_TIME, + (TaskFactory) (id, type, action, description, parentTaskId, headers) -> new SearchTask( + id, + type, + action, + descriptionSupplier(description), + parentTaskId, + headers + ) + ), + listener + ); + assertTrue(listener.latch.await(TIMEOUT.getSeconds(), TimeUnit.SECONDS)); + + Exception caughtException = listener.getException(); + assertNotNull("SearchTask should have been cancelled with TaskCancelledException", caughtException); + MatcherAssert.assertThat(caughtException, instanceOf(TaskCancelledException.class)); + MatcherAssert.assertThat(caughtException.getMessage(), containsString("elapsed time exceeded")); + } + + public void testSearchShardTaskCancellationWithHighElapsedTime() throws InterruptedException { + Settings request = Settings.builder() + .put(SearchBackpressureSettings.SETTING_MODE.getKey(), "enforced") + .put(SearchShardTaskSettings.SETTING_ELAPSED_TIME_MILLIS_THRESHOLD.getKey(), 1000) + .build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get()); + + ExceptionCatchingListener listener = new ExceptionCatchingListener(); + client().execute(TestTransportAction.ACTION, new TestRequest<>(RequestType.HIGH_ELAPSED_TIME, SearchShardTask::new), listener); + assertTrue(listener.latch.await(TIMEOUT.getSeconds(), TimeUnit.SECONDS)); + + Exception caughtException = listener.getException(); + assertNotNull("SearchShardTask should have been cancelled with TaskCancelledException", caughtException); + MatcherAssert.assertThat(caughtException, instanceOf(TaskCancelledException.class)); + MatcherAssert.assertThat(caughtException.getMessage(), containsString("elapsed time exceeded")); + } + + public void testSearchTaskCancellationWithHighCpu() throws InterruptedException { + Settings request = Settings.builder() + .put(SearchBackpressureSettings.SETTING_MODE.getKey(), "enforced") + .put(SearchTaskSettings.SETTING_CPU_TIME_MILLIS_THRESHOLD.getKey(), 1000) + .build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get()); + + ExceptionCatchingListener listener = new ExceptionCatchingListener(); + client().execute( + TestTransportAction.ACTION, + new TestRequest<>( + RequestType.HIGH_CPU, + (TaskFactory) (id, type, action, description, parentTaskId, headers) -> new SearchTask( + id, + type, + action, + descriptionSupplier(description), + parentTaskId, + headers + ) + ), + listener + ); + assertTrue(listener.latch.await(TIMEOUT.getSeconds(), TimeUnit.SECONDS)); + + Exception caughtException = listener.getException(); + assertNotNull("SearchTask should have been cancelled with TaskCancelledException", caughtException); + MatcherAssert.assertThat(caughtException, instanceOf(TaskCancelledException.class)); + MatcherAssert.assertThat(caughtException.getMessage(), containsString("cpu usage exceeded")); + } + + public void testSearchShardTaskCancellationWithHighCpu() throws InterruptedException { + Settings request = Settings.builder() + .put(SearchBackpressureSettings.SETTING_MODE.getKey(), "enforced") + .put(SearchShardTaskSettings.SETTING_CPU_TIME_MILLIS_THRESHOLD.getKey(), 1000) + .build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get()); + + ExceptionCatchingListener listener = new ExceptionCatchingListener(); + client().execute(TestTransportAction.ACTION, new TestRequest<>(RequestType.HIGH_CPU, SearchShardTask::new), listener); + assertTrue(listener.latch.await(TIMEOUT.getSeconds(), TimeUnit.SECONDS)); + + Exception caughtException = listener.getException(); + assertNotNull("SearchShardTask should have been cancelled with TaskCancelledException", caughtException); + MatcherAssert.assertThat(caughtException, instanceOf(TaskCancelledException.class)); + MatcherAssert.assertThat(caughtException.getMessage(), containsString("cpu usage exceeded")); + } + + public void testSearchTaskCancellationWithHighHeapUsage() throws InterruptedException { + // Before SearchBackpressureService cancels a task based on its heap usage, we need to build up the heap moving average + // To build up the heap moving average, we need to hit the same node with multiple requests and then hit the same node with a + // request having higher heap usage + String node = randomFrom(internalCluster().getNodeNames()); + Settings request = Settings.builder() + .put(SearchBackpressureSettings.SETTING_MODE.getKey(), "enforced") + .put(SearchTaskSettings.SETTING_HEAP_PERCENT_THRESHOLD.getKey(), 0.0) + .put(SearchTaskSettings.SETTING_HEAP_VARIANCE_THRESHOLD.getKey(), 1.0) + .put(SearchTaskSettings.SETTING_HEAP_MOVING_AVERAGE_WINDOW_SIZE.getKey(), MOVING_AVERAGE_WINDOW_SIZE) + .build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get()); + + ExceptionCatchingListener listener = new ExceptionCatchingListener(); + for (int i = 0; i < MOVING_AVERAGE_WINDOW_SIZE; i++) { + client(node).execute( + TestTransportAction.ACTION, + new TestRequest<>( + RequestType.HIGH_HEAP, + (TaskFactory) (id, type, action, description, parentTaskId, headers) -> new SearchTask( + id, + type, + action, + descriptionSupplier(description), + parentTaskId, + headers + ) + ), + listener + ); + } + + listener = new ExceptionCatchingListener(); + client(node).execute( + TestTransportAction.ACTION, + new TestRequest<>( + RequestType.HIGHER_HEAP, + (TaskFactory) (id, type, action, description, parentTaskId, headers) -> new SearchTask( + id, + type, + action, + descriptionSupplier(description), + parentTaskId, + headers + ) + ), + listener + ); + assertTrue(listener.latch.await(TIMEOUT.getSeconds(), TimeUnit.SECONDS)); + + Exception caughtException = listener.getException(); + assertNotNull("SearchTask should have been cancelled with TaskCancelledException", caughtException); + MatcherAssert.assertThat(caughtException, instanceOf(TaskCancelledException.class)); + MatcherAssert.assertThat(caughtException.getMessage(), containsString("heap usage exceeded")); + } + + public void testSearchShardTaskCancellationWithHighHeapUsage() throws InterruptedException { + // Before SearchBackpressureService cancels a task based on its heap usage, we need to build up the heap moving average + // To build up the heap moving average, we need to hit the same node with multiple requests and then hit the same node with a + // request having higher heap usage + String node = randomFrom(internalCluster().getNodeNames()); + Settings request = Settings.builder() + .put(SearchBackpressureSettings.SETTING_MODE.getKey(), "enforced") + .put(SearchShardTaskSettings.SETTING_HEAP_PERCENT_THRESHOLD.getKey(), 0.0) + .put(SearchShardTaskSettings.SETTING_HEAP_VARIANCE_THRESHOLD.getKey(), 1.0) + .put(SearchShardTaskSettings.SETTING_HEAP_MOVING_AVERAGE_WINDOW_SIZE.getKey(), MOVING_AVERAGE_WINDOW_SIZE) + .build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get()); + + ExceptionCatchingListener listener = new ExceptionCatchingListener(); + for (int i = 0; i < MOVING_AVERAGE_WINDOW_SIZE; i++) { + client(node).execute(TestTransportAction.ACTION, new TestRequest<>(RequestType.HIGH_HEAP, SearchShardTask::new), listener); + } + + listener = new ExceptionCatchingListener(); + client(node).execute(TestTransportAction.ACTION, new TestRequest<>(RequestType.HIGHER_HEAP, SearchShardTask::new), listener); + assertTrue(listener.latch.await(TIMEOUT.getSeconds(), TimeUnit.SECONDS)); + + Exception caughtException = listener.getException(); + assertNotNull("SearchShardTask should have been cancelled with TaskCancelledException", caughtException); + MatcherAssert.assertThat(caughtException, instanceOf(TaskCancelledException.class)); + MatcherAssert.assertThat(caughtException.getMessage(), containsString("heap usage exceeded")); + } + + public void testSearchCancellationWithBackpressureDisabled() throws InterruptedException { + Settings request = Settings.builder().put(SearchBackpressureSettings.SETTING_MODE.getKey(), "monitor_only").build(); + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(request).get()); + + ExceptionCatchingListener listener = new ExceptionCatchingListener(); + client().execute(TestTransportAction.ACTION, new TestRequest<>(RequestType.HIGH_ELAPSED_TIME, SearchShardTask::new), listener); + // waiting for the TIMEOUT * 3 time for the request to complete and the latch to countdown. + assertTrue( + "SearchShardTask should have been completed by now and countdown the latch", + listener.latch.await(TIMEOUT.getSeconds() * 3, TimeUnit.SECONDS) + ); + + Exception caughtException = listener.getException(); + assertNull("SearchShardTask shouldn't have cancelled for monitor_only mode", caughtException); + } + + private static class ExceptionCatchingListener implements ActionListener { + private final CountDownLatch latch; + private Exception exception = null; + + public ExceptionCatchingListener() { + this.latch = new CountDownLatch(1); + } + + @Override + public void onResponse(TestResponse r) { + latch.countDown(); + } + + @Override + public void onFailure(Exception e) { + this.exception = e; + latch.countDown(); + } + + private Exception getException() { + return exception; + } + } + + enum RequestType { + HIGH_CPU, + HIGH_HEAP, + HIGHER_HEAP, + HIGH_ELAPSED_TIME; + } + + private Supplier descriptionSupplier(String description) { + return () -> description; + } + + interface TaskFactory { + T createTask(long id, String type, String action, String description, TaskId parentTaskId, Map headers); + } + + public static class TestRequest extends ActionRequest { + private final RequestType type; + private TaskFactory taskFactory; + + public TestRequest(RequestType type, TaskFactory taskFactory) { + this.type = type; + this.taskFactory = taskFactory; + } + + public TestRequest(StreamInput in) throws IOException { + super(in); + this.type = in.readEnum(RequestType.class); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public Task createTask(long id, String type, String action, TaskId parentTaskId, Map headers) { + return taskFactory.createTask(id, type, action, "", parentTaskId, headers); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeEnum(type); + } + + public RequestType getType() { + return this.type; + } + } + + public static class TestResponse extends ActionResponse { + public TestResponse() {} + + public TestResponse(StreamInput in) {} + + @Override + public void writeTo(StreamOutput out) throws IOException {} + } + + public static class TestTransportAction extends HandledTransportAction { + public static final ActionType ACTION = new ActionType<>("internal::test_action", TestResponse::new); + private final ThreadPool threadPool; + + @Inject + public TestTransportAction(TransportService transportService, ThreadPool threadPool, ActionFilters actionFilters) { + super(ACTION.name(), transportService, actionFilters, TestRequest::new); + this.threadPool = threadPool; + } + + @Override + protected void doExecute(Task task, TestRequest request, ActionListener listener) { + threadPool.executor(ThreadPool.Names.SEARCH).execute(() -> { + try { + CancellableTask cancellableTask = (CancellableTask) task; + long startTime = System.nanoTime(); + + // Doing a busy-wait until task cancellation or timeout. + // We are running HIGH_HEAP requests to build up heap moving average and not expect it to get cancelled. + do { + doWork(request); + } while (request.type != RequestType.HIGH_HEAP + && cancellableTask.isCancelled() == false + && (System.nanoTime() - startTime) < TIMEOUT.getNanos()); + + if (cancellableTask.isCancelled()) { + throw new TaskCancelledException(cancellableTask.getReasonCancelled()); + } else { + listener.onResponse(new TestResponse()); + } + } catch (Exception e) { + listener.onFailure(e); + } + }); + } + + private void doWork(TestRequest request) throws InterruptedException { + switch (request.getType()) { + case HIGH_CPU: + long i = 0, j = 1, k = 1, iterations = 1000; + do { + j += i; + k *= j; + i++; + } while (i < iterations); + break; + case HIGH_HEAP: + Byte[] bytes = new Byte[100000]; + int[] ints = new int[1000]; + break; + case HIGHER_HEAP: + Byte[] more_bytes = new Byte[1000000]; + int[] more_ints = new int[10000]; + break; + case HIGH_ELAPSED_TIME: + Thread.sleep(100); + break; + } + } + } + + public static class TestPlugin extends Plugin implements ActionPlugin { + @Override + public List> getActions() { + return Collections.singletonList(new ActionHandler<>(TestTransportAction.ACTION, TestTransportAction.class)); + } + + @Override + public List> getClientActions() { + return Collections.singletonList(TestTransportAction.ACTION); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchRedStateIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchRedStateIndexIT.java index 3c2aa6642633e..44c4981dfdb36 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchRedStateIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchRedStateIndexIT.java @@ -32,8 +32,9 @@ package org.opensearch.search.basic; -import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.ClusterState; @@ -41,14 +42,18 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.ShardRoutingState; import org.opensearch.common.settings.Settings; -import org.opensearch.rest.RestStatus; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.rest.RestStatus; import org.opensearch.search.SearchService; import org.opensearch.test.OpenSearchIntegTestCase; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.After; +import java.util.Arrays; +import java.util.Collection; import java.util.List; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -56,7 +61,24 @@ import static org.hamcrest.Matchers.lessThan; @OpenSearchIntegTestCase.ClusterScope(minNumDataNodes = 2) -public class SearchRedStateIndexIT extends OpenSearchIntegTestCase { +public class SearchRedStateIndexIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchRedStateIndexIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } public void testAllowPartialsWithRedState() throws Exception { final int numShards = cluster().numDataNodes() + 2; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWhileCreatingIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWhileCreatingIndexIT.java index 1d8512e101f78..71af7215c4eb7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWhileCreatingIndexIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWhileCreatingIndexIT.java @@ -32,13 +32,21 @@ package org.opensearch.search.basic; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.indices.refresh.RefreshResponse; import org.opensearch.action.search.SearchResponse; import org.opensearch.client.Client; import org.opensearch.cluster.health.ClusterHealthStatus; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -46,7 +54,25 @@ * This test basically verifies that search with a single shard active (cause we indexed to it) and other * shards possibly not active at all (cause they haven't allocated) will still work. */ -public class SearchWhileCreatingIndexIT extends OpenSearchIntegTestCase { +public class SearchWhileCreatingIndexIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchWhileCreatingIndexIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testIndexCausesIndexCreation() throws Exception { searchWhileCreatingIndex(false, 1); // 1 replica in our default... } @@ -88,8 +114,13 @@ private void searchWhileCreatingIndex(boolean createIndex, int numberOfReplicas) ClusterHealthStatus status = client().admin().cluster().prepareHealth("test").get().getStatus(); while (status != ClusterHealthStatus.GREEN) { // first, verify that search normal search works - SearchResponse searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.termQuery("field", "test")).get(); + SearchResponse searchResponse = client().prepareSearch("test") + .setPreference("_primary") + .setQuery(QueryBuilders.termQuery("field", "test")) + .execute() + .actionGet(); assertHitCount(searchResponse, 1); + Client client = client(); searchResponse = client.prepareSearch("test") .setPreference(preference + Integer.toString(counter++)) diff --git a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWhileRelocatingIT.java b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWhileRelocatingIT.java index c184d876dcb33..4f0dda9adfa52 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWhileRelocatingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWhileRelocatingIT.java @@ -32,28 +32,52 @@ package org.opensearch.search.basic; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.Priority; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.SearchHits; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoTimeout; import static org.opensearch.test.hamcrest.OpenSearchAssertions.formatShardStatus; import static org.hamcrest.Matchers.equalTo; @OpenSearchIntegTestCase.ClusterScope(minNumDataNodes = 2) -public class SearchWhileRelocatingIT extends OpenSearchIntegTestCase { +public class SearchWhileRelocatingIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchWhileRelocatingIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } public void testSearchAndRelocateConcurrentlyRandomReplicas() throws Exception { testSearchAndRelocateConcurrently(randomIntBetween(0, 1)); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWithRandomExceptionsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWithRandomExceptionsIT.java index 0b55ea9119d89..aa82b9d21c7fb 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWithRandomExceptionsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWithRandomExceptionsIT.java @@ -32,29 +32,31 @@ package org.opensearch.search.basic; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.FilterDirectoryReader; import org.apache.lucene.index.LeafReader; import org.apache.lucene.tests.util.English; - import org.opensearch.OpenSearchException; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.admin.indices.refresh.RefreshResponse; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.Settings.Builder; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.index.MockEngineFactoryPlugin; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.Plugin; import org.opensearch.search.sort.SortOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.engine.MockEngineSupport; import org.opensearch.test.engine.ThrowingLeafReaderWrapper; @@ -66,9 +68,27 @@ import java.util.Random; import java.util.concurrent.ExecutionException; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; -public class SearchWithRandomExceptionsIT extends OpenSearchIntegTestCase { +public class SearchWithRandomExceptionsIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchWithRandomExceptionsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -81,16 +101,15 @@ protected boolean addMockInternalEngine() { } public void testRandomExceptions() throws IOException, InterruptedException, ExecutionException { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("test") - .field("type", "keyword") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("test") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + .toString(); final double lowLevelRate; final double topLevelRate; if (frequently()) { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWithRandomIOExceptionsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWithRandomIOExceptionsIT.java index b0adc00f37fee..446a0bce58d66 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWithRandomIOExceptionsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/basic/SearchWithRandomIOExceptionsIT.java @@ -32,8 +32,9 @@ package org.opensearch.search.basic; -import org.apache.lucene.tests.util.English; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.apache.lucene.tests.util.English; import org.opensearch.OpenSearchException; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -42,14 +43,15 @@ import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.client.Requests; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.Plugin; import org.opensearch.search.sort.SortOrder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.store.MockFSDirectoryFactory; import org.opensearch.test.store.MockFSIndexStore; @@ -58,10 +60,28 @@ import java.util.Collection; import java.util.concurrent.ExecutionException; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; -public class SearchWithRandomIOExceptionsIT extends OpenSearchIntegTestCase { +public class SearchWithRandomIOExceptionsIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchWithRandomIOExceptionsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -69,16 +89,15 @@ protected Collection> nodePlugins() { } public void testRandomDirectoryIOExceptions() throws IOException, InterruptedException, ExecutionException { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("test") - .field("type", "keyword") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("test") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + .toString(); final double exceptionRate; final double exceptionOnOpenRate; if (frequently()) { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/basic/TransportSearchFailuresIT.java b/server/src/internalClusterTest/java/org/opensearch/search/basic/TransportSearchFailuresIT.java index 34b202cc09cf7..cbe52abf5279b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/basic/TransportSearchFailuresIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/basic/TransportSearchFailuresIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.basic; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; import org.opensearch.action.admin.indices.refresh.RefreshResponse; @@ -41,23 +43,46 @@ import org.opensearch.client.Requests; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Priority; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.search.builder.SearchSourceBuilder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.client.Requests.clusterHealthRequest; import static org.opensearch.client.Requests.refreshRequest; import static org.opensearch.client.Requests.searchRequest; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; -public class TransportSearchFailuresIT extends OpenSearchIntegTestCase { +public class TransportSearchFailuresIT extends ParameterizedOpenSearchIntegTestCase { + + public TransportSearchFailuresIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected int maximumNumberOfReplicas() { return 1; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/basic/TransportTwoNodesSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/basic/TransportTwoNodesSearchIT.java index 5cd6e76e1e487..ce5f3f63faa66 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/basic/TransportTwoNodesSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/basic/TransportTwoNodesSearchIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.basic; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.search.MultiSearchResponse; import org.opensearch.action.search.SearchPhaseExecutionException; @@ -39,7 +41,8 @@ import org.opensearch.client.Requests; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.functionscore.ScriptScoreFunctionBuilder; @@ -51,9 +54,11 @@ import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Set; import java.util.TreeSet; @@ -66,6 +71,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.builder.SearchSourceBuilder.searchSource; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; @@ -74,7 +80,24 @@ import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; -public class TransportTwoNodesSearchIT extends OpenSearchIntegTestCase { +public class TransportTwoNodesSearchIT extends ParameterizedOpenSearchIntegTestCase { + + public TransportTwoNodesSearchIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected int numberOfReplicas() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/fetch/FetchSubPhasePluginIT.java b/server/src/internalClusterTest/java/org/opensearch/search/fetch/FetchSubPhasePluginIT.java index def247676ac1a..86df25c4dad65 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/fetch/FetchSubPhasePluginIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/fetch/FetchSubPhasePluginIT.java @@ -32,28 +32,33 @@ package org.opensearch.search.fetch; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.util.BytesRef; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.ParsingException; import org.opensearch.common.document.DocumentField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.SearchPlugin; import org.opensearch.search.SearchExtBuilder; import org.opensearch.search.builder.SearchSourceBuilder; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -64,11 +69,30 @@ import static java.util.Collections.singletonList; import static org.opensearch.client.Requests.indexRequest; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; import static org.hamcrest.CoreMatchers.equalTo; @ClusterScope(scope = Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 2) -public class FetchSubPhasePluginIT extends OpenSearchIntegTestCase { +public class FetchSubPhasePluginIT extends ParameterizedOpenSearchIntegTestCase { + + public FetchSubPhasePluginIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Collections.singletonList(FetchTermVectorsPlugin.class); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/InnerHitsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/InnerHitsIT.java index 837e1e7e23ddf..9b3e1337418cc 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/InnerHitsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/InnerHitsIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.fetch.subphase; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.util.ArrayUtil; import org.opensearch.action.index.IndexRequestBuilder; @@ -39,7 +41,8 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.InnerHitBuilder; @@ -54,8 +57,8 @@ import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder; import org.opensearch.search.sort.FieldSortBuilder; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; import java.util.Arrays; @@ -72,6 +75,7 @@ import static org.opensearch.index.query.QueryBuilders.matchQuery; import static org.opensearch.index.query.QueryBuilders.nestedQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAllSuccessful; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -84,7 +88,24 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class InnerHitsIT extends OpenSearchIntegTestCase { +public class InnerHitsIT extends ParameterizedOpenSearchIntegTestCase { + + public InnerHitsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/MatchedQueriesIT.java b/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/MatchedQueriesIT.java index 488c253535827..23b5d0cab0697 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/MatchedQueriesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/MatchedQueriesIT.java @@ -32,16 +32,23 @@ package org.opensearch.search.fetch.subphase; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentHelper; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.TermQueryBuilder; import org.opensearch.search.SearchHit; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.index.query.QueryBuilders.boolQuery; import static org.opensearch.index.query.QueryBuilders.constantScoreQuery; @@ -52,11 +59,30 @@ import static org.opensearch.index.query.QueryBuilders.termQuery; import static org.opensearch.index.query.QueryBuilders.termsQuery; import static org.opensearch.index.query.QueryBuilders.wrapperQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItemInArray; -public class MatchedQueriesIT extends OpenSearchIntegTestCase { +public class MatchedQueriesIT extends ParameterizedOpenSearchIntegTestCase { + + public MatchedQueriesIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testSimpleMatchedQueryFromFilteredQuery() throws Exception { createIndex("test"); ensureGreen(); @@ -328,6 +354,7 @@ public void testMatchedWithShould() throws Exception { .should(queryStringQuery("dolor").queryName("dolor")) .should(queryStringQuery("elit").queryName("elit")) ) + .setPreference("_primary") .get(); assertHitCount(searchResponse, 2L); @@ -353,9 +380,9 @@ public void testMatchedWithWrapperQuery() throws Exception { refresh(); MatchQueryBuilder matchQueryBuilder = matchQuery("content", "amet").queryName("abc"); - BytesReference matchBytes = XContentHelper.toXContent(matchQueryBuilder, XContentType.JSON, false); + BytesReference matchBytes = XContentHelper.toXContent(matchQueryBuilder, MediaTypeRegistry.JSON, false); TermQueryBuilder termQueryBuilder = termQuery("content", "amet").queryName("abc"); - BytesReference termBytes = XContentHelper.toXContent(termQueryBuilder, XContentType.JSON, false); + BytesReference termBytes = XContentHelper.toXContent(termQueryBuilder, MediaTypeRegistry.JSON, false); QueryBuilder[] queries = new QueryBuilder[] { wrapperQuery(matchBytes), constantScoreQuery(wrapperQuery(termBytes)) }; for (QueryBuilder query : queries) { SearchResponse searchResponse = client().prepareSearch().setQuery(query).get(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/highlight/CustomHighlighterSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/highlight/CustomHighlighterSearchIT.java index 7df5b9b88a69c..fe17c3e22d43c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/highlight/CustomHighlighterSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/highlight/CustomHighlighterSearchIT.java @@ -31,12 +31,16 @@ package org.opensearch.search.fetch.subphase.highlight; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.Before; import java.io.IOException; @@ -45,6 +49,7 @@ import java.util.HashMap; import java.util.Map; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHighlight; import static org.hamcrest.Matchers.equalTo; @@ -52,7 +57,24 @@ * Integration test for highlighters registered by a plugin. */ @ClusterScope(scope = Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 1) -public class CustomHighlighterSearchIT extends OpenSearchIntegTestCase { +public class CustomHighlighterSearchIT extends ParameterizedOpenSearchIntegTestCase { + + public CustomHighlighterSearchIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/highlight/HighlighterSearchIT.java index 193a48cf0daa6..4cdf5ae8e674f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/highlight/HighlighterSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/fetch/subphase/highlight/HighlighterSearchIT.java @@ -31,26 +31,28 @@ package org.opensearch.search.fetch.subphase.highlight; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.carrotsearch.randomizedtesting.generators.RandomPicks; + import org.apache.lucene.analysis.Analyzer; -import org.apache.lucene.tests.analysis.MockAnalyzer; -import org.apache.lucene.tests.analysis.MockTokenizer; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.search.join.ScoreMode; - +import org.apache.lucene.tests.analysis.MockAnalyzer; +import org.apache.lucene.tests.analysis.MockTokenizer; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.WriteRequest; -import org.opensearch.common.Strings; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.Settings.Builder; import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.analysis.AbstractIndexAnalyzerProvider; import org.opensearch.index.analysis.AnalyzerProvider; import org.opensearch.index.analysis.PreConfiguredTokenFilter; @@ -65,17 +67,15 @@ import org.opensearch.indices.analysis.AnalysisModule; import org.opensearch.plugins.AnalysisPlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.search.SearchHit; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder.BoundaryScannerType; import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder.Field; import org.opensearch.search.sort.SortBuilders; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.MockKeywordPlugin; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matcher; import org.hamcrest.Matchers; @@ -109,6 +109,7 @@ import static org.opensearch.index.query.QueryBuilders.regexpQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; import static org.opensearch.index.query.QueryBuilders.wildcardQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.builder.SearchSourceBuilder.highlight; import static org.opensearch.search.builder.SearchSourceBuilder.searchSource; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; @@ -127,10 +128,28 @@ import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; -public class HighlighterSearchIT extends OpenSearchIntegTestCase { +public class HighlighterSearchIT extends ParameterizedOpenSearchIntegTestCase { + // TODO as we move analyzers out of the core we need to move some of these into HighlighterWithAnalyzersTests private static final String[] ALL_TYPES = new String[] { "plain", "fvh", "unified" }; + public HighlighterSearchIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Arrays.asList(InternalSettingsPlugin.class, MockKeywordPlugin.class, MockAnalysisPlugin.class); @@ -3231,26 +3250,25 @@ public void testCopyToFields() throws Exception { } public void testACopyFieldWithNestedQuery() throws Exception { - String mapping = Strings.toString( - jsonBuilder().startObject() - .startObject("properties") - .startObject("foo") - .field("type", "nested") - .startObject("properties") - .startObject("text") - .field("type", "text") - .field("copy_to", "foo_text") - .endObject() - .endObject() - .endObject() - .startObject("foo_text") - .field("type", "text") - .field("term_vector", "with_positions_offsets") - .field("store", true) - .endObject() - .endObject() - .endObject() - ); + String mapping = jsonBuilder().startObject() + .startObject("properties") + .startObject("foo") + .field("type", "nested") + .startObject("properties") + .startObject("text") + .field("type", "text") + .field("copy_to", "foo_text") + .endObject() + .endObject() + .endObject() + .startObject("foo_text") + .field("type", "text") + .field("term_vector", "with_positions_offsets") + .field("store", true) + .endObject() + .endObject() + .endObject() + .toString(); prepareCreate("test").setMapping(mapping).get(); client().prepareIndex("test") @@ -3361,25 +3379,24 @@ public void testHighlightQueryRewriteDatesWithNow() throws Exception { } public void testWithNestedQuery() throws Exception { - String mapping = Strings.toString( - jsonBuilder().startObject() - .startObject("properties") - .startObject("text") - .field("type", "text") - .field("index_options", "offsets") - .field("term_vector", "with_positions_offsets") - .endObject() - .startObject("foo") - .field("type", "nested") - .startObject("properties") - .startObject("text") - .field("type", "text") - .endObject() - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = jsonBuilder().startObject() + .startObject("properties") + .startObject("text") + .field("type", "text") + .field("index_options", "offsets") + .field("term_vector", "with_positions_offsets") + .endObject() + .startObject("foo") + .field("type", "nested") + .startObject("properties") + .startObject("text") + .field("type", "text") + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); prepareCreate("test").setMapping(mapping).get(); client().prepareIndex("test") diff --git a/server/src/internalClusterTest/java/org/opensearch/search/fieldcaps/FieldCapabilitiesIT.java b/server/src/internalClusterTest/java/org/opensearch/search/fieldcaps/FieldCapabilitiesIT.java index dacf388e2faa0..f5d1b8234558e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/fieldcaps/FieldCapabilitiesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/fieldcaps/FieldCapabilitiesIT.java @@ -32,15 +32,19 @@ package org.opensearch.search.fieldcaps; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.fieldcaps.FieldCapabilities; import org.opensearch.action.fieldcaps.FieldCapabilitiesResponse; import org.opensearch.action.index.IndexRequestBuilder; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.MapperPlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.Before; import java.util.ArrayList; @@ -52,9 +56,27 @@ import java.util.function.Function; import java.util.function.Predicate; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; -public class FieldCapabilitiesIT extends OpenSearchIntegTestCase { +public class FieldCapabilitiesIT extends ParameterizedOpenSearchIntegTestCase { + + public FieldCapabilitiesIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Before public void setUp() throws Exception { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/fields/SearchFieldsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/fields/SearchFieldsIT.java index 25782f8dc18db..799bbf91a567d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/fields/SearchFieldsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/fields/SearchFieldsIT.java @@ -32,26 +32,29 @@ package org.opensearch.search.fields; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.common.Numbers; import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.document.DocumentField; import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; import org.opensearch.common.time.DateUtils; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; @@ -59,13 +62,13 @@ import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.lookup.FieldLookup; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; +import java.math.BigInteger; import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -88,6 +91,7 @@ import static org.opensearch.common.util.set.Sets.newHashSet; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFailures; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -100,7 +104,24 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class SearchFieldsIT extends OpenSearchIntegTestCase { +public class SearchFieldsIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchFieldsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -113,6 +134,20 @@ public static class CustomScriptPlugin extends MockScriptPlugin { protected Map, Object>> pluginScripts() { Map, Object>> scripts = new HashMap<>(); + scripts.put("doc['unsigned_num1'].value", vars -> { + Map doc = (Map) vars.get("doc"); + ScriptDocValues.UnsignedLongs num1 = (ScriptDocValues.UnsignedLongs) doc.get("unsigned_num1"); + return num1.getValue(); + }); + + scripts.put("doc['unsigned_num1'].value * factor", vars -> { + Map doc = (Map) vars.get("doc"); + ScriptDocValues.UnsignedLongs num1 = (ScriptDocValues.UnsignedLongs) doc.get("unsigned_num1"); + Map params = (Map) vars.get("params"); + Double factor = (Double) params.get("factor"); + return num1.getValue().multiply(Numbers.toBigIntegerExact(factor)); + }); + scripts.put("doc['num1'].value", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Doubles num1 = (ScriptDocValues.Doubles) doc.get("num1"); @@ -140,9 +175,9 @@ protected Map, Object>> pluginScripts() { }); scripts.put("_fields['num1'].value", vars -> fieldsScript(vars, "num1")); + scripts.put("_fields['unsigned_num1'].value", vars -> fieldsScript(vars, "unsigned_num1")); scripts.put("_fields._uid.value", vars -> fieldsScript(vars, "_uid")); scripts.put("_fields._id.value", vars -> fieldsScript(vars, "_id")); - scripts.put("_fields._type.value", vars -> fieldsScript(vars, "_type")); scripts.put("_source.obj1", vars -> sourceScript(vars, "obj1")); scripts.put("_source.obj1.test", vars -> sourceScript(vars, "obj1.test")); @@ -185,29 +220,28 @@ static Object docScript(Map vars, String fieldName) { public void testStoredFields() throws Exception { createIndex("test"); - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject(MapperService.SINGLE_MAPPING_NAME) - .startObject("properties") - .startObject("field1") - .field("type", "text") - .field("store", true) - .endObject() - .startObject("field2") - .field("type", "text") - .field("store", false) - .endObject() - .startObject("field3") - .field("type", "text") - .field("store", true) - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject(MapperService.SINGLE_MAPPING_NAME) + .startObject("properties") + .startObject("field1") + .field("type", "text") + .field("store", true) + .endObject() + .startObject("field2") + .field("type", "text") + .field("store", false) + .endObject() + .startObject("field3") + .field("type", "text") + .field("store", true) + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); - client().admin().indices().preparePutMapping().setSource(mapping, XContentType.JSON).get(); + client().admin().indices().preparePutMapping().setSource(mapping, MediaTypeRegistry.JSON).get(); client().prepareIndex("test") .setId("1") @@ -288,21 +322,20 @@ public void testStoredFields() throws Exception { public void testScriptDocAndFields() throws Exception { createIndex("test"); - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject(MapperService.SINGLE_MAPPING_NAME) - .startObject("properties") - .startObject("num1") - .field("type", "double") - .field("store", true) - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject(MapperService.SINGLE_MAPPING_NAME) + .startObject("properties") + .startObject("num1") + .field("type", "double") + .field("store", true) + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); - client().admin().indices().preparePutMapping().setSource(mapping, XContentType.JSON).get(); + client().admin().indices().preparePutMapping().setSource(mapping, MediaTypeRegistry.JSON).get(); client().prepareIndex("test") .setId("1") @@ -387,23 +420,124 @@ public void testScriptDocAndFields() throws Exception { assertThat(response.getHits().getAt(2).getFields().get("sNum1").getValues().get(0), equalTo(6.0)); } - public void testScriptFieldWithNanos() throws Exception { + public void testScriptWithUnsignedLong() throws Exception { createIndex("test"); - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject(MapperService.SINGLE_MAPPING_NAME) - .startObject("properties") - .startObject("date") - .field("type", "date_nanos") - .endObject() - .endObject() - .endObject() - .endObject() + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject(MapperService.SINGLE_MAPPING_NAME) + .startObject("properties") + .startObject("unsigned_num1") + .field("type", "unsigned_long") + .field("store", true) + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); + + client().admin().indices().preparePutMapping().setSource(mapping, MediaTypeRegistry.JSON).get(); + + client().prepareIndex("test") + .setId("1") + .setSource(jsonBuilder().startObject().field("test", "value beck").field("unsigned_num1", BigInteger.valueOf(1)).endObject()) + .get(); + client().admin().indices().prepareFlush().get(); + client().prepareIndex("test") + .setId("2") + .setSource(jsonBuilder().startObject().field("test", "value beck").field("unsigned_num1", BigInteger.valueOf(2)).endObject()) + .get(); + client().admin().indices().prepareFlush().get(); + client().prepareIndex("test") + .setId("3") + .setSource( + jsonBuilder().startObject() + .field("test", "value beck") + .field("unsigned_num1", new BigInteger("9322337203685477580")) + .endObject() + ) + .get(); + client().admin().indices().refresh(refreshRequest()).actionGet(); + + SearchResponse response = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort("unsigned_num1", SortOrder.ASC) + .addScriptField( + "sNum1", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['unsigned_num1'].value", Collections.emptyMap()) + ) + .addScriptField( + "sNum1_field", + new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_fields['unsigned_num1'].value", Collections.emptyMap()) + ) + .get(); + + assertNoFailures(response); + + logger.info("running doc['unsigned_num1'].value"); + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertFalse(response.getHits().getAt(0).hasSource()); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + Set fields = new HashSet<>(response.getHits().getAt(0).getFields().keySet()); + assertThat(fields, equalTo(newHashSet("sNum1", "sNum1_field"))); + assertThat(response.getHits().getAt(0).getFields().get("sNum1").getValues().get(0), equalTo(BigInteger.valueOf(1))); + assertThat(response.getHits().getAt(0).getFields().get("sNum1_field").getValues().get(0), equalTo(BigInteger.valueOf(1))); + assertThat(response.getHits().getAt(1).getId(), equalTo("2")); + fields = new HashSet<>(response.getHits().getAt(0).getFields().keySet()); + assertThat(fields, equalTo(newHashSet("sNum1", "sNum1_field"))); + assertThat(response.getHits().getAt(1).getFields().get("sNum1").getValues().get(0), equalTo(BigInteger.valueOf(2))); + assertThat(response.getHits().getAt(1).getFields().get("sNum1_field").getValues().get(0), equalTo(BigInteger.valueOf(2))); + assertThat(response.getHits().getAt(2).getId(), equalTo("3")); + fields = new HashSet<>(response.getHits().getAt(0).getFields().keySet()); + assertThat(fields, equalTo(newHashSet("sNum1", "sNum1_field"))); + assertThat(response.getHits().getAt(2).getFields().get("sNum1").getValues().get(0), equalTo(new BigInteger("9322337203685477580"))); + assertThat( + response.getHits().getAt(2).getFields().get("sNum1_field").getValues().get(0), + equalTo(new BigInteger("9322337203685477580")) + ); + + logger.info("running doc['unsigned_num1'].value * factor"); + Map params = MapBuilder.newMapBuilder().put("factor", 2.0).map(); + response = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort("unsigned_num1", SortOrder.ASC) + .addScriptField("sNum1", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "doc['unsigned_num1'].value * factor", params)) + .get(); + + assertThat(response.getHits().getTotalHits().value, equalTo(3L)); + assertThat(response.getHits().getAt(0).getId(), equalTo("1")); + fields = new HashSet<>(response.getHits().getAt(0).getFields().keySet()); + assertThat(fields, equalTo(singleton("sNum1"))); + assertThat(response.getHits().getAt(0).getFields().get("sNum1").getValues().get(0), equalTo(BigInteger.valueOf(2))); + assertThat(response.getHits().getAt(1).getId(), equalTo("2")); + fields = new HashSet<>(response.getHits().getAt(0).getFields().keySet()); + assertThat(fields, equalTo(singleton("sNum1"))); + assertThat(response.getHits().getAt(1).getFields().get("sNum1").getValues().get(0), equalTo(BigInteger.valueOf(4))); + assertThat(response.getHits().getAt(2).getId(), equalTo("3")); + fields = new HashSet<>(response.getHits().getAt(0).getFields().keySet()); + assertThat(fields, equalTo(singleton("sNum1"))); + assertThat( + response.getHits().getAt(2).getFields().get("sNum1").getValues().get(0), + equalTo(new BigInteger("18644674407370955160")) ); + } + + public void testScriptFieldWithNanos() throws Exception { + createIndex("test"); - client().admin().indices().preparePutMapping().setSource(mapping, XContentType.JSON).get(); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject(MapperService.SINGLE_MAPPING_NAME) + .startObject("properties") + .startObject("date") + .field("type", "date_nanos") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); + + client().admin().indices().preparePutMapping().setSource(mapping, MediaTypeRegistry.JSON).get(); String date = "2019-01-31T10:00:00.123456789Z"; indexRandom( true, @@ -471,42 +605,6 @@ public void testIdBasedScriptFields() throws Exception { assertThat(fields, equalTo(singleton("id"))); assertThat(response.getHits().getAt(i).getFields().get("id").getValue(), equalTo(Integer.toString(i))); } - - response = client().prepareSearch() - .setQuery(matchAllQuery()) - .addSort("num1", SortOrder.ASC) - .setSize(numDocs) - .addScriptField("type", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_fields._type.value", Collections.emptyMap())) - .get(); - - assertNoFailures(response); - - assertThat(response.getHits().getTotalHits().value, equalTo((long) numDocs)); - for (int i = 0; i < numDocs; i++) { - assertThat(response.getHits().getAt(i).getId(), equalTo(Integer.toString(i))); - Set fields = new HashSet<>(response.getHits().getAt(i).getFields().keySet()); - assertThat(fields, equalTo(singleton("type"))); - assertThat(response.getHits().getAt(i).getFields().get("type").getValue(), equalTo(MapperService.SINGLE_MAPPING_NAME)); - } - - response = client().prepareSearch() - .setQuery(matchAllQuery()) - .addSort("num1", SortOrder.ASC) - .setSize(numDocs) - .addScriptField("id", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_fields._id.value", Collections.emptyMap())) - .addScriptField("type", new Script(ScriptType.INLINE, CustomScriptPlugin.NAME, "_fields._type.value", Collections.emptyMap())) - .get(); - - assertNoFailures(response); - - assertThat(response.getHits().getTotalHits().value, equalTo((long) numDocs)); - for (int i = 0; i < numDocs; i++) { - assertThat(response.getHits().getAt(i).getId(), equalTo(Integer.toString(i))); - Set fields = new HashSet<>(response.getHits().getAt(i).getFields().keySet()); - assertThat(fields, equalTo(newHashSet("type", "id"))); - assertThat(response.getHits().getAt(i).getFields().get("type").getValue(), equalTo(MapperService.SINGLE_MAPPING_NAME)); - assertThat(response.getHits().getAt(i).getFields().get("id").getValue(), equalTo(Integer.toString(i))); - } } public void testScriptFieldUsingSource() throws Exception { @@ -623,56 +721,59 @@ public void testPartialFields() throws Exception { public void testStoredFieldsWithoutSource() throws Exception { createIndex("test"); - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject(MapperService.SINGLE_MAPPING_NAME) - .startObject("_source") - .field("enabled", false) - .endObject() - .startObject("properties") - .startObject("byte_field") - .field("type", "byte") - .field("store", true) - .endObject() - .startObject("short_field") - .field("type", "short") - .field("store", true) - .endObject() - .startObject("integer_field") - .field("type", "integer") - .field("store", true) - .endObject() - .startObject("long_field") - .field("type", "long") - .field("store", true) - .endObject() - .startObject("float_field") - .field("type", "float") - .field("store", true) - .endObject() - .startObject("double_field") - .field("type", "double") - .field("store", true) - .endObject() - .startObject("date_field") - .field("type", "date") - .field("store", true) - .endObject() - .startObject("boolean_field") - .field("type", "boolean") - .field("store", true) - .endObject() - .startObject("binary_field") - .field("type", "binary") - .field("store", true) - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject(MapperService.SINGLE_MAPPING_NAME) + .startObject("_source") + .field("enabled", false) + .endObject() + .startObject("properties") + .startObject("byte_field") + .field("type", "byte") + .field("store", true) + .endObject() + .startObject("short_field") + .field("type", "short") + .field("store", true) + .endObject() + .startObject("integer_field") + .field("type", "integer") + .field("store", true) + .endObject() + .startObject("long_field") + .field("type", "long") + .field("store", true) + .endObject() + .startObject("float_field") + .field("type", "float") + .field("store", true) + .endObject() + .startObject("double_field") + .field("type", "double") + .field("store", true) + .endObject() + .startObject("date_field") + .field("type", "date") + .field("store", true) + .endObject() + .startObject("boolean_field") + .field("type", "boolean") + .field("store", true) + .endObject() + .startObject("binary_field") + .field("type", "binary") + .field("store", true) + .endObject() + .startObject("unsigned_long_field") + .field("type", "unsigned_long") + .field("store", true) + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); - client().admin().indices().preparePutMapping().setSource(mapping, XContentType.JSON).get(); + client().admin().indices().preparePutMapping().setSource(mapping, MediaTypeRegistry.JSON).get(); ZonedDateTime date = ZonedDateTime.of(2012, 3, 22, 0, 0, 0, 0, ZoneOffset.UTC); client().prepareIndex("test") @@ -688,6 +789,7 @@ public void testStoredFieldsWithoutSource() throws Exception { .field("date_field", DateFormatter.forPattern("date_optional_time").format(date)) .field("boolean_field", true) .field("binary_field", Base64.getEncoder().encodeToString("testing text".getBytes("UTF-8"))) + .field("unsigned_long_field", new BigInteger("10223372036854775807")) .endObject() ) .get(); @@ -705,6 +807,7 @@ public void testStoredFieldsWithoutSource() throws Exception { .addStoredField("date_field") .addStoredField("boolean_field") .addStoredField("binary_field") + .addStoredField("unsigned_long_field") .get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); @@ -722,7 +825,8 @@ public void testStoredFieldsWithoutSource() throws Exception { "double_field", "date_field", "boolean_field", - "binary_field" + "binary_field", + "unsigned_long_field" ) ) ); @@ -738,6 +842,7 @@ public void testStoredFieldsWithoutSource() throws Exception { assertThat(searchHit.getFields().get("date_field").getValue(), equalTo((Object) dateTime)); assertThat(searchHit.getFields().get("boolean_field").getValue(), equalTo((Object) Boolean.TRUE)); assertThat(searchHit.getFields().get("binary_field").getValue(), equalTo(new BytesArray("testing text".getBytes("UTF8")))); + assertThat(searchHit.getFields().get("unsigned_long_field").getValue(), equalTo(new BigInteger("10223372036854775807"))); } public void testSearchFieldsMetadata() throws Exception { @@ -826,7 +931,7 @@ public void testGetFieldsComplexField() throws Exception { .endObject() ); - client().prepareIndex("my-index").setId("1").setRefreshPolicy(IMMEDIATE).setSource(source, XContentType.JSON).get(); + client().prepareIndex("my-index").setId("1").setRefreshPolicy(IMMEDIATE).setSource(source, MediaTypeRegistry.JSON).get(); String field = "field1.field2.field3.field4"; @@ -853,58 +958,60 @@ public void testSingleValueFieldDatatField() throws ExecutionException, Interrup public void testDocValueFields() throws Exception { createIndex("test"); - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject(MapperService.SINGLE_MAPPING_NAME) - .startObject("_source") - .field("enabled", false) - .endObject() - .startObject("properties") - .startObject("text_field") - .field("type", "text") - .field("fielddata", true) - .endObject() - .startObject("keyword_field") - .field("type", "keyword") - .endObject() - .startObject("byte_field") - .field("type", "byte") - .endObject() - .startObject("short_field") - .field("type", "short") - .endObject() - .startObject("integer_field") - .field("type", "integer") - .endObject() - .startObject("long_field") - .field("type", "long") - .endObject() - .startObject("float_field") - .field("type", "float") - .endObject() - .startObject("double_field") - .field("type", "double") - .endObject() - .startObject("date_field") - .field("type", "date") - .endObject() - .startObject("boolean_field") - .field("type", "boolean") - .endObject() - .startObject("binary_field") - .field("type", "binary") - .field("doc_values", true) // off by default on binary fields - .endObject() - .startObject("ip_field") - .field("type", "ip") - .endObject() - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject(MapperService.SINGLE_MAPPING_NAME) + .startObject("_source") + .field("enabled", false) + .endObject() + .startObject("properties") + .startObject("text_field") + .field("type", "text") + .field("fielddata", true) + .endObject() + .startObject("keyword_field") + .field("type", "keyword") + .endObject() + .startObject("byte_field") + .field("type", "byte") + .endObject() + .startObject("short_field") + .field("type", "short") + .endObject() + .startObject("integer_field") + .field("type", "integer") + .endObject() + .startObject("long_field") + .field("type", "long") + .endObject() + .startObject("float_field") + .field("type", "float") + .endObject() + .startObject("double_field") + .field("type", "double") + .endObject() + .startObject("date_field") + .field("type", "date") + .endObject() + .startObject("boolean_field") + .field("type", "boolean") + .endObject() + .startObject("binary_field") + .field("type", "binary") + .field("doc_values", true) // off by default on binary fields + .endObject() + .startObject("ip_field") + .field("type", "ip") + .endObject() + .startObject("flat_object_field") + .field("type", "flat_object") + .endObject() + .endObject() + .endObject() + .endObject() + .toString(); - client().admin().indices().preparePutMapping().setSource(mapping, XContentType.JSON).get(); + client().admin().indices().preparePutMapping().setSource(mapping, MediaTypeRegistry.JSON).get(); ZonedDateTime date = ZonedDateTime.of(2012, 3, 22, 0, 0, 0, 0, ZoneOffset.UTC); client().prepareIndex("test") @@ -923,6 +1030,10 @@ public void testDocValueFields() throws Exception { .field("boolean_field", true) .field("binary_field", new byte[] { 42, 100 }) .field("ip_field", "::1") + .field("flat_object_field") + .startObject() + .field("foo", "bar") + .endObject() .endObject() ) .get(); @@ -942,7 +1053,8 @@ public void testDocValueFields() throws Exception { .addDocValueField("date_field") .addDocValueField("boolean_field") .addDocValueField("binary_field") - .addDocValueField("ip_field"); + .addDocValueField("ip_field") + .addDocValueField("flat_object_field"); SearchResponse searchResponse = builder.get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); @@ -963,11 +1075,11 @@ public void testDocValueFields() throws Exception { "text_field", "keyword_field", "binary_field", - "ip_field" + "ip_field", + "flat_object_field" ) ) ); - assertThat(searchResponse.getHits().getAt(0).getFields().get("byte_field").getValue().toString(), equalTo("1")); assertThat(searchResponse.getHits().getAt(0).getFields().get("short_field").getValue().toString(), equalTo("2")); assertThat(searchResponse.getHits().getAt(0).getFields().get("integer_field").getValue(), equalTo((Object) 3L)); @@ -983,6 +1095,7 @@ public void testDocValueFields() throws Exception { assertThat(searchResponse.getHits().getAt(0).getFields().get("keyword_field").getValue(), equalTo("foo")); assertThat(searchResponse.getHits().getAt(0).getFields().get("binary_field").getValue(), equalTo("KmQ")); assertThat(searchResponse.getHits().getAt(0).getFields().get("ip_field").getValue(), equalTo("::1")); + assertThat(searchResponse.getHits().getAt(0).getFields().get("flat_object_field").getValue(), equalTo("flat_object_field.foo")); builder = client().prepareSearch().setQuery(matchAllQuery()).addDocValueField("*field"); searchResponse = builder.get(); @@ -1005,7 +1118,8 @@ public void testDocValueFields() throws Exception { "text_field", "keyword_field", "binary_field", - "ip_field" + "ip_field", + "flat_object_field" ) ) ); @@ -1025,6 +1139,7 @@ public void testDocValueFields() throws Exception { assertThat(searchResponse.getHits().getAt(0).getFields().get("keyword_field").getValue(), equalTo("foo")); assertThat(searchResponse.getHits().getAt(0).getFields().get("binary_field").getValue(), equalTo("KmQ")); assertThat(searchResponse.getHits().getAt(0).getFields().get("ip_field").getValue(), equalTo("::1")); + assertThat(searchResponse.getHits().getAt(0).getFields().get("flat_object_field").getValue(), equalTo("flat_object_field.foo")); builder = client().prepareSearch() .setQuery(matchAllQuery()) @@ -1039,7 +1154,9 @@ public void testDocValueFields() throws Exception { .addDocValueField("date_field", "use_field_mapping") .addDocValueField("boolean_field", "use_field_mapping") .addDocValueField("binary_field", "use_field_mapping") - .addDocValueField("ip_field", "use_field_mapping"); + .addDocValueField("ip_field", "use_field_mapping") + .addDocValueField("flat_object_field", "use_field_mapping"); + ; searchResponse = builder.get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); @@ -1060,7 +1177,8 @@ public void testDocValueFields() throws Exception { "text_field", "keyword_field", "binary_field", - "ip_field" + "ip_field", + "flat_object_field" ) ) ); @@ -1080,6 +1198,7 @@ public void testDocValueFields() throws Exception { assertThat(searchResponse.getHits().getAt(0).getFields().get("keyword_field").getValue(), equalTo("foo")); assertThat(searchResponse.getHits().getAt(0).getFields().get("binary_field").getValue(), equalTo("KmQ")); assertThat(searchResponse.getHits().getAt(0).getFields().get("ip_field").getValue(), equalTo("::1")); + assertThat(searchResponse.getHits().getAt(0).getFields().get("flat_object_field").getValue(), equalTo("flat_object_field.foo")); builder = client().prepareSearch() .setQuery(matchAllQuery()) diff --git a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/DecayFunctionScoreIT.java b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/DecayFunctionScoreIT.java index 709a916d98838..6eb528e0bb7d3 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/DecayFunctionScoreIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/DecayFunctionScoreIT.java @@ -32,19 +32,22 @@ package org.opensearch.search.functionscore; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.Version; -import org.opensearch.action.ActionFuture; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.SearchType; import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.lucene.search.function.CombineFunction; import org.opensearch.common.lucene.search.function.FunctionScoreQuery; import org.opensearch.common.lucene.search.function.FunctionScoreQuery.ScoreMode; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.functionscore.FunctionScoreQueryBuilder; @@ -53,12 +56,14 @@ import org.opensearch.search.MultiValueMode; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Locale; @@ -72,6 +77,7 @@ import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.exponentialDecayFunction; import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.gaussDecayFunction; import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.linearDecayFunction; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.builder.SearchSourceBuilder.searchSource; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; @@ -85,7 +91,24 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; -public class DecayFunctionScoreIT extends OpenSearchIntegTestCase { +public class DecayFunctionScoreIT extends ParameterizedOpenSearchIntegTestCase { + + public DecayFunctionScoreIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected boolean forbidPrivateIndexSettings() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/ExplainableScriptIT.java b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/ExplainableScriptIT.java index f67b913a75871..c43863a92c283 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/ExplainableScriptIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/ExplainableScriptIT.java @@ -32,14 +32,18 @@ package org.opensearch.search.functionscore; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.IndexSearcher; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.SearchType; import org.opensearch.common.lucene.search.function.CombineFunction; import org.opensearch.common.lucene.search.function.Functions; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.ScriptPlugin; @@ -52,9 +56,9 @@ import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.lookup.SearchLookup; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; import java.io.IOException; @@ -72,13 +76,31 @@ import static org.opensearch.index.query.QueryBuilders.functionScoreQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.builder.SearchSourceBuilder.searchSource; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @ClusterScope(scope = Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 1) -public class ExplainableScriptIT extends OpenSearchIntegTestCase { +public class ExplainableScriptIT extends ParameterizedOpenSearchIntegTestCase { + + public ExplainableScriptIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } public static class ExplainableScriptPlugin extends Plugin implements ScriptPlugin { @Override @@ -93,7 +115,7 @@ public String getType() { public T compile(String scriptName, String scriptSource, ScriptContext context, Map params) { assert scriptSource.equals("explainable_script"); assert context == ScoreScript.CONTEXT; - ScoreScript.Factory factory = (params1, lookup) -> new ScoreScript.LeafFactory() { + ScoreScript.Factory factory = (params1, lookup, indexSearcher) -> new ScoreScript.LeafFactory() { @Override public boolean needs_score() { return false; @@ -101,7 +123,7 @@ public boolean needs_score() { @Override public ScoreScript newInstance(LeafReaderContext ctx) throws IOException { - return new MyScript(params1, lookup, ctx); + return new MyScript(params1, lookup, indexSearcher, ctx); } }; return context.factoryClazz.cast(factory); @@ -117,8 +139,8 @@ public Set> getSupportedContexts() { static class MyScript extends ScoreScript implements ExplainableScoreScript { - MyScript(Map params, SearchLookup lookup, LeafReaderContext leafContext) { - super(params, lookup, leafContext); + MyScript(Map params, SearchLookup lookup, IndexSearcher indexSearcher, LeafReaderContext leafContext) { + super(params, lookup, indexSearcher, leafContext); } @Override diff --git a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScoreFieldValueIT.java b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScoreFieldValueIT.java index 4e1df591cb245..b09914c4aa764 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScoreFieldValueIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScoreFieldValueIT.java @@ -32,29 +32,54 @@ package org.opensearch.search.functionscore; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.lucene.search.function.FieldValueFactorFunction; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.SearchHit; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.arrayWithSize; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.functionScoreQuery; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.simpleQueryStringQuery; import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.fieldValueFactorFunction; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFailures; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertOrderedSearchHits; +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.containsString; /** * Tests for the {@code field_value_factor} function in a function_score query. */ -public class FunctionScoreFieldValueIT extends OpenSearchIntegTestCase { +public class FunctionScoreFieldValueIT extends ParameterizedOpenSearchIntegTestCase { + + public FunctionScoreFieldValueIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testFieldValueFactor() throws IOException { assertAcked( prepareCreate("test").setMapping( diff --git a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScoreIT.java b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScoreIT.java index 3d24933f66d17..88395f25700d2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScoreIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScoreIT.java @@ -32,10 +32,14 @@ package org.opensearch.search.functionscore; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.lucene.search.function.CombineFunction; import org.opensearch.common.lucene.search.function.FunctionScoreQuery; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.functionscore.FunctionScoreQueryBuilder.FilterFunctionBuilder; @@ -45,11 +49,12 @@ import org.opensearch.script.ScriptType; import org.opensearch.search.SearchHit; import org.opensearch.search.aggregations.bucket.terms.Terms; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -63,6 +68,7 @@ import static org.opensearch.index.query.QueryBuilders.functionScoreQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.search.builder.SearchSourceBuilder.searchSource; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; @@ -72,11 +78,28 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -public class FunctionScoreIT extends OpenSearchIntegTestCase { +public class FunctionScoreIT extends ParameterizedOpenSearchIntegTestCase { static final String TYPE = "type"; static final String INDEX = "index"; + public FunctionScoreIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Collections.singleton(CustomScriptPlugin.class); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScorePluginIT.java b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScorePluginIT.java index a7a14f3b0d889..1df4acac0dcf0 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScorePluginIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/FunctionScorePluginIT.java @@ -32,13 +32,17 @@ package org.opensearch.search.functionscore; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.Explanation; -import org.opensearch.action.ActionFuture; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.SearchType; import org.opensearch.common.Priority; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.query.functionscore.DecayFunction; import org.opensearch.index.query.functionscore.DecayFunctionBuilder; import org.opensearch.index.query.functionscore.DecayFunctionParser; @@ -46,9 +50,9 @@ import org.opensearch.plugins.Plugin; import org.opensearch.plugins.SearchPlugin; import org.opensearch.search.SearchHits; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; import java.io.IOException; @@ -62,11 +66,30 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.functionScoreQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.builder.SearchSourceBuilder.searchSource; import static org.hamcrest.Matchers.equalTo; @ClusterScope(scope = Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 1) -public class FunctionScorePluginIT extends OpenSearchIntegTestCase { +public class FunctionScorePluginIT extends ParameterizedOpenSearchIntegTestCase { + + public FunctionScorePluginIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Arrays.asList(CustomDistanceScorePlugin.class); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/QueryRescorerIT.java b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/QueryRescorerIT.java index c4a41ad5b76e2..de4c85301547c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/QueryRescorerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/QueryRescorerIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.functionscore; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.Explanation; import org.apache.lucene.tests.util.English; import org.opensearch.action.index.IndexRequestBuilder; @@ -41,8 +43,9 @@ import org.opensearch.common.lucene.search.function.CombineFunction; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.Settings.Builder; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.Operator; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -52,9 +55,10 @@ import org.opensearch.search.rescore.QueryRescoreMode; import org.opensearch.search.rescore.QueryRescorerBuilder; import org.opensearch.search.sort.SortBuilders; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; @@ -70,6 +74,7 @@ import static org.opensearch.index.query.QueryBuilders.queryStringQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.weightFactorFunction; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFirstHit; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFourthHit; @@ -86,7 +91,25 @@ import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.notNullValue; -public class QueryRescorerIT extends OpenSearchIntegTestCase { +public class QueryRescorerIT extends ParameterizedOpenSearchIntegTestCase { + + public QueryRescorerIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testEnforceWindowSize() { createIndex("test"); // this diff --git a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/RandomScoreFunctionIT.java b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/RandomScoreFunctionIT.java index 0701e96b71f38..8f43cefd2d53b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/functionscore/RandomScoreFunctionIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/functionscore/RandomScoreFunctionIT.java @@ -31,8 +31,12 @@ package org.opensearch.search.functionscore; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.util.ArrayUtil; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.index.mapper.SeqNoFieldMapper; import org.opensearch.index.query.functionscore.FunctionScoreQueryBuilder; @@ -43,7 +47,7 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.SearchHit; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.CoreMatchers; import java.util.Arrays; @@ -60,6 +64,7 @@ import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.randomFunction; import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.scriptFunction; import static org.opensearch.script.MockScriptPlugin.NAME; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.allOf; @@ -71,7 +76,24 @@ import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.nullValue; -public class RandomScoreFunctionIT extends OpenSearchIntegTestCase { +public class RandomScoreFunctionIT extends ParameterizedOpenSearchIntegTestCase { + + public RandomScoreFunctionIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -117,7 +139,7 @@ public void testConsistentHitsWithSameSeed() throws Exception { for (int o = 0; o < outerIters; o++) { final int seed = randomInt(); String preference = randomRealisticUnicodeOfLengthBetween(1, 10); // at least one char!! - // randomPreference should not start with '_' (reserved for known preference types (e.g. _shards) + // randomPreference should not start with '_' (reserved for known preference types (e.g. _shards, _primary) while (preference.startsWith("_")) { preference = randomRealisticUnicodeOfLengthBetween(1, 10); } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/AbstractGeoBoundingBoxQueryIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/AbstractGeoBoundingBoxQueryIT.java index 3e63987af20f2..2f48ea0f64e35 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/AbstractGeoBoundingBoxQueryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/AbstractGeoBoundingBoxQueryIT.java @@ -36,8 +36,8 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.GeoValidationMethod; import org.opensearch.search.SearchHit; import org.opensearch.test.OpenSearchIntegTestCase; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/AbstractGeoDistanceIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/AbstractGeoDistanceIT.java index d28410ec170d4..272f07e874fdf 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/AbstractGeoDistanceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/AbstractGeoDistanceIT.java @@ -40,8 +40,8 @@ import org.opensearch.common.geo.GeoUtils; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.DistanceUnit; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.geometry.utils.Geohash; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.index.query.IdsQueryBuilder; @@ -66,11 +66,11 @@ import java.util.Map; import java.util.function.Function; -import static org.hamcrest.Matchers.anyOf; -import static org.hamcrest.Matchers.equalTo; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.equalTo; /** base class for testing geo_distance queries on geo_ field types */ abstract class AbstractGeoDistanceIT extends OpenSearchIntegTestCase { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoBoundingBoxQueryGeoPointsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoBoundingBoxQueryGeoPointsIT.java index d039e1200a862..083dd829ef5ee 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoBoundingBoxQueryGeoPointsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoBoundingBoxQueryGeoPointsIT.java @@ -8,7 +8,7 @@ package org.opensearch.search.geo; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoBoundingBoxQueryGeoShapesIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoBoundingBoxQueryGeoShapesIT.java index 44f9dd6d01144..1b99d9fc09146 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoBoundingBoxQueryGeoShapesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoBoundingBoxQueryGeoShapesIT.java @@ -8,7 +8,7 @@ package org.opensearch.search.geo; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoDistanceQueryGeoPointsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoDistanceQueryGeoPointsIT.java index af19763577666..f171555aed13a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoDistanceQueryGeoPointsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoDistanceQueryGeoPointsIT.java @@ -8,8 +8,8 @@ package org.opensearch.search.geo; +import org.opensearch.core.xcontent.XContentBuilder; import org.junit.Before; -import org.opensearch.common.xcontent.XContentBuilder; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoDistanceQueryGeoShapesIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoDistanceQueryGeoShapesIT.java index f7e0b7dda4b2c..26702d8313e4b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoDistanceQueryGeoShapesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoDistanceQueryGeoShapesIT.java @@ -8,8 +8,8 @@ package org.opensearch.search.geo; +import org.opensearch.core.xcontent.XContentBuilder; import org.junit.Before; -import org.opensearch.common.xcontent.XContentBuilder; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoFilterIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoFilterIT.java index 975cb5f7e3b8c..00524c6e04707 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoFilterIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoFilterIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.geo; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy; @@ -39,7 +41,6 @@ import org.apache.lucene.spatial.query.SpatialArgs; import org.apache.lucene.spatial.query.SpatialOperation; import org.apache.lucene.spatial.query.UnsupportedSpatialOperation; - import org.opensearch.Version; import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.opensearch.action.bulk.BulkItemResponse; @@ -47,8 +48,6 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.geo.GeoUtils; import org.opensearch.common.geo.builders.CoordinatesBuilder; @@ -57,35 +56,40 @@ import org.opensearch.common.geo.builders.PointBuilder; import org.opensearch.common.geo.builders.PolygonBuilder; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.common.util.io.Streams; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.core.internal.io.Streams; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.SearchHit; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; - import org.junit.BeforeClass; -import org.locationtech.spatial4j.context.SpatialContext; -import org.locationtech.spatial4j.distance.DistanceUtils; -import org.locationtech.spatial4j.exception.InvalidShapeException; -import org.locationtech.spatial4j.shape.Shape; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Random; import java.util.zip.GZIPInputStream; +import org.locationtech.spatial4j.context.SpatialContext; +import org.locationtech.spatial4j.distance.DistanceUtils; +import org.locationtech.spatial4j.exception.InvalidShapeException; +import org.locationtech.spatial4j.shape.Shape; + import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.geometry.utils.Geohash.addNeighbors; import static org.opensearch.index.query.QueryBuilders.geoBoundingBoxQuery; import static org.opensearch.index.query.QueryBuilders.geoDistanceQuery; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.matchQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFirstHit; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.hasId; @@ -95,7 +99,24 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; -public class GeoFilterIT extends OpenSearchIntegTestCase { +public class GeoFilterIT extends ParameterizedOpenSearchIntegTestCase { + + public GeoFilterIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected boolean forbidPrivateIndexSettings() { @@ -211,17 +232,16 @@ public void testShapeRelations() throws Exception { assertTrue("Disjoint relation is not supported", disjointSupport); assertTrue("within relation is not supported", withinSupport); - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("area") - .field("type", "geo_shape") - .field("tree", "geohash") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("area") + .field("type", "geo_shape") + .field("tree", "geohash") + .endObject() + .endObject() + .endObject() + .toString(); CreateIndexRequestBuilder mappingRequest = client().admin().indices().prepareCreate("shapes").setMapping(mapping); mappingRequest.get(); @@ -244,7 +264,7 @@ public void testShapeRelations() throws Exception { ); BytesReference data = BytesReference.bytes(jsonBuilder().startObject().field("area", polygon).endObject()); - client().prepareIndex("shapes").setId("1").setSource(data, XContentType.JSON).get(); + client().prepareIndex("shapes").setId("1").setSource(data, MediaTypeRegistry.JSON).get(); client().admin().indices().prepareRefresh().get(); // Point in polygon @@ -307,7 +327,7 @@ public void testShapeRelations() throws Exception { ); data = BytesReference.bytes(jsonBuilder().startObject().field("area", inverse).endObject()); - client().prepareIndex("shapes").setId("2").setSource(data, XContentType.JSON).get(); + client().prepareIndex("shapes").setId("2").setSource(data, MediaTypeRegistry.JSON).get(); client().admin().indices().prepareRefresh().get(); // re-check point on polygon hole @@ -346,7 +366,7 @@ public void testShapeRelations() throws Exception { ); data = BytesReference.bytes(jsonBuilder().startObject().field("area", builder).endObject()); - client().prepareIndex("shapes").setId("1").setSource(data, XContentType.JSON).get(); + client().prepareIndex("shapes").setId("1").setSource(data, MediaTypeRegistry.JSON).get(); client().admin().indices().prepareRefresh().get(); // Create a polygon crossing longitude 180 with hole. @@ -359,7 +379,7 @@ public void testShapeRelations() throws Exception { ); data = BytesReference.bytes(jsonBuilder().startObject().field("area", builder).endObject()); - client().prepareIndex("shapes").setId("1").setSource(data, XContentType.JSON).get(); + client().prepareIndex("shapes").setId("1").setSource(data, MediaTypeRegistry.JSON).get(); client().admin().indices().prepareRefresh().get(); result = client().prepareSearch() diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoPolygonIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoPolygonIT.java index d0b017732b270..85cb087585d31 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoPolygonIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoPolygonIT.java @@ -32,28 +32,52 @@ package org.opensearch.search.geo; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.Version; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.SearchHit; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.boolQuery; import static org.opensearch.index.query.QueryBuilders.geoPolygonQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class GeoPolygonIT extends OpenSearchIntegTestCase { +public class GeoPolygonIT extends ParameterizedOpenSearchIntegTestCase { + + public GeoPolygonIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected boolean forbidPrivateIndexSettings() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoShapeIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoShapeIntegrationIT.java index 89eb6038d8110..1f9b6ae434f75 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoShapeIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/GeoShapeIntegrationIT.java @@ -32,31 +32,53 @@ package org.opensearch.search.geo; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.routing.IndexShardRoutingTable; -import org.opensearch.common.Strings; import org.opensearch.common.geo.builders.PointBuilder; import org.opensearch.common.geo.builders.ShapeBuilder; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.GeoShapeFieldMapper; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.indices.IndicesService; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.index.query.QueryBuilders.geoShapeQuery; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; - import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; -public class GeoShapeIntegrationIT extends OpenSearchIntegTestCase { +public class GeoShapeIntegrationIT extends ParameterizedOpenSearchIntegTestCase { + + public GeoShapeIntegrationIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Settings nodeSettings(int nodeOrdinal) { @@ -73,32 +95,30 @@ protected Settings nodeSettings(int nodeOrdinal) { */ public void testOrientationPersistence() throws Exception { String idxName = "orientation"; - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("location") - .field("type", "geo_shape") - .field("orientation", "left") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("location") + .field("type", "geo_shape") + .field("orientation", "left") + .endObject() + .endObject() + .endObject() + .toString(); // create index assertAcked(prepareCreate(idxName).setMapping(mapping)); - mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("location") - .field("type", "geo_shape") - .field("orientation", "right") - .endObject() - .endObject() - .endObject() - ); + mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("location") + .field("type", "geo_shape") + .field("orientation", "right") + .endObject() + .endObject() + .endObject() + .toString(); assertAcked(prepareCreate(idxName + "2").setMapping(mapping)); ensureGreen(idxName, idxName + "2"); @@ -140,44 +160,43 @@ public void testIgnoreMalformed() throws Exception { ensureGreen(); // test self crossing ccw poly not crossing dateline - String polygonGeoJson = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .field("type", "Polygon") - .startArray("coordinates") - .startArray() - .startArray() - .value(176.0) - .value(15.0) - .endArray() - .startArray() - .value(-177.0) - .value(10.0) - .endArray() - .startArray() - .value(-177.0) - .value(-10.0) - .endArray() - .startArray() - .value(176.0) - .value(-15.0) - .endArray() - .startArray() - .value(-177.0) - .value(15.0) - .endArray() - .startArray() - .value(172.0) - .value(0.0) - .endArray() - .startArray() - .value(176.0) - .value(15.0) - .endArray() - .endArray() - .endArray() - .endObject() - ); + String polygonGeoJson = XContentFactory.jsonBuilder() + .startObject() + .field("type", "Polygon") + .startArray("coordinates") + .startArray() + .startArray() + .value(176.0) + .value(15.0) + .endArray() + .startArray() + .value(-177.0) + .value(10.0) + .endArray() + .startArray() + .value(-177.0) + .value(-10.0) + .endArray() + .startArray() + .value(176.0) + .value(-15.0) + .endArray() + .startArray() + .value(-177.0) + .value(15.0) + .endArray() + .startArray() + .value(172.0) + .value(0.0) + .endArray() + .startArray() + .value(176.0) + .value(15.0) + .endArray() + .endArray() + .endArray() + .endObject() + .toString(); indexRandom(true, client().prepareIndex("test").setId("0").setSource("shape", polygonGeoJson)); SearchResponse searchResponse = client().prepareSearch("test").setQuery(matchAllQuery()).get(); @@ -200,7 +219,7 @@ public void testMappingUpdate() throws Exception { IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> client().admin().indices().preparePutMapping("test").setSource(update, XContentType.JSON).get() + () -> client().admin().indices().preparePutMapping("test").setSource(update, MediaTypeRegistry.JSON).get() ); assertThat(e.getMessage(), containsString("using [BKD] strategy cannot be merged with")); } @@ -231,7 +250,7 @@ public void testIndexShapeRouting() throws Exception { + " }\n" + "}"; - indexRandom(true, client().prepareIndex("test").setId("0").setSource(source, XContentType.JSON).setRouting("ABC")); + indexRandom(true, client().prepareIndex("test").setId("0").setSource(source, MediaTypeRegistry.JSON).setRouting("ABC")); SearchResponse searchResponse = client().prepareSearch("test") .setQuery(geoShapeQuery("shape", "0").indexedShapeIndex("test").indexedShapeRouting("ABC")) @@ -267,8 +286,8 @@ public void testIndexPolygonDateLine() throws Exception { String source = "{\n" + " \"shape\" : \"POLYGON((179 0, -179 0, -179 2, 179 2, 179 0))\"" + "}"; - indexRandom(true, client().prepareIndex("quad").setId("0").setSource(source, XContentType.JSON)); - indexRandom(true, client().prepareIndex("vector").setId("0").setSource(source, XContentType.JSON)); + indexRandom(true, client().prepareIndex("quad").setId("0").setSource(source, MediaTypeRegistry.JSON)); + indexRandom(true, client().prepareIndex("vector").setId("0").setSource(source, MediaTypeRegistry.JSON)); try { ClusterUpdateSettingsRequest updateSettingsRequest = new ClusterUpdateSettingsRequest(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/geo/LegacyGeoShapeIntegrationIT.java b/server/src/internalClusterTest/java/org/opensearch/search/geo/LegacyGeoShapeIntegrationIT.java index 61af97d46e7f3..d21d6036c9673 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/geo/LegacyGeoShapeIntegrationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/geo/LegacyGeoShapeIntegrationIT.java @@ -32,67 +32,87 @@ package org.opensearch.search.geo; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.routing.IndexShardRoutingTable; -import org.opensearch.common.Strings; import org.opensearch.common.geo.builders.ShapeBuilder; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; import org.opensearch.geometry.Circle; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.LegacyGeoShapeFieldMapper; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.indices.IndicesService; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.index.query.QueryBuilders.geoShapeQuery; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; -public class LegacyGeoShapeIntegrationIT extends OpenSearchIntegTestCase { +public class LegacyGeoShapeIntegrationIT extends ParameterizedOpenSearchIntegTestCase { + + public LegacyGeoShapeIntegrationIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } /** * Test that orientation parameter correctly persists across cluster restart */ public void testOrientationPersistence() throws Exception { String idxName = "orientation"; - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("location") - .field("type", "geo_shape") - .field("tree", "quadtree") - .field("orientation", "left") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("location") + .field("type", "geo_shape") + .field("tree", "quadtree") + .field("orientation", "left") + .endObject() + .endObject() + .endObject() + .toString(); // create index assertAcked(prepareCreate(idxName).setMapping(mapping)); - mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("location") - .field("type", "geo_shape") - .field("tree", "quadtree") - .field("orientation", "right") - .endObject() - .endObject() - .endObject() - ); + mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("location") + .field("type", "geo_shape") + .field("tree", "quadtree") + .field("orientation", "right") + .endObject() + .endObject() + .endObject() + .toString(); assertAcked(prepareCreate(idxName + "2").setMapping(mapping)); ensureGreen(idxName, idxName + "2"); @@ -136,44 +156,43 @@ public void testIgnoreMalformed() throws Exception { ensureGreen(); // test self crossing ccw poly not crossing dateline - String polygonGeoJson = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .field("type", "Polygon") - .startArray("coordinates") - .startArray() - .startArray() - .value(176.0) - .value(15.0) - .endArray() - .startArray() - .value(-177.0) - .value(10.0) - .endArray() - .startArray() - .value(-177.0) - .value(-10.0) - .endArray() - .startArray() - .value(176.0) - .value(-15.0) - .endArray() - .startArray() - .value(-177.0) - .value(15.0) - .endArray() - .startArray() - .value(172.0) - .value(0.0) - .endArray() - .startArray() - .value(176.0) - .value(15.0) - .endArray() - .endArray() - .endArray() - .endObject() - ); + String polygonGeoJson = XContentFactory.jsonBuilder() + .startObject() + .field("type", "Polygon") + .startArray("coordinates") + .startArray() + .startArray() + .value(176.0) + .value(15.0) + .endArray() + .startArray() + .value(-177.0) + .value(10.0) + .endArray() + .startArray() + .value(-177.0) + .value(-10.0) + .endArray() + .startArray() + .value(176.0) + .value(-15.0) + .endArray() + .startArray() + .value(-177.0) + .value(15.0) + .endArray() + .startArray() + .value(172.0) + .value(0.0) + .endArray() + .startArray() + .value(176.0) + .value(15.0) + .endArray() + .endArray() + .endArray() + .endObject() + .toString(); indexRandom(true, client().prepareIndex("test").setId("0").setSource("shape", polygonGeoJson)); SearchResponse searchResponse = client().prepareSearch("test").setQuery(matchAllQuery()).get(); @@ -207,7 +226,7 @@ public void testIndexShapeRouting() throws Exception { + " }\n" + "}"; - indexRandom(true, client().prepareIndex("test").setId("0").setSource(source, XContentType.JSON).setRouting("ABC")); + indexRandom(true, client().prepareIndex("test").setId("0").setSource(source, MediaTypeRegistry.JSON).setRouting("ABC")); SearchResponse searchResponse = client().prepareSearch("test") .setQuery(geoShapeQuery("shape", "0").indexedShapeIndex("test").indexedShapeRouting("ABC")) diff --git a/server/src/internalClusterTest/java/org/opensearch/search/morelikethis/MoreLikeThisIT.java b/server/src/internalClusterTest/java/org/opensearch/search/morelikethis/MoreLikeThisIT.java index b3253b036bda6..4197641e33f3b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/morelikethis/MoreLikeThisIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/morelikethis/MoreLikeThisIT.java @@ -32,26 +32,29 @@ package org.opensearch.search.morelikethis; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.RoutingMissingException; import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.health.ClusterHealthStatus; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.MoreLikeThisQueryBuilder; import org.opensearch.index.query.MoreLikeThisQueryBuilder.Item; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.plugins.Plugin; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -64,6 +67,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.moreLikeThisQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; @@ -75,7 +79,24 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; -public class MoreLikeThisIT extends OpenSearchIntegTestCase { +public class MoreLikeThisIT extends ParameterizedOpenSearchIntegTestCase { + + public MoreLikeThisIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -270,7 +291,7 @@ public void testMoreLikeThisWithAliasesInLikeDocuments() throws Exception { String indexName = "foo"; String aliasName = "foo_name"; - String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("properties").endObject().endObject()); + String mapping = XContentFactory.jsonBuilder().startObject().startObject("properties").endObject().endObject().toString(); client().admin().indices().prepareCreate(indexName).setMapping(mapping).get(); client().admin().indices().prepareAliases().addAlias(indexName, aliasName).get(); @@ -292,7 +313,7 @@ public void testMoreLikeThisWithAliasesInLikeDocuments() throws Exception { } public void testMoreLikeThisIssue2197() throws Exception { - String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("properties").endObject().endObject()); + String mapping = XContentFactory.jsonBuilder().startObject().startObject("properties").endObject().endObject().toString(); client().admin().indices().prepareCreate("foo").setMapping(mapping).get(); client().prepareIndex("foo") .setId("1") @@ -313,7 +334,7 @@ public void testMoreLikeThisIssue2197() throws Exception { // Issue #2489 public void testMoreLikeWithCustomRouting() throws Exception { - String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("properties").endObject().endObject()); + String mapping = XContentFactory.jsonBuilder().startObject().startObject("properties").endObject().endObject().toString(); client().admin().indices().prepareCreate("foo").setMapping(mapping).get(); ensureGreen(); @@ -333,7 +354,7 @@ public void testMoreLikeWithCustomRouting() throws Exception { // Issue #3039 public void testMoreLikeThisIssueRoutingNotSerialized() throws Exception { - String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("properties").endObject().endObject()); + String mapping = XContentFactory.jsonBuilder().startObject().startObject("properties").endObject().endObject().toString(); assertAcked( prepareCreate("foo", 2, Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 2).put(SETTING_NUMBER_OF_REPLICAS, 0)).setMapping( mapping diff --git a/server/src/internalClusterTest/java/org/opensearch/search/msearch/MultiSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/msearch/MultiSearchIT.java index 8226663abf49e..bc1d2833ecbbf 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/msearch/MultiSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/msearch/MultiSearchIT.java @@ -32,19 +32,44 @@ package org.opensearch.search.msearch; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchResponse; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFirstHit; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; import static org.opensearch.test.hamcrest.OpenSearchAssertions.hasId; import static org.hamcrest.Matchers.equalTo; -public class MultiSearchIT extends OpenSearchIntegTestCase { +public class MultiSearchIT extends ParameterizedOpenSearchIntegTestCase { + + public MultiSearchIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } public void testSimpleMultiSearch() { createIndex("test"); @@ -73,7 +98,7 @@ public void testSimpleMultiSearchMoreRequests() { createIndex("test"); int numDocs = randomIntBetween(0, 16); for (int i = 0; i < numDocs; i++) { - client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", XContentType.JSON).get(); + client().prepareIndex("test").setId(Integer.toString(i)).setSource("{}", MediaTypeRegistry.JSON).get(); } refresh(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/nested/SimpleNestedIT.java b/server/src/internalClusterTest/java/org/opensearch/search/nested/SimpleNestedIT.java index d2d23cd47fc01..83dec7b27a897 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/nested/SimpleNestedIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/nested/SimpleNestedIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.nested; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.Explanation; import org.apache.lucene.search.join.ScoreMode; import org.opensearch.action.DocWriteResponse; @@ -45,15 +47,19 @@ import org.opensearch.action.search.SearchType; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.sort.NestedSortBuilder; import org.opensearch.search.sort.SortBuilders; import org.opensearch.search.sort.SortMode; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; @@ -61,17 +67,35 @@ import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.nestedQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.startsWith; -public class SimpleNestedIT extends OpenSearchIntegTestCase { +public class SimpleNestedIT extends ParameterizedOpenSearchIntegTestCase { + + public SimpleNestedIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testSimpleNested() throws Exception { assertAcked(prepareCreate("test").setMapping("nested1", "type=nested")); ensureGreen(); @@ -783,7 +807,7 @@ public void testNestedSortWithMultiLevelFiltering() throws Exception { + " }\n" + " ]\n" + "}", - XContentType.JSON + MediaTypeRegistry.JSON ) .get(); @@ -835,7 +859,7 @@ public void testNestedSortWithMultiLevelFiltering() throws Exception { + " }\n" + " ]\n" + "}", - XContentType.JSON + MediaTypeRegistry.JSON ) .get(); refresh(); @@ -987,7 +1011,7 @@ public void testLeakingSortValues() throws Exception { + " }\n" + " ]\n" + "}", - XContentType.JSON + MediaTypeRegistry.JSON ) .get(); @@ -1006,7 +1030,7 @@ public void testLeakingSortValues() throws Exception { + " } \n" + " ]\n" + "}", - XContentType.JSON + MediaTypeRegistry.JSON ) .get(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/pit/DeletePitMultiNodeIT.java b/server/src/internalClusterTest/java/org/opensearch/search/pit/DeletePitMultiNodeIT.java new file mode 100644 index 0000000000000..43b7179a335f8 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/search/pit/DeletePitMultiNodeIT.java @@ -0,0 +1,368 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pit; + +import org.opensearch.ExceptionsHelper; +import org.opensearch.action.LatchedActionListener; +import org.opensearch.action.admin.indices.stats.IndicesStatsRequest; +import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import org.opensearch.action.search.CreatePitAction; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitAction; +import org.opensearch.action.search.DeletePitInfo; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.action.search.SearchPhaseExecutionException; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.search.ShardSearchFailure; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.search.SearchContextMissingException; +import org.opensearch.search.builder.PointInTimeBuilder; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; +import org.junit.After; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.hamcrest.Matchers.blankOrNullString; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; + +/** + * Multi node integration tests for delete PIT use cases + */ +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, numDataNodes = 2) +public class DeletePitMultiNodeIT extends OpenSearchIntegTestCase { + + @Before + public void setupIndex() throws ExecutionException, InterruptedException { + createIndex("index", Settings.builder().put("index.number_of_shards", 5).put("index.number_of_replicas", 1).build()); + client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).execute().get(); + ensureGreen(); + } + + @After + public void clearIndex() { + client().admin().indices().prepareDelete("index").get(); + } + + private CreatePitResponse createPitOnIndex(String index) throws ExecutionException, InterruptedException { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { index }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + return execute.get(); + } + + public void testDeletePit() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + execute = client().execute(CreatePitAction.INSTANCE, request); + pitResponse = execute.get(); + pitIds.add(pitResponse.getId()); + validatePitStats("index", 10, 0); + DeletePitRequest deletePITRequest = new DeletePitRequest(pitIds); + ActionFuture deleteExecute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + DeletePitResponse deletePITResponse = deleteExecute.get(); + assertEquals(2, deletePITResponse.getDeletePitResults().size()); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertTrue(pitIds.contains(deletePitInfo.getPitId())); + assertTrue(deletePitInfo.isSuccessful()); + } + validatePitStats("index", 0, 10); + /** + * Checking deleting the same PIT id again results in succeeded + */ + deleteExecute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + deletePITResponse = deleteExecute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertTrue(pitIds.contains(deletePitInfo.getPitId())); + assertTrue(deletePitInfo.isSuccessful()); + } + } + + public void testDeletePitWithValidAndDeletedIds() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + validatePitStats("index", 5, 0); + + /** + * Delete Pit #1 + */ + DeletePitRequest deletePITRequest = new DeletePitRequest(pitIds); + ActionFuture deleteExecute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + DeletePitResponse deletePITResponse = deleteExecute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertTrue(pitIds.contains(deletePitInfo.getPitId())); + assertTrue(deletePitInfo.isSuccessful()); + } + validatePitStats("index", 0, 5); + execute = client().execute(CreatePitAction.INSTANCE, request); + pitResponse = execute.get(); + pitIds.add(pitResponse.getId()); + validatePitStats("index", 5, 5); + /** + * Delete PIT with both Ids #1 (which is deleted) and #2 (which is present) + */ + deletePITRequest = new DeletePitRequest(pitIds); + deleteExecute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + deletePITResponse = deleteExecute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertTrue(pitIds.contains(deletePitInfo.getPitId())); + assertTrue(deletePitInfo.isSuccessful()); + } + validatePitStats("index", 0, 10); + } + + public void testDeletePitWithValidAndInvalidIds() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + pitIds.add("nondecodableid"); + DeletePitRequest deletePITRequest = new DeletePitRequest(pitIds); + ActionFuture deleteExecute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + Exception e = assertThrows(ExecutionException.class, () -> deleteExecute.get()); + assertThat(e.getMessage(), containsString("invalid id")); + } + + public void testDeleteAllPits() throws Exception { + createPitOnIndex("index"); + createIndex("index1", Settings.builder().put("index.number_of_shards", 5).put("index.number_of_replicas", 1).build()); + client().prepareIndex("index1").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).execute().get(); + ensureGreen(); + createPitOnIndex("index1"); + validatePitStats("index", 5, 0); + validatePitStats("index1", 5, 0); + DeletePitRequest deletePITRequest = new DeletePitRequest("_all"); + + /** + * When we invoke delete again, returns success after clearing the remaining readers. Asserting reader context + * not found exceptions don't result in failures ( as deletion in one node is successful ) + */ + ActionFuture execute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + DeletePitResponse deletePITResponse = execute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertThat(deletePitInfo.getPitId(), not(blankOrNullString())); + assertTrue(deletePitInfo.isSuccessful()); + } + validatePitStats("index", 0, 5); + validatePitStats("index1", 0, 5); + client().admin().indices().prepareDelete("index1").get(); + } + + public void testDeletePitWhileNodeDrop() throws Exception { + CreatePitResponse pitResponse = createPitOnIndex("index"); + createIndex("index1", Settings.builder().put("index.number_of_shards", 5).put("index.number_of_replicas", 1).build()); + client().prepareIndex("index1").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).execute().get(); + ensureGreen(); + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + CreatePitResponse pitResponse1 = createPitOnIndex("index1"); + pitIds.add(pitResponse1.getId()); + DeletePitRequest deletePITRequest = new DeletePitRequest(pitIds); + internalCluster().restartRandomDataNode(new InternalTestCluster.RestartCallback() { + @Override + public Settings onNodeStopped(String nodeName) throws Exception { + ActionFuture execute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + try { + DeletePitResponse deletePITResponse = execute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertTrue(pitIds.contains(deletePitInfo.getPitId())); + } + } catch (Exception e) { + throw new AssertionError(e); + } + return super.onNodeStopped(nodeName); + } + }); + + ensureGreen(); + /** + * When we invoke delete again, returns success after clearing the remaining readers. Asserting reader context + * not found exceptions don't result in failures ( as deletion in one node is successful ) + */ + ActionFuture execute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + DeletePitResponse deletePITResponse = execute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertTrue(pitIds.contains(deletePitInfo.getPitId())); + assertTrue(deletePitInfo.isSuccessful()); + } + client().admin().indices().prepareDelete("index1").get(); + } + + public void testDeleteAllPitsWhileNodeDrop() throws Exception { + createIndex("index1", Settings.builder().put("index.number_of_shards", 5).put("index.number_of_replicas", 1).build()); + client().prepareIndex("index1").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).execute().get(); + createPitOnIndex("index1"); + ensureGreen(); + DeletePitRequest deletePITRequest = new DeletePitRequest("_all"); + internalCluster().restartRandomDataNode(new InternalTestCluster.RestartCallback() { + @Override + public Settings onNodeStopped(String nodeName) throws Exception { + ActionFuture execute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + try { + DeletePitResponse deletePITResponse = execute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertThat(deletePitInfo.getPitId(), not(blankOrNullString())); + } + } catch (Exception e) { + assertTrue(e.getMessage().contains("Node not connected")); + } + return super.onNodeStopped(nodeName); + } + }); + ensureGreen(); + /** + * When we invoke delete again, returns success as all readers are cleared. (Delete all on node which is Up and + * once the node restarts, all active contexts are cleared in the node ) + */ + ActionFuture execute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + DeletePitResponse deletePITResponse = execute.get(); + assertEquals(0, deletePITResponse.getDeletePitResults().size()); + client().admin().indices().prepareDelete("index1").get(); + } + + public void testDeleteWhileSearch() throws Exception { + CreatePitResponse pitResponse = createPitOnIndex("index"); + ensureGreen(); + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + DeletePitRequest deletePITRequest = new DeletePitRequest(pitIds); + Thread[] threads = new Thread[5]; + CountDownLatch latch = new CountDownLatch(threads.length); + final AtomicBoolean deleted = new AtomicBoolean(false); + + for (int i = 0; i < threads.length; i++) { + threads[i] = new Thread(() -> { + latch.countDown(); + try { + latch.await(); + for (int j = 0; j < 30; j++) { + SearchResponse searchResponse = client().prepareSearch() + .setSize(2) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId()).setKeepAlive(TimeValue.timeValueDays(1))) + .execute() + .get(); + if (searchResponse.getFailedShards() != 0) { + verifySearchContextMissingException(searchResponse.getShardFailures()); + } + } + } catch (Exception e) { + /** + * assert for exception once delete pit goes through. throw error in case of any exeption before that. + */ + if (deleted.get() == true) { + Throwable t = ExceptionsHelper.unwrapCause(e.getCause()); + assertTrue(e.toString(), t instanceof SearchPhaseExecutionException); + verifySearchContextMissingException(((SearchPhaseExecutionException) t).shardFailures()); + return; + } + throw new AssertionError(e); + } + }); + threads[i].setName("opensearch[node_s_0][search]"); + threads[i].start(); + } + deleted.set(true); + ActionFuture execute = client().execute(DeletePitAction.INSTANCE, deletePITRequest); + DeletePitResponse deletePITResponse = execute.get(); + for (DeletePitInfo deletePitInfo : deletePITResponse.getDeletePitResults()) { + assertTrue(pitIds.contains(deletePitInfo.getPitId())); + assertTrue(deletePitInfo.isSuccessful()); + } + + for (Thread thread : threads) { + thread.join(); + } + } + + private void verifySearchContextMissingException(ShardSearchFailure[] failures) { + for (ShardSearchFailure failure : failures) { + Throwable cause = ExceptionsHelper.unwrapCause(failure.getCause()); + assertTrue(failure.toString(), cause instanceof SearchContextMissingException); + } + } + + public void testtConcurrentDeletes() throws InterruptedException, ExecutionException { + CreatePitResponse pitResponse = createPitOnIndex("index"); + ensureGreen(); + int concurrentRuns = randomIntBetween(20, 50); + List pitIds = new ArrayList<>(); + pitIds.add(pitResponse.getId()); + DeletePitRequest deletePITRequest = new DeletePitRequest(pitIds); + AtomicInteger numDeleteAcknowledged = new AtomicInteger(); + TestThreadPool testThreadPool = null; + try { + testThreadPool = new TestThreadPool(DeletePitMultiNodeIT.class.getName()); + List operationThreads = new ArrayList<>(); + CountDownLatch countDownLatch = new CountDownLatch(concurrentRuns); + for (int i = 0; i < concurrentRuns; i++) { + Runnable thread = () -> { + logger.info("Triggering pit delete --->"); + LatchedActionListener listener = new LatchedActionListener<>(new ActionListener() { + @Override + public void onResponse(DeletePitResponse deletePitResponse) { + if (deletePitResponse.getDeletePitResults().get(0).isSuccessful()) { + numDeleteAcknowledged.incrementAndGet(); + } + } + + @Override + public void onFailure(Exception e) {} + }, countDownLatch); + client().execute(DeletePitAction.INSTANCE, deletePITRequest, listener); + }; + operationThreads.add(thread); + } + TestThreadPool finalTestThreadPool = testThreadPool; + operationThreads.forEach(runnable -> finalTestThreadPool.executor("generic").execute(runnable)); + countDownLatch.await(); + assertEquals(concurrentRuns, numDeleteAcknowledged.get()); + } finally { + ThreadPool.terminate(testThreadPool, 500, TimeUnit.MILLISECONDS); + } + } + + public void validatePitStats(String index, long expectedPitCurrent, long expectedPitCount) throws ExecutionException, + InterruptedException { + IndicesStatsRequest indicesStatsRequest = new IndicesStatsRequest(); + indicesStatsRequest.indices(index); + indicesStatsRequest.all(); + IndicesStatsResponse indicesStatsResponse = client().admin().indices().stats(indicesStatsRequest).get(); + long pitCurrent = indicesStatsResponse.getIndex(index).getTotal().search.getTotal().getPitCurrent(); + long pitCount = indicesStatsResponse.getIndex(index).getTotal().search.getTotal().getPitCount(); + assertEquals(expectedPitCurrent, pitCurrent); + assertEquals(expectedPitCount, pitCount); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/pit/PitMultiNodeIT.java b/server/src/internalClusterTest/java/org/opensearch/search/pit/PitMultiNodeIT.java new file mode 100644 index 0000000000000..413e5eafe56c7 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/search/pit/PitMultiNodeIT.java @@ -0,0 +1,494 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.pit; + +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.opensearch.action.LatchedActionListener; +import org.opensearch.action.admin.cluster.state.ClusterStateRequest; +import org.opensearch.action.admin.cluster.state.ClusterStateResponse; +import org.opensearch.action.admin.indices.stats.IndicesStatsRequest; +import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import org.opensearch.action.search.CreatePitAction; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitAction; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.action.search.GetAllPitNodesRequest; +import org.opensearch.action.search.GetAllPitNodesResponse; +import org.opensearch.action.search.GetAllPitsAction; +import org.opensearch.action.search.PitTestsUtil; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.action.ActionListener; +import org.opensearch.search.builder.PointInTimeBuilder; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import org.opensearch.threadpool.TestThreadPool; +import org.opensearch.threadpool.ThreadPool; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import static org.opensearch.action.search.PitTestsUtil.assertSegments; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.containsString; + +/** + * Multi node integration tests for PIT creation and search operation with PIT ID. + */ +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, numDataNodes = 2) +public class PitMultiNodeIT extends ParameterizedOpenSearchIntegTestCase { + public PitMultiNodeIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + + @Before + public void setupIndex() throws ExecutionException, InterruptedException { + createIndex("index", Settings.builder().put("index.number_of_shards", 2).put("index.number_of_replicas", 0).build()); + client().prepareIndex("index").setId("1").setSource("field", "value").setRefreshPolicy(IMMEDIATE).execute().get(); + ensureGreen(); + } + + @After + public void clearIndex() { + client().admin().indices().prepareDelete("index").get(); + } + + public void testPit() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + SearchResponse searchResponse = client().prepareSearch("index") + .setSize(2) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId()).setKeepAlive(TimeValue.timeValueDays(1))) + .get(); + assertEquals(2, searchResponse.getSuccessfulShards()); + assertEquals(2, searchResponse.getTotalShards()); + validatePitStats("index", 2, 2); + PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, client(), pitResponse.getId()); + } + + public void testCreatePitWhileNodeDropWithAllowPartialCreationFalse() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), false); + request.setIndices(new String[] { "index" }); + internalCluster().restartRandomDataNode(new InternalTestCluster.RestartCallback() { + @Override + public Settings onNodeStopped(String nodeName) throws Exception { + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + ExecutionException ex = expectThrows(ExecutionException.class, execute::get); + assertTrue(ex.getMessage().contains("Failed to execute phase [create_pit]")); + validatePitStats("index", 0, 0); + return super.onNodeStopped(nodeName); + } + }); + } + + public void testCreatePitWhileNodeDropWithAllowPartialCreationTrue() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + internalCluster().restartRandomDataNode(new InternalTestCluster.RestartCallback() { + @Override + public Settings onNodeStopped(String nodeName) throws Exception { + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + assertSegments(false, "index", 1, client(), pitResponse.getId()); + assertEquals(1, pitResponse.getSuccessfulShards()); + assertEquals(2, pitResponse.getTotalShards()); + SearchResponse searchResponse = client().prepareSearch("index") + .setSize(2) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId()).setKeepAlive(TimeValue.timeValueDays(1))) + .get(); + assertEquals(1, searchResponse.getSuccessfulShards()); + assertEquals(1, searchResponse.getTotalShards()); + validatePitStats("index", 1, 1); + return super.onNodeStopped(nodeName); + } + }); + } + + public void testPitSearchWithNodeDrop() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + internalCluster().restartRandomDataNode(new InternalTestCluster.RestartCallback() { + @Override + public Settings onNodeStopped(String nodeName) throws Exception { + SearchResponse searchResponse = client().prepareSearch() + .setSize(2) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId()).setKeepAlive(TimeValue.timeValueDays(1))) + .get(); + assertEquals(1, searchResponse.getSuccessfulShards()); + assertEquals(1, searchResponse.getFailedShards()); + assertEquals(0, searchResponse.getSkippedShards()); + assertEquals(2, searchResponse.getTotalShards()); + validatePitStats("index", 1, 1); + PitTestsUtil.assertUsingGetAllPits(client(), pitResponse.getId(), pitResponse.getCreationTime()); + return super.onNodeStopped(nodeName); + } + }); + } + + public void testPitSearchWithNodeDropWithPartialSearchResultsFalse() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + internalCluster().restartRandomDataNode(new InternalTestCluster.RestartCallback() { + @Override + public Settings onNodeStopped(String nodeName) throws Exception { + ActionFuture execute = client().prepareSearch() + .setSize(2) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId()).setKeepAlive(TimeValue.timeValueDays(1))) + .setAllowPartialSearchResults(false) + .execute(); + ExecutionException ex = expectThrows(ExecutionException.class, execute::get); + assertTrue(ex.getMessage().contains("Partial shards failure")); + return super.onNodeStopped(nodeName); + } + }); + } + + public void testPitInvalidDefaultKeepAlive() { + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put("point_in_time.max_keep_alive", "1m").put("search.default_keep_alive", "2m")) + .get() + ); + assertThat(exc.getMessage(), containsString("was (2m > 1m)")); + assertAcked( + client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put("search.default_keep_alive", "5m").put("point_in_time.max_keep_alive", "5m")) + .get() + ); + assertAcked( + client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put("search.default_keep_alive", "2m")) + .get() + ); + assertAcked( + client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put("point_in_time.max_keep_alive", "2m")) + .get() + ); + exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put("search.default_keep_alive", "3m")) + .get() + ); + assertThat(exc.getMessage(), containsString("was (3m > 2m)")); + assertAcked( + client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put("search.default_keep_alive", "1m")) + .get() + ); + exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put("point_in_time.max_keep_alive", "30s")) + .get() + ); + assertThat(exc.getMessage(), containsString("was (1m > 30s)")); + assertAcked( + client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().putNull("*")) + .setTransientSettings(Settings.builder().putNull("*")) + ); + } + + public void testConcurrentCreates() throws InterruptedException { + CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueDays(1), true); + createPitRequest.setIndices(new String[] { "index" }); + + int concurrentRuns = randomIntBetween(20, 50); + AtomicInteger numSuccess = new AtomicInteger(); + TestThreadPool testThreadPool = null; + try { + testThreadPool = new TestThreadPool(PitMultiNodeIT.class.getName()); + List operationThreads = new ArrayList<>(); + CountDownLatch countDownLatch = new CountDownLatch(concurrentRuns); + Set createSet = new HashSet<>(); + for (int i = 0; i < concurrentRuns; i++) { + Runnable thread = () -> { + logger.info("Triggering pit create --->"); + LatchedActionListener listener = new LatchedActionListener<>(new ActionListener() { + @Override + public void onResponse(CreatePitResponse createPitResponse) { + if (createSet.add(createPitResponse.getId())) { + numSuccess.incrementAndGet(); + } + } + + @Override + public void onFailure(Exception e) {} + }, countDownLatch); + client().execute(CreatePitAction.INSTANCE, createPitRequest, listener); + }; + operationThreads.add(thread); + } + TestThreadPool finalTestThreadPool = testThreadPool; + operationThreads.forEach(runnable -> finalTestThreadPool.executor("generic").execute(runnable)); + countDownLatch.await(); + assertEquals(concurrentRuns, numSuccess.get()); + } finally { + ThreadPool.terminate(testThreadPool, 500, TimeUnit.MILLISECONDS); + } + } + + public void testConcurrentCreatesWithDeletes() throws InterruptedException, ExecutionException { + CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueDays(1), true); + createPitRequest.setIndices(new String[] { "index" }); + List pitIds = new ArrayList<>(); + String id = client().execute(CreatePitAction.INSTANCE, createPitRequest).get().getId(); + pitIds.add(id); + DeletePitRequest deletePITRequest = new DeletePitRequest(pitIds); + Set createSet = new HashSet<>(); + AtomicInteger numSuccess = new AtomicInteger(); + TestThreadPool testThreadPool = null; + try { + testThreadPool = new TestThreadPool(PitMultiNodeIT.class.getName()); + int concurrentRuns = randomIntBetween(20, 50); + + List operationThreads = new ArrayList<>(); + CountDownLatch countDownLatch = new CountDownLatch(concurrentRuns); + long randomDeleteThread = randomLongBetween(0, concurrentRuns - 1); + for (int i = 0; i < concurrentRuns; i++) { + int currentThreadIteration = i; + Runnable thread = () -> { + if (currentThreadIteration == randomDeleteThread) { + LatchedActionListener listener = new LatchedActionListener<>(new ActionListener() { + @Override + public void onResponse(CreatePitResponse createPitResponse) { + if (createSet.add(createPitResponse.getId())) { + numSuccess.incrementAndGet(); + } + } + + @Override + public void onFailure(Exception e) {} + }, countDownLatch); + client().execute(CreatePitAction.INSTANCE, createPitRequest, listener); + } else { + LatchedActionListener listener = new LatchedActionListener<>(new ActionListener() { + @Override + public void onResponse(DeletePitResponse deletePitResponse) { + if (deletePitResponse.getDeletePitResults().get(0).isSuccessful()) { + numSuccess.incrementAndGet(); + } + } + + @Override + public void onFailure(Exception e) {} + }, countDownLatch); + client().execute(DeletePitAction.INSTANCE, deletePITRequest, listener); + } + }; + operationThreads.add(thread); + } + TestThreadPool finalTestThreadPool = testThreadPool; + operationThreads.forEach(runnable -> finalTestThreadPool.executor("generic").execute(runnable)); + countDownLatch.await(); + assertEquals(concurrentRuns, numSuccess.get()); + + } finally { + ThreadPool.terminate(testThreadPool, 500, TimeUnit.MILLISECONDS); + } + } + + public void validatePitStats(String index, long expectedPitCurrent, long expectedOpenContexts) throws ExecutionException, + InterruptedException { + IndicesStatsRequest indicesStatsRequest = new IndicesStatsRequest(); + indicesStatsRequest.indices("index"); + indicesStatsRequest.all(); + IndicesStatsResponse indicesStatsResponse = client().admin().indices().stats(indicesStatsRequest).get(); + long pitCurrent = indicesStatsResponse.getIndex(index).getTotal().search.getTotal().getPitCurrent(); + long openContexts = indicesStatsResponse.getIndex(index).getTotal().search.getOpenContexts(); + assertEquals(expectedPitCurrent, pitCurrent); + assertEquals(expectedOpenContexts, openContexts); + } + + public void testGetAllPits() throws Exception { + client().admin().indices().prepareCreate("index1").get(); + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index", "index1" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + CreatePitResponse pitResponse1 = client().execute(CreatePitAction.INSTANCE, request).get(); + CreatePitResponse pitResponse2 = client().execute(CreatePitAction.INSTANCE, request).get(); + final ClusterStateRequest clusterStateRequest = new ClusterStateRequest(); + clusterStateRequest.local(false); + clusterStateRequest.clear().nodes(true).routingTable(true).indices("*"); + ClusterStateResponse clusterStateResponse = client().admin().cluster().state(clusterStateRequest).get(); + final List nodes = new LinkedList<>(); + for (final DiscoveryNode node : clusterStateResponse.getState().nodes().getDataNodes().values()) { + nodes.add(node); + } + DiscoveryNode[] disNodesArr = new DiscoveryNode[nodes.size()]; + nodes.toArray(disNodesArr); + GetAllPitNodesRequest getAllPITNodesRequest = new GetAllPitNodesRequest(disNodesArr); + ActionFuture execute1 = client().execute(GetAllPitsAction.INSTANCE, getAllPITNodesRequest); + GetAllPitNodesResponse getPitResponse = execute1.get(); + assertEquals(3, getPitResponse.getPitInfos().size()); + List resultPitIds = getPitResponse.getPitInfos().stream().map(p -> p.getPitId()).collect(Collectors.toList()); + // asserting that we get all unique PIT IDs + Assert.assertTrue(resultPitIds.contains(pitResponse.getId())); + Assert.assertTrue(resultPitIds.contains(pitResponse1.getId())); + Assert.assertTrue(resultPitIds.contains(pitResponse2.getId())); + client().admin().indices().prepareDelete("index1").get(); + } + + public void testGetAllPitsDuringNodeDrop() throws Exception { + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "index" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + GetAllPitNodesRequest getAllPITNodesRequest = new GetAllPitNodesRequest(getDiscoveryNodes()); + internalCluster().restartRandomDataNode(new InternalTestCluster.RestartCallback() { + @Override + public Settings onNodeStopped(String nodeName) throws Exception { + ActionFuture execute1 = client().execute(GetAllPitsAction.INSTANCE, getAllPITNodesRequest); + GetAllPitNodesResponse getPitResponse = execute1.get(); + // we still get a pit id from the data node which is up + assertEquals(1, getPitResponse.getPitInfos().size()); + // failure for node drop + assertEquals(1, getPitResponse.failures().size()); + assertTrue(getPitResponse.failures().get(0).getMessage().contains("Failed node")); + return super.onNodeStopped(nodeName); + } + }); + } + + private DiscoveryNode[] getDiscoveryNodes() throws ExecutionException, InterruptedException { + final ClusterStateRequest clusterStateRequest = new ClusterStateRequest(); + clusterStateRequest.local(false); + clusterStateRequest.clear().nodes(true).routingTable(true).indices("*"); + ClusterStateResponse clusterStateResponse = client().admin().cluster().state(clusterStateRequest).get(); + final List nodes = new LinkedList<>(clusterStateResponse.getState().nodes().getDataNodes().values()); + DiscoveryNode[] disNodesArr = new DiscoveryNode[nodes.size()]; + nodes.toArray(disNodesArr); + return disNodesArr; + } + + public void testConcurrentGetWithDeletes() throws InterruptedException, ExecutionException { + CreatePitRequest createPitRequest = new CreatePitRequest(TimeValue.timeValueDays(1), true); + createPitRequest.setIndices(new String[] { "index" }); + List pitIds = new ArrayList<>(); + String id = client().execute(CreatePitAction.INSTANCE, createPitRequest).get().getId(); + pitIds.add(id); + DeletePitRequest deletePITRequest = new DeletePitRequest(pitIds); + GetAllPitNodesRequest getAllPITNodesRequest = new GetAllPitNodesRequest(getDiscoveryNodes()); + AtomicInteger numSuccess = new AtomicInteger(); + TestThreadPool testThreadPool = null; + try { + testThreadPool = new TestThreadPool(PitMultiNodeIT.class.getName()); + int concurrentRuns = randomIntBetween(20, 50); + + List operationThreads = new ArrayList<>(); + CountDownLatch countDownLatch = new CountDownLatch(concurrentRuns); + long randomDeleteThread = randomLongBetween(0, concurrentRuns - 1); + for (int i = 0; i < concurrentRuns; i++) { + int currentThreadIteration = i; + Runnable thread = () -> { + if (currentThreadIteration == randomDeleteThread) { + LatchedActionListener listener = new LatchedActionListener<>(new ActionListener() { + @Override + public void onResponse(GetAllPitNodesResponse getAllPitNodesResponse) { + if (getAllPitNodesResponse.failures().isEmpty()) { + numSuccess.incrementAndGet(); + } + } + + @Override + public void onFailure(Exception e) {} + }, countDownLatch); + client().execute(GetAllPitsAction.INSTANCE, getAllPITNodesRequest, listener); + } else { + LatchedActionListener listener = new LatchedActionListener<>(new ActionListener() { + @Override + public void onResponse(DeletePitResponse deletePitResponse) { + if (deletePitResponse.getDeletePitResults().get(0).isSuccessful()) { + numSuccess.incrementAndGet(); + } + } + + @Override + public void onFailure(Exception e) {} + }, countDownLatch); + client().execute(DeletePitAction.INSTANCE, deletePITRequest, listener); + } + }; + operationThreads.add(thread); + } + TestThreadPool finalTestThreadPool = testThreadPool; + operationThreads.forEach(runnable -> finalTestThreadPool.executor("generic").execute(runnable)); + countDownLatch.await(); + assertEquals(concurrentRuns, numSuccess.get()); + + } finally { + ThreadPool.terminate(testThreadPool, 500, TimeUnit.MILLISECONDS); + } + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/preference/SearchPreferenceIT.java b/server/src/internalClusterTest/java/org/opensearch/search/preference/SearchPreferenceIT.java index c69555d00170b..425764b1c88d2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/preference/SearchPreferenceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/preference/SearchPreferenceIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.preference; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.opensearch.action.search.SearchRequestBuilder; @@ -41,20 +43,25 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.routing.OperationRouting; import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.common.Strings; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.node.Node; -import org.opensearch.rest.RestStatus; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Set; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -64,7 +71,24 @@ import static org.hamcrest.Matchers.not; @OpenSearchIntegTestCase.ClusterScope(minNumDataNodes = 2) -public class SearchPreferenceIT extends OpenSearchIntegTestCase { +public class SearchPreferenceIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchPreferenceIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override public Settings nodeSettings(int nodeOrdinal) { @@ -89,7 +113,9 @@ public void testStopOneNodePreferenceWithRedState() throws IOException { internalCluster().stopRandomDataNode(); client().admin().cluster().prepareHealth().setWaitForStatus(ClusterHealthStatus.RED).get(); String[] preferences = new String[] { + "_primary", "_local", + "_primary_first", "_prefer_nodes:somenode", "_prefer_nodes:server2", "_prefer_nodes:somenode,server2" }; @@ -134,7 +160,7 @@ public void testNoPreferenceRandom() { } public void testSimplePreference() { - client().admin().indices().prepareCreate("test").setSettings("{\"number_of_replicas\": 1}", XContentType.JSON).get(); + client().admin().indices().prepareCreate("test").setSettings("{\"number_of_replicas\": 1}", MediaTypeRegistry.JSON).get(); ensureGreen(); client().prepareIndex("test").setSource("field1", "value1").get(); @@ -143,11 +169,24 @@ public void testSimplePreference() { SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).get(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("_local").get(); + searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("_local").execute().actionGet(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); - searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("1234").get(); + searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("_primary").execute().actionGet(); assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + + searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("_primary_first").execute().actionGet(); + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + + searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("_replica").execute().actionGet(); + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + + searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("_replica_first").execute().actionGet(); + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + + searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setPreference("1234").execute().actionGet(); + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(1L)); + } public void testThatSpecifyingNonExistingNodesReturnsUsefulError() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/profile/ProfilerSingleNodeNetworkTest.java b/server/src/internalClusterTest/java/org/opensearch/search/profile/ProfilerSingleNodeNetworkTest.java index 12d68c9c38ca1..de7677e3b3708 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/profile/ProfilerSingleNodeNetworkTest.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/profile/ProfilerSingleNodeNetworkTest.java @@ -18,9 +18,9 @@ import java.util.List; import java.util.Map; +import static org.opensearch.search.profile.query.RandomQueryGenerator.randomQueryBuilder; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -import static org.opensearch.search.profile.query.RandomQueryGenerator.randomQueryBuilder; public class ProfilerSingleNodeNetworkTest extends OpenSearchSingleNodeTestCase { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/profile/aggregation/AggregationProfilerIT.java b/server/src/internalClusterTest/java/org/opensearch/search/profile/aggregation/AggregationProfilerIT.java index f3d1a479f1b46..82dd6225fda4e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/profile/aggregation/AggregationProfilerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/profile/aggregation/AggregationProfilerIT.java @@ -32,44 +32,66 @@ package org.opensearch.search.profile.aggregation; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.search.aggregations.Aggregator.SubAggCollectionMode; import org.opensearch.search.aggregations.BucketOrder; +import org.opensearch.search.aggregations.InternalAggregation; +import org.opensearch.search.aggregations.bucket.global.Global; import org.opensearch.search.aggregations.bucket.sampler.DiversifiedOrdinalsSamplerAggregator; import org.opensearch.search.aggregations.bucket.terms.GlobalOrdinalsStringTermsAggregator; +import org.opensearch.search.aggregations.metrics.Stats; import org.opensearch.search.profile.ProfileResult; import org.opensearch.search.profile.ProfileShardResult; +import org.opensearch.search.profile.query.CollectorResult; +import org.opensearch.search.profile.query.QueryProfileShardResult; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import org.hamcrest.core.IsNull; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.aggregations.AggregationBuilders.avg; import static org.opensearch.search.aggregations.AggregationBuilders.diversifiedSampler; +import static org.opensearch.search.aggregations.AggregationBuilders.global; import static org.opensearch.search.aggregations.AggregationBuilders.histogram; import static org.opensearch.search.aggregations.AggregationBuilders.max; +import static org.opensearch.search.aggregations.AggregationBuilders.stats; import static org.opensearch.search.aggregations.AggregationBuilders.terms; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.sameInstance; @OpenSearchIntegTestCase.SuiteScopeTestCase -public class AggregationProfilerIT extends OpenSearchIntegTestCase { +public class AggregationProfilerIT extends ParameterizedOpenSearchIntegTestCase { + private static final String BUILD_LEAF_COLLECTOR = AggregationTimingType.BUILD_LEAF_COLLECTOR.toString(); private static final String COLLECT = AggregationTimingType.COLLECT.toString(); private static final String POST_COLLECTION = AggregationTimingType.POST_COLLECTION.toString(); private static final String INITIALIZE = AggregationTimingType.INITIALIZE.toString(); private static final String BUILD_AGGREGATION = AggregationTimingType.BUILD_AGGREGATION.toString(); private static final String REDUCE = AggregationTimingType.REDUCE.toString(); - private static final Set BREAKDOWN_KEYS = org.opensearch.common.collect.Set.of( + private static final Set BREAKDOWN_KEYS = Set.of( INITIALIZE, BUILD_LEAF_COLLECTOR, COLLECT, @@ -81,7 +103,52 @@ public class AggregationProfilerIT extends OpenSearchIntegTestCase { COLLECT + "_count", POST_COLLECTION + "_count", BUILD_AGGREGATION + "_count", - REDUCE + "_count" + REDUCE + "_count", + INITIALIZE + "_start_time", + BUILD_LEAF_COLLECTOR + "_start_time", + COLLECT + "_start_time", + POST_COLLECTION + "_start_time", + BUILD_AGGREGATION + "_start_time", + REDUCE + "_start_time" + ); + + private static final Set CONCURRENT_SEARCH_BREAKDOWN_KEYS = Set.of( + INITIALIZE, + BUILD_LEAF_COLLECTOR, + COLLECT, + POST_COLLECTION, + BUILD_AGGREGATION, + REDUCE, + INITIALIZE + "_count", + BUILD_LEAF_COLLECTOR + "_count", + COLLECT + "_count", + POST_COLLECTION + "_count", + BUILD_AGGREGATION + "_count", + REDUCE + "_count", + "max_" + INITIALIZE, + "max_" + BUILD_LEAF_COLLECTOR, + "max_" + COLLECT, + "max_" + POST_COLLECTION, + "max_" + BUILD_AGGREGATION, + "max_" + REDUCE, + "min_" + INITIALIZE, + "min_" + BUILD_LEAF_COLLECTOR, + "min_" + COLLECT, + "min_" + POST_COLLECTION, + "min_" + BUILD_AGGREGATION, + "min_" + REDUCE, + "avg_" + INITIALIZE, + "avg_" + BUILD_LEAF_COLLECTOR, + "avg_" + COLLECT, + "avg_" + POST_COLLECTION, + "avg_" + BUILD_AGGREGATION, + "avg_" + REDUCE, + "max_" + BUILD_LEAF_COLLECTOR + "_count", + "max_" + COLLECT + "_count", + "min_" + BUILD_LEAF_COLLECTOR + "_count", + "min_" + COLLECT + "_count", + "avg_" + BUILD_LEAF_COLLECTOR + "_count", + "avg_" + COLLECT + "_count" ); private static final String TOTAL_BUCKETS = "total_buckets"; @@ -95,6 +162,26 @@ public class AggregationProfilerIT extends OpenSearchIntegTestCase { private static final String NUMBER_FIELD = "number"; private static final String TAG_FIELD = "tag"; private static final String STRING_FIELD = "string_field"; + private final int numDocs = 5; + private static final String REASON_SEARCH_TOP_HITS = "search_top_hits"; + private static final String REASON_AGGREGATION = "aggregation"; + + public AggregationProfilerIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected int numberOfShards() { @@ -107,7 +194,7 @@ protected void setupSuiteScopeCluster() throws Exception { client().admin() .indices() .prepareCreate("idx") - .setSettings(org.opensearch.common.collect.Map.of("number_of_shards", 1, "number_of_replicas", 0)) + .setSettings(Map.of("number_of_shards", 1, "number_of_replicas", 0)) .setMapping(STRING_FIELD, "type=keyword", NUMBER_FIELD, "type=integer", TAG_FIELD, "type=keyword") .get() ); @@ -118,7 +205,7 @@ protected void setupSuiteScopeCluster() throws Exception { randomStrings[i] = randomAlphaOfLength(10); } - for (int i = 0; i < 5; i++) { + for (int i = 0; i < numDocs; i++) { builders.add( client().prepareIndex("idx") .setSource( @@ -159,14 +246,25 @@ public void testSimpleProfile() { assertThat(histoAggResult.getTime(), greaterThan(0L)); Map breakdown = histoAggResult.getTimeBreakdown(); assertThat(breakdown, notNullValue()); - assertThat(breakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (histoAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(breakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResultWithConcurrentSearchEnabled(collectorResult, 2); + } + } else { + assertThat(breakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResult(collectorResult, 2); + } + } assertThat(breakdown.get(INITIALIZE), greaterThan(0L)); assertThat(breakdown.get(COLLECT), greaterThan(0L)); assertThat(breakdown.get(BUILD_AGGREGATION).longValue(), greaterThan(0L)); assertThat(breakdown.get(REDUCE), equalTo(0L)); Map debug = histoAggResult.getDebugInfo(); assertThat(debug, notNullValue()); - assertThat(debug.keySet(), equalTo(org.opensearch.common.collect.Set.of(TOTAL_BUCKETS))); + assertThat(debug.keySet(), equalTo(Set.of(TOTAL_BUCKETS))); assertThat(((Number) debug.get(TOTAL_BUCKETS)).longValue(), greaterThan(0L)); } } @@ -202,14 +300,25 @@ public void testMultiLevelProfile() { assertThat(histoAggResult.getTime(), greaterThan(0L)); Map histoBreakdown = histoAggResult.getTimeBreakdown(); assertThat(histoBreakdown, notNullValue()); - assertThat(histoBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (histoAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(histoBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResultWithConcurrentSearchEnabled(collectorResult, 2); + } + } else { + assertThat(histoBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResult(collectorResult, 2); + } + } assertThat(histoBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(histoBreakdown.get(COLLECT), greaterThan(0L)); assertThat(histoBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(histoBreakdown.get(REDUCE), equalTo(0L)); Map histoDebugInfo = histoAggResult.getDebugInfo(); assertThat(histoDebugInfo, notNullValue()); - assertThat(histoDebugInfo.keySet(), equalTo(org.opensearch.common.collect.Set.of(TOTAL_BUCKETS))); + assertThat(histoDebugInfo.keySet(), equalTo(Set.of(TOTAL_BUCKETS))); assertThat(((Number) histoDebugInfo.get(TOTAL_BUCKETS)).longValue(), greaterThan(0L)); assertThat(histoAggResult.getProfiledChildren().size(), equalTo(1)); @@ -220,7 +329,12 @@ public void testMultiLevelProfile() { assertThat(termsAggResult.getTime(), greaterThan(0L)); Map termsBreakdown = termsAggResult.getTimeBreakdown(); assertThat(termsBreakdown, notNullValue()); - assertThat(termsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (termsAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(termsBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(termsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(termsBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(termsBreakdown.get(COLLECT), greaterThan(0L)); assertThat(termsBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); @@ -235,12 +349,17 @@ public void testMultiLevelProfile() { assertThat(avgAggResult.getTime(), greaterThan(0L)); Map avgBreakdown = termsAggResult.getTimeBreakdown(); assertThat(avgBreakdown, notNullValue()); - assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (avgAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(avgBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(avgBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(avgBreakdown.get(COLLECT), greaterThan(0L)); assertThat(avgBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(avgBreakdown.get(REDUCE), equalTo(0L)); - assertThat(avgAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(avgAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(avgAggResult.getProfiledChildren().size(), equalTo(0)); } } @@ -288,14 +407,25 @@ public void testMultiLevelProfileBreadthFirst() { assertThat(histoAggResult.getTime(), greaterThan(0L)); Map histoBreakdown = histoAggResult.getTimeBreakdown(); assertThat(histoBreakdown, notNullValue()); - assertThat(histoBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (histoAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(histoBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResultWithConcurrentSearchEnabled(collectorResult, 2); + } + } else { + assertThat(histoBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResult(collectorResult, 2); + } + } assertThat(histoBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(histoBreakdown.get(COLLECT), greaterThan(0L)); assertThat(histoBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(histoBreakdown.get(REDUCE), equalTo(0L)); Map histoDebugInfo = histoAggResult.getDebugInfo(); assertThat(histoDebugInfo, notNullValue()); - assertThat(histoDebugInfo.keySet(), equalTo(org.opensearch.common.collect.Set.of(TOTAL_BUCKETS))); + assertThat(histoDebugInfo.keySet(), equalTo(Set.of(TOTAL_BUCKETS))); assertThat(((Number) histoDebugInfo.get(TOTAL_BUCKETS)).longValue(), greaterThan(0L)); assertThat(histoAggResult.getProfiledChildren().size(), equalTo(1)); @@ -306,7 +436,12 @@ public void testMultiLevelProfileBreadthFirst() { assertThat(termsAggResult.getTime(), greaterThan(0L)); Map termsBreakdown = termsAggResult.getTimeBreakdown(); assertThat(termsBreakdown, notNullValue()); - assertThat(termsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (termsAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(termsBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(termsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(termsBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(termsBreakdown.get(COLLECT), greaterThan(0L)); assertThat(termsBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); @@ -321,12 +456,17 @@ public void testMultiLevelProfileBreadthFirst() { assertThat(avgAggResult.getTime(), greaterThan(0L)); Map avgBreakdown = avgAggResult.getTimeBreakdown(); assertThat(avgBreakdown, notNullValue()); - assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (avgAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(avgBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(avgBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(avgBreakdown.get(COLLECT), greaterThan(0L)); assertThat(avgBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(avgBreakdown.get(REDUCE), equalTo(0L)); - assertThat(avgAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(avgAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(avgAggResult.getProfiledChildren().size(), equalTo(0)); } } @@ -359,17 +499,25 @@ public void testDiversifiedAggProfile() { assertThat(diversifyAggResult.getTime(), greaterThan(0L)); Map diversifyBreakdown = diversifyAggResult.getTimeBreakdown(); assertThat(diversifyBreakdown, notNullValue()); - assertThat(diversifyBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (diversifyAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(diversifyBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResultWithConcurrentSearchEnabled(collectorResult, 2); + } + } else { + assertThat(diversifyBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResult(collectorResult, 2); + } + } assertThat(diversifyBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(diversifyBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(diversifyBreakdown.get(COLLECT), greaterThan(0L)); assertThat(diversifyBreakdown.get(POST_COLLECTION), greaterThan(0L)); assertThat(diversifyBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(diversifyBreakdown.get(REDUCE), equalTo(0L)); - assertThat( - diversifyAggResult.getDebugInfo(), - equalTo(org.opensearch.common.collect.Map.of(DEFERRED, org.opensearch.common.collect.List.of("max"))) - ); + assertThat(diversifyAggResult.getDebugInfo(), equalTo(Map.of(DEFERRED, List.of("max")))); assertThat(diversifyAggResult.getProfiledChildren().size(), equalTo(1)); ProfileResult maxAggResult = diversifyAggResult.getProfiledChildren().get(0); @@ -379,14 +527,19 @@ public void testDiversifiedAggProfile() { assertThat(maxAggResult.getTime(), greaterThan(0L)); Map maxBreakdown = maxAggResult.getTimeBreakdown(); assertThat(maxBreakdown, notNullValue()); - assertThat(maxBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (maxAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(maxBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(maxBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(diversifyBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(diversifyBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(diversifyBreakdown.get(COLLECT), greaterThan(0L)); assertThat(diversifyBreakdown.get(POST_COLLECTION), greaterThan(0L)); assertThat(maxBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(maxBreakdown.get(REDUCE), equalTo(0L)); - assertThat(maxAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(maxAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(maxAggResult.getProfiledChildren().size(), equalTo(0)); } } @@ -432,7 +585,18 @@ public void testComplexProfile() { assertThat(histoAggResult.getTime(), greaterThan(0L)); Map histoBreakdown = histoAggResult.getTimeBreakdown(); assertThat(histoBreakdown, notNullValue()); - assertThat(histoBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (histoAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(histoBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResultWithConcurrentSearchEnabled(collectorResult, 2); + } + } else { + assertThat(histoBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResult(collectorResult, 2); + } + } assertThat(histoBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(histoBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(histoBreakdown.get(COLLECT), greaterThan(0L)); @@ -441,7 +605,7 @@ public void testComplexProfile() { assertThat(histoBreakdown.get(REDUCE), equalTo(0L)); Map histoDebugInfo = histoAggResult.getDebugInfo(); assertThat(histoDebugInfo, notNullValue()); - assertThat(histoDebugInfo.keySet(), equalTo(org.opensearch.common.collect.Set.of(TOTAL_BUCKETS))); + assertThat(histoDebugInfo.keySet(), equalTo(Set.of(TOTAL_BUCKETS))); assertThat(((Number) histoDebugInfo.get(TOTAL_BUCKETS)).longValue(), greaterThan(0L)); assertThat(histoAggResult.getProfiledChildren().size(), equalTo(2)); @@ -455,7 +619,12 @@ public void testComplexProfile() { assertThat(tagsAggResult.getTime(), greaterThan(0L)); Map tagsBreakdown = tagsAggResult.getTimeBreakdown(); assertThat(tagsBreakdown, notNullValue()); - assertThat(tagsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (tagsAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(tagsBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(tagsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(tagsBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(tagsBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(tagsBreakdown.get(COLLECT), greaterThan(0L)); @@ -475,14 +644,19 @@ public void testComplexProfile() { assertThat(avgAggResult.getTime(), greaterThan(0L)); Map avgBreakdown = avgAggResult.getTimeBreakdown(); assertThat(avgBreakdown, notNullValue()); - assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (avgAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(avgBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(avgBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(avgBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(avgBreakdown.get(COLLECT), greaterThan(0L)); assertThat(avgBreakdown.get(POST_COLLECTION), greaterThan(0L)); assertThat(avgBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(avgBreakdown.get(REDUCE), equalTo(0L)); - assertThat(avgAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(avgAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(avgAggResult.getProfiledChildren().size(), equalTo(0)); ProfileResult maxAggResult = tagsAggResultSubAggregations.get("max"); @@ -491,14 +665,19 @@ public void testComplexProfile() { assertThat(maxAggResult.getTime(), greaterThan(0L)); Map maxBreakdown = maxAggResult.getTimeBreakdown(); assertThat(maxBreakdown, notNullValue()); - assertThat(maxBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (maxAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(maxBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(maxBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(maxBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(maxBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(maxBreakdown.get(COLLECT), greaterThan(0L)); assertThat(maxBreakdown.get(POST_COLLECTION), greaterThan(0L)); assertThat(maxBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(maxBreakdown.get(REDUCE), equalTo(0L)); - assertThat(maxAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(maxAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(maxAggResult.getProfiledChildren().size(), equalTo(0)); ProfileResult stringsAggResult = histoAggResultSubAggregations.get("strings"); @@ -507,7 +686,12 @@ public void testComplexProfile() { assertThat(stringsAggResult.getTime(), greaterThan(0L)); Map stringsBreakdown = stringsAggResult.getTimeBreakdown(); assertThat(stringsBreakdown, notNullValue()); - assertThat(stringsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (stringsAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(stringsBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(stringsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(stringsBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(stringsBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(stringsBreakdown.get(COLLECT), greaterThan(0L)); @@ -527,14 +711,19 @@ public void testComplexProfile() { assertThat(avgAggResult.getTime(), greaterThan(0L)); avgBreakdown = avgAggResult.getTimeBreakdown(); assertThat(avgBreakdown, notNullValue()); - assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (avgAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(avgBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(avgBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(avgBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(avgBreakdown.get(COLLECT), greaterThan(0L)); assertThat(avgBreakdown.get(POST_COLLECTION), greaterThan(0L)); assertThat(avgBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(avgBreakdown.get(REDUCE), equalTo(0L)); - assertThat(avgAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(avgAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(avgAggResult.getProfiledChildren().size(), equalTo(0)); maxAggResult = stringsAggResultSubAggregations.get("max"); @@ -543,14 +732,19 @@ public void testComplexProfile() { assertThat(maxAggResult.getTime(), greaterThan(0L)); maxBreakdown = maxAggResult.getTimeBreakdown(); assertThat(maxBreakdown, notNullValue()); - assertThat(maxBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (maxAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(maxBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(maxBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(maxBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(maxBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(maxBreakdown.get(COLLECT), greaterThan(0L)); assertThat(maxBreakdown.get(POST_COLLECTION), greaterThan(0L)); assertThat(maxBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(maxBreakdown.get(REDUCE), equalTo(0L)); - assertThat(maxAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(maxAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(maxAggResult.getProfiledChildren().size(), equalTo(0)); tagsAggResult = stringsAggResultSubAggregations.get("tags"); @@ -560,7 +754,12 @@ public void testComplexProfile() { assertThat(tagsAggResult.getTime(), greaterThan(0L)); tagsBreakdown = tagsAggResult.getTimeBreakdown(); assertThat(tagsBreakdown, notNullValue()); - assertThat(tagsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (tagsAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(tagsBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(tagsBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(tagsBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(tagsBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(tagsBreakdown.get(COLLECT), greaterThan(0L)); @@ -580,14 +779,19 @@ public void testComplexProfile() { assertThat(avgAggResult.getTime(), greaterThan(0L)); avgBreakdown = avgAggResult.getTimeBreakdown(); assertThat(avgBreakdown, notNullValue()); - assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (avgAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(avgBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(avgBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(avgBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(avgBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(avgBreakdown.get(COLLECT), greaterThan(0L)); assertThat(avgBreakdown.get(POST_COLLECTION), greaterThan(0L)); assertThat(avgBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(avgBreakdown.get(REDUCE), equalTo(0L)); - assertThat(avgAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(avgAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(avgAggResult.getProfiledChildren().size(), equalTo(0)); maxAggResult = tagsAggResultSubAggregations.get("max"); @@ -596,14 +800,19 @@ public void testComplexProfile() { assertThat(maxAggResult.getTime(), greaterThan(0L)); maxBreakdown = maxAggResult.getTimeBreakdown(); assertThat(maxBreakdown, notNullValue()); - assertThat(maxBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + if (maxAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(maxBreakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + } else { + assertThat(maxBreakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + } assertThat(maxBreakdown.get(INITIALIZE), greaterThan(0L)); assertThat(maxBreakdown.get(BUILD_LEAF_COLLECTOR), greaterThan(0L)); assertThat(maxBreakdown.get(COLLECT), greaterThan(0L)); assertThat(maxBreakdown.get(POST_COLLECTION), greaterThan(0L)); assertThat(maxBreakdown.get(BUILD_AGGREGATION), greaterThan(0L)); assertThat(maxBreakdown.get(REDUCE), equalTo(0L)); - assertThat(maxAggResult.getDebugInfo(), equalTo(org.opensearch.common.collect.Map.of())); + assertThat(maxAggResult.getDebugInfo(), equalTo(Map.of())); assertThat(maxAggResult.getProfiledChildren().size(), equalTo(0)); } } @@ -636,4 +845,165 @@ public void testNoProfile() { assertThat(profileResults, notNullValue()); assertThat(profileResults.size(), equalTo(0)); } + + public void testGlobalAggWithStatsSubAggregatorProfile() { + boolean profileEnabled = true; + SearchResponse response = client().prepareSearch("idx") + .addAggregation(global("global").subAggregation(stats("value_stats").field(NUMBER_FIELD))) + .setProfile(profileEnabled) + .get(); + + assertSearchResponse(response); + Global global = response.getAggregations().get("global"); + assertThat(global, IsNull.notNullValue()); + assertThat(global.getName(), equalTo("global")); + assertThat(global.getDocCount(), equalTo((long) numDocs)); + assertThat((long) ((InternalAggregation) global).getProperty("_count"), equalTo((long) numDocs)); + assertThat(global.getAggregations().asList().isEmpty(), is(false)); + + Stats stats = global.getAggregations().get("value_stats"); + assertThat((Stats) ((InternalAggregation) global).getProperty("value_stats"), sameInstance(stats)); + assertThat(stats, IsNull.notNullValue()); + assertThat(stats.getName(), equalTo("value_stats")); + + Map profileResults = response.getProfileResults(); + assertThat(profileResults, notNullValue()); + assertThat(profileResults.size(), equalTo(getNumShards("idx").numPrimaries)); + for (ProfileShardResult profileShardResult : profileResults.values()) { + assertThat(profileShardResult, notNullValue()); + List queryProfileShardResults = profileShardResult.getQueryProfileResults(); + assertEquals(queryProfileShardResults.size(), 2); + // ensure there is no multi collector getting added with only global agg + for (QueryProfileShardResult queryProfileShardResult : queryProfileShardResults) { + assertEquals(queryProfileShardResult.getQueryResults().size(), 1); + if (queryProfileShardResult.getQueryResults().get(0).getQueryName().equals("MatchAllDocsQuery")) { + assertEquals(0, queryProfileShardResult.getQueryResults().get(0).getProfiledChildren().size()); + assertEquals("search_top_hits", queryProfileShardResult.getCollectorResult().getReason()); + assertEquals(0, queryProfileShardResult.getCollectorResult().getProfiledChildren().size()); + } else if (queryProfileShardResult.getQueryResults().get(0).getQueryName().equals("ConstantScoreQuery")) { + assertEquals(1, queryProfileShardResult.getQueryResults().get(0).getProfiledChildren().size()); + assertEquals("aggregation_global", queryProfileShardResult.getCollectorResult().getReason()); + assertEquals(0, queryProfileShardResult.getCollectorResult().getProfiledChildren().size()); + } else { + fail("unexpected profile shard result in the response"); + } + } + AggregationProfileShardResult aggProfileResults = profileShardResult.getAggregationProfileResults(); + assertThat(aggProfileResults, notNullValue()); + List aggProfileResultsList = aggProfileResults.getProfileResults(); + assertThat(aggProfileResultsList, notNullValue()); + assertEquals(1, aggProfileResultsList.size()); + ProfileResult globalAggResult = aggProfileResultsList.get(0); + assertThat(globalAggResult, notNullValue()); + assertEquals("GlobalAggregator", globalAggResult.getQueryName()); + assertEquals("global", globalAggResult.getLuceneDescription()); + assertEquals(1, globalAggResult.getProfiledChildren().size()); + assertThat(globalAggResult.getTime(), greaterThan(0L)); + Map breakdown = globalAggResult.getTimeBreakdown(); + assertThat(breakdown, notNullValue()); + if (globalAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertEquals(CONCURRENT_SEARCH_BREAKDOWN_KEYS, breakdown.keySet()); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResultWithConcurrentSearchEnabled(collectorResult, 0); + } + } else { + assertEquals(BREAKDOWN_KEYS, breakdown.keySet()); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResult(collectorResult, 0); + } + } + assertThat(breakdown.get(INITIALIZE), greaterThan(0L)); + assertThat(breakdown.get(COLLECT), greaterThan(0L)); + assertThat(breakdown.get(BUILD_AGGREGATION).longValue(), greaterThan(0L)); + assertEquals(0, breakdown.get(REDUCE).intValue()); + } + } + + public void testMultipleAggregationsProfile() { + SearchResponse response = client().prepareSearch("idx") + .setProfile(true) + .addAggregation(histogram("histo_1").field(NUMBER_FIELD).interval(1L)) + .addAggregation(histogram("histo_2").field(NUMBER_FIELD).interval(1L)) + .get(); + assertSearchResponse(response); + Map profileResults = response.getProfileResults(); + assertThat(profileResults, notNullValue()); + assertThat(profileResults.size(), equalTo(getNumShards("idx").numPrimaries)); + for (ProfileShardResult profileShardResult : profileResults.values()) { + assertThat(profileShardResult, notNullValue()); + List queryProfilerResults = profileShardResult.getQueryProfileResults(); + assertThat(queryProfilerResults, notNullValue()); + for (QueryProfileShardResult queryProfilerResult : queryProfilerResults) { + CollectorResult collectorResult = queryProfilerResult.getCollectorResult(); + String reason = collectorResult.getReason(); + assertThat(reason, equalTo("search_multi")); + List children = collectorResult.getProfiledChildren(); + assertThat(children.size(), equalTo(2)); + assertThat(children.get(1).getName(), containsString("[histo_1, histo_2]")); + } + AggregationProfileShardResult aggProfileResults = profileShardResult.getAggregationProfileResults(); + assertThat(aggProfileResults, notNullValue()); + List aggProfileResultsList = aggProfileResults.getProfileResults(); + assertThat(aggProfileResultsList, notNullValue()); + assertThat(aggProfileResultsList.size(), equalTo(2)); + for (ProfileResult histoAggResult : aggProfileResultsList) { + assertThat(histoAggResult, notNullValue()); + assertThat(histoAggResult.getQueryName(), equalTo("NumericHistogramAggregator")); + assertThat(histoAggResult.getLuceneDescription(), containsString("histo_")); + assertThat(histoAggResult.getProfiledChildren().size(), equalTo(0)); + assertThat(histoAggResult.getTime(), greaterThan(0L)); + Map breakdown = histoAggResult.getTimeBreakdown(); + assertThat(breakdown, notNullValue()); + if (histoAggResult.getMaxSliceTime() != null) { + // concurrent segment search enabled + assertThat(breakdown.keySet(), equalTo(CONCURRENT_SEARCH_BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResultWithConcurrentSearchEnabled(collectorResult, 2); + } + } else { + assertThat(breakdown.keySet(), equalTo(BREAKDOWN_KEYS)); + for (QueryProfileShardResult collectorResult : profileShardResult.getQueryProfileResults()) { + assertCollectorResult(collectorResult, 2); + } + } + assertThat(breakdown.get(INITIALIZE), greaterThan(0L)); + assertThat(breakdown.get(COLLECT), greaterThan(0L)); + assertThat(breakdown.get(BUILD_AGGREGATION).longValue(), greaterThan(0L)); + assertThat(breakdown.get(REDUCE), equalTo(0L)); + Map debug = histoAggResult.getDebugInfo(); + assertThat(debug, notNullValue()); + assertThat(debug.keySet(), equalTo(Set.of(TOTAL_BUCKETS))); + assertThat(((Number) debug.get(TOTAL_BUCKETS)).longValue(), greaterThan(0L)); + } + } + } + + private void assertCollectorResult(QueryProfileShardResult collectorResult, int expectedChildrenCount) { + long nodeTime = collectorResult.getCollectorResult().getTime(); + assertThat(collectorResult.getCollectorResult().getMaxSliceTime(), equalTo(nodeTime)); + assertThat(collectorResult.getCollectorResult().getMinSliceTime(), equalTo(nodeTime)); + assertThat(collectorResult.getCollectorResult().getAvgSliceTime(), equalTo(nodeTime)); + assertThat(collectorResult.getCollectorResult().getReduceTime(), equalTo(0L)); + assertThat(collectorResult.getCollectorResult().getSliceCount(), equalTo(1)); + assertThat(collectorResult.getCollectorResult().getProfiledChildren().size(), equalTo(expectedChildrenCount)); + if (expectedChildrenCount == 2) { + assertThat(collectorResult.getCollectorResult().getProfiledChildren().get(0).getReason(), equalTo(REASON_SEARCH_TOP_HITS)); + assertThat(collectorResult.getCollectorResult().getProfiledChildren().get(1).getReason(), equalTo(REASON_AGGREGATION)); + } + } + + private void assertCollectorResultWithConcurrentSearchEnabled(QueryProfileShardResult collectorResult, int expectedChildrenCount) { + long nodeTime = collectorResult.getCollectorResult().getTime(); + assertThat(collectorResult.getCollectorResult().getMaxSliceTime(), lessThanOrEqualTo(nodeTime)); + assertThat(collectorResult.getCollectorResult().getMinSliceTime(), lessThanOrEqualTo(nodeTime)); + assertThat(collectorResult.getCollectorResult().getAvgSliceTime(), lessThanOrEqualTo(nodeTime)); + assertThat(collectorResult.getCollectorResult().getReduceTime(), greaterThan(0L)); + assertThat(collectorResult.getCollectorResult().getSliceCount(), greaterThanOrEqualTo(1)); + assertThat(collectorResult.getCollectorResult().getProfiledChildren().size(), equalTo(expectedChildrenCount)); + if (expectedChildrenCount == 2) { + assertThat(collectorResult.getCollectorResult().getProfiledChildren().get(0).getReason(), equalTo(REASON_SEARCH_TOP_HITS)); + assertThat(collectorResult.getCollectorResult().getProfiledChildren().get(1).getReason(), equalTo(REASON_AGGREGATION)); + } + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/profile/query/QueryProfilerIT.java b/server/src/internalClusterTest/java/org/opensearch/search/profile/query/QueryProfilerIT.java index a74f359f2542e..5f794d2abf878 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/profile/query/QueryProfilerIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/profile/query/QueryProfilerIT.java @@ -34,7 +34,11 @@ import org.apache.lucene.tests.util.English; import org.opensearch.action.index.IndexRequestBuilder; -import org.opensearch.action.search.*; +import org.opensearch.action.search.MultiSearchResponse; +import org.opensearch.action.search.SearchRequestBuilder; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.action.search.SearchType; +import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.common.settings.Settings; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryBuilders; @@ -44,15 +48,19 @@ import org.opensearch.search.sort.SortOrder; import org.opensearch.test.OpenSearchIntegTestCase; -import java.util.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; -import static org.hamcrest.Matchers.is; +import static org.opensearch.search.profile.query.RandomQueryGenerator.randomQueryBuilder; +import static org.hamcrest.Matchers.emptyOrNullString; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.emptyOrNullString; -import static org.hamcrest.Matchers.equalTo; -import static org.opensearch.search.profile.query.RandomQueryGenerator.randomQueryBuilder; public class QueryProfilerIT extends OpenSearchIntegTestCase { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/query/ExistsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/query/ExistsIT.java index 75b8e46802061..e3253ea583ac2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/query/ExistsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/query/ExistsIT.java @@ -32,18 +32,23 @@ package org.opensearch.search.query; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.explain.ExplainResponse; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.SearchHit; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -52,11 +57,29 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSearchResponse; -public class ExistsIT extends OpenSearchIntegTestCase { +public class ExistsIT extends ParameterizedOpenSearchIntegTestCase { + + public ExistsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } // TODO: move this to a unit test somewhere... public void testEmptyIndex() throws Exception { @@ -140,14 +163,7 @@ public void testExists() throws Exception { assertSearchResponse(resp); try { assertEquals( - String.format( - Locale.ROOT, - "exists(%s, %d) mapping: %s response: %s", - fieldName, - count, - Strings.toString(mapping), - resp - ), + String.format(Locale.ROOT, "exists(%s, %d) mapping: %s response: %s", fieldName, count, mapping.toString(), resp), count, resp.getHits().getTotalHits().value ); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/query/MultiMatchQueryIT.java b/server/src/internalClusterTest/java/org/opensearch/search/query/MultiMatchQueryIT.java index d87bbfb1fb69c..457114bac33b8 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/query/MultiMatchQueryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/query/MultiMatchQueryIT.java @@ -31,15 +31,18 @@ package org.opensearch.search.query; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.Fuzziness; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.MatchQueryBuilder; import org.opensearch.index.query.MultiMatchQueryBuilder; import org.opensearch.index.query.Operator; @@ -50,13 +53,13 @@ import org.opensearch.search.SearchHits; import org.opensearch.search.sort.SortBuilders; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.MockKeywordPlugin; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.Before; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -73,6 +76,7 @@ import static org.opensearch.index.query.QueryBuilders.matchQuery; import static org.opensearch.index.query.QueryBuilders.multiMatchQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFirstHit; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -88,7 +92,24 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThan; -public class MultiMatchQueryIT extends OpenSearchIntegTestCase { +public class MultiMatchQueryIT extends ParameterizedOpenSearchIntegTestCase { + + public MultiMatchQueryIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -1024,7 +1045,7 @@ public void testFuzzyFieldLevelBoosting() throws InterruptedException, Execution SearchResponse searchResponse = client().prepareSearch(idx) .setExplain(true) - .setQuery(multiMatchQuery("foo").field("title", 100).field("body").fuzziness(0)) + .setQuery(multiMatchQuery("foo").field("title", 100).field("body").fuzziness(Fuzziness.ZERO)) .get(); SearchHit[] hits = searchResponse.getHits().getHits(); assertNotEquals("both documents should be on different shards", hits[0].getShard().getShardId(), hits[1].getShard().getShardId()); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/query/QueryStringIT.java b/server/src/internalClusterTest/java/org/opensearch/search/query/QueryStringIT.java index 5c7e53fda3f23..099eb934f4f4d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/query/QueryStringIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/query/QueryStringIT.java @@ -32,30 +32,36 @@ package org.opensearch.search.query; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ExceptionsHelper; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.Operator; import org.opensearch.index.query.QueryStringQueryBuilder; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.SearchModule; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.Before; import org.junit.BeforeClass; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.queryStringQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.StreamsUtils.copyToStringFromClasspath; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -64,10 +70,27 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -public class QueryStringIT extends OpenSearchIntegTestCase { +public class QueryStringIT extends ParameterizedOpenSearchIntegTestCase { private static int CLUSTER_MAX_CLAUSE_COUNT; + public QueryStringIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @BeforeClass public static void createRandomClusterSetting() { CLUSTER_MAX_CLAUSE_COUNT = randomIntBetween(50, 100); @@ -76,7 +99,7 @@ public static void createRandomClusterSetting() { @Before public void setup() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - prepareCreate("test").setSource(indexBody, XContentType.JSON).get(); + prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON).get(); ensureGreen("test"); } @@ -161,7 +184,7 @@ public void testWithLotsOfTypes() throws Exception { public void testDocWithAllTypes() throws Exception { List reqs = new ArrayList<>(); String docBody = copyToStringFromClasspath("/org/opensearch/search/query/all-example-document.json"); - reqs.add(client().prepareIndex("test").setId("1").setSource(docBody, XContentType.JSON)); + reqs.add(client().prepareIndex("test").setId("1").setSource(docBody, MediaTypeRegistry.JSON)); indexRandom(true, false, reqs); SearchResponse resp = client().prepareSearch("test").setQuery(queryStringQuery("foo")).get(); @@ -216,11 +239,44 @@ public void testKeywordWithWhitespace() throws Exception { assertHitCount(resp, 3L); } + public void testRegexCaseInsensitivity() throws Exception { + createIndex("messages"); + List indexRequests = new ArrayList<>(); + indexRequests.add(client().prepareIndex("messages").setId("1").setSource("message", "message: this is a TLS handshake")); + indexRequests.add(client().prepareIndex("messages").setId("2").setSource("message", "message: this is a tcp handshake")); + indexRandom(true, false, indexRequests); + + SearchResponse response = client().prepareSearch("messages").setQuery(queryStringQuery("/TLS/").defaultField("message")).get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertHits(response.getHits(), "1"); + + response = client().prepareSearch("messages").setQuery(queryStringQuery("/tls/").defaultField("message")).get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertHits(response.getHits(), "1"); + + response = client().prepareSearch("messages").setQuery(queryStringQuery("/TCP/").defaultField("message")).get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertHits(response.getHits(), "2"); + + response = client().prepareSearch("messages").setQuery(queryStringQuery("/tcp/").defaultField("message")).get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertHits(response.getHits(), "2"); + + response = client().prepareSearch("messages").setQuery(queryStringQuery("/HANDSHAKE/").defaultField("message")).get(); + assertNoFailures(response); + assertHitCount(response, 2); + assertHits(response.getHits(), "1", "2"); + } + public void testAllFields() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); Settings.Builder settings = Settings.builder().put("index.query.default_field", "*"); - prepareCreate("test_1").setSource(indexBody, XContentType.JSON).setSettings(settings).get(); + prepareCreate("test_1").setSource(indexBody, MediaTypeRegistry.JSON).setSettings(settings).get(); ensureGreen("test_1"); List reqs = new ArrayList<>(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/query/ScriptScoreQueryIT.java b/server/src/internalClusterTest/java/org/opensearch/search/query/ScriptScoreQueryIT.java index d736365a6e236..7ba582811bbc2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/query/ScriptScoreQueryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/query/ScriptScoreQueryIT.java @@ -32,10 +32,13 @@ package org.opensearch.search.query; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.RangeQueryBuilder; @@ -43,8 +46,9 @@ import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -54,6 +58,7 @@ import static org.opensearch.index.query.QueryBuilders.boolQuery; import static org.opensearch.index.query.QueryBuilders.matchQuery; import static org.opensearch.index.query.QueryBuilders.scriptScoreQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFirstHit; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; @@ -62,7 +67,24 @@ import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertThirdHit; import static org.opensearch.test.hamcrest.OpenSearchAssertions.hasScore; -public class ScriptScoreQueryIT extends OpenSearchIntegTestCase { +public class ScriptScoreQueryIT extends ParameterizedOpenSearchIntegTestCase { + + public ScriptScoreQueryIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/query/SearchQueryIT.java b/server/src/internalClusterTest/java/org/opensearch/search/query/SearchQueryIT.java index fed5561c1df64..53bded1fc493c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/query/SearchQueryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/query/SearchQueryIT.java @@ -32,27 +32,30 @@ package org.opensearch.search.query; -import org.apache.lucene.tests.analysis.MockTokenizer; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.analysis.pattern.PatternReplaceCharFilter; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.join.ScoreMode; -import org.apache.lucene.util.AttributeSource; +import org.apache.lucene.tests.analysis.MockTokenizer; import org.apache.lucene.tests.util.English; - +import org.apache.lucene.util.AttributeSource; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.SearchType; -import org.opensearch.bootstrap.JavaVersion; import org.opensearch.common.document.DocumentField; import org.opensearch.common.lucene.search.SpanBooleanQueryRewriteWithMaxClause; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.unit.Fuzziness; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.analysis.CharFilterFactory; import org.opensearch.index.analysis.NormalizingCharFilterFactory; import org.opensearch.index.analysis.TokenizerFactory; @@ -73,12 +76,11 @@ import org.opensearch.indices.analysis.AnalysisModule.AnalysisProvider; import org.opensearch.plugins.AnalysisPlugin; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.aggregations.AggregationBuilders; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.junit.annotations.TestIssueLogging; import java.io.IOException; @@ -90,8 +92,10 @@ import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; @@ -125,6 +129,7 @@ import static org.opensearch.index.query.QueryBuilders.termsQuery; import static org.opensearch.index.query.QueryBuilders.wildcardQuery; import static org.opensearch.index.query.QueryBuilders.wrapperQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFailures; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFirstHit; @@ -137,11 +142,29 @@ import static org.opensearch.test.hamcrest.OpenSearchAssertions.hasId; import static org.opensearch.test.hamcrest.OpenSearchAssertions.hasScore; import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -public class SearchQueryIT extends OpenSearchIntegTestCase { +public class SearchQueryIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchQueryIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -760,21 +783,21 @@ public void testMatchQueryFuzzy() throws Exception { client().prepareIndex("test").setId("2").setSource("text", "Unity") ); - SearchResponse searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness("0")).get(); + SearchResponse searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.ZERO)).get(); assertHitCount(searchResponse, 0L); - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness("1")).get(); + searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.ONE)).get(); assertHitCount(searchResponse, 2L); assertSearchHits(searchResponse, "1", "2"); - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness("AUTO")).get(); + searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.AUTO)).get(); assertHitCount(searchResponse, 2L); assertSearchHits(searchResponse, "1", "2"); - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness("AUTO:5,7")).get(); + searchResponse = client().prepareSearch().setQuery(matchQuery("text", "uniy").fuzziness(Fuzziness.customAuto(5, 7))).get(); assertHitCount(searchResponse, 0L); - searchResponse = client().prepareSearch().setQuery(matchQuery("text", "unify").fuzziness("AUTO:5,7")).get(); + searchResponse = client().prepareSearch().setQuery(matchQuery("text", "unify").fuzziness(Fuzziness.customAuto(5, 7))).get(); assertHitCount(searchResponse, 1L); assertSearchHits(searchResponse, "2"); } @@ -1860,7 +1883,6 @@ public void testRangeQueryWithTimeZone() throws Exception { * on "Configuring IDEs And Running Tests". */ public void testRangeQueryWithLocaleMapping() throws Exception { - assumeTrue("need java 9 for testing ", JavaVersion.current().compareTo(JavaVersion.parse("9")) >= 0); assert ("SPI,COMPAT".equals(System.getProperty("java.locale.providers"))) : "`-Djava.locale.providers=SPI,COMPAT` needs to be set"; assertAcked( @@ -1895,8 +1917,8 @@ public void testRangeQueryWithLocaleMapping() throws Exception { } public void testSearchEmptyDoc() { - assertAcked(prepareCreate("test").setSettings("{\"index.analysis.analyzer.default.type\":\"keyword\"}", XContentType.JSON)); - client().prepareIndex("test").setId("1").setSource("{}", XContentType.JSON).get(); + assertAcked(prepareCreate("test").setSettings("{\"index.analysis.analyzer.default.type\":\"keyword\"}", MediaTypeRegistry.JSON)); + client().prepareIndex("test").setId("1").setSource("{}", MediaTypeRegistry.JSON).get(); refresh(); assertHitCount(client().prepareSearch().setQuery(matchAllQuery()).get(), 1L); @@ -2091,8 +2113,14 @@ public void testWildcardQueryNormalizationOnTextField() { refresh(); { + // test default case insensitivity: false WildcardQueryBuilder wildCardQuery = wildcardQuery("field1", "Bb*"); SearchResponse searchResponse = client().prepareSearch().setQuery(wildCardQuery).get(); + assertHitCount(searchResponse, 0L); + + // test case insensitivity set to true + wildCardQuery = wildcardQuery("field1", "Bb*").caseInsensitive(true); + searchResponse = client().prepareSearch().setQuery(wildCardQuery).get(); assertHitCount(searchResponse, 1L); wildCardQuery = wildcardQuery("field1", "bb*"); @@ -2101,6 +2129,24 @@ public void testWildcardQueryNormalizationOnTextField() { } } + /** tests wildcard case sensitivity */ + public void testWildcardCaseSensitivity() { + assertAcked(prepareCreate("test").setMapping("field", "type=text")); + client().prepareIndex("test").setId("1").setSource("field", "lowercase text").get(); + refresh(); + + // test case sensitive + SearchResponse response = client().prepareSearch("test").setQuery(wildcardQuery("field", "Text").caseInsensitive(false)).get(); + assertNoFailures(response); + assertHitCount(response, 0); + + // test case insensitive + response = client().prepareSearch("test").setQuery(wildcardQuery("field", "Text").caseInsensitive(true)).get(); + assertNoFailures(response); + assertHitCount(response, 1); + assertHits(response.getHits(), "1"); + } + /** * Reserved characters should be excluded when the normalization is applied for keyword fields. * See https://github.com/elastic/elasticsearch/issues/46300 for details. @@ -2177,4 +2223,16 @@ public void testIssueFuzzyInsideSpanMulti() { SearchResponse response = client().prepareSearch("test").setQuery(query).get(); assertHitCount(response, 1); } + + /** + * asserts the search response hits include the expected ids + */ + private void assertHits(SearchHits hits, String... ids) { + assertThat(hits.getTotalHits().value, equalTo((long) ids.length)); + Set hitIds = new HashSet<>(); + for (SearchHit hit : hits.getHits()) { + hitIds.add(hit.getId()); + } + assertThat(hitIds, containsInAnyOrder(ids)); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/query/SimpleQueryStringIT.java b/server/src/internalClusterTest/java/org/opensearch/search/query/SimpleQueryStringIT.java index e0391d9cbc971..384d2b7423e66 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/query/SimpleQueryStringIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/query/SimpleQueryStringIT.java @@ -32,20 +32,21 @@ package org.opensearch.search.query; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; - import org.opensearch.ExceptionsHelper; import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.analysis.PreConfiguredTokenFilter; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.BoolQueryBuilder; @@ -59,12 +60,12 @@ import org.opensearch.search.SearchHits; import org.opensearch.search.SearchModule; import org.opensearch.search.builder.SearchSourceBuilder; -import org.opensearch.test.OpenSearchIntegTestCase; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.junit.BeforeClass; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -78,6 +79,7 @@ import static org.opensearch.index.query.QueryBuilders.queryStringQuery; import static org.opensearch.index.query.QueryBuilders.simpleQueryStringQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.StreamsUtils.copyToStringFromClasspath; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFailures; @@ -93,10 +95,27 @@ /** * Tests for the {@code simple_query_string} query */ -public class SimpleQueryStringIT extends OpenSearchIntegTestCase { +public class SimpleQueryStringIT extends ParameterizedOpenSearchIntegTestCase { private static int CLUSTER_MAX_CLAUSE_COUNT; + public SimpleQueryStringIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @BeforeClass public static void createRandomClusterSetting() { CLUSTER_MAX_CLAUSE_COUNT = randomIntBetween(60, 100); @@ -373,17 +392,16 @@ public void testLenientFlagBeingTooLenient() throws Exception { } public void testSimpleQueryStringAnalyzeWildcard() throws ExecutionException, InterruptedException, IOException { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("location") - .field("type", "text") - .field("analyzer", "standard") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("location") + .field("type", "text") + .field("analyzer", "standard") + .endObject() + .endObject() + .endObject() + .toString(); CreateIndexRequestBuilder mappingRequest = client().admin().indices().prepareCreate("test1").setMapping(mapping); mappingRequest.get(); @@ -420,17 +438,16 @@ public void testSimpleQueryStringOnIndexMetaField() throws Exception { public void testEmptySimpleQueryStringWithAnalysis() throws Exception { // https://github.com/elastic/elasticsearch/issues/18202 - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("body") - .field("type", "text") - .field("analyzer", "stop") - .endObject() - .endObject() - .endObject() - ); + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("body") + .field("type", "text") + .field("analyzer", "stop") + .endObject() + .endObject() + .endObject() + .toString(); CreateIndexRequestBuilder mappingRequest = client().admin().indices().prepareCreate("test1").setMapping(mapping); mappingRequest.get(); @@ -444,7 +461,7 @@ public void testEmptySimpleQueryStringWithAnalysis() throws Exception { public void testBasicAllQuery() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - prepareCreate("test").setSource(indexBody, XContentType.JSON).get(); + prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON).get(); ensureGreen("test"); List reqs = new ArrayList<>(); @@ -468,7 +485,7 @@ public void testBasicAllQuery() throws Exception { public void testWithDate() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - prepareCreate("test").setSource(indexBody, XContentType.JSON).get(); + prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON).get(); ensureGreen("test"); List reqs = new ArrayList<>(); @@ -495,7 +512,7 @@ public void testWithDate() throws Exception { public void testWithLotsOfTypes() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - prepareCreate("test").setSource(indexBody, XContentType.JSON).get(); + prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON).get(); ensureGreen("test"); List reqs = new ArrayList<>(); @@ -526,12 +543,12 @@ public void testWithLotsOfTypes() throws Exception { public void testDocWithAllTypes() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - prepareCreate("test").setSource(indexBody, XContentType.JSON).get(); + prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON).get(); ensureGreen("test"); List reqs = new ArrayList<>(); String docBody = copyToStringFromClasspath("/org/opensearch/search/query/all-example-document.json"); - reqs.add(client().prepareIndex("test").setId("1").setSource(docBody, XContentType.JSON)); + reqs.add(client().prepareIndex("test").setId("1").setSource(docBody, MediaTypeRegistry.JSON)); indexRandom(true, false, reqs); SearchResponse resp = client().prepareSearch("test").setQuery(simpleQueryStringQuery("foo")).get(); @@ -571,7 +588,7 @@ public void testDocWithAllTypes() throws Exception { public void testKeywordWithWhitespace() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - prepareCreate("test").setSource(indexBody, XContentType.JSON).get(); + prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON).get(); ensureGreen("test"); List reqs = new ArrayList<>(); @@ -591,7 +608,7 @@ public void testKeywordWithWhitespace() throws Exception { public void testAllFieldsWithSpecifiedLeniency() throws IOException { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - prepareCreate("test").setSource(indexBody, XContentType.JSON).get(); + prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON).get(); ensureGreen("test"); SearchPhaseExecutionException e = expectThrows( @@ -638,7 +655,7 @@ private void doAssertLimitExceededException(String field, int exceedingFieldCoun public void testFieldAlias() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - assertAcked(prepareCreate("test").setSource(indexBody, XContentType.JSON)); + assertAcked(prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON)); ensureGreen("test"); List indexRequests = new ArrayList<>(); @@ -656,7 +673,7 @@ public void testFieldAlias() throws Exception { public void testFieldAliasWithWildcardField() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - assertAcked(prepareCreate("test").setSource(indexBody, XContentType.JSON)); + assertAcked(prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON)); ensureGreen("test"); List indexRequests = new ArrayList<>(); @@ -674,7 +691,7 @@ public void testFieldAliasWithWildcardField() throws Exception { public void testFieldAliasOnDisallowedFieldType() throws Exception { String indexBody = copyToStringFromClasspath("/org/opensearch/search/query/all-query-index.json"); - assertAcked(prepareCreate("test").setSource(indexBody, XContentType.JSON)); + assertAcked(prepareCreate("test").setSource(indexBody, MediaTypeRegistry.JSON)); ensureGreen("test"); List indexRequests = new ArrayList<>(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/scriptfilter/ScriptQuerySearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/scriptfilter/ScriptQuerySearchIT.java index 5e45fd8d0ad2a..34967528f2c4f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/scriptfilter/ScriptQuerySearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/scriptfilter/ScriptQuerySearchIT.java @@ -32,12 +32,15 @@ package org.opensearch.search.scriptfilter; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexModule; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.plugins.Plugin; @@ -45,8 +48,9 @@ import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.Arrays; @@ -61,12 +65,29 @@ import static java.util.Collections.emptyMap; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.scriptQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE) -public class ScriptQuerySearchIT extends OpenSearchIntegTestCase { +public class ScriptQuerySearchIT extends ParameterizedOpenSearchIntegTestCase { + public ScriptQuerySearchIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/scroll/DuelScrollIT.java b/server/src/internalClusterTest/java/org/opensearch/search/scroll/DuelScrollIT.java index 19cf1ee3a0ee7..c7a6d18f881c6 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/scroll/DuelScrollIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/scroll/DuelScrollIT.java @@ -32,7 +32,7 @@ package org.opensearch.search.scroll; -import com.carrotsearch.hppc.IntHashSet; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.opensearch.action.index.IndexRequestBuilder; @@ -40,22 +40,44 @@ import org.opensearch.action.search.SearchType; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.QueryBuilders; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.sort.SortBuilder; import org.opensearch.search.sort.SortBuilders; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; -public class DuelScrollIT extends OpenSearchIntegTestCase { +public class DuelScrollIT extends ParameterizedOpenSearchIntegTestCase { + public DuelScrollIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testDuelQueryThenFetch() throws Exception { TestContext context = create(SearchType.DFS_QUERY_THEN_FETCH, SearchType.QUERY_THEN_FETCH); @@ -145,7 +167,7 @@ private TestContext create(SearchType... searchTypes) throws Exception { boolean unevenRouting = randomBoolean(); int numMissingDocs = scaledRandomIntBetween(0, numDocs / 100); - IntHashSet missingDocs = new IntHashSet(numMissingDocs); + final Set missingDocs = new HashSet<>(numMissingDocs); for (int i = 0; i < numMissingDocs; i++) { while (!missingDocs.add(randomInt(numDocs))) { } diff --git a/server/src/internalClusterTest/java/org/opensearch/search/scroll/SearchScrollIT.java b/server/src/internalClusterTest/java/org/opensearch/search/scroll/SearchScrollIT.java index 3818566e2eb11..0eee136acac69 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/scroll/SearchScrollIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/scroll/SearchScrollIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.scroll; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.ExceptionsHelper; import org.opensearch.action.search.ClearScrollResponse; import org.opensearch.action.search.SearchPhaseExecutionException; @@ -41,28 +43,30 @@ import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Priority; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.RangeQueryBuilder; -import org.opensearch.rest.RestStatus; import org.opensearch.search.SearchHit; import org.opensearch.search.sort.FieldSortBuilder; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; - import org.junit.After; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -71,6 +75,7 @@ import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.queryStringQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; @@ -87,7 +92,24 @@ /** * Tests for scrolling. */ -public class SearchScrollIT extends OpenSearchIntegTestCase { +public class SearchScrollIT extends ParameterizedOpenSearchIntegTestCase { + public SearchScrollIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @After public void cleanup() throws Exception { assertAcked( diff --git a/server/src/internalClusterTest/java/org/opensearch/search/scroll/SearchScrollWithFailingNodesIT.java b/server/src/internalClusterTest/java/org/opensearch/search/scroll/SearchScrollWithFailingNodesIT.java index a56f8667fab48..f16b9a4d67b49 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/scroll/SearchScrollWithFailingNodesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/scroll/SearchScrollWithFailingNodesIT.java @@ -32,26 +32,50 @@ package org.opensearch.search.scroll; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.routing.allocation.decider.ShardsLimitAllocationDecider; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAllSuccessful; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; -@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0, numClientNodes = 0) -public class SearchScrollWithFailingNodesIT extends OpenSearchIntegTestCase { +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 2, numClientNodes = 0) +public class SearchScrollWithFailingNodesIT extends ParameterizedOpenSearchIntegTestCase { + public SearchScrollWithFailingNodesIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected int numberOfShards() { return 2; @@ -63,8 +87,6 @@ protected int numberOfReplicas() { } public void testScanScrollWithShardExceptions() throws Exception { - internalCluster().startNode(); - internalCluster().startNode(); assertAcked( prepareCreate("test") // Enforces that only one shard can only be allocated to a single node @@ -97,7 +119,7 @@ public void testScanScrollWithShardExceptions() throws Exception { assertThat(numHits, equalTo(100L)); clearScroll("_all"); - internalCluster().stopRandomNonMasterNode(); + internalCluster().stopRandomNonClusterManagerNode(); searchResponse = client().prepareSearch().setQuery(matchAllQuery()).setSize(10).setScroll(TimeValue.timeValueMinutes(1)).get(); assertThat(searchResponse.getSuccessfulShards(), lessThan(searchResponse.getTotalShards())); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/searchafter/SearchAfterIT.java b/server/src/internalClusterTest/java/org/opensearch/search/searchafter/SearchAfterIT.java index 926e21294ffc8..00ac574b8bd72 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/searchafter/SearchAfterIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/searchafter/SearchAfterIT.java @@ -32,35 +32,64 @@ package org.opensearch.search.searchafter; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.CreatePitAction; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.common.UUIDs; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.SearchHit; +import org.opensearch.search.builder.PointInTimeBuilder; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; -import java.util.List; import java.util.ArrayList; -import java.util.Comparator; -import java.util.Collections; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; -import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -public class SearchAfterIT extends OpenSearchIntegTestCase { +public class SearchAfterIT extends ParameterizedOpenSearchIntegTestCase { private static final String INDEX_NAME = "test"; private static final int NUM_DOCS = 100; + public SearchAfterIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testsShouldFail() throws Exception { assertAcked(client().admin().indices().prepareCreate("test").setMapping("field1", "type=long", "field2", "type=keyword").get()); ensureGreen(); @@ -155,6 +184,58 @@ public void testsShouldFail() throws Exception { } } + public void testPitWithSearchAfter() throws Exception { + assertAcked(client().admin().indices().prepareCreate("test").setMapping("field1", "type=long", "field2", "type=keyword").get()); + ensureGreen(); + indexRandom( + true, + client().prepareIndex("test").setId("0").setSource("field1", 0), + client().prepareIndex("test").setId("1").setSource("field1", 100, "field2", "toto"), + client().prepareIndex("test").setId("2").setSource("field1", 101), + client().prepareIndex("test").setId("3").setSource("field1", 99) + ); + + CreatePitRequest request = new CreatePitRequest(TimeValue.timeValueDays(1), true); + request.setIndices(new String[] { "test" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, request); + CreatePitResponse pitResponse = execute.get(); + SearchResponse sr = client().prepareSearch() + .addSort("field1", SortOrder.ASC) + .setQuery(matchAllQuery()) + .searchAfter(new Object[] { 99 }) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId())) + .get(); + assertEquals(2, sr.getHits().getHits().length); + sr = client().prepareSearch() + .addSort("field1", SortOrder.ASC) + .setQuery(matchAllQuery()) + .searchAfter(new Object[] { 100 }) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId())) + .get(); + assertEquals(1, sr.getHits().getHits().length); + sr = client().prepareSearch() + .addSort("field1", SortOrder.ASC) + .setQuery(matchAllQuery()) + .searchAfter(new Object[] { 0 }) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId())) + .get(); + assertEquals(3, sr.getHits().getHits().length); + /** + * Add new data and assert PIT results remain the same and normal search results gets refreshed + */ + indexRandom(true, client().prepareIndex("test").setId("4").setSource("field1", 102)); + sr = client().prepareSearch() + .addSort("field1", SortOrder.ASC) + .setQuery(matchAllQuery()) + .searchAfter(new Object[] { 0 }) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId())) + .get(); + assertEquals(3, sr.getHits().getHits().length); + sr = client().prepareSearch().addSort("field1", SortOrder.ASC).setQuery(matchAllQuery()).searchAfter(new Object[] { 0 }).get(); + assertEquals(4, sr.getHits().getHits().length); + client().admin().indices().prepareDelete("test").get(); + } + public void testWithNullStrings() throws InterruptedException { assertAcked(client().admin().indices().prepareCreate("test").setMapping("field2", "type=keyword").get()); ensureGreen(); @@ -324,24 +405,22 @@ private void createIndexMappingsFromObjectType(String indexName, List ty ensureGreen(); } - // Convert Integer, Short, Byte and Boolean to Long in order to match the conversion done + // Convert Integer, Short, Byte and Boolean to Int in order to match the conversion done // by the internal hits when populating the sort values. private List convertSortValues(List sortValues) { List converted = new ArrayList<>(); for (int i = 0; i < sortValues.size(); i++) { Object from = sortValues.get(i); - if (from instanceof Integer) { - converted.add(((Integer) from).longValue()); - } else if (from instanceof Short) { - converted.add(((Short) from).longValue()); + if (from instanceof Short) { + converted.add(((Short) from).intValue()); } else if (from instanceof Byte) { - converted.add(((Byte) from).longValue()); + converted.add(((Byte) from).intValue()); } else if (from instanceof Boolean) { boolean b = (boolean) from; if (b) { - converted.add(1L); + converted.add(1); } else { - converted.add(0L); + converted.add(0); } } else { converted.add(from); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/simple/SimpleSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/simple/SimpleSearchIT.java index 7382d4e157bc4..95b36311f6b8b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/simple/SimpleSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/simple/SimpleSearchIT.java @@ -32,26 +32,31 @@ package org.opensearch.search.simple; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.support.WriteRequest.RefreshPolicy; import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.IndexSettings; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.TermQueryBuilder; -import org.opensearch.rest.RestStatus; import org.opensearch.search.rescore.QueryRescorerBuilder; import org.opensearch.search.sort.SortOrder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; @@ -62,14 +67,34 @@ import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.queryStringQuery; import static org.opensearch.index.query.QueryBuilders.rangeQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFailures; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.apache.lucene.search.TotalHits.Relation.EQUAL_TO; +import static org.apache.lucene.search.TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO; + +public class SimpleSearchIT extends ParameterizedOpenSearchIntegTestCase { + + public SimpleSearchIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() } + ); + } -public class SimpleSearchIT extends OpenSearchIntegTestCase { + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } public void testSearchNullIndex() { expectThrows( @@ -99,7 +124,7 @@ public void testSearchRandomPreference() throws InterruptedException, ExecutionE int iters = scaledRandomIntBetween(10, 20); for (int i = 0; i < iters; i++) { String randomPreference = randomUnicodeOfLengthBetween(0, 4); - // randomPreference should not start with '_' (reserved for known preference types (e.g. _shards) + // randomPreference should not start with '_' (reserved for known preference types (e.g. _shards, _primary) while (randomPreference.startsWith("_")) { randomPreference = randomUnicodeOfLengthBetween(0, 4); } @@ -274,13 +299,18 @@ public void testSimpleTerminateAfterCount() throws Exception { refresh(); SearchResponse searchResponse; + int size; for (int i = 1; i < max; i++) { + size = randomIntBetween(0, max); searchResponse = client().prepareSearch("test") .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(max)) .setTerminateAfter(i) + .setSize(size) + .setTrackTotalHits(true) .get(); assertHitCount(searchResponse, i); assertTrue(searchResponse.isTerminatedEarly()); + assertEquals(Math.min(i, size), searchResponse.getHits().getHits().length); } searchResponse = client().prepareSearch("test") @@ -292,6 +322,83 @@ public void testSimpleTerminateAfterCount() throws Exception { assertFalse(searchResponse.isTerminatedEarly()); } + public void testSimpleTerminateAfterTrackTotalHitsUpTo() throws Exception { + prepareCreate("test").setSettings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0)).get(); + ensureGreen(); + int numDocs = 29; + List docbuilders = new ArrayList<>(numDocs); + + for (int i = 1; i <= numDocs; i++) { + String id = String.valueOf(i); + docbuilders.add(client().prepareIndex("test").setId(id).setSource("field", i)); + } + + indexRandom(true, docbuilders); + ensureGreen(); + refresh(); + + // size=0 is a special case where topDocsCollector is not added + int size = randomIntBetween(0, 1); + SearchResponse searchResponse; + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(numDocs)) + .setTerminateAfter(10) + .setSize(size) + .setTrackTotalHitsUpTo(5) + .get(); + assertTrue(searchResponse.isTerminatedEarly()); + assertEquals(5, searchResponse.getHits().getTotalHits().value); + assertEquals(GREATER_THAN_OR_EQUAL_TO, searchResponse.getHits().getTotalHits().relation); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(numDocs)) + .setTerminateAfter(5) + .setSize(size) + .setTrackTotalHitsUpTo(10) + .get(); + assertTrue(searchResponse.isTerminatedEarly()); + assertEquals(5, searchResponse.getHits().getTotalHits().value); + assertEquals(EQUAL_TO, searchResponse.getHits().getTotalHits().relation); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(numDocs)) + .setTerminateAfter(5) + .setSize(size) + .setTrackTotalHitsUpTo(5) + .get(); + assertTrue(searchResponse.isTerminatedEarly()); + assertEquals(5, searchResponse.getHits().getTotalHits().value); + assertEquals(EQUAL_TO, searchResponse.getHits().getTotalHits().relation); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(numDocs)) + .setTerminateAfter(5) + .setSize(size) + .setTrackTotalHits(true) + .get(); + assertTrue(searchResponse.isTerminatedEarly()); + assertEquals(5, searchResponse.getHits().getTotalHits().value); + assertEquals(EQUAL_TO, searchResponse.getHits().getTotalHits().relation); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(numDocs)) + .setTerminateAfter(numDocs * 2) + .setSize(size) + .setTrackTotalHits(true) + .get(); + assertFalse(searchResponse.isTerminatedEarly()); + assertEquals(numDocs, searchResponse.getHits().getTotalHits().value); + assertEquals(EQUAL_TO, searchResponse.getHits().getTotalHits().relation); + + searchResponse = client().prepareSearch("test") + .setQuery(QueryBuilders.rangeQuery("field").gte(1).lte(numDocs)) + .setSize(size) + .setTrackTotalHitsUpTo(5) + .get(); + assertEquals(5, searchResponse.getHits().getTotalHits().value); + assertEquals(GREATER_THAN_OR_EQUAL_TO, searchResponse.getHits().getTotalHits().relation); + } + public void testSimpleIndexSortEarlyTerminate() throws Exception { prepareCreate("test").setSettings( Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0).put("index.sort.field", "rank") @@ -326,7 +433,7 @@ public void testSimpleIndexSortEarlyTerminate() throws Exception { public void testInsaneFromAndSize() throws Exception { createIndex("idx"); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertWindowFails(client().prepareSearch("idx").setFrom(Integer.MAX_VALUE)); assertWindowFails(client().prepareSearch("idx").setSize(Integer.MAX_VALUE)); @@ -334,7 +441,7 @@ public void testInsaneFromAndSize() throws Exception { public void testTooLargeFromAndSize() throws Exception { createIndex("idx"); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertWindowFails(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY))); assertWindowFails(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1)); @@ -347,7 +454,7 @@ public void testTooLargeFromAndSize() throws Exception { public void testLargeFromAndSizeSucceeds() throws Exception { createIndex("idx"); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertHitCount(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) - 10).get(), 1); assertHitCount(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)).get(), 1); @@ -365,7 +472,7 @@ public void testTooLargeFromAndSizeOkBySetting() throws Exception { Settings.builder() .put(IndexSettings.MAX_RESULT_WINDOW_SETTING.getKey(), IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 2) ).get(); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertHitCount(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)).get(), 1); assertHitCount(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1).get(), 1); @@ -393,7 +500,7 @@ public void testTooLargeFromAndSizeOkByDynamicSetting() throws Exception { ) .get() ); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertHitCount(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY)).get(), 1); assertHitCount(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) + 1).get(), 1); @@ -408,7 +515,7 @@ public void testTooLargeFromAndSizeOkByDynamicSetting() throws Exception { public void testTooLargeFromAndSizeBackwardsCompatibilityRecommendation() throws Exception { prepareCreate("idx").setSettings(Settings.builder().put(IndexSettings.MAX_RESULT_WINDOW_SETTING.getKey(), Integer.MAX_VALUE)).get(); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertHitCount(client().prepareSearch("idx").setFrom(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10).get(), 1); assertHitCount(client().prepareSearch("idx").setSize(IndexSettings.MAX_RESULT_WINDOW_SETTING.get(Settings.EMPTY) * 10).get(), 1); @@ -423,7 +530,7 @@ public void testTooLargeFromAndSizeBackwardsCompatibilityRecommendation() throws public void testTooLargeRescoreWindow() throws Exception { createIndex("idx"); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertRescoreWindowFails(Integer.MAX_VALUE); assertRescoreWindowFails(IndexSettings.MAX_RESCORE_WINDOW_SETTING.get(Settings.EMPTY) + 1); @@ -433,7 +540,7 @@ public void testTooLargeRescoreOkBySetting() throws Exception { int defaultMaxWindow = IndexSettings.MAX_RESCORE_WINDOW_SETTING.get(Settings.EMPTY); prepareCreate("idx").setSettings(Settings.builder().put(IndexSettings.MAX_RESCORE_WINDOW_SETTING.getKey(), defaultMaxWindow * 2)) .get(); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertHitCount( client().prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)).get(), @@ -450,7 +557,7 @@ public void testTooLargeRescoreOkByResultWindowSetting() throws Exception { defaultMaxWindow * 2 ) ).get(); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertHitCount( client().prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)).get(), @@ -468,7 +575,7 @@ public void testTooLargeRescoreOkByDynamicSetting() throws Exception { .setSettings(Settings.builder().put(IndexSettings.MAX_RESCORE_WINDOW_SETTING.getKey(), defaultMaxWindow * 2)) .get() ); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertHitCount( client().prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)).get(), @@ -489,7 +596,7 @@ public void testTooLargeRescoreOkByDynamicResultWindowSetting() throws Exception ) .get() ); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); assertHitCount( client().prepareSearch("idx").addRescorer(new QueryRescorerBuilder(matchAllQuery()).windowSize(defaultMaxWindow + 1)).get(), @@ -515,7 +622,7 @@ public void testTermQueryBigInt() throws Exception { client().prepareIndex("idx") .setId("1") - .setSource("{\"field\" : 80315953321748200608 }", XContentType.JSON) + .setSource("{\"field\" : 80315953321748200608 }", MediaTypeRegistry.JSON) .setRefreshPolicy(RefreshPolicy.IMMEDIATE) .get(); @@ -529,7 +636,7 @@ public void testTermQueryBigInt() throws Exception { public void testTooLongRegexInRegexpQuery() throws Exception { createIndex("idx"); - indexRandom(true, client().prepareIndex("idx").setSource("{}", XContentType.JSON)); + indexRandom(true, client().prepareIndex("idx").setSource("{}", MediaTypeRegistry.JSON)); int defaultMaxRegexLength = IndexSettings.MAX_REGEX_LENGTH_SETTING.get(Settings.EMPTY); StringBuilder regexp = new StringBuilder(defaultMaxRegexLength); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/slice/SearchSliceIT.java b/server/src/internalClusterTest/java/org/opensearch/search/slice/SearchSliceIT.java index 9c735c42052e3..27a56f9d14f08 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/slice/SearchSliceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/slice/SearchSliceIT.java @@ -32,61 +32,91 @@ package org.opensearch.search.slice; -import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.search.CreatePitAction; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.Scroll; import org.opensearch.search.SearchException; import org.opensearch.search.SearchHit; +import org.opensearch.search.builder.PointInTimeBuilder; import org.opensearch.search.sort.SortBuilders; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; -import java.util.List; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.concurrent.ExecutionException; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.startsWith; -public class SearchSliceIT extends OpenSearchIntegTestCase { - private void setupIndex(int numDocs, int numberOfShards) throws IOException, ExecutionException, InterruptedException { - String mapping = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("properties") - .startObject("invalid_random_kw") - .field("type", "keyword") - .field("doc_values", "false") - .endObject() - .startObject("random_int") - .field("type", "integer") - .field("doc_values", "true") - .endObject() - .startObject("invalid_random_int") - .field("type", "integer") - .field("doc_values", "false") - .endObject() - .endObject() - .endObject() +public class SearchSliceIT extends ParameterizedOpenSearchIntegTestCase { + public SearchSliceIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + + private void setupIndex(int numDocs, int numberOfShards) throws IOException, ExecutionException, InterruptedException { + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("invalid_random_kw") + .field("type", "keyword") + .field("doc_values", "false") + .endObject() + .startObject("random_int") + .field("type", "integer") + .field("doc_values", "true") + .endObject() + .startObject("invalid_random_int") + .field("type", "integer") + .field("doc_values", "false") + .endObject() + .endObject() + .endObject() + .toString(); assertAcked( client().admin() .indices() .prepareCreate("test") - .setSettings(Settings.builder().put("number_of_shards", numberOfShards).put("index.max_slices_per_scroll", 10000)) + .setSettings( + Settings.builder() + .put("number_of_shards", numberOfShards) + .put("index.max_slices_per_scroll", 10000) + .put("index.max_slices_per_pit", 10000) + ) .setMapping(mapping) ); ensureGreen(); @@ -129,6 +159,78 @@ public void testSearchSort() throws Exception { } } + public void testSearchSortWithoutPitOrScroll() throws Exception { + int numShards = randomIntBetween(1, 7); + int numDocs = randomIntBetween(100, 1000); + setupIndex(numDocs, numShards); + int fetchSize = randomIntBetween(10, 100); + SearchRequestBuilder request = client().prepareSearch("test") + .setQuery(matchAllQuery()) + .setSize(fetchSize) + .addSort(SortBuilders.fieldSort("_doc")); + SliceBuilder sliceBuilder = new SliceBuilder("_id", 0, 4); + SearchPhaseExecutionException ex = expectThrows(SearchPhaseExecutionException.class, () -> request.slice(sliceBuilder).get()); + assertTrue(ex.getMessage().contains("all shards failed")); + } + + public void testSearchSortWithPIT() throws Exception { + int numShards = randomIntBetween(1, 7); + int numDocs = randomIntBetween(100, 1000); + setupIndex(numDocs, numShards); + int max = randomIntBetween(2, numShards * 3); + CreatePitRequest pitRequest = new CreatePitRequest(TimeValue.timeValueDays(1), true); + pitRequest.setIndices(new String[] { "test" }); + ActionFuture execute = client().execute(CreatePitAction.INSTANCE, pitRequest); + CreatePitResponse pitResponse = execute.get(); + for (String field : new String[] { "_id", "random_int", "static_int" }) { + int fetchSize = randomIntBetween(10, 100); + + // test _doc sort + SearchRequestBuilder request = client().prepareSearch("test") + .setQuery(matchAllQuery()) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId())) + .setSize(fetchSize) + .addSort(SortBuilders.fieldSort("_doc")); + assertSearchSlicesWithPIT(request, field, max, numDocs); + + // test numeric sort + request = client().prepareSearch("test") + .setQuery(matchAllQuery()) + .setPointInTime(new PointInTimeBuilder(pitResponse.getId())) + .setSize(fetchSize) + .addSort(SortBuilders.fieldSort("random_int")); + assertSearchSlicesWithPIT(request, field, max, numDocs); + } + client().admin().indices().prepareDelete("test").get(); + } + + private void assertSearchSlicesWithPIT(SearchRequestBuilder request, String field, int numSlice, int numDocs) { + int totalResults = 0; + List keys = new ArrayList<>(); + for (int id = 0; id < numSlice; id++) { + SliceBuilder sliceBuilder = new SliceBuilder(field, id, numSlice); + SearchResponse searchResponse = request.slice(sliceBuilder).setFrom(0).get(); + totalResults += searchResponse.getHits().getHits().length; + int expectedSliceResults = (int) searchResponse.getHits().getTotalHits().value; + int numSliceResults = searchResponse.getHits().getHits().length; + for (SearchHit hit : searchResponse.getHits().getHits()) { + assertTrue(keys.add(hit.getId())); + } + while (searchResponse.getHits().getHits().length > 0) { + searchResponse = request.setFrom(numSliceResults).slice(sliceBuilder).get(); + totalResults += searchResponse.getHits().getHits().length; + numSliceResults += searchResponse.getHits().getHits().length; + for (SearchHit hit : searchResponse.getHits().getHits()) { + assertTrue(keys.add(hit.getId())); + } + } + assertThat(numSliceResults, equalTo(expectedSliceResults)); + } + assertThat(totalResults, equalTo(numDocs)); + assertThat(keys.size(), equalTo(numDocs)); + assertThat(new HashSet(keys).size(), equalTo(numDocs)); + } + public void testWithPreferenceAndRoutings() throws Exception { int numShards = 10; int totalDocs = randomIntBetween(100, 1000); @@ -217,7 +319,7 @@ public void testInvalidQuery() throws Exception { ); Throwable rootCause = findRootCause(exc); assertThat(rootCause.getClass(), equalTo(SearchException.class)); - assertThat(rootCause.getMessage(), equalTo("`slice` cannot be used outside of a scroll context")); + assertThat(rootCause.getMessage(), equalTo("`slice` cannot be used outside of a scroll context or PIT context")); } private void assertSearchSlicesWithScroll(SearchRequestBuilder request, String field, int numSlice, int numDocs) { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/sort/FieldSortIT.java b/server/src/internalClusterTest/java/org/opensearch/search/sort/FieldSortIT.java index 4bf4cd138cbd1..bee242b933dfd 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/sort/FieldSortIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/sort/FieldSortIT.java @@ -32,10 +32,11 @@ package org.opensearch.search.sort; -import org.apache.lucene.util.BytesRef; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.tests.util.TestUtil; +import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.UnicodeUtil; - import org.opensearch.OpenSearchException; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.bulk.BulkRequestBuilder; @@ -44,27 +45,29 @@ import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.ShardSearchFailure; import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.Numbers; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.query.functionscore.ScoreFunctionBuilders; import org.opensearch.indices.IndicesService; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; - +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.hamcrest.Matchers; import java.io.IOException; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -86,6 +89,7 @@ import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.functionscore.ScoreFunctionBuilders.fieldValueFactorFunction; import static org.opensearch.script.MockScriptPlugin.NAME; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFirstHit; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -103,8 +107,26 @@ import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.oneOf; + +public class FieldSortIT extends ParameterizedOpenSearchIntegTestCase { + public FieldSortIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } -public class FieldSortIT extends OpenSearchIntegTestCase { public static class CustomScriptPlugin extends MockScriptPlugin { @Override protected Map, Object>> pluginScripts() { @@ -142,7 +164,7 @@ public void testIssue8226() { assertAcked(prepareCreate("test_" + i).addAlias(new Alias("test"))); } if (i > 0) { - client().prepareIndex("test_" + i).setId("" + i).setSource("{\"entry\": " + i + "}", XContentType.JSON).get(); + client().prepareIndex("test_" + i).setId("" + i).setSource("{\"entry\": " + i + "}", MediaTypeRegistry.JSON).get(); } } refresh(); @@ -494,9 +516,9 @@ public void testScoreSortDirectionWithFunctionScore() throws Exception { public void testIssue2986() { assertAcked(client().admin().indices().prepareCreate("test").setMapping("field1", "type=keyword").get()); - client().prepareIndex("test").setId("1").setSource("{\"field1\":\"value1\"}", XContentType.JSON).get(); - client().prepareIndex("test").setId("2").setSource("{\"field1\":\"value2\"}", XContentType.JSON).get(); - client().prepareIndex("test").setId("3").setSource("{\"field1\":\"value3\"}", XContentType.JSON).get(); + client().prepareIndex("test").setId("1").setSource("{\"field1\":\"value1\"}", MediaTypeRegistry.JSON).get(); + client().prepareIndex("test").setId("2").setSource("{\"field1\":\"value2\"}", MediaTypeRegistry.JSON).get(); + client().prepareIndex("test").setId("3").setSource("{\"field1\":\"value3\"}", MediaTypeRegistry.JSON).get(); refresh(); SearchResponse result = client().prepareSearch("test") .setQuery(matchAllQuery()) @@ -577,6 +599,9 @@ public void testSimpleSorts() throws Exception { .startObject("long_value") .field("type", "long") .endObject() + .startObject("unsigned_long_value") + .field("type", "unsigned_long") + .endObject() .startObject("float_value") .field("type", "float") .endObject() @@ -588,6 +613,7 @@ public void testSimpleSorts() throws Exception { ) ); ensureGreen(); + BigInteger UNSIGNED_LONG_BASE = Numbers.MAX_UNSIGNED_LONG_VALUE.subtract(BigInteger.valueOf(100000)); List builders = new ArrayList<>(); for (int i = 0; i < 10; i++) { IndexRequestBuilder builder = client().prepareIndex("test") @@ -600,6 +626,7 @@ public void testSimpleSorts() throws Exception { .field("short_value", i) .field("integer_value", i) .field("long_value", i) + .field("unsigned_long_value", UNSIGNED_LONG_BASE.add(BigInteger.valueOf(10000 * i))) .field("float_value", 0.1 * i) .field("double_value", 0.1 * i) .endObject() @@ -622,7 +649,6 @@ public void testSimpleSorts() throws Exception { // STRING int size = 1 + random.nextInt(10); - SearchResponse searchResponse = client().prepareSearch() .setQuery(matchAllQuery()) .setSize(size) @@ -791,6 +817,45 @@ public void testSimpleSorts() throws Exception { } assertNoFailures(searchResponse); + + // UNSIGNED_LONG + size = 1 + random.nextInt(10); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .setSize(size) + .addSort("unsigned_long_value", SortOrder.ASC) + .get(); + + assertHitCount(searchResponse, 10); + assertThat(searchResponse.getHits().getHits().length, equalTo(size)); + for (int i = 0; i < size; i++) { + assertThat(searchResponse.getHits().getAt(i).getId(), equalTo(Integer.toString(i))); + assertThat( + ((Number) searchResponse.getHits().getAt(i).getSortValues()[0]), + equalTo(UNSIGNED_LONG_BASE.add(BigInteger.valueOf(10000 * i))) + ); + } + + assertThat(searchResponse.toString(), not(containsString("error"))); + size = 1 + random.nextInt(10); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .setSize(size) + .addSort("unsigned_long_value", SortOrder.DESC) + .get(); + assertHitCount(searchResponse, 10L); + assertHitCount(searchResponse, 10); + assertThat(searchResponse.getHits().getHits().length, equalTo(size)); + for (int i = 0; i < size; i++) { + assertThat(searchResponse.getHits().getAt(i).getId(), equalTo(Integer.toString(9 - i))); + assertThat( + ((Number) searchResponse.getHits().getAt(i).getSortValues()[0]), + equalTo(UNSIGNED_LONG_BASE.add(BigInteger.valueOf(10000 * (9 - i)))) + ); + } + + assertThat(searchResponse.toString(), not(containsString("error"))); + assertNoFailures(searchResponse); } public void testSortMissingNumbers() throws Exception { @@ -805,6 +870,9 @@ public void testSortMissingNumbers() throws Exception { .startObject("d_value") .field("type", "float") .endObject() + .startObject("u_value") + .field("type", "unsigned_long") + .endObject() .endObject() .endObject() ) @@ -812,19 +880,24 @@ public void testSortMissingNumbers() throws Exception { ensureGreen(); client().prepareIndex("test") .setId("1") - .setSource(jsonBuilder().startObject().field("id", "1").field("i_value", -1).field("d_value", -1.1).endObject()) + .setSource( + jsonBuilder().startObject().field("id", "1").field("i_value", -1).field("d_value", -1.1).field("u_value", 1).endObject() + ) .get(); client().prepareIndex("test").setId("2").setSource(jsonBuilder().startObject().field("id", "2").endObject()).get(); client().prepareIndex("test") .setId("3") - .setSource(jsonBuilder().startObject().field("id", "1").field("i_value", 2).field("d_value", 2.2).endObject()) + .setSource( + jsonBuilder().startObject().field("id", "3").field("i_value", 2).field("d_value", 2.2).field("u_value", 2).endObject() + ) .get(); flush(); refresh(); + // DOUBLE logger.info("--> sort with no missing (same as missing _last)"); SearchResponse searchResponse = client().prepareSearch() .setQuery(matchAllQuery()) @@ -860,6 +933,247 @@ public void testSortMissingNumbers() throws Exception { assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2")); assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("1")); assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); + + // FLOAT + logger.info("--> sort with no missing (same as missing _last)"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("d_value").order(SortOrder.ASC)) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("3")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2")); + + logger.info("--> sort with missing _last"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("d_value").order(SortOrder.ASC).missing("_last")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("3")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2")); + + logger.info("--> sort with missing _first"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("d_value").order(SortOrder.ASC).missing("_first")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); + + // UNSIGNED_LONG + logger.info("--> sort with no missing (same as missing _last)"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("u_value").order(SortOrder.ASC)) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("3")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2")); + + logger.info("--> sort with missing _last"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("u_value").order(SortOrder.ASC).missing("_last")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("3")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2")); + + logger.info("--> sort with missing _first"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("u_value").order(SortOrder.ASC).missing("_first")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); + } + + public void testSortMissingNumbersMinMax() throws Exception { + assertAcked( + prepareCreate("test").setMapping( + XContentFactory.jsonBuilder() + .startObject() + .startObject("properties") + .startObject("l_value") + .field("type", "long") + .endObject() + .startObject("d_value") + .field("type", "float") + .endObject() + .startObject("u_value") + .field("type", "unsigned_long") + .endObject() + .endObject() + .endObject() + ) + ); + ensureGreen(); + client().prepareIndex("test") + .setId("1") + .setSource( + jsonBuilder().startObject() + .field("id", "1") + .field("l_value", Long.MIN_VALUE) + .field("d_value", Float.MIN_VALUE) + .field("u_value", Numbers.MIN_UNSIGNED_LONG_VALUE) + .endObject() + ) + .get(); + + client().prepareIndex("test").setId("2").setSource(jsonBuilder().startObject().field("id", "2").endObject()).get(); + + client().prepareIndex("test") + .setId("3") + .setSource( + jsonBuilder().startObject() + .field("id", "3") + .field("l_value", Long.MAX_VALUE) + .field("d_value", Float.MAX_VALUE) + .field("u_value", Numbers.MAX_UNSIGNED_LONG_VALUE) + .endObject() + ) + .get(); + + flush(); + refresh(); + + // LONG + logger.info("--> sort with no missing (same as missing _last)"); + SearchResponse searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("l_value").order(SortOrder.ASC)) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + // The order here could be unstable (depends on document order) since missing == field value + assertThat(searchResponse.getHits().getAt(1).getId(), is(oneOf("3", "2"))); + assertThat(searchResponse.getHits().getAt(2).getId(), is(oneOf("2", "3"))); + + logger.info("--> sort with missing _last"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("l_value").order(SortOrder.ASC).missing("_last")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + // The order here could be unstable (depends on document order) since missing == field value + assertThat(searchResponse.getHits().getAt(1).getId(), is(oneOf("3", "2"))); + assertThat(searchResponse.getHits().getAt(2).getId(), is(oneOf("2", "3"))); + + logger.info("--> sort with missing _first"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("l_value").order(SortOrder.ASC).missing("_first")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + // The order here could be unstable (depends on document order) since missing == field value + assertThat(searchResponse.getHits().getAt(0).getId(), is(oneOf("2", "1"))); + assertThat(searchResponse.getHits().getAt(1).getId(), is(oneOf("1", "2"))); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); + + // FLOAT + logger.info("--> sort with no missing (same as missing _last)"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("d_value").order(SortOrder.ASC)) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("3")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2")); + + logger.info("--> sort with missing _last"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("d_value").order(SortOrder.ASC).missing("_last")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("3")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("2")); + + logger.info("--> sort with missing _first"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("d_value").order(SortOrder.ASC).missing("_first")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("2")); + assertThat(searchResponse.getHits().getAt(1).getId(), equalTo("1")); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); + + // UNSIGNED_LONG + logger.info("--> sort with no missing (same as missing _last)"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("u_value").order(SortOrder.ASC)) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + // The order here could be unstable (depends on document order) since missing == field value + assertThat(searchResponse.getHits().getAt(1).getId(), is(oneOf("3", "2"))); + assertThat(searchResponse.getHits().getAt(2).getId(), is(oneOf("2", "3"))); + + logger.info("--> sort with missing _last"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("u_value").order(SortOrder.ASC).missing("_last")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + assertThat(searchResponse.getHits().getAt(0).getId(), equalTo("1")); + // The order here could be unstable (depends on document order) since missing == field value + assertThat(searchResponse.getHits().getAt(1).getId(), is(oneOf("3", "2"))); + assertThat(searchResponse.getHits().getAt(2).getId(), is(oneOf("2", "3"))); + + logger.info("--> sort with missing _first"); + searchResponse = client().prepareSearch() + .setQuery(matchAllQuery()) + .addSort(SortBuilders.fieldSort("u_value").order(SortOrder.ASC).missing("_first")) + .get(); + assertNoFailures(searchResponse); + + assertThat(searchResponse.getHits().getTotalHits().value, equalTo(3L)); + // The order here could be unstable (depends on document order) since missing == field value + assertThat(searchResponse.getHits().getAt(0).getId(), is(oneOf("2", "1"))); + assertThat(searchResponse.getHits().getAt(1).getId(), is(oneOf("1", "2"))); + assertThat(searchResponse.getHits().getAt(2).getId(), equalTo("3")); } public void testSortMissingStrings() throws IOException { @@ -1964,7 +2278,7 @@ public void testLongSortOptimizationCorrectResults() { bulkBuilder = client().prepareBulk(); } String source = "{\"long_field\":" + randomLong() + "}"; - bulkBuilder.add(client().prepareIndex("test1").setId(Integer.toString(i)).setSource(source, XContentType.JSON)); + bulkBuilder.add(client().prepareIndex("test1").setId(Integer.toString(i)).setSource(source, MediaTypeRegistry.JSON)); } refresh(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/sort/GeoDistanceIT.java b/server/src/internalClusterTest/java/org/opensearch/search/sort/GeoDistanceIT.java index 5cc9ab77bcb2d..6886f8d67589e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/sort/GeoDistanceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/sort/GeoDistanceIT.java @@ -32,24 +32,30 @@ package org.opensearch.search.sort; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.Version; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.geo.GeoDistance; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.geometry.utils.Geohash; import org.opensearch.index.query.QueryBuilders; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFirstHit; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -60,7 +66,24 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -public class GeoDistanceIT extends OpenSearchIntegTestCase { +public class GeoDistanceIT extends ParameterizedOpenSearchIntegTestCase { + + public GeoDistanceIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected boolean forbidPrivateIndexSettings() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/sort/GeoDistanceSortBuilderIT.java b/server/src/internalClusterTest/java/org/opensearch/search/sort/GeoDistanceSortBuilderIT.java index aebd83592e793..7880fc24fd846 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/sort/GeoDistanceSortBuilderIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/sort/GeoDistanceSortBuilderIT.java @@ -32,6 +32,8 @@ package org.opensearch.search.sort; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.Version; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.metadata.IndexMetadata; @@ -39,28 +41,47 @@ import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.DistanceUnit; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.GeoValidationMethod; import org.opensearch.search.builder.SearchSourceBuilder; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.VersionUtils; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.sort.SortBuilders.fieldSort; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertOrderedSearchHits; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertSortValues; import static org.hamcrest.Matchers.closeTo; -public class GeoDistanceSortBuilderIT extends OpenSearchIntegTestCase { +public class GeoDistanceSortBuilderIT extends ParameterizedOpenSearchIntegTestCase { + public GeoDistanceSortBuilderIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } private static final String LOCATION_FIELD = "location"; diff --git a/server/src/internalClusterTest/java/org/opensearch/search/sort/SimpleSortIT.java b/server/src/internalClusterTest/java/org/opensearch/search/sort/SimpleSortIT.java index 8ff0790e7cb48..ddfbc3cce2be6 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/sort/SimpleSortIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/sort/SimpleSortIT.java @@ -32,12 +32,15 @@ package org.opensearch.search.sort; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchResponse; import org.opensearch.action.search.ShardSearchFailure; -import org.opensearch.common.Strings; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.geo.GeoUtils; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptPlugin; @@ -45,8 +48,8 @@ import org.opensearch.script.ScriptType; import org.opensearch.search.SearchHit; import org.opensearch.search.sort.ScriptSortBuilder.ScriptSortType; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; @@ -62,6 +65,7 @@ import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; import static org.opensearch.index.query.QueryBuilders.termQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.sort.SortBuilders.scriptSort; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -71,10 +75,27 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; -public class SimpleSortIT extends OpenSearchIntegTestCase { +public class SimpleSortIT extends ParameterizedOpenSearchIntegTestCase { private static final String DOUBLE_APOSTROPHE = "\u0027\u0027"; + public SimpleSortIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + @Override protected Collection> nodePlugins() { return Arrays.asList(CustomScriptPlugin.class, InternalSettingsPlugin.class); @@ -237,24 +258,23 @@ public void testSimpleSorts() throws Exception { } public void testSortMinValueScript() throws IOException { - String mapping = Strings.toString( - jsonBuilder().startObject() - .startObject("properties") - .startObject("lvalue") - .field("type", "long") - .endObject() - .startObject("dvalue") - .field("type", "double") - .endObject() - .startObject("svalue") - .field("type", "keyword") - .endObject() - .startObject("gvalue") - .field("type", "geo_point") - .endObject() - .endObject() - .endObject() - ); + String mapping = jsonBuilder().startObject() + .startObject("properties") + .startObject("lvalue") + .field("type", "long") + .endObject() + .startObject("dvalue") + .field("type", "double") + .endObject() + .startObject("svalue") + .field("type", "keyword") + .endObject() + .startObject("gvalue") + .field("type", "geo_point") + .endObject() + .endObject() + .endObject() + .toString(); assertAcked(prepareCreate("test").setMapping(mapping)); ensureGreen(); @@ -351,18 +371,17 @@ public void testDocumentsWithNullValue() throws Exception { // TODO: sort shouldn't fail when sort field is mapped dynamically // We have to specify mapping explicitly because by the time search is performed dynamic mapping might not // be propagated to all nodes yet and sort operation fail when the sort field is not defined - String mapping = Strings.toString( - jsonBuilder().startObject() - .startObject("properties") - .startObject("id") - .field("type", "keyword") - .endObject() - .startObject("svalue") - .field("type", "keyword") - .endObject() - .endObject() - .endObject() - ); + String mapping = jsonBuilder().startObject() + .startObject("properties") + .startObject("id") + .field("type", "keyword") + .endObject() + .startObject("svalue") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + .toString(); assertAcked(prepareCreate("test").setMapping(mapping)); ensureGreen(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/sort/SortFromPluginIT.java b/server/src/internalClusterTest/java/org/opensearch/search/sort/SortFromPluginIT.java index e9fc1c54ad234..7bcded86fcaa8 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/sort/SortFromPluginIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/sort/SortFromPluginIT.java @@ -8,21 +8,42 @@ package org.opensearch.search.sort; -import static org.hamcrest.Matchers.equalTo; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.plugins.Plugin; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.sort.plugin.CustomSortBuilder; import org.opensearch.search.sort.plugin.CustomSortPlugin; import org.opensearch.test.InternalSettingsPlugin; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.util.Arrays; import java.util.Collection; -public class SortFromPluginIT extends OpenSearchIntegTestCase { +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; +import static org.hamcrest.Matchers.equalTo; + +public class SortFromPluginIT extends ParameterizedOpenSearchIntegTestCase { + public SortFromPluginIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/source/MetadataFetchingIT.java b/server/src/internalClusterTest/java/org/opensearch/search/source/MetadataFetchingIT.java index 4f6dd89285bee..c98a38ea0bb97 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/source/MetadataFetchingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/source/MetadataFetchingIT.java @@ -31,26 +31,51 @@ package org.opensearch.search.source; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.apache.lucene.search.join.ScoreMode; import org.opensearch.ExceptionsHelper; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.InnerHitBuilder; import org.opensearch.index.query.NestedQueryBuilder; import org.opensearch.index.query.TermQueryBuilder; import org.opensearch.search.SearchException; import org.opensearch.search.SearchHits; import org.opensearch.search.fetch.subphase.FetchSourceContext; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class MetadataFetchingIT extends OpenSearchIntegTestCase { +public class MetadataFetchingIT extends ParameterizedOpenSearchIntegTestCase { + + public MetadataFetchingIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testSimple() { assertAcked(prepareCreate("test")); ensureGreen(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/source/SourceFetchingIT.java b/server/src/internalClusterTest/java/org/opensearch/search/source/SourceFetchingIT.java index 11223d11ff30d..eeef5403fe898 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/source/SourceFetchingIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/source/SourceFetchingIT.java @@ -32,14 +32,40 @@ package org.opensearch.search.source; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.IsEqual.equalTo; -public class SourceFetchingIT extends OpenSearchIntegTestCase { +public class SourceFetchingIT extends ParameterizedOpenSearchIntegTestCase { + + public SourceFetchingIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testSourceDefaultBehavior() { createIndex("test"); ensureGreen(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/stats/ConcurrentSearchStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/stats/ConcurrentSearchStatsIT.java new file mode 100644 index 0000000000000..abfd39793f1a4 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/search/stats/ConcurrentSearchStatsIT.java @@ -0,0 +1,364 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.search.stats; + +import org.opensearch.action.admin.cluster.node.stats.NodesStatsAction; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequestBuilder; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; +import org.opensearch.action.admin.indices.stats.IndexStats; +import org.opensearch.action.admin.indices.stats.IndicesStatsRequestBuilder; +import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.index.IndexModule; +import org.opensearch.index.IndexSettings; +import org.opensearch.indices.IndicesQueryCache; +import org.opensearch.indices.IndicesService; +import org.opensearch.plugins.Plugin; +import org.opensearch.script.MockScriptPlugin; +import org.opensearch.script.Script; +import org.opensearch.script.ScriptType; +import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.threadpool.ThreadPoolStats; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +import static org.opensearch.index.query.QueryBuilders.scriptQuery; +import static org.opensearch.search.SearchBootstrapSettings.CONCURRENT_SEGMENT_SEARCH_TARGET_MAX_SLICE_COUNT_KEY; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 1, numClientNodes = 0, supportsDedicatedMasters = false) +public class ConcurrentSearchStatsIT extends OpenSearchIntegTestCase { + + private final int SEGMENT_SLICE_COUNT = 4; + + @Override + protected Collection> nodePlugins() { + return Arrays.asList(ScriptedDelayedPlugin.class, InternalSettingsPlugin.class); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + // Filter/Query cache is cleaned periodically, default is 60s, so make sure it runs often. Thread.sleep for 60s is bad + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(IndicesService.INDICES_CACHE_CLEAN_INTERVAL_SETTING.getKey(), "1ms") + .put(IndicesQueryCache.INDICES_QUERIES_CACHE_ALL_SEGMENTS_SETTING.getKey(), true) + .put(CONCURRENT_SEGMENT_SEARCH_TARGET_MAX_SLICE_COUNT_KEY, SEGMENT_SLICE_COUNT) + .build(); + } + + @Override + public Settings indexSettings() { + return Settings.builder() + .put(super.indexSettings()) + .put(IndexModule.INDEX_QUERY_CACHE_EVERYTHING_SETTING.getKey(), false) + .put(IndexModule.INDEX_QUERY_CACHE_ENABLED_SETTING.getKey(), false) + .put(IndexSettings.INDEX_SOFT_DELETES_RETENTION_OPERATIONS_SETTING.getKey(), 0) + .build(); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + + public void testConcurrentQueryCount() throws Exception { + String INDEX_1 = "test-" + randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + String INDEX_2 = "test-" + randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + int NUM_SHARDS = randomIntBetween(1, 5); + createIndex( + INDEX_1, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, NUM_SHARDS) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + createIndex( + INDEX_2, + Settings.builder() + .put(indexSettings()) + .put("search.concurrent_segment_search.enabled", false) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, NUM_SHARDS) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + + ensureGreen(); + + indexRandom( + false, + true, + client().prepareIndex(INDEX_1).setId("1").setSource("foo", "bar"), + client().prepareIndex(INDEX_1).setId("2").setSource("foo", "baz"), + client().prepareIndex(INDEX_2).setId("1").setSource("foo", "bar"), + client().prepareIndex(INDEX_2).setId("2").setSource("foo", "baz") + ); + + refresh(); + + // Search with custom plugin to ensure that queryTime is significant + client().prepareSearch(INDEX_1, INDEX_2) + .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedDelayedPlugin.SCRIPT_NAME, Collections.emptyMap()))) + .execute() + .actionGet(); + client().prepareSearch(INDEX_1) + .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedDelayedPlugin.SCRIPT_NAME, Collections.emptyMap()))) + .execute() + .actionGet(); + client().prepareSearch(INDEX_2) + .setQuery(scriptQuery(new Script(ScriptType.INLINE, "mockscript", ScriptedDelayedPlugin.SCRIPT_NAME, Collections.emptyMap()))) + .execute() + .actionGet(); + + IndicesStatsRequestBuilder builder = client().admin().indices().prepareStats(); + IndicesStatsResponse stats = builder.execute().actionGet(); + + assertEquals(4 * NUM_SHARDS, stats.getTotal().search.getTotal().getQueryCount()); + assertEquals(2 * NUM_SHARDS, stats.getTotal().search.getTotal().getConcurrentQueryCount()); + assertThat(stats.getTotal().search.getTotal().getQueryTimeInMillis(), greaterThan(0L)); + assertThat(stats.getTotal().search.getTotal().getConcurrentQueryTimeInMillis(), greaterThan(0L)); + assertThat( + stats.getTotal().search.getTotal().getConcurrentQueryTimeInMillis(), + lessThan(stats.getTotal().search.getTotal().getQueryTimeInMillis()) + ); + } + + /** + * Test average concurrency is correctly calculated across indices for the same node + */ + public void testAvgConcurrencyNodeLevel() throws InterruptedException { + int NUM_SHARDS = 1; + String INDEX_1 = "test-" + randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + String INDEX_2 = "test-" + randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + + // Create index test1 with 4 segments + createIndex( + INDEX_1, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, NUM_SHARDS) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + ensureGreen(); + for (int i = 0; i < 4; i++) { + client().prepareIndex(INDEX_1).setId(Integer.toString(i)).setSource("field", "value" + i).get(); + refresh(); + } + + client().prepareSearch(INDEX_1).execute().actionGet(); + NodesStatsResponse nodesStatsResponse = client().admin().cluster().prepareNodesStats().execute().actionGet(); + + assertEquals(1, nodesStatsResponse.getNodes().size(), 0); + double expectedConcurrency = SEGMENT_SLICE_COUNT; + assertEquals( + SEGMENT_SLICE_COUNT, + nodesStatsResponse.getNodes().get(0).getIndices().getSearch().getTotal().getConcurrentAvgSliceCount(), + 0 + ); + + forceMerge(); + // Sleep to make sure force merge completes + Thread.sleep(1000); + client().prepareSearch(INDEX_1).execute().actionGet(); + + nodesStatsResponse = client().admin().cluster().prepareNodesStats().execute().actionGet(); + + assertEquals(1, nodesStatsResponse.getNodes().size(), 0); + expectedConcurrency = (SEGMENT_SLICE_COUNT + 1) / 2.0; + assertEquals( + expectedConcurrency, + nodesStatsResponse.getNodes().get(0).getIndices().getSearch().getTotal().getConcurrentAvgSliceCount(), + 0 + ); + + // Create second index test2 with 4 segments + createIndex( + INDEX_2, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, NUM_SHARDS) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + ensureGreen(); + for (int i = 0; i < 4; i++) { + client().prepareIndex(INDEX_2).setId(Integer.toString(i)).setSource("field", "value" + i).get(); + refresh(); + } + + client().prepareSearch(INDEX_2).execute().actionGet(); + nodesStatsResponse = client().admin().cluster().prepareNodesStats().execute().actionGet(); + + assertEquals(1, nodesStatsResponse.getNodes().size(), 0); + expectedConcurrency = (SEGMENT_SLICE_COUNT + 1 + SEGMENT_SLICE_COUNT) / 3.0; + assertEquals( + expectedConcurrency, + nodesStatsResponse.getNodes().get(0).getIndices().getSearch().getTotal().getConcurrentAvgSliceCount(), + 0 + ); + + forceMerge(); + // Sleep to make sure force merge completes + Thread.sleep(1000); + client().prepareSearch(INDEX_2).execute().actionGet(); + nodesStatsResponse = client().admin().cluster().prepareNodesStats().execute().actionGet(); + + assertEquals(1, nodesStatsResponse.getNodes().size(), 0); + expectedConcurrency = (SEGMENT_SLICE_COUNT + 1 + SEGMENT_SLICE_COUNT + 1) / 4.0; + assertEquals( + expectedConcurrency, + nodesStatsResponse.getNodes().get(0).getIndices().getSearch().getTotal().getConcurrentAvgSliceCount(), + 0 + ); + + // Check that non-concurrent search requests do not affect the average concurrency + client().admin() + .indices() + .prepareUpdateSettings(INDEX_1) + .setSettings(Settings.builder().put("search.concurrent_segment_search.enabled", false)) + .execute() + .actionGet(); + client().admin() + .indices() + .prepareUpdateSettings(INDEX_2) + .setSettings(Settings.builder().put("search.concurrent_segment_search.enabled", false)) + .execute() + .actionGet(); + client().prepareSearch(INDEX_1).execute().actionGet(); + client().prepareSearch(INDEX_2).execute().actionGet(); + assertEquals(1, nodesStatsResponse.getNodes().size(), 0); + assertEquals( + expectedConcurrency, + nodesStatsResponse.getNodes().get(0).getIndices().getSearch().getTotal().getConcurrentAvgSliceCount(), + 0 + ); + } + + /** + * Test average concurrency is correctly calculated across shard for the same index + */ + public void testAvgConcurrencyIndexLevel() throws InterruptedException { + int NUM_SHARDS = 2; + String INDEX = "test-" + randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + createIndex( + INDEX, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, NUM_SHARDS) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + ensureGreen(); + // Create 4 segments on each shard + for (int i = 0; i < 4; i++) { + client().prepareIndex(INDEX).setId(Integer.toString(i)).setSource("field", "value" + i).setRouting("0").get(); + refresh(); + } + for (int i = 4; i < 8; i++) { + client().prepareIndex(INDEX).setId(Integer.toString(i)).setSource("field", "value" + i).setRouting("1").get(); + refresh(); + } + client().prepareSearch(INDEX).execute().actionGet(); + IndicesStatsResponse indicesStatsResponse = client().admin().indices().prepareStats().execute().actionGet(); + + IndexStats stats = indicesStatsResponse.getIndices().get(INDEX); + assertNotNull(stats); + double expectedConcurrency = (SEGMENT_SLICE_COUNT * NUM_SHARDS) / (double) NUM_SHARDS; + assertEquals(expectedConcurrency, stats.getTotal().getSearch().getTotal().getConcurrentAvgSliceCount(), 0); + + forceMerge(); + // Sleep to make sure force merge completes + Thread.sleep(1000); + client().prepareSearch(INDEX).execute().actionGet(); + + indicesStatsResponse = client().admin().indices().prepareStats().execute().actionGet(); + stats = indicesStatsResponse.getIndices().get(INDEX); + assertNotNull(stats); + expectedConcurrency = (SEGMENT_SLICE_COUNT * NUM_SHARDS + 1 * NUM_SHARDS) / (NUM_SHARDS * 2.0); + assertEquals(expectedConcurrency, stats.getTotal().getSearch().getTotal().getConcurrentAvgSliceCount(), 0); + + // Check that non-concurrent search requests do not affect the average concurrency + client().admin() + .indices() + .prepareUpdateSettings(INDEX) + .setSettings(Settings.builder().put("search.concurrent_segment_search.enabled", false)) + .execute() + .actionGet(); + + client().prepareSearch(INDEX).execute().actionGet(); + + indicesStatsResponse = client().admin().indices().prepareStats().execute().actionGet(); + stats = indicesStatsResponse.getIndices().get(INDEX); + assertNotNull(stats); + assertEquals(expectedConcurrency, stats.getTotal().getSearch().getTotal().getConcurrentAvgSliceCount(), 0); + } + + public void testThreadPoolWaitTime() throws Exception { + int NUM_SHARDS = 1; + String INDEX = "test-" + randomAlphaOfLength(5).toLowerCase(Locale.ROOT); + createIndex( + INDEX, + Settings.builder() + .put(indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, NUM_SHARDS) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0) + .build() + ); + + ensureGreen(); + + for (int i = 0; i < 10; i++) { + client().prepareIndex(INDEX).setId(Integer.toString(i)).setSource("field", "value" + i).get(); + refresh(); + } + + client().prepareSearch(INDEX).execute().actionGet(); + + NodesStatsRequestBuilder nodesStatsRequestBuilder = new NodesStatsRequestBuilder( + client().admin().cluster(), + NodesStatsAction.INSTANCE + ).setNodesIds().all(); + NodesStatsResponse nodesStatsResponse = nodesStatsRequestBuilder.execute().actionGet(); + ThreadPoolStats threadPoolStats = nodesStatsResponse.getNodes().get(0).getThreadPool(); + + for (ThreadPoolStats.Stats stats : threadPoolStats) { + if (stats.getName().equals(ThreadPool.Names.INDEX_SEARCHER)) { + assertThat(stats.getWaitTime().nanos(), greaterThan(0L)); + } + } + } + + public static class ScriptedDelayedPlugin extends MockScriptPlugin { + static final String SCRIPT_NAME = "search_timeout"; + + @Override + public Map, Object>> pluginScripts() { + return Collections.singletonMap(SCRIPT_NAME, params -> { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return true; + }); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/search/stats/SearchStatsIT.java b/server/src/internalClusterTest/java/org/opensearch/search/stats/SearchStatsIT.java index c72b5d40553b3..253a8b2b14824 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/stats/SearchStatsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/stats/SearchStatsIT.java @@ -32,9 +32,12 @@ package org.opensearch.search.stats; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; +import org.opensearch.action.search.SearchPhaseName; import org.opensearch.action.search.SearchResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.routing.GroupShardsIterator; @@ -42,6 +45,7 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.index.query.QueryBuilders; import org.opensearch.index.search.stats.SearchStats.Stats; import org.opensearch.plugins.Plugin; @@ -50,7 +54,9 @@ import org.opensearch.script.ScriptType; import org.opensearch.search.fetch.subphase.highlight.HighlightBuilder; import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -58,9 +64,11 @@ import java.util.Set; import java.util.function.Function; +import static org.opensearch.action.search.TransportSearchAction.SEARCH_REQUEST_STATS_ENABLED_KEY; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; import static org.opensearch.index.query.QueryBuilders.matchAllQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAllSuccessful; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; @@ -72,8 +80,25 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -@OpenSearchIntegTestCase.ClusterScope(minNumDataNodes = 2) -public class SearchStatsIT extends OpenSearchIntegTestCase { +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, minNumDataNodes = 2) +public class SearchStatsIT extends ParameterizedOpenSearchIntegTestCase { + + public SearchStatsIT(Settings dynamicSettings) { + super(dynamicSettings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } @Override protected Collection> nodePlugins() { @@ -103,6 +128,11 @@ public void testSimpleStats() throws Exception { assertThat(numNodes, greaterThanOrEqualTo(2)); final int shardsIdx1 = randomIntBetween(1, 10); // we make sure each node gets at least a single shard... final int shardsIdx2 = Math.max(numNodes - shardsIdx1, randomIntBetween(1, 10)); + client().admin() + .cluster() + .prepareUpdateSettings() + .setPersistentSettings(Settings.builder().put(SEARCH_REQUEST_STATS_ENABLED_KEY, true).build()) + .get(); assertThat(numNodes, lessThanOrEqualTo(shardsIdx1 + shardsIdx2)); assertAcked( prepareCreate("test1").setSettings( @@ -165,20 +195,40 @@ public void testSimpleStats() throws Exception { Set nodeIdsWithIndex = nodeIdsWithIndex("test1", "test2"); int num = 0; + int numOfCoordinators = 0; + for (NodeStats stat : nodeStats.getNodes()) { Stats total = stat.getIndices().getSearch().getTotal(); + if (total.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.QUERY.getName()).getTimeInMillis() > 0) { + assertThat( + total.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.FETCH.getName()).getTimeInMillis(), + greaterThan(0L) + ); + assertEquals( + iters, + total.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.FETCH.getName()).getTotal() + ); + assertEquals( + iters, + total.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.EXPAND.getName()).getTotal() + ); + assertEquals( + iters, + total.getRequestStatsLongHolder().getRequestStatsHolder().get(SearchPhaseName.FETCH.getName()).getTotal() + ); + numOfCoordinators += 1; + } if (nodeIdsWithIndex.contains(stat.getNode().getId())) { assertThat(total.getQueryCount(), greaterThan(0L)); assertThat(total.getQueryTimeInMillis(), greaterThan(0L)); num++; } else { - assertThat(total.getQueryCount(), equalTo(0L)); + assertThat(total.getQueryCount(), greaterThanOrEqualTo(0L)); assertThat(total.getQueryTimeInMillis(), equalTo(0L)); } } - + assertThat(numOfCoordinators, greaterThan(0)); assertThat(num, greaterThan(0)); - } private Set nodeIdsWithIndex(String... indices) { diff --git a/server/src/internalClusterTest/java/org/opensearch/search/suggest/CompletionSuggestSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/suggest/CompletionSuggestSearchIT.java index 690564fe1cac8..30dba87f8ef5d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/suggest/CompletionSuggestSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/suggest/CompletionSuggestSearchIT.java @@ -31,7 +31,9 @@ package org.opensearch.search.suggest; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.carrotsearch.randomizedtesting.generators.RandomStrings; + import org.apache.lucene.analysis.TokenStreamToAutomaton; import org.apache.lucene.search.suggest.document.ContextSuggestField; import org.apache.lucene.tests.util.LuceneTestCase.SuppressCodecs; @@ -46,8 +48,9 @@ import org.opensearch.common.FieldMemoryStats; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.Fuzziness; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperParsingException; import org.opensearch.index.mapper.MapperService; import org.opensearch.plugins.Plugin; @@ -61,8 +64,8 @@ import org.opensearch.search.suggest.completion.context.CategoryContextMapping; import org.opensearch.search.suggest.completion.context.ContextMapping; import org.opensearch.search.suggest.completion.context.GeoContextMapping; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; @@ -78,8 +81,9 @@ import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; -import static org.opensearch.common.util.CollectionUtils.iterableAsArrayList; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.core.common.util.CollectionUtils.iterableAsArrayList; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAllSuccessful; import static org.opensearch.test.hamcrest.OpenSearchAssertions.hasId; @@ -95,7 +99,24 @@ import static org.hamcrest.Matchers.notNullValue; @SuppressCodecs("*") // requires custom completion format -public class CompletionSuggestSearchIT extends OpenSearchIntegTestCase { +public class CompletionSuggestSearchIT extends ParameterizedOpenSearchIntegTestCase { + public CompletionSuggestSearchIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + private final String INDEX = RandomStrings.randomAsciiOfLength(random(), 10).toLowerCase(Locale.ROOT); private final String FIELD = RandomStrings.randomAsciiOfLength(random(), 10).toLowerCase(Locale.ROOT); private final CompletionMappingBuilder completionMappingBuilder = new CompletionMappingBuilder(); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/suggest/ContextCompletionSuggestSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/suggest/ContextCompletionSuggestSearchIT.java index 27d3d455330f5..bac3e7fb61683 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/suggest/ContextCompletionSuggestSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/suggest/ContextCompletionSuggestSearchIT.java @@ -31,18 +31,20 @@ package org.opensearch.search.suggest; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.carrotsearch.randomizedtesting.generators.RandomStrings; -import org.apache.lucene.tests.util.LuceneTestCase.SuppressCodecs; +import org.apache.lucene.tests.util.LuceneTestCase.SuppressCodecs; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.search.SearchResponse; import org.opensearch.common.geo.GeoPoint; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.Fuzziness; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.rest.RestStatus; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.suggest.CompletionSuggestSearchIT.CompletionMappingBuilder; import org.opensearch.search.suggest.completion.CompletionSuggestionBuilder; import org.opensearch.search.suggest.completion.context.CategoryContextMapping; @@ -51,11 +53,12 @@ import org.opensearch.search.suggest.completion.context.ContextMapping; import org.opensearch.search.suggest.completion.context.GeoContextMapping; import org.opensearch.search.suggest.completion.context.GeoQueryContext; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -64,12 +67,29 @@ import java.util.Map; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertNoFailures; import static org.hamcrest.core.IsEqual.equalTo; @SuppressCodecs("*") // requires custom completion format -public class ContextCompletionSuggestSearchIT extends OpenSearchIntegTestCase { +public class ContextCompletionSuggestSearchIT extends ParameterizedOpenSearchIntegTestCase { + public ContextCompletionSuggestSearchIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } private final String INDEX = RandomStrings.randomAsciiOfLength(random(), 10).toLowerCase(Locale.ROOT); private final String FIELD = RandomStrings.randomAsciiOfLength(random(), 10).toLowerCase(Locale.ROOT); diff --git a/server/src/internalClusterTest/java/org/opensearch/search/suggest/SuggestSearchIT.java b/server/src/internalClusterTest/java/org/opensearch/search/suggest/SuggestSearchIT.java index 07e58db836740..32bb0e34054bb 100644 --- a/server/src/internalClusterTest/java/org/opensearch/search/suggest/SuggestSearchIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/search/suggest/SuggestSearchIT.java @@ -32,16 +32,18 @@ package org.opensearch.search.suggest; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.OpenSearchException; import org.opensearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.action.search.SearchPhaseExecutionException; import org.opensearch.action.search.SearchRequestBuilder; import org.opensearch.action.search.SearchResponse; -import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexSettings; import org.opensearch.plugins.Plugin; import org.opensearch.plugins.ScriptPlugin; @@ -55,7 +57,7 @@ import org.opensearch.search.suggest.phrase.StupidBackoff; import org.opensearch.search.suggest.term.TermSuggestionBuilder; import org.opensearch.search.suggest.term.TermSuggestionBuilder.SuggestMode; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; import org.opensearch.test.hamcrest.OpenSearchAssertions; import java.io.IOException; @@ -74,6 +76,7 @@ import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; import static org.opensearch.index.query.QueryBuilders.matchQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.opensearch.search.suggest.SuggestBuilders.phraseSuggestion; import static org.opensearch.search.suggest.SuggestBuilders.termSuggestion; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; @@ -93,7 +96,23 @@ * possible these tests should declare for the first request, make the request, modify the configuration for the next request, make that * request, modify again, request again, etc. This makes it very obvious what changes between requests. */ -public class SuggestSearchIT extends OpenSearchIntegTestCase { +public class SuggestSearchIT extends ParameterizedOpenSearchIntegTestCase { + public SuggestSearchIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } // see #3196 public void testSuggestAcrossMultipleIndices() throws IOException { @@ -1305,14 +1324,13 @@ public void testPhraseSuggesterCollate() throws InterruptedException, ExecutionE assertSuggestionSize(searchSuggest, 0, 10, "title"); // suggest with collate - String filterString = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("match_phrase") - .field("{{field}}", "{{suggestion}}") - .endObject() - .endObject() - ); + String filterString = XContentFactory.jsonBuilder() + .startObject() + .startObject("match_phrase") + .field("{{field}}", "{{suggestion}}") + .endObject() + .endObject() + .toString(); PhraseSuggestionBuilder filteredQuerySuggest = suggest.collateQuery(filterString); filteredQuerySuggest.collateParams(Collections.singletonMap("field", "title")); searchSuggest = searchSuggest("united states house of representatives elections in washington 2006", "title", filteredQuerySuggest); @@ -1325,9 +1343,13 @@ public void testPhraseSuggesterCollate() throws InterruptedException, ExecutionE NumShards numShards = getNumShards("test"); // collate suggest with bad query - String incorrectFilterString = Strings.toString( - XContentFactory.jsonBuilder().startObject().startObject("test").field("title", "{{suggestion}}").endObject().endObject() - ); + String incorrectFilterString = XContentFactory.jsonBuilder() + .startObject() + .startObject("test") + .field("title", "{{suggestion}}") + .endObject() + .endObject() + .toString(); PhraseSuggestionBuilder incorrectFilteredSuggest = suggest.collateQuery(incorrectFilterString); Map> namedSuggestion = new HashMap<>(); namedSuggestion.put("my_title_suggestion", incorrectFilteredSuggest); @@ -1339,9 +1361,13 @@ public void testPhraseSuggesterCollate() throws InterruptedException, ExecutionE } // suggest with collation - String filterStringAsFilter = Strings.toString( - XContentFactory.jsonBuilder().startObject().startObject("match_phrase").field("title", "{{suggestion}}").endObject().endObject() - ); + String filterStringAsFilter = XContentFactory.jsonBuilder() + .startObject() + .startObject("match_phrase") + .field("title", "{{suggestion}}") + .endObject() + .endObject() + .toString(); PhraseSuggestionBuilder filteredFilterSuggest = suggest.collateQuery(filterStringAsFilter); searchSuggest = searchSuggest( @@ -1352,9 +1378,13 @@ public void testPhraseSuggesterCollate() throws InterruptedException, ExecutionE assertSuggestionSize(searchSuggest, 0, 2, "title"); // collate suggest with bad query - String filterStr = Strings.toString( - XContentFactory.jsonBuilder().startObject().startObject("pprefix").field("title", "{{suggestion}}").endObject().endObject() - ); + String filterStr = XContentFactory.jsonBuilder() + .startObject() + .startObject("pprefix") + .field("title", "{{suggestion}}") + .endObject() + .endObject() + .toString(); suggest.collateQuery(filterStr); try { @@ -1365,14 +1395,13 @@ public void testPhraseSuggesterCollate() throws InterruptedException, ExecutionE } // collate script failure due to no additional params - String collateWithParams = Strings.toString( - XContentFactory.jsonBuilder() - .startObject() - .startObject("{{query_type}}") - .field("{{query_field}}", "{{suggestion}}") - .endObject() - .endObject() - ); + String collateWithParams = XContentFactory.jsonBuilder() + .startObject() + .startObject("{{query_type}}") + .field("{{query_field}}", "{{suggestion}}") + .endObject() + .endObject() + .toString(); try { searchSuggest("united states house of representatives elections in washington 2006", numShards.numPrimaries, namedSuggestion); diff --git a/server/src/internalClusterTest/java/org/opensearch/similarity/SimilarityIT.java b/server/src/internalClusterTest/java/org/opensearch/similarity/SimilarityIT.java index 929aac388b678..8c9bff9833462 100644 --- a/server/src/internalClusterTest/java/org/opensearch/similarity/SimilarityIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/similarity/SimilarityIT.java @@ -32,17 +32,41 @@ package org.opensearch.similarity; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + import org.opensearch.action.search.SearchResponse; import org.opensearch.common.settings.Settings; -import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.test.ParameterizedOpenSearchIntegTestCase; + +import java.util.Arrays; +import java.util.Collection; import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; import static org.opensearch.index.query.QueryBuilders.matchQuery; +import static org.opensearch.search.SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; -public class SimilarityIT extends OpenSearchIntegTestCase { +public class SimilarityIT extends ParameterizedOpenSearchIntegTestCase { + public SimilarityIT(Settings settings) { + super(settings); + } + + @ParametersFactory + public static Collection parameters() { + return Arrays.asList( + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), false).build() }, + new Object[] { Settings.builder().put(CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING.getKey(), true).build() } + ); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder().put(super.featureFlagSettings()).put(FeatureFlags.CONCURRENT_SEGMENT_SEARCH, "true").build(); + } + public void testCustomBM25Similarity() throws Exception { try { client().admin().indices().prepareDelete("test").execute().actionGet(); diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/AbortedRestoreIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/AbortedRestoreIT.java index 6a8e45920e806..318c7d82380ca 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/AbortedRestoreIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/AbortedRestoreIT.java @@ -32,17 +32,16 @@ package org.opensearch.snapshots; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; import org.opensearch.action.admin.indices.recovery.RecoveryResponse; import org.opensearch.action.support.IndicesOptions; import org.opensearch.cluster.routing.RecoverySource; import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.action.ActionFuture; import org.opensearch.indices.recovery.RecoveryState; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.threadpool.ThreadPool; import org.opensearch.threadpool.ThreadPoolStats; - import org.hamcrest.Matcher; import java.util.List; @@ -59,7 +58,7 @@ public class AbortedRestoreIT extends AbstractSnapshotIntegTestCase { public void testAbortedRestoreAlsoAbortFileRestores() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String indexName = "test-abort-restore"; diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/BlobStoreIncrementalityIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/BlobStoreIncrementalityIT.java index 5a3d2c6c4bafd..9a40ea2c95b28 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/BlobStoreIncrementalityIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/BlobStoreIncrementalityIT.java @@ -59,7 +59,7 @@ public class BlobStoreIncrementalityIT extends AbstractSnapshotIntegTestCase { public void testIncrementalBehaviorOnPrimaryFailover() throws InterruptedException, ExecutionException, IOException { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String primaryNode = internalCluster().startDataOnlyNode(); final String indexName = "test-index"; createIndex( @@ -148,7 +148,7 @@ public void testIncrementalBehaviorOnPrimaryFailover() throws InterruptedExcepti } public void testForceMergeCausesFullSnapshot() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().ensureAtLeastNumDataNodes(2); final String indexName = "test-index"; createIndex(indexName, Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).build()); diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/CloneSnapshotIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/CloneSnapshotIT.java index 137be2187fe57..00e2d9bd92158 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/CloneSnapshotIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/CloneSnapshotIT.java @@ -31,40 +31,42 @@ package org.opensearch.snapshots; -import org.opensearch.action.ActionFuture; +import org.opensearch.action.ActionRunnable; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.status.SnapshotIndexStatus; import org.opensearch.action.admin.cluster.snapshots.status.SnapshotStatus; +import org.opensearch.action.support.PlainActionFuture; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.Client; import org.opensearch.cluster.SnapshotsInProgress; +import org.opensearch.cluster.metadata.RepositoryMetadata; +import org.opensearch.common.UUIDs; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.index.IndexNotFoundException; +import org.opensearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshot; +import org.opensearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshots; +import org.opensearch.index.snapshots.blobstore.IndexShardSnapshot; +import org.opensearch.index.snapshots.blobstore.SnapshotFiles; +import org.opensearch.repositories.IndexId; import org.opensearch.repositories.RepositoriesService; import org.opensearch.repositories.RepositoryData; +import org.opensearch.repositories.RepositoryShardId; +import org.opensearch.repositories.blobstore.BlobStoreRepository; import org.opensearch.snapshots.mockstore.MockRepository; import org.opensearch.test.OpenSearchIntegTestCase; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.ExecutionException; +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.hamcrest.Matchers.containsString; - -import org.opensearch.action.ActionRunnable; -import org.opensearch.action.support.PlainActionFuture; -import org.opensearch.common.UUIDs; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshot; -import org.opensearch.index.snapshots.blobstore.BlobStoreIndexShardSnapshots; -import org.opensearch.index.snapshots.blobstore.SnapshotFiles; -import org.opensearch.repositories.IndexId; -import org.opensearch.repositories.RepositoryShardId; -import org.opensearch.repositories.blobstore.BlobStoreRepository; - -import java.nio.file.Path; - import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -72,7 +74,7 @@ public class CloneSnapshotIT extends AbstractSnapshotIntegTestCase { public void testShardClone() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "repo-name"; final Path repoPath = randomRepoPath(); @@ -91,7 +93,7 @@ public void testShardClone() throws Exception { final String sourceSnapshot = "source-snapshot"; final SnapshotInfo sourceSnapshotInfo = createFullSnapshot(repoName, sourceSnapshot); - final BlobStoreRepository repository = (BlobStoreRepository) internalCluster().getCurrentMasterNodeInstance( + final BlobStoreRepository repository = (BlobStoreRepository) internalCluster().getCurrentClusterManagerNodeInstance( RepositoriesService.class ).repository(repoName); final RepositoryData repositoryData = getRepositoryData(repoName); @@ -143,7 +145,7 @@ public void testShardClone() throws Exception { } public void testCloneSnapshotIndex() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "repo-name"; createRepository(repoName, "fs"); @@ -171,8 +173,157 @@ public void testCloneSnapshotIndex() throws Exception { assertEquals(status1.getStats().getTotalSize(), status2.getStats().getTotalSize()); } + public void testCloneShallowSnapshotIndex() throws Exception { + disableRepoConsistencyCheck("This test uses remote store repository"); + final String remoteStoreRepoName = "remote-store-repo-name"; + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(remoteStoreRepoName, remoteStoreRepoPath)); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(remoteStoreRepoName, remoteStoreRepoPath)); + + final String snapshotRepoName = "snapshot-repo-name"; + final Path snapshotRepoPath = randomRepoPath(); + createRepository(snapshotRepoName, "fs", snapshotRepoPath); + + final String shallowSnapshotRepoName = "shallow-snapshot-repo-name"; + final Path shallowSnapshotRepoPath = randomRepoPath(); + createRepository(shallowSnapshotRepoName, "fs", snapshotRepoSettingsForShallowCopy(shallowSnapshotRepoPath)); + + final String indexName = "index-1"; + createIndexWithRandomDocs(indexName, randomIntBetween(5, 10)); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + final String snapshot = "snapshot"; + createFullSnapshot(snapshotRepoName, snapshot); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length == 0); + + indexRandomDocs(indexName, randomIntBetween(20, 100)); + + final String shallowSnapshot = "shallow-snapshot"; + createFullSnapshot(shallowSnapshotRepoName, shallowSnapshot); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length == 1); + + if (randomBoolean()) { + assertAcked(admin().indices().prepareDelete(indexName)); + } + + final String sourceSnapshot = shallowSnapshot; + final String targetSnapshot = "target-snapshot"; + assertAcked(startClone(shallowSnapshotRepoName, sourceSnapshot, targetSnapshot, indexName, remoteStoreEnabledIndexName).get()); + logger.info("Lock files count: {}", getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length == 2); + } + + public void testShallowCloneNameAvailability() throws Exception { + disableRepoConsistencyCheck("This test uses remote store repository"); + final String remoteStoreRepoName = "remote-store-repo-name"; + final Path remoteStorePath = randomRepoPath().toAbsolutePath(); + internalCluster().startClusterManagerOnlyNode( + Settings.builder() + .put(LARGE_SNAPSHOT_POOL_SETTINGS) + .put(remoteStoreClusterSettings(remoteStoreRepoName, remoteStorePath)) + .build() + ); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(remoteStoreRepoName, remoteStorePath)); + + final String shallowSnapshotRepoName = "shallow-snapshot-repo-name"; + final Path shallowSnapshotRepoPath = randomRepoPath(); + createRepository(shallowSnapshotRepoName, "fs", snapshotRepoSettingsForShallowCopy(shallowSnapshotRepoPath)); + + final String indexName = "index-1"; + createIndexWithRandomDocs(indexName, randomIntBetween(5, 10)); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + final String shallowSnapshot1 = "snapshot1"; + createFullSnapshot(shallowSnapshotRepoName, shallowSnapshot1); + + final String shallowSnapshot2 = "snapshot2"; + createFullSnapshot(shallowSnapshotRepoName, shallowSnapshot2); + + ExecutionException ex = expectThrows( + ExecutionException.class, + () -> startClone(shallowSnapshotRepoName, shallowSnapshot1, shallowSnapshot2, indexName, remoteStoreEnabledIndexName).get() + ); + assertThat(ex.getMessage(), containsString("snapshot with the same name already exists")); + } + + public void testCloneAfterRepoShallowSettingEnabled() throws Exception { + disableRepoConsistencyCheck("This test uses remote store repository"); + final String remoteStoreRepoName = "remote-store-repo-name"; + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(remoteStoreRepoName, remoteStoreRepoPath)); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(remoteStoreRepoName, remoteStoreRepoPath)); + + final String snapshotRepoName = "snapshot-repo-name"; + final Path snapshotRepoPath = randomRepoPath(); + createRepository(snapshotRepoName, "fs", snapshotRepoPath); + + final String indexName = "index-1"; + createIndexWithRandomDocs(indexName, randomIntBetween(5, 10)); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + final String snapshot = "snapshot"; + createFullSnapshot(snapshotRepoName, snapshot); + assertEquals(getSnapshot(snapshotRepoName, snapshot).state(), SnapshotState.SUCCESS); + + // Updating the snapshot repository flag to enable shallow snapshots + createRepository(snapshotRepoName, "fs", snapshotRepoSettingsForShallowCopy(snapshotRepoPath)); + RepositoryMetadata updatedRepositoryMetadata = clusterAdmin().prepareGetRepositories(snapshotRepoName).get().repositories().get(0); + assertTrue(updatedRepositoryMetadata.settings().getAsBoolean(BlobStoreRepository.REMOTE_STORE_INDEX_SHALLOW_COPY.getKey(), false)); + + final String targetSnapshot = "target-snapshot"; + assertAcked(startClone(snapshotRepoName, snapshot, targetSnapshot, indexName, remoteStoreEnabledIndexName).get()); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length == 0); + assertEquals(getSnapshot(snapshotRepoName, targetSnapshot).isRemoteStoreIndexShallowCopyEnabled(), false); + } + + public void testCloneAfterRepoShallowSettingDisabled() throws Exception { + disableRepoConsistencyCheck("This test uses remote store repository"); + final String remoteStoreRepoName = "remote-store-repo-name"; + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(remoteStoreRepoName, remoteStoreRepoPath)); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(remoteStoreRepoName, remoteStoreRepoPath)); + + final String snapshotRepoName = "snapshot-repo-name"; + final Path snapshotRepoPath = randomRepoPath(); + createRepository(snapshotRepoName, "fs", snapshotRepoSettingsForShallowCopy(snapshotRepoPath)); + + final String indexName = "index-1"; + createIndexWithRandomDocs(indexName, randomIntBetween(5, 10)); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + final String snapshot = "snapshot"; + createFullSnapshot(snapshotRepoName, snapshot); + assertEquals(getSnapshot(snapshotRepoName, snapshot).state(), SnapshotState.SUCCESS); + + // Updating the snapshot repository flag to enable shallow snapshots + createRepository(snapshotRepoName, "fs", snapshotRepoPath); + RepositoryMetadata updatedRepositoryMetadata = clusterAdmin().prepareGetRepositories(snapshotRepoName).get().repositories().get(0); + assertFalse(updatedRepositoryMetadata.settings().getAsBoolean(BlobStoreRepository.REMOTE_STORE_INDEX_SHALLOW_COPY.getKey(), false)); + + final String targetSnapshot = "target-snapshot"; + assertAcked(startClone(snapshotRepoName, snapshot, targetSnapshot, indexName, remoteStoreEnabledIndexName).get()); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length == 2); + assertEquals(getSnapshot(snapshotRepoName, targetSnapshot).isRemoteStoreIndexShallowCopyEnabled(), true); + } + public void testClonePreventsSnapshotDelete() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "repo-name"; createRepository(repoName, "mock"); @@ -185,9 +336,9 @@ public void testClonePreventsSnapshotDelete() throws Exception { indexRandomDocs(indexName, randomIntBetween(20, 100)); final String targetSnapshot = "target-snapshot"; - blockNodeOnAnyFiles(repoName, masterName); + blockNodeOnAnyFiles(repoName, clusterManagerName); final ActionFuture cloneFuture = startClone(repoName, sourceSnapshot, targetSnapshot, indexName); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); assertFalse(cloneFuture.isDone()); ConcurrentSnapshotExecutionException ex = expectThrows( @@ -196,7 +347,7 @@ public void testClonePreventsSnapshotDelete() throws Exception { ); assertThat(ex.getMessage(), containsString("cannot delete snapshot while it is being cloned")); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); assertAcked(cloneFuture.get()); final List status = clusterAdmin().prepareSnapshotStatus(repoName) .setSnapshots(sourceSnapshot, targetSnapshot) @@ -210,7 +361,7 @@ public void testClonePreventsSnapshotDelete() throws Exception { } public void testConcurrentCloneAndSnapshot() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "repo-name"; createRepository(repoName, "mock"); @@ -234,7 +385,7 @@ public void testConcurrentCloneAndSnapshot() throws Exception { public void testLongRunningCloneAllowsConcurrentSnapshot() throws Exception { // large snapshot pool so blocked snapshot threads from cloning don't prevent concurrent snapshot finalizations - final String masterNode = internalCluster().startMasterOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -245,9 +396,9 @@ public void testLongRunningCloneAllowsConcurrentSnapshot() throws Exception { createFullSnapshot(repoName, sourceSnapshot); final String targetSnapshot = "target-snapshot"; - blockMasterOnShardClone(repoName); + blockClusterManagerOnShardClone(repoName); final ActionFuture cloneFuture = startClone(repoName, sourceSnapshot, targetSnapshot, indexSlow); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); final String indexFast = "index-fast"; createIndexWithRandomDocs(indexFast, randomIntBetween(20, 100)); @@ -257,13 +408,13 @@ public void testLongRunningCloneAllowsConcurrentSnapshot() throws Exception { ); assertThat(cloneFuture.isDone(), is(false)); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); assertAcked(cloneFuture.get()); } public void testLongRunningSnapshotAllowsConcurrentClone() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -293,7 +444,7 @@ public void testLongRunningSnapshotAllowsConcurrentClone() throws Exception { } public void testDeletePreventsClone() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "repo-name"; createRepository(repoName, "mock"); @@ -306,9 +457,9 @@ public void testDeletePreventsClone() throws Exception { indexRandomDocs(indexName, randomIntBetween(20, 100)); final String targetSnapshot = "target-snapshot"; - blockNodeOnAnyFiles(repoName, masterName); + blockNodeOnAnyFiles(repoName, clusterManagerName); final ActionFuture deleteFuture = startDeleteSnapshot(repoName, sourceSnapshot); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); assertFalse(deleteFuture.isDone()); ConcurrentSnapshotExecutionException ex = expectThrows( @@ -317,13 +468,13 @@ public void testDeletePreventsClone() throws Exception { ); assertThat(ex.getMessage(), containsString("cannot clone from snapshot that is being deleted")); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); assertAcked(deleteFuture.get()); } public void testBackToBackClonesForIndexNotInCluster() throws Exception { // large snapshot pool so blocked snapshot threads from cloning don't prevent concurrent snapshot finalizations - final String masterNode = internalCluster().startMasterOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -336,9 +487,9 @@ public void testBackToBackClonesForIndexNotInCluster() throws Exception { assertAcked(admin().indices().prepareDelete(indexBlocked).get()); final String targetSnapshot1 = "target-snapshot"; - blockMasterOnShardClone(repoName); + blockClusterManagerOnShardClone(repoName); final ActionFuture cloneFuture1 = startClone(repoName, sourceSnapshot, targetSnapshot1, indexBlocked); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); assertThat(cloneFuture1.isDone(), is(false)); final int extraClones = randomIntBetween(1, 5); @@ -366,7 +517,7 @@ public void testBackToBackClonesForIndexNotInCluster() throws Exception { assertFalse(extraSnapshotFuture.isDone()); } - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); assertAcked(cloneFuture1.get()); for (ActionFuture extraCloneFuture : extraCloneFutures) { @@ -377,8 +528,8 @@ public void testBackToBackClonesForIndexNotInCluster() throws Exception { } } - public void testMasterFailoverDuringCloneStep1() throws Exception { - internalCluster().startMasterOnlyNodes(3); + public void testClusterManagerFailoverDuringCloneStep1() throws Exception { + internalCluster().startClusterManagerOnlyNodes(3); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -388,13 +539,13 @@ public void testMasterFailoverDuringCloneStep1() throws Exception { final String sourceSnapshot = "source-snapshot"; createFullSnapshot(repoName, sourceSnapshot); - blockMasterOnReadIndexMeta(repoName); + blockClusterManagerOnReadIndexMeta(repoName); final String cloneName = "target-snapshot"; final ActionFuture cloneFuture = startCloneFromDataNode(repoName, sourceSnapshot, cloneName, testIndex); awaitNumberOfSnapshotsInProgress(1); - final String masterNode = internalCluster().getMasterName(); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); - internalCluster().restartNode(masterNode); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); + internalCluster().restartNode(clusterManagerNode); boolean cloneSucceeded = false; try { cloneFuture.actionGet(TimeValue.timeValueSeconds(30L)); @@ -404,16 +555,17 @@ public void testMasterFailoverDuringCloneStep1() throws Exception { // snapshot on disconnect slowly enough for it to work out } - awaitNoMoreRunningOperations(internalCluster().getMasterName()); + awaitNoMoreRunningOperations(internalCluster().getClusterManagerName()); - // Check if the clone operation worked out by chance as a result of the clone request being retried because of the master failover + // Check if the clone operation worked out by chance as a result of the clone request being retried + // because of the cluster-manager failover cloneSucceeded = cloneSucceeded || getRepositoryData(repoName).getSnapshotIds().stream().anyMatch(snapshotId -> snapshotId.getName().equals(cloneName)); assertAllSnapshotsSuccessful(getRepositoryData(repoName), cloneSucceeded ? 2 : 1); } public void testFailsOnCloneMissingIndices() { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "repo-name"; final Path repoPath = randomRepoPath(); @@ -430,9 +582,9 @@ public void testFailsOnCloneMissingIndices() { ); } - public void testMasterFailoverDuringCloneStep2() throws Exception { + public void testClusterManagerFailoverDuringCloneStep2() throws Exception { // large snapshot pool so blocked snapshot threads from cloning don't prevent concurrent snapshot finalizations - internalCluster().startMasterOnlyNodes(3, LARGE_SNAPSHOT_POOL_SETTINGS); + internalCluster().startClusterManagerOnlyNodes(3, LARGE_SNAPSHOT_POOL_SETTINGS); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -443,21 +595,21 @@ public void testMasterFailoverDuringCloneStep2() throws Exception { createFullSnapshot(repoName, sourceSnapshot); final String targetSnapshot = "target-snapshot"; - blockMasterOnShardClone(repoName); + blockClusterManagerOnShardClone(repoName); final ActionFuture cloneFuture = startCloneFromDataNode(repoName, sourceSnapshot, targetSnapshot, testIndex); awaitNumberOfSnapshotsInProgress(1); - final String masterNode = internalCluster().getMasterName(); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); - internalCluster().restartNode(masterNode); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); + internalCluster().restartNode(clusterManagerNode); expectThrows(SnapshotException.class, cloneFuture::actionGet); - awaitNoMoreRunningOperations(internalCluster().getMasterName()); + awaitNoMoreRunningOperations(internalCluster().getClusterManagerName()); assertAllSnapshotsSuccessful(getRepositoryData(repoName), 2); } public void testExceptionDuringShardClone() throws Exception { // large snapshot pool so blocked snapshot threads from cloning don't prevent concurrent snapshot finalizations - internalCluster().startMasterOnlyNodes(3, LARGE_SNAPSHOT_POOL_SETTINGS); + internalCluster().startClusterManagerOnlyNodes(3, LARGE_SNAPSHOT_POOL_SETTINGS); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -468,20 +620,20 @@ public void testExceptionDuringShardClone() throws Exception { createFullSnapshot(repoName, sourceSnapshot); final String targetSnapshot = "target-snapshot"; - blockMasterFromFinalizingSnapshotOnSnapFile(repoName); + blockClusterManagerFromFinalizingSnapshotOnSnapFile(repoName); final ActionFuture cloneFuture = startCloneFromDataNode(repoName, sourceSnapshot, targetSnapshot, testIndex); awaitNumberOfSnapshotsInProgress(1); - final String masterNode = internalCluster().getMasterName(); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); - unblockNode(repoName, masterNode); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); + unblockNode(repoName, clusterManagerNode); expectThrows(SnapshotException.class, cloneFuture::actionGet); - awaitNoMoreRunningOperations(internalCluster().getMasterName()); + awaitNoMoreRunningOperations(internalCluster().getClusterManagerName()); assertAllSnapshotsSuccessful(getRepositoryData(repoName), 1); assertAcked(startDeleteSnapshot(repoName, sourceSnapshot).get()); } public void testDoesNotStartOnBrokenSourceSnapshot() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -490,8 +642,8 @@ public void testDoesNotStartOnBrokenSourceSnapshot() throws Exception { final String sourceSnapshot = "source-snapshot"; blockDataNode(repoName, dataNode); - final Client masterClient = internalCluster().masterClient(); - final ActionFuture sourceSnapshotFuture = masterClient.admin() + final Client clusterManagerClient = internalCluster().clusterManagerClient(); + final ActionFuture sourceSnapshotFuture = clusterManagerClient.admin() .cluster() .prepareCreateSnapshot(repoName, sourceSnapshot) .setWaitForCompletion(true) @@ -503,7 +655,7 @@ public void testDoesNotStartOnBrokenSourceSnapshot() throws Exception { final SnapshotException sne = expectThrows( SnapshotException.class, - () -> startClone(masterClient, repoName, sourceSnapshot, "target-snapshot", testIndex).actionGet( + () -> startClone(clusterManagerClient, repoName, sourceSnapshot, "target-snapshot", testIndex).actionGet( TimeValue.timeValueSeconds(30L) ) ); @@ -516,7 +668,7 @@ public void testDoesNotStartOnBrokenSourceSnapshot() throws Exception { } public void testStartSnapshotWithSuccessfulShardClonePendingFinalization() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -527,18 +679,18 @@ public void testStartSnapshotWithSuccessfulShardClonePendingFinalization() throw final String sourceSnapshot = "source-snapshot"; createFullSnapshot(repoName, sourceSnapshot); - blockMasterOnWriteIndexFile(repoName); + blockClusterManagerOnWriteIndexFile(repoName); final String cloneName = "clone-blocked"; final ActionFuture blockedClone = startClone(repoName, sourceSnapshot, cloneName, indexName); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); awaitNumberOfSnapshotsInProgress(1); blockNodeOnAnyFiles(repoName, dataNode); final ActionFuture otherSnapshot = startFullSnapshot(repoName, "other-snapshot"); awaitNumberOfSnapshotsInProgress(2); assertFalse(blockedClone.isDone()); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); awaitNumberOfSnapshotsInProgress(1); - awaitMasterFinishRepoOperations(); + awaitClusterManagerFinishRepoOperations(); unblockNode(repoName, dataNode); assertAcked(blockedClone.get()); assertEquals(getSnapshot(repoName, cloneName).state(), SnapshotState.SUCCESS); @@ -546,7 +698,7 @@ public void testStartSnapshotWithSuccessfulShardClonePendingFinalization() throw } public void testStartCloneWithSuccessfulShardClonePendingFinalization() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -557,18 +709,18 @@ public void testStartCloneWithSuccessfulShardClonePendingFinalization() throws E final String sourceSnapshot = "source-snapshot"; createFullSnapshot(repoName, sourceSnapshot); - blockMasterOnWriteIndexFile(repoName); + blockClusterManagerOnWriteIndexFile(repoName); final String cloneName = "clone-blocked"; final ActionFuture blockedClone = startClone(repoName, sourceSnapshot, cloneName, indexName); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); awaitNumberOfSnapshotsInProgress(1); final String otherCloneName = "other-clone"; final ActionFuture otherClone = startClone(repoName, sourceSnapshot, otherCloneName, indexName); awaitNumberOfSnapshotsInProgress(2); assertFalse(blockedClone.isDone()); - unblockNode(repoName, masterName); - awaitNoMoreRunningOperations(masterName); - awaitMasterFinishRepoOperations(); + unblockNode(repoName, clusterManagerName); + awaitNoMoreRunningOperations(clusterManagerName); + awaitClusterManagerFinishRepoOperations(); assertAcked(blockedClone.get()); assertAcked(otherClone.get()); assertEquals(getSnapshot(repoName, cloneName).state(), SnapshotState.SUCCESS); @@ -576,7 +728,7 @@ public void testStartCloneWithSuccessfulShardClonePendingFinalization() throws E } public void testStartCloneWithSuccessfulShardSnapshotPendingFinalization() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -587,9 +739,9 @@ public void testStartCloneWithSuccessfulShardSnapshotPendingFinalization() throw final String sourceSnapshot = "source-snapshot"; createFullSnapshot(repoName, sourceSnapshot); - blockMasterOnWriteIndexFile(repoName); + blockClusterManagerOnWriteIndexFile(repoName); final ActionFuture blockedSnapshot = startFullSnapshot(repoName, "snap-blocked"); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); awaitNumberOfSnapshotsInProgress(1); final String cloneName = "clone"; final ActionFuture clone = startClone(repoName, sourceSnapshot, cloneName, indexName); @@ -602,11 +754,11 @@ public void testStartCloneWithSuccessfulShardSnapshotPendingFinalization() throw }); assertFalse(blockedSnapshot.isDone()); } finally { - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); } awaitNoMoreRunningOperations(); - awaitMasterFinishRepoOperations(); + awaitClusterManagerFinishRepoOperations(); assertSuccessful(blockedSnapshot); assertAcked(clone.get()); @@ -641,13 +793,13 @@ private static ActionFuture startClone( return client.admin().cluster().prepareCloneSnapshot(repoName, sourceSnapshot, targetSnapshot).setIndices(indices).execute(); } - private void blockMasterOnReadIndexMeta(String repoName) { - ((MockRepository) internalCluster().getCurrentMasterNodeInstance(RepositoriesService.class).repository(repoName)) + private void blockClusterManagerOnReadIndexMeta(String repoName) { + ((MockRepository) internalCluster().getCurrentClusterManagerNodeInstance(RepositoriesService.class).repository(repoName)) .setBlockOnReadIndexMeta(); } - private void blockMasterOnShardClone(String repoName) { - ((MockRepository) internalCluster().getCurrentMasterNodeInstance(RepositoriesService.class).repository(repoName)) + private void blockClusterManagerOnShardClone(String repoName) { + ((MockRepository) internalCluster().getCurrentClusterManagerNodeInstance(RepositoriesService.class).repository(repoName)) .setBlockOnWriteShardLevelMeta(); } @@ -688,18 +840,14 @@ private static BlobStoreIndexShardSnapshot readShardSnapshot( RepositoryShardId repositoryShardId, SnapshotId snapshotId ) { - return PlainActionFuture.get( - f -> repository.threadPool() - .generic() - .execute( - ActionRunnable.supply( - f, - () -> repository.loadShardSnapshot( - repository.shardContainer(repositoryShardId.index(), repositoryShardId.shardId()), - snapshotId - ) - ) - ) - ); + return PlainActionFuture.get(f -> repository.threadPool().generic().execute(ActionRunnable.supply(f, () -> { + IndexShardSnapshot indexShardSnapshot = repository.loadShardSnapshot( + repository.shardContainer(repositoryShardId.index(), repositoryShardId.shardId()), + snapshotId + ); + assert indexShardSnapshot instanceof BlobStoreIndexShardSnapshot + : "indexShardSnapshot should be an instance of BlobStoreIndexShardSnapshot"; + return (BlobStoreIndexShardSnapshot) indexShardSnapshot; + }))); } } diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/ConcurrentSnapshotsIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/ConcurrentSnapshotsIT.java index 9adb0ff6260e8..4e90a6cd007b2 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/ConcurrentSnapshotsIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/ConcurrentSnapshotsIT.java @@ -31,11 +31,7 @@ package org.opensearch.snapshots; -import com.carrotsearch.hppc.cursors.ObjectCursor; - import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; import org.opensearch.action.StepListener; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest; @@ -47,11 +43,13 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.SnapshotDeletionsInProgress; import org.opensearch.cluster.SnapshotsInProgress; -import org.opensearch.common.Strings; import org.opensearch.common.UUIDs; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.UncategorizedExecutionException; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; import org.opensearch.discovery.AbstractDisruptionTestCase; import org.opensearch.plugins.Plugin; import org.opensearch.repositories.RepositoryData; @@ -59,8 +57,8 @@ import org.opensearch.repositories.ShardGenerations; import org.opensearch.repositories.blobstore.BlobStoreRepository; import org.opensearch.snapshots.mockstore.MockRepository; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.disruption.NetworkDisruption; import org.opensearch.test.transport.MockTransportService; @@ -106,7 +104,7 @@ protected Settings nodeSettings(int nodeOrdinal) { } public void testLongRunningSnapshotAllowsConcurrentSnapshot() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -139,7 +137,7 @@ public void testLongRunningSnapshotAllowsConcurrentSnapshot() throws Exception { } public void testDeletesAreBatched() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -205,7 +203,7 @@ public void testDeletesAreBatched() throws Exception { } public void testBlockedRepoDoesNotBlockOtherRepos() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String blockedRepoName = "test-repo-blocked"; final String otherRepoName = "test-repo"; @@ -224,14 +222,14 @@ public void testBlockedRepoDoesNotBlockOtherRepos() throws Exception { .setWaitForCompletion(false) .get(); - unblockNode(blockedRepoName, internalCluster().getMasterName()); + unblockNode(blockedRepoName, internalCluster().getClusterManagerName()); expectThrows(SnapshotException.class, createSlowFuture::actionGet); assertBusy(() -> assertThat(currentSnapshots(otherRepoName), empty()), 30L, TimeUnit.SECONDS); } public void testMultipleReposAreIndependent() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); // We're blocking a some of the snapshot threads when we block the first repo below so we have to make sure we have enough threads // left for the second concurrent snapshot. final String dataNode = startDataNodeWithLargeSnapshotPool(); @@ -255,7 +253,7 @@ public void testMultipleReposAreIndependent() throws Exception { } public void testMultipleReposAreIndependent2() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); // We're blocking a some of the snapshot threads when we block the first repo below so we have to make sure we have enough threads // left for the second repository's concurrent operations. final String dataNode = startDataNodeWithLargeSnapshotPool(); @@ -280,7 +278,7 @@ public void testMultipleReposAreIndependent2() throws Exception { } public void testMultipleReposAreIndependent3() throws Exception { - final String masterNode = internalCluster().startMasterOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(LARGE_SNAPSHOT_POOL_SETTINGS); internalCluster().startDataOnlyNode(); final String blockedRepoName = "test-repo-blocked"; final String otherRepoName = "test-repo"; @@ -289,19 +287,19 @@ public void testMultipleReposAreIndependent3() throws Exception { createIndexWithContent("test-index"); createFullSnapshot(blockedRepoName, "blocked-snapshot"); - blockNodeOnAnyFiles(blockedRepoName, masterNode); + blockNodeOnAnyFiles(blockedRepoName, clusterManagerNode); final ActionFuture slowDeleteFuture = startDeleteSnapshot(blockedRepoName, "*"); logger.info("--> waiting for concurrent snapshot(s) to finish"); createNSnapshots(otherRepoName, randomIntBetween(1, 5)); assertAcked(startDeleteSnapshot(otherRepoName, "*").get()); - unblockNode(blockedRepoName, masterNode); + unblockNode(blockedRepoName, clusterManagerNode); assertAcked(slowDeleteFuture.actionGet()); } public void testSnapshotRunsAfterInProgressDelete() throws Exception { - final String masterNode = internalCluster().startMasterOnlyNode(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -312,13 +310,13 @@ public void testSnapshotRunsAfterInProgressDelete() throws Exception { final String firstSnapshot = "first-snapshot"; createFullSnapshot(repoName, firstSnapshot); - blockMasterFromFinalizingSnapshotOnIndexFile(repoName); + blockClusterManagerFromFinalizingSnapshotOnIndexFile(repoName); final ActionFuture deleteFuture = startDeleteSnapshot(repoName, firstSnapshot); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); final ActionFuture snapshotFuture = startFullSnapshot(repoName, "second-snapshot"); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); final UncategorizedExecutionException ex = expectThrows(UncategorizedExecutionException.class, deleteFuture::actionGet); assertThat(ex.getRootCause(), instanceOf(IOException.class)); @@ -326,7 +324,7 @@ public void testSnapshotRunsAfterInProgressDelete() throws Exception { } public void testAbortOneOfMultipleSnapshots() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -386,7 +384,7 @@ public void testAbortOneOfMultipleSnapshots() throws Exception { } public void testCascadedAborts() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -447,8 +445,8 @@ public void testCascadedAborts() throws Exception { assertThat(client().admin().cluster().prepareGetSnapshots(repoName).get().getSnapshots(), empty()); } - public void testMasterFailOverWithQueuedDeletes() throws Exception { - internalCluster().startMasterOnlyNodes(3); + public void testClusterManagerFailOverWithQueuedDeletes() throws Exception { + internalCluster().startClusterManagerOnlyNodes(3); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -458,7 +456,10 @@ public void testMasterFailOverWithQueuedDeletes() throws Exception { final String firstSnapshot = "snapshot-one"; blockDataNode(repoName, dataNode); - final ActionFuture firstSnapshotResponse = startFullSnapshotFromNonMasterClient(repoName, firstSnapshot); + final ActionFuture firstSnapshotResponse = startFullSnapshotFromNonClusterManagerClient( + repoName, + firstSnapshot + ); waitForBlock(dataNode, repoName, TimeValue.timeValueSeconds(30L)); final String dataNode2 = internalCluster().startDataOnlyNode(); @@ -475,11 +476,14 @@ public void testMasterFailOverWithQueuedDeletes() throws Exception { return snapshotsInProgress.entries().size() == 2 && snapshotHasCompletedShard(secondSnapshot, snapshotsInProgress); }); - final ActionFuture firstDeleteFuture = startDeleteFromNonMasterClient(repoName, firstSnapshot); + final ActionFuture firstDeleteFuture = startDeleteFromNonClusterManagerClient(repoName, firstSnapshot); awaitNDeletionsInProgress(1); blockNodeOnAnyFiles(repoName, dataNode2); - final ActionFuture snapshotThreeFuture = startFullSnapshotFromNonMasterClient(repoName, "snapshot-three"); + final ActionFuture snapshotThreeFuture = startFullSnapshotFromNonClusterManagerClient( + repoName, + "snapshot-three" + ); waitForBlock(dataNode2, repoName, TimeValue.timeValueSeconds(30L)); assertThat(firstSnapshotResponse.isDone(), is(false)); @@ -488,7 +492,7 @@ public void testMasterFailOverWithQueuedDeletes() throws Exception { logger.info("--> waiting for all three snapshots to show up as in-progress"); assertBusy(() -> assertThat(currentSnapshots(repoName), hasSize(3)), 30L, TimeUnit.SECONDS); - final ActionFuture deleteAllSnapshots = startDeleteFromNonMasterClient(repoName, "*"); + final ActionFuture deleteAllSnapshots = startDeleteFromNonClusterManagerClient(repoName, "*"); logger.info("--> wait for delete to be enqueued in cluster state"); awaitClusterState(state -> { final SnapshotDeletionsInProgress deletionsInProgress = state.custom(SnapshotDeletionsInProgress.TYPE); @@ -506,8 +510,8 @@ public void testMasterFailOverWithQueuedDeletes() throws Exception { } }, 30L, TimeUnit.SECONDS); - logger.info("--> stopping current master node"); - internalCluster().stopCurrentMasterNode(); + logger.info("--> stopping current cluster-manager node"); + internalCluster().stopCurrentClusterManagerNode(); unblockNode(repoName, dataNode); unblockNode(repoName, dataNode2); @@ -516,13 +520,14 @@ public void testMasterFailOverWithQueuedDeletes() throws Exception { try { assertAcked(deleteFuture.actionGet()); } catch (RepositoryException rex) { - // rarely the master node fails over twice when shutting down the initial master and fails the transport listener + // rarely the cluster-manager node fails over twice + // when shutting down the initial cluster-manager and fails the transport listener assertThat(rex.repository(), is("_all")); assertThat(rex.getMessage(), endsWith("Failed to update cluster state during repository operation")); } catch (SnapshotMissingException sme) { - // very rarely a master node fail-over happens at such a time that the client on the data-node sees a disconnect exception - // after the master has already started the delete, leading to the delete retry to run into a situation where the - // snapshot has already been deleted potentially + // very rarely a cluster-manager node fail-over happens at such a time + // that the client on the data-node sees a disconnect exception after the cluster-manager has already started the delete, + // leading to the delete retry to run into a situation where the snapshot has already been deleted potentially assertThat(sme.getSnapshotName(), is(firstSnapshot)); } } @@ -540,7 +545,7 @@ public void testMasterFailOverWithQueuedDeletes() throws Exception { } public void testAssertMultipleSnapshotsAndPrimaryFailOver() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -551,7 +556,10 @@ public void testAssertMultipleSnapshotsAndPrimaryFailOver() throws Exception { index(testIndex, "_doc", "some_id", "foo", "bar"); blockDataNode(repoName, dataNode); - final ActionFuture firstSnapshotResponse = startFullSnapshotFromMasterClient(repoName, "snapshot-one"); + final ActionFuture firstSnapshotResponse = startFullSnapshotFromClusterManagerClient( + repoName, + "snapshot-one" + ); waitForBlock(dataNode, repoName, TimeValue.timeValueSeconds(30L)); internalCluster().startDataOnlyNode(); @@ -559,7 +567,10 @@ public void testAssertMultipleSnapshotsAndPrimaryFailOver() throws Exception { ensureGreen(testIndex); final String secondSnapshot = "snapshot-two"; - final ActionFuture secondSnapshotResponse = startFullSnapshotFromMasterClient(repoName, secondSnapshot); + final ActionFuture secondSnapshotResponse = startFullSnapshotFromClusterManagerClient( + repoName, + secondSnapshot + ); // make sure second snapshot is in progress before restarting data node waitUntilInprogress(repoName, secondSnapshot, TimeValue.timeValueSeconds(5L)); @@ -571,16 +582,16 @@ public void testAssertMultipleSnapshotsAndPrimaryFailOver() throws Exception { } public void testQueuedDeletesWithFailures() throws Exception { - final String masterNode = internalCluster().startMasterOnlyNode(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); createIndexWithContent("index-one"); createNSnapshots(repoName, randomIntBetween(2, 5)); - blockMasterFromFinalizingSnapshotOnIndexFile(repoName); + blockClusterManagerFromFinalizingSnapshotOnIndexFile(repoName); final ActionFuture firstDeleteFuture = startDeleteSnapshot(repoName, "*"); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); final ActionFuture snapshotFuture = startFullSnapshot(repoName, "snapshot-queued"); awaitNumberOfSnapshotsInProgress(1); @@ -588,7 +599,7 @@ public void testQueuedDeletesWithFailures() throws Exception { final ActionFuture secondDeleteFuture = startDeleteSnapshot(repoName, "*"); awaitNDeletionsInProgress(2); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); expectThrows(UncategorizedExecutionException.class, firstDeleteFuture::actionGet); // Second delete works out cleanly since the repo is unblocked now @@ -601,7 +612,7 @@ public void testQueuedDeletesWithFailures() throws Exception { } public void testQueuedDeletesWithOverlap() throws Exception { - final String masterNode = internalCluster().startMasterOnlyNode(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -615,7 +626,7 @@ public void testQueuedDeletesWithOverlap() throws Exception { final ActionFuture secondDeleteFuture = startDeleteSnapshot(repoName, "*"); awaitNDeletionsInProgress(2); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); assertThat(firstDeleteFuture.get().isAcknowledged(), is(true)); // Second delete works out cleanly since the repo is unblocked now @@ -627,8 +638,8 @@ public void testQueuedDeletesWithOverlap() throws Exception { assertThat(client().admin().cluster().prepareGetSnapshots(repoName).get().getSnapshots(), empty()); } - public void testQueuedOperationsOnMasterRestart() throws Exception { - internalCluster().startMasterOnlyNodes(3); + public void testQueuedOperationsOnClusterManagerRestart() throws Exception { + internalCluster().startClusterManagerOnlyNodes(3); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -642,39 +653,39 @@ public void testQueuedOperationsOnMasterRestart() throws Exception { startDeleteSnapshot(repoName, "*"); awaitNDeletionsInProgress(2); - internalCluster().stopCurrentMasterNode(); + internalCluster().stopCurrentClusterManagerNode(); ensureStableCluster(3); awaitNoMoreRunningOperations(); } - public void testQueuedOperationsOnMasterDisconnect() throws Exception { - internalCluster().startMasterOnlyNodes(3); + public void testQueuedOperationsOnClusterManagerDisconnect() throws Exception { + internalCluster().startClusterManagerOnlyNodes(3); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); createIndexWithContent("index-one"); createNSnapshots(repoName, randomIntBetween(2, 5)); - final String masterNode = internalCluster().getMasterName(); - final NetworkDisruption networkDisruption = isolateMasterDisruption(NetworkDisruption.DISCONNECT); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + final NetworkDisruption networkDisruption = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); internalCluster().setDisruptionScheme(networkDisruption); - blockNodeOnAnyFiles(repoName, masterNode); - ActionFuture firstDeleteFuture = client(masterNode).admin() + blockNodeOnAnyFiles(repoName, clusterManagerNode); + ActionFuture firstDeleteFuture = client(clusterManagerNode).admin() .cluster() .prepareDeleteSnapshot(repoName, "*") .execute(); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); - final ActionFuture createThirdSnapshot = client(masterNode).admin() + final ActionFuture createThirdSnapshot = client(clusterManagerNode).admin() .cluster() .prepareCreateSnapshot(repoName, "snapshot-three") .setWaitForCompletion(true) .execute(); awaitNumberOfSnapshotsInProgress(1); - final ActionFuture secondDeleteFuture = client(masterNode).admin() + final ActionFuture secondDeleteFuture = client(clusterManagerNode).admin() .cluster() .prepareDeleteSnapshot(repoName, "*") .execute(); @@ -682,7 +693,7 @@ public void testQueuedOperationsOnMasterDisconnect() throws Exception { networkDisruption.startDisrupting(); ensureStableCluster(3, dataNode); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); networkDisruption.stopDisrupting(); logger.info("--> make sure all failing requests get a response"); @@ -693,31 +704,31 @@ public void testQueuedOperationsOnMasterDisconnect() throws Exception { awaitNoMoreRunningOperations(); } - public void testQueuedOperationsOnMasterDisconnectAndRepoFailure() throws Exception { - internalCluster().startMasterOnlyNodes(3); + public void testQueuedOperationsOnClusterManagerDisconnectAndRepoFailure() throws Exception { + internalCluster().startClusterManagerOnlyNodes(3); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); createIndexWithContent("index-one"); createNSnapshots(repoName, randomIntBetween(2, 5)); - final String masterNode = internalCluster().getMasterName(); - final NetworkDisruption networkDisruption = isolateMasterDisruption(NetworkDisruption.DISCONNECT); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + final NetworkDisruption networkDisruption = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); internalCluster().setDisruptionScheme(networkDisruption); - blockMasterFromFinalizingSnapshotOnIndexFile(repoName); - final ActionFuture firstFailedSnapshotFuture = startFullSnapshotFromMasterClient( + blockClusterManagerFromFinalizingSnapshotOnIndexFile(repoName); + final ActionFuture firstFailedSnapshotFuture = startFullSnapshotFromClusterManagerClient( repoName, "failing-snapshot-1" ); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); - final ActionFuture secondFailedSnapshotFuture = startFullSnapshotFromMasterClient( + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); + final ActionFuture secondFailedSnapshotFuture = startFullSnapshotFromClusterManagerClient( repoName, "failing-snapshot-2" ); awaitNumberOfSnapshotsInProgress(2); - final ActionFuture failedDeleteFuture = client(masterNode).admin() + final ActionFuture failedDeleteFuture = client(clusterManagerNode).admin() .cluster() .prepareDeleteSnapshot(repoName, "*") .execute(); @@ -725,7 +736,7 @@ public void testQueuedOperationsOnMasterDisconnectAndRepoFailure() throws Except networkDisruption.startDisrupting(); ensureStableCluster(3, dataNode); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); networkDisruption.stopDisrupting(); logger.info("--> make sure all failing requests get a response"); @@ -736,10 +747,10 @@ public void testQueuedOperationsOnMasterDisconnectAndRepoFailure() throws Except awaitNoMoreRunningOperations(); } - public void testQueuedOperationsAndBrokenRepoOnMasterFailOver() throws Exception { + public void testQueuedOperationsAndBrokenRepoOnClusterManagerFailOver() throws Exception { disableRepoConsistencyCheck("This test corrupts the repository on purpose"); - internalCluster().startMasterOnlyNodes(3); + internalCluster().startClusterManagerOnlyNodes(3); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; final Path repoPath = randomRepoPath(); @@ -755,20 +766,20 @@ public void testQueuedOperationsAndBrokenRepoOnMasterFailOver() throws Exception client().admin().cluster().prepareCreateSnapshot(repoName, "snapshot-three").setWaitForCompletion(false).get(); - final ActionFuture deleteFuture = startDeleteFromNonMasterClient(repoName, "*"); + final ActionFuture deleteFuture = startDeleteFromNonClusterManagerClient(repoName, "*"); awaitNDeletionsInProgress(2); - internalCluster().stopCurrentMasterNode(); + internalCluster().stopCurrentClusterManagerNode(); ensureStableCluster(3); awaitNoMoreRunningOperations(); expectThrows(RepositoryException.class, deleteFuture::actionGet); } - public void testQueuedSnapshotOperationsAndBrokenRepoOnMasterFailOver() throws Exception { + public void testQueuedSnapshotOperationsAndBrokenRepoOnClusterManagerFailOver() throws Exception { disableRepoConsistencyCheck("This test corrupts the repository on purpose"); - internalCluster().startMasterOnlyNodes(3); + internalCluster().startClusterManagerOnlyNodes(3); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; final Path repoPath = randomRepoPath(); @@ -777,15 +788,15 @@ public void testQueuedSnapshotOperationsAndBrokenRepoOnMasterFailOver() throws E createNSnapshots(repoName, randomIntBetween(2, 5)); final long generation = getRepositoryData(repoName).getGenId(); - final String masterNode = internalCluster().getMasterName(); - blockNodeOnAnyFiles(repoName, masterNode); - final ActionFuture snapshotThree = startFullSnapshotFromNonMasterClient(repoName, "snapshot-three"); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + blockNodeOnAnyFiles(repoName, clusterManagerNode); + final ActionFuture snapshotThree = startFullSnapshotFromNonClusterManagerClient(repoName, "snapshot-three"); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); corruptIndexN(repoPath, generation); - final ActionFuture snapshotFour = startFullSnapshotFromNonMasterClient(repoName, "snapshot-four"); - internalCluster().stopCurrentMasterNode(); + final ActionFuture snapshotFour = startFullSnapshotFromNonClusterManagerClient(repoName, "snapshot-four"); + internalCluster().stopCurrentClusterManagerNode(); ensureStableCluster(3); awaitNoMoreRunningOperations(); @@ -793,10 +804,10 @@ public void testQueuedSnapshotOperationsAndBrokenRepoOnMasterFailOver() throws E expectThrows(OpenSearchException.class, snapshotFour::actionGet); } - public void testQueuedSnapshotOperationsAndBrokenRepoOnMasterFailOver2() throws Exception { + public void testQueuedSnapshotOperationsAndBrokenRepoOnClusterManagerFailOver2() throws Exception { disableRepoConsistencyCheck("This test corrupts the repository on purpose"); - internalCluster().startMasterOnlyNodes(3); + internalCluster().startClusterManagerOnlyNodes(3); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; final Path repoPath = randomRepoPath(); @@ -805,31 +816,31 @@ public void testQueuedSnapshotOperationsAndBrokenRepoOnMasterFailOver2() throws createNSnapshots(repoName, randomIntBetween(2, 5)); final long generation = getRepositoryData(repoName).getGenId(); - final String masterNode = internalCluster().getMasterName(); - blockMasterFromFinalizingSnapshotOnIndexFile(repoName); - final ActionFuture snapshotThree = startFullSnapshotFromNonMasterClient(repoName, "snapshot-three"); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + blockClusterManagerFromFinalizingSnapshotOnIndexFile(repoName); + final ActionFuture snapshotThree = startFullSnapshotFromNonClusterManagerClient(repoName, "snapshot-three"); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); corruptIndexN(repoPath, generation); - final ActionFuture snapshotFour = startFullSnapshotFromNonMasterClient(repoName, "snapshot-four"); + final ActionFuture snapshotFour = startFullSnapshotFromNonClusterManagerClient(repoName, "snapshot-four"); awaitNumberOfSnapshotsInProgress(2); - final NetworkDisruption networkDisruption = isolateMasterDisruption(NetworkDisruption.DISCONNECT); + final NetworkDisruption networkDisruption = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); internalCluster().setDisruptionScheme(networkDisruption); networkDisruption.startDisrupting(); ensureStableCluster(3, dataNode); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); networkDisruption.stopDisrupting(); awaitNoMoreRunningOperations(); expectThrows(OpenSearchException.class, snapshotThree::actionGet); expectThrows(OpenSearchException.class, snapshotFour::actionGet); } - public void testQueuedSnapshotOperationsAndBrokenRepoOnMasterFailOverMultipleRepos() throws Exception { + public void testQueuedSnapshotOperationsAndBrokenRepoOnClusterManagerFailOverMultipleRepos() throws Exception { disableRepoConsistencyCheck("This test corrupts the repository on purpose"); - internalCluster().startMasterOnlyNodes(3, LARGE_SNAPSHOT_POOL_SETTINGS); + internalCluster().startClusterManagerOnlyNodes(3, LARGE_SNAPSHOT_POOL_SETTINGS); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; final Path repoPath = randomRepoPath(); @@ -837,32 +848,32 @@ public void testQueuedSnapshotOperationsAndBrokenRepoOnMasterFailOverMultipleRep createIndexWithContent("index-one"); createNSnapshots(repoName, randomIntBetween(2, 5)); - final String masterNode = internalCluster().getMasterName(); + final String clusterManagerNode = internalCluster().getClusterManagerName(); final String blockedRepoName = "repo-blocked"; createRepository(blockedRepoName, "mock"); createNSnapshots(blockedRepoName, randomIntBetween(1, 5)); - blockNodeOnAnyFiles(blockedRepoName, masterNode); - final ActionFuture deleteFuture = startDeleteFromNonMasterClient(blockedRepoName, "*"); - waitForBlock(masterNode, blockedRepoName, TimeValue.timeValueSeconds(30L)); + blockNodeOnAnyFiles(blockedRepoName, clusterManagerNode); + final ActionFuture deleteFuture = startDeleteFromNonClusterManagerClient(blockedRepoName, "*"); + waitForBlock(clusterManagerNode, blockedRepoName, TimeValue.timeValueSeconds(30L)); awaitNDeletionsInProgress(1); - final ActionFuture createBlockedSnapshot = startFullSnapshotFromNonMasterClient( + final ActionFuture createBlockedSnapshot = startFullSnapshotFromNonClusterManagerClient( blockedRepoName, "queued-snapshot" ); awaitNumberOfSnapshotsInProgress(1); final long generation = getRepositoryData(repoName).getGenId(); - blockNodeOnAnyFiles(repoName, masterNode); - final ActionFuture snapshotThree = startFullSnapshotFromNonMasterClient(repoName, "snapshot-three"); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + blockNodeOnAnyFiles(repoName, clusterManagerNode); + final ActionFuture snapshotThree = startFullSnapshotFromNonClusterManagerClient(repoName, "snapshot-three"); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); awaitNumberOfSnapshotsInProgress(2); corruptIndexN(repoPath, generation); - final ActionFuture snapshotFour = startFullSnapshotFromNonMasterClient(repoName, "snapshot-four"); + final ActionFuture snapshotFour = startFullSnapshotFromNonClusterManagerClient(repoName, "snapshot-four"); awaitNumberOfSnapshotsInProgress(3); - internalCluster().stopCurrentMasterNode(); + internalCluster().stopCurrentClusterManagerNode(); ensureStableCluster(3); awaitNoMoreRunningOperations(); @@ -872,13 +883,13 @@ public void testQueuedSnapshotOperationsAndBrokenRepoOnMasterFailOverMultipleRep try { createBlockedSnapshot.actionGet(); } catch (OpenSearchException ex) { - // Ignored, thrown most of the time but due to retries when shutting down the master could randomly pass when the request is - // retried and gets executed after the above delete + // Ignored, thrown most of the time but due to retries when shutting down the cluster-manager could randomly pass + // when the request is retried and gets executed after the above delete } } public void testMultipleSnapshotsQueuedAfterDelete() throws Exception { - final String masterNode = internalCluster().startMasterOnlyNode(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -889,7 +900,7 @@ public void testMultipleSnapshotsQueuedAfterDelete() throws Exception { final ActionFuture snapshotThree = startFullSnapshot(repoName, "snapshot-three"); final ActionFuture snapshotFour = startFullSnapshot(repoName, "snapshot-four"); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); assertSuccessful(snapshotThree); assertSuccessful(snapshotFour); @@ -897,7 +908,7 @@ public void testMultipleSnapshotsQueuedAfterDelete() throws Exception { } public void testMultiplePartialSnapshotsQueuedAfterDelete() throws Exception { - final String masterNode = internalCluster().startMasterOnlyNode(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -911,7 +922,7 @@ public void testMultiplePartialSnapshotsQueuedAfterDelete() throws Exception { awaitNumberOfSnapshotsInProgress(2); assertAcked(client().admin().indices().prepareDelete("index-two")); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); assertThat(snapshotThree.get().getSnapshotInfo().state(), is(SnapshotState.PARTIAL)); assertThat(snapshotFour.get().getSnapshotInfo().state(), is(SnapshotState.PARTIAL)); @@ -919,7 +930,7 @@ public void testMultiplePartialSnapshotsQueuedAfterDelete() throws Exception { } public void testQueuedSnapshotsWaitingForShardReady() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNodes(2); final String repoName = "test-repo"; createRepository(repoName, "fs"); @@ -977,7 +988,7 @@ public void testQueuedSnapshotsWaitingForShardReady() throws Exception { } public void testBackToBackQueuedDeletes() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -990,7 +1001,7 @@ public void testBackToBackQueuedDeletes() throws Exception { final ActionFuture deleteSnapshotTwo = startDeleteSnapshot(repoName, snapshotTwo); awaitNDeletionsInProgress(2); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); assertAcked(deleteSnapshotOne.get()); assertAcked(deleteSnapshotTwo.get()); @@ -1001,7 +1012,7 @@ public void testBackToBackQueuedDeletes() throws Exception { } public void testQueuedOperationsAfterFinalizationFailure() throws Exception { - internalCluster().startMasterOnlyNodes(3); + internalCluster().startClusterManagerOnlyNodes(3); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -1011,76 +1022,76 @@ public void testQueuedOperationsAfterFinalizationFailure() throws Exception { final ActionFuture snapshotThree = startAndBlockFailingFullSnapshot(repoName, "snap-other"); - final String masterName = internalCluster().getMasterName(); + final String clusterManagerName = internalCluster().getClusterManagerName(); final String snapshotOne = snapshotNames.get(0); final ActionFuture deleteSnapshotOne = startDeleteSnapshot(repoName, snapshotOne); awaitNDeletionsInProgress(1); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); expectThrows(SnapshotException.class, snapshotThree::actionGet); assertAcked(deleteSnapshotOne.get()); } public void testStartDeleteDuringFinalizationCleanup() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); createIndexWithContent("index-test"); createNSnapshots(repoName, randomIntBetween(1, 5)); final String snapshotName = "snap-name"; - blockMasterFromDeletingIndexNFile(repoName); + blockClusterManagerFromDeletingIndexNFile(repoName); final ActionFuture snapshotFuture = startFullSnapshot(repoName, snapshotName); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); final ActionFuture deleteFuture = startDeleteSnapshot(repoName, snapshotName); awaitNDeletionsInProgress(1); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); assertSuccessful(snapshotFuture); assertAcked(deleteFuture.get(30L, TimeUnit.SECONDS)); } public void testEquivalentDeletesAreDeduplicated() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); createIndexWithContent("index-test"); createNSnapshots(repoName, randomIntBetween(1, 5)); - blockNodeOnAnyFiles(repoName, masterName); + blockNodeOnAnyFiles(repoName, clusterManagerName); final int deletes = randomIntBetween(2, 10); final List> deleteResponses = new ArrayList<>(deletes); for (int i = 0; i < deletes; ++i) { deleteResponses.add(client().admin().cluster().prepareDeleteSnapshot(repoName, "*").execute()); } - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); awaitNDeletionsInProgress(1); for (ActionFuture deleteResponse : deleteResponses) { assertFalse(deleteResponse.isDone()); } awaitNDeletionsInProgress(1); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); for (ActionFuture deleteResponse : deleteResponses) { assertAcked(deleteResponse.get()); } } - public void testMasterFailoverOnFinalizationLoop() throws Exception { - internalCluster().startMasterOnlyNodes(3); + public void testClusterManagerFailoverOnFinalizationLoop() throws Exception { + internalCluster().startClusterManagerOnlyNodes(3); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); createIndexWithContent("index-test"); - final NetworkDisruption networkDisruption = isolateMasterDisruption(NetworkDisruption.DISCONNECT); + final NetworkDisruption networkDisruption = isolateClusterManagerDisruption(NetworkDisruption.DISCONNECT); internalCluster().setDisruptionScheme(networkDisruption); final List snapshotNames = createNSnapshots(repoName, randomIntBetween(2, 5)); - final String masterName = internalCluster().getMasterName(); - blockMasterFromDeletingIndexNFile(repoName); - final ActionFuture snapshotThree = startFullSnapshotFromMasterClient(repoName, "snap-other"); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + final String clusterManagerName = internalCluster().getClusterManagerName(); + blockClusterManagerFromDeletingIndexNFile(repoName); + final ActionFuture snapshotThree = startFullSnapshotFromClusterManagerClient(repoName, "snap-other"); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); final String snapshotOne = snapshotNames.get(0); final ActionFuture deleteSnapshotOne = startDeleteSnapshot(repoName, snapshotOne); @@ -1088,7 +1099,7 @@ public void testMasterFailoverOnFinalizationLoop() throws Exception { networkDisruption.startDisrupting(); ensureStableCluster(3, dataNode); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); networkDisruption.stopDisrupting(); ensureStableCluster(4); @@ -1102,7 +1113,7 @@ public void testMasterFailoverOnFinalizationLoop() throws Exception { } public void testStatusMultipleSnapshotsMultipleRepos() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); // We're blocking a some of the snapshot threads when we block the first repo below so we have to make sure we have enough threads // left for the second concurrent snapshot. final String dataNode = startDataNodeWithLargeSnapshotPool(); @@ -1146,7 +1157,7 @@ public void testStatusMultipleSnapshotsMultipleRepos() throws Exception { } public void testInterleavedAcrossMultipleRepos() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); // We're blocking a some of the snapshot threads when we block the first repo below so we have to make sure we have enough threads // left for the second concurrent snapshot. final String dataNode = startDataNodeWithLargeSnapshotPool(); @@ -1180,10 +1191,10 @@ public void testInterleavedAcrossMultipleRepos() throws Exception { assertSuccessful(createSlowFuture3); } - public void testMasterFailoverAndMultipleQueuedUpSnapshotsAcrossTwoRepos() throws Exception { + public void testClusterManagerFailoverAndMultipleQueuedUpSnapshotsAcrossTwoRepos() throws Exception { disableRepoConsistencyCheck("This test corrupts the repository on purpose"); - internalCluster().startMasterOnlyNodes(3, LARGE_SNAPSHOT_POOL_SETTINGS); + internalCluster().startClusterManagerOnlyNodes(3, LARGE_SNAPSHOT_POOL_SETTINGS); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; final String otherRepoName = "other-test-repo"; @@ -1197,8 +1208,8 @@ public void testMasterFailoverAndMultipleQueuedUpSnapshotsAcrossTwoRepos() throw corruptIndexN(repoPath, getRepositoryData(repoName).getGenId()); - blockMasterFromFinalizingSnapshotOnIndexFile(repoName); - blockMasterFromFinalizingSnapshotOnIndexFile(otherRepoName); + blockClusterManagerFromFinalizingSnapshotOnIndexFile(repoName); + blockClusterManagerFromFinalizingSnapshotOnIndexFile(otherRepoName); client().admin().cluster().prepareCreateSnapshot(repoName, "snapshot-blocked-1").setWaitForCompletion(false).get(); client().admin().cluster().prepareCreateSnapshot(repoName, "snapshot-blocked-2").setWaitForCompletion(false).get(); @@ -1206,11 +1217,11 @@ public void testMasterFailoverAndMultipleQueuedUpSnapshotsAcrossTwoRepos() throw client().admin().cluster().prepareCreateSnapshot(otherRepoName, "snapshot-other-blocked-2").setWaitForCompletion(false).get(); awaitNumberOfSnapshotsInProgress(4); - final String initialMaster = internalCluster().getMasterName(); - waitForBlock(initialMaster, repoName, TimeValue.timeValueSeconds(30L)); - waitForBlock(initialMaster, otherRepoName, TimeValue.timeValueSeconds(30L)); + final String initialClusterManager = internalCluster().getClusterManagerName(); + waitForBlock(initialClusterManager, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(initialClusterManager, otherRepoName, TimeValue.timeValueSeconds(30L)); - internalCluster().stopCurrentMasterNode(); + internalCluster().stopCurrentClusterManagerNode(); ensureStableCluster(3, dataNode); awaitNoMoreRunningOperations(); @@ -1219,7 +1230,7 @@ public void testMasterFailoverAndMultipleQueuedUpSnapshotsAcrossTwoRepos() throw } public void testConcurrentOperationsLimit() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -1237,7 +1248,7 @@ public void testConcurrentOperationsLimit() throws Exception { ); final List snapshotNames = createNSnapshots(repoName, limitToTest + 1); - blockNodeOnAnyFiles(repoName, masterName); + blockNodeOnAnyFiles(repoName, clusterManagerName); int blockedSnapshots = 0; boolean blockedDelete = false; final List> snapshotFutures = new ArrayList<>(); @@ -1255,7 +1266,7 @@ public void testConcurrentOperationsLimit() throws Exception { if (blockedDelete) { awaitNDeletionsInProgress(1); } - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); final String expectedFailureMessage = "Cannot start another operation, already running [" + limitToTest @@ -1275,7 +1286,7 @@ public void testConcurrentOperationsLimit() throws Exception { assertThat(csen2.getMessage(), containsString(expectedFailureMessage)); } - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); if (deleteFuture != null) { assertAcked(deleteFuture.get()); } @@ -1285,7 +1296,7 @@ public void testConcurrentOperationsLimit() throws Exception { } public void testConcurrentSnapshotWorksWithOldVersionRepo() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; final Path repoPath = randomRepoPath(); @@ -1322,23 +1333,23 @@ public void testConcurrentSnapshotWorksWithOldVersionRepo() throws Exception { } public void testQueuedDeleteAfterFinalizationFailure() throws Exception { - final String masterNode = internalCluster().startMasterOnlyNode(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); - blockMasterFromFinalizingSnapshotOnIndexFile(repoName); + blockClusterManagerFromFinalizingSnapshotOnIndexFile(repoName); final String snapshotName = "snap-1"; final ActionFuture snapshotFuture = startFullSnapshot(repoName, snapshotName); - waitForBlock(masterNode, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerNode, repoName, TimeValue.timeValueSeconds(30L)); final ActionFuture deleteFuture = startDeleteSnapshot(repoName, snapshotName); awaitNDeletionsInProgress(1); - unblockNode(repoName, masterNode); + unblockNode(repoName, clusterManagerNode); assertAcked(deleteFuture.get()); final SnapshotException sne = expectThrows(SnapshotException.class, snapshotFuture::actionGet); assertThat(sne.getCause().getMessage(), containsString("exception after block")); } public void testAbortNotStartedSnapshotWithoutIO() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -1365,7 +1376,7 @@ public void testAbortNotStartedSnapshotWithoutIO() throws Exception { } public void testStartWithSuccessfulShardSnapshotPendingFinalization() throws Exception { - final String masterName = internalCluster().startMasterOnlyNode(); + final String clusterManagerName = internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "mock"); @@ -1373,18 +1384,18 @@ public void testStartWithSuccessfulShardSnapshotPendingFinalization() throws Exc createIndexWithContent("test-idx"); createFullSnapshot(repoName, "first-snapshot"); - blockMasterOnWriteIndexFile(repoName); + blockClusterManagerOnWriteIndexFile(repoName); final ActionFuture blockedSnapshot = startFullSnapshot(repoName, "snap-blocked"); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); awaitNumberOfSnapshotsInProgress(1); blockNodeOnAnyFiles(repoName, dataNode); final ActionFuture otherSnapshot = startFullSnapshot(repoName, "other-snapshot"); awaitNumberOfSnapshotsInProgress(2); assertFalse(blockedSnapshot.isDone()); - unblockNode(repoName, masterName); + unblockNode(repoName, clusterManagerName); awaitNumberOfSnapshotsInProgress(1); - awaitMasterFinishRepoOperations(); + awaitClusterManagerFinishRepoOperations(); unblockNode(repoName, dataNode); assertSuccessful(blockedSnapshot); @@ -1416,14 +1427,14 @@ private List createNSnapshots(String repoName, int count) { return snapshotNames; } - private ActionFuture startDeleteFromNonMasterClient(String repoName, String snapshotName) { - logger.info("--> deleting snapshot [{}] from repo [{}] from non master client", snapshotName, repoName); - return internalCluster().nonMasterClient().admin().cluster().prepareDeleteSnapshot(repoName, snapshotName).execute(); + private ActionFuture startDeleteFromNonClusterManagerClient(String repoName, String snapshotName) { + logger.info("--> deleting snapshot [{}] from repo [{}] from non cluster-manager client", snapshotName, repoName); + return internalCluster().nonClusterManagerClient().admin().cluster().prepareDeleteSnapshot(repoName, snapshotName).execute(); } - private ActionFuture startFullSnapshotFromNonMasterClient(String repoName, String snapshotName) { - logger.info("--> creating full snapshot [{}] to repo [{}] from non master client", snapshotName, repoName); - return internalCluster().nonMasterClient() + private ActionFuture startFullSnapshotFromNonClusterManagerClient(String repoName, String snapshotName) { + logger.info("--> creating full snapshot [{}] to repo [{}] from non cluster-manager client", snapshotName, repoName); + return internalCluster().nonClusterManagerClient() .admin() .cluster() .prepareCreateSnapshot(repoName, snapshotName) @@ -1431,9 +1442,9 @@ private ActionFuture startFullSnapshotFromNonMasterClien .execute(); } - private ActionFuture startFullSnapshotFromMasterClient(String repoName, String snapshotName) { - logger.info("--> creating full snapshot [{}] to repo [{}] from master client", snapshotName, repoName); - return internalCluster().masterClient() + private ActionFuture startFullSnapshotFromClusterManagerClient(String repoName, String snapshotName) { + logger.info("--> creating full snapshot [{}] to repo [{}] from cluster-manager client", snapshotName, repoName); + return internalCluster().clusterManagerClient() .admin() .cluster() .prepareCreateSnapshot(repoName, snapshotName) @@ -1453,8 +1464,8 @@ private void createIndexWithContent(String indexName, String nodeInclude, String private static boolean snapshotHasCompletedShard(String snapshot, SnapshotsInProgress snapshotsInProgress) { for (SnapshotsInProgress.Entry entry : snapshotsInProgress.entries()) { if (entry.snapshot().getSnapshotId().getName().equals(snapshot)) { - for (ObjectCursor shard : entry.shards().values()) { - if (shard.value.state().completed()) { + for (final SnapshotsInProgress.ShardSnapshotStatus shard : entry.shards().values()) { + if (shard.state().completed()) { return true; } } @@ -1488,18 +1499,18 @@ private static List currentSnapshots(String repoName) { private ActionFuture startAndBlockOnDeleteSnapshot(String repoName, String snapshotName) throws InterruptedException { - final String masterName = internalCluster().getMasterName(); - blockNodeOnAnyFiles(repoName, masterName); + final String clusterManagerName = internalCluster().getClusterManagerName(); + blockNodeOnAnyFiles(repoName, clusterManagerName); final ActionFuture fut = startDeleteSnapshot(repoName, snapshotName); - waitForBlock(masterName, repoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(clusterManagerName, repoName, TimeValue.timeValueSeconds(30L)); return fut; } private ActionFuture startAndBlockFailingFullSnapshot(String blockedRepoName, String snapshotName) throws InterruptedException { - blockMasterFromFinalizingSnapshotOnIndexFile(blockedRepoName); + blockClusterManagerFromFinalizingSnapshotOnIndexFile(blockedRepoName); final ActionFuture fut = startFullSnapshot(blockedRepoName, snapshotName); - waitForBlock(internalCluster().getMasterName(), blockedRepoName, TimeValue.timeValueSeconds(30L)); + waitForBlock(internalCluster().getClusterManagerName(), blockedRepoName, TimeValue.timeValueSeconds(30L)); return fut; } diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/CorruptedBlobStoreRepositoryIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/CorruptedBlobStoreRepositoryIT.java index c253f1a4f876e..fbf2acf7b08a6 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/CorruptedBlobStoreRepositoryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/CorruptedBlobStoreRepositoryIT.java @@ -43,11 +43,11 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.RepositoriesMetadata; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.repositories.IndexId; import org.opensearch.repositories.IndexMetaDataGenerations; import org.opensearch.repositories.RepositoriesService; @@ -205,7 +205,7 @@ public void testConcurrentlyChangeRepositoryContentsInBwCMode() throws Exception equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()) ); - final Repository repository = internalCluster().getMasterNodeInstance(RepositoriesService.class).repository(repoName); + final Repository repository = internalCluster().getClusterManagerNodeInstance(RepositoriesService.class).repository(repoName); logger.info("--> move index-N blob to next generation"); final RepositoryData repositoryData = getRepositoryData(repository); @@ -215,7 +215,7 @@ public void testConcurrentlyChangeRepositoryContentsInBwCMode() throws Exception logger.info("--> verify index-N blob is found at the new location"); assertThat(getRepositoryData(repository).getGenId(), is(beforeMoveGen + 1)); - final SnapshotsService snapshotsService = internalCluster().getCurrentMasterNodeInstance(SnapshotsService.class); + final SnapshotsService snapshotsService = internalCluster().getCurrentClusterManagerNodeInstance(SnapshotsService.class); logger.info("--> wait for all listeners on snapshots service to be resolved to avoid snapshot task batching causing a conflict"); assertBusy(() -> assertTrue(snapshotsService.assertAllListenersResolved())); @@ -267,7 +267,8 @@ public void testFindDanglingLatestGeneration() throws Exception { equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()) ); - final Repository repository = internalCluster().getCurrentMasterNodeInstance(RepositoriesService.class).repository(repoName); + final Repository repository = internalCluster().getCurrentClusterManagerNodeInstance(RepositoriesService.class) + .repository(repoName); logger.info("--> move index-N blob to next generation"); final RepositoryData repositoryData = getRepositoryData(repoName); @@ -361,14 +362,14 @@ public void testHandlingMissingRootLevelSnapshotMetadata() throws Exception { Files.write( repo.resolve(BlobStoreRepository.INDEX_FILE_PREFIX + withoutVersions.getGenId()), BytesReference.toBytes( - BytesReference.bytes(withoutVersions.snapshotsToXContent(XContentFactory.jsonBuilder(), Version.CURRENT)) + BytesReference.bytes(withoutVersions.snapshotsToXContent(MediaTypeRegistry.JSON.contentBuilder(), Version.CURRENT)) ), StandardOpenOption.TRUNCATE_EXISTING ); logger.info("--> verify that repo is assumed in old metadata format"); - final SnapshotsService snapshotsService = internalCluster().getCurrentMasterNodeInstance(SnapshotsService.class); - final ThreadPool threadPool = internalCluster().getCurrentMasterNodeInstance(ThreadPool.class); + final SnapshotsService snapshotsService = internalCluster().getCurrentClusterManagerNodeInstance(SnapshotsService.class); + final ThreadPool threadPool = internalCluster().getCurrentClusterManagerNodeInstance(ThreadPool.class); assertThat( PlainActionFuture.get( f -> threadPool.generic() @@ -435,7 +436,8 @@ public void testMountCorruptedRepositoryData() throws Exception { ); logger.info("--> corrupt index-N blob"); - final Repository repository = internalCluster().getCurrentMasterNodeInstance(RepositoriesService.class).repository(repoName); + final Repository repository = internalCluster().getCurrentClusterManagerNodeInstance(RepositoriesService.class) + .repository(repoName); final RepositoryData repositoryData = getRepositoryData(repoName); Files.write(repo.resolve("index-" + repositoryData.getGenId()), randomByteArrayOfLength(randomIntBetween(1, 100))); @@ -444,7 +446,8 @@ public void testMountCorruptedRepositoryData() throws Exception { final String otherRepoName = "other-repo"; createRepository(otherRepoName, "fs", Settings.builder().put("location", repo).put("compress", false)); - final Repository otherRepo = internalCluster().getCurrentMasterNodeInstance(RepositoriesService.class).repository(otherRepoName); + final Repository otherRepo = internalCluster().getCurrentClusterManagerNodeInstance(RepositoriesService.class) + .repository(otherRepoName); logger.info("--> verify loading repository data from newly mounted repository throws RepositoryException"); expectThrows(RepositoryException.class, () -> getRepositoryData(otherRepo)); @@ -519,7 +522,7 @@ public void testRepairBrokenShardGenerations() throws Exception { Files.write( repoPath.resolve(BlobStoreRepository.INDEX_FILE_PREFIX + repositoryData1.getGenId()), BytesReference.toBytes( - BytesReference.bytes(brokenRepoData.snapshotsToXContent(XContentFactory.jsonBuilder(), Version.CURRENT)) + BytesReference.bytes(brokenRepoData.snapshotsToXContent(MediaTypeRegistry.JSON.contentBuilder(), Version.CURRENT)) ), StandardOpenOption.TRUNCATE_EXISTING ); diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/DedicatedClusterSnapshotRestoreIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/DedicatedClusterSnapshotRestoreIT.java index 47d57e1260b5f..86d5d21adbadd 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/DedicatedClusterSnapshotRestoreIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/DedicatedClusterSnapshotRestoreIT.java @@ -32,11 +32,7 @@ package org.opensearch.snapshots; -import com.carrotsearch.hppc.IntHashSet; -import com.carrotsearch.hppc.IntSet; - import org.opensearch.Version; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; @@ -59,24 +55,26 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.CheckedFunction; -import org.opensearch.common.ParseField; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.env.Environment; import org.opensearch.index.seqno.RetentionLeaseActions; import org.opensearch.index.seqno.RetentionLeases; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.recovery.PeerRecoveryTargetService; import org.opensearch.indices.recovery.RecoveryState; import org.opensearch.node.Node; @@ -86,15 +84,14 @@ import org.opensearch.rest.AbstractRestChannel; import org.opensearch.rest.RestRequest; import org.opensearch.rest.RestResponse; -import org.opensearch.rest.RestStatus; import org.opensearch.rest.action.admin.cluster.RestClusterStateAction; import org.opensearch.rest.action.admin.cluster.RestGetRepositoriesAction; import org.opensearch.snapshots.mockstore.MockRepository; +import org.opensearch.test.InternalTestCluster; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; -import org.opensearch.test.InternalTestCluster; import org.opensearch.test.TestCustomMetadata; -import org.opensearch.test.disruption.BusyMasterServiceDisruption; +import org.opensearch.test.disruption.BusyClusterManagerServiceDisruption; import org.opensearch.test.disruption.ServiceDisruptionScheme; import org.opensearch.test.rest.FakeRestRequest; import org.opensearch.test.transport.MockTransportService; @@ -115,8 +112,10 @@ import java.util.Collection; import java.util.Collections; import java.util.EnumSet; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -124,7 +123,7 @@ import java.util.function.Consumer; import static org.opensearch.index.seqno.RetentionLeaseActions.RETAIN_ALL; -import static org.opensearch.test.NodeRoles.nonMasterNode; +import static org.opensearch.test.NodeRoles.nonClusterManagerNode; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertFutureThrows; import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertRequestBuilderThrows; @@ -482,8 +481,8 @@ public void testSnapshotWithStuckNode() throws Exception { try { assertAcked(deleteSnapshotResponseFuture.actionGet()); } catch (SnapshotMissingException ex) { - // When master node is closed during this test, it sometime manages to delete the snapshot files before - // completely stopping. In this case the retried delete snapshot operation on the new master can fail + // When cluster-manager node is closed during this test, it sometime manages to delete the snapshot files before + // completely stopping. In this case the retried delete snapshot operation on the new cluster-manager can fail // with SnapshotMissingException } @@ -738,7 +737,7 @@ public boolean clearData(String nodeName) { ensureGreen("test-idx"); - IntSet reusedShards = new IntHashSet(); + final Set reusedShards = new HashSet<>(); List recoveryStates = client().admin() .indices() .prepareRecoveries("test-idx") @@ -759,8 +758,8 @@ public void testRegistrationFailure() { logger.info("--> start first node"); internalCluster().startNode(); logger.info("--> start second node"); - // Make sure the first node is elected as master - internalCluster().startNode(nonMasterNode()); + // Make sure the first node is elected as cluster-manager + internalCluster().startNode(nonClusterManagerNode()); // Register mock repositories for (int i = 0; i < 5; i++) { clusterAdmin().preparePutRepository("test-repo" + i) @@ -835,9 +834,9 @@ public void sendResponse(RestResponse response) { } } - public void testMasterShutdownDuringSnapshot() throws Exception { - logger.info("--> starting two master nodes and two data nodes"); - internalCluster().startMasterOnlyNodes(2); + public void testClusterManagerShutdownDuringSnapshot() throws Exception { + logger.info("--> starting two cluster-manager nodes and two data nodes"); + internalCluster().startClusterManagerOnlyNodes(2); internalCluster().startDataOnlyNodes(2); final Path repoPath = randomRepoPath(); @@ -859,8 +858,8 @@ public void testMasterShutdownDuringSnapshot() throws Exception { .setIndices("test-idx") .get(); - logger.info("--> stopping master node"); - internalCluster().stopCurrentMasterNode(); + logger.info("--> stopping cluster-manager node"); + internalCluster().stopCurrentClusterManagerNode(); logger.info("--> wait until the snapshot is done"); @@ -873,9 +872,9 @@ public void testMasterShutdownDuringSnapshot() throws Exception { assertEquals(0, snapshotInfo.failedShards()); } - public void testMasterAndDataShutdownDuringSnapshot() throws Exception { - logger.info("--> starting three master nodes and two data nodes"); - internalCluster().startMasterOnlyNodes(3); + public void testClusterManagerAndDataShutdownDuringSnapshot() throws Exception { + logger.info("--> starting three cluster-manager nodes and two data nodes"); + internalCluster().startClusterManagerOnlyNodes(3); internalCluster().startDataOnlyNodes(2); final Path repoPath = randomRepoPath(); @@ -890,7 +889,7 @@ public void testMasterAndDataShutdownDuringSnapshot() throws Exception { final int numberOfShards = getNumShards("test-idx").numPrimaries; logger.info("number of shards: {}", numberOfShards); - final String masterNode = blockMasterFromFinalizingSnapshotOnSnapFile("test-repo"); + final String clusterManagerNode = blockClusterManagerFromFinalizingSnapshotOnSnapFile("test-repo"); final String dataNode = blockNodeWithIndex("test-repo", "test-idx"); dataNodeClient().admin() @@ -902,8 +901,8 @@ public void testMasterAndDataShutdownDuringSnapshot() throws Exception { logger.info("--> stopping data node {}", dataNode); stopNode(dataNode); - logger.info("--> stopping master node {} ", masterNode); - internalCluster().stopCurrentMasterNode(); + logger.info("--> stopping cluster-manager node {} ", clusterManagerNode); + internalCluster().stopCurrentClusterManagerNode(); logger.info("--> wait until the snapshot is done"); @@ -925,8 +924,8 @@ public void testMasterAndDataShutdownDuringSnapshot() throws Exception { * the cluster. */ public void testRestoreShrinkIndex() throws Exception { - logger.info("--> starting a master node and a data node"); - internalCluster().startMasterOnlyNode(); + logger.info("--> starting a cluster-manager node and a data node"); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repo = "test-repo"; @@ -1143,9 +1142,9 @@ public void testDeduplicateIndexMetadata() throws Exception { assertThat(snapshot3IndexMetaFiles, hasSize(1)); // should have deleted the metadata blob referenced by the first two snapshots } - public void testDataNodeRestartWithBusyMasterDuringSnapshot() throws Exception { - logger.info("--> starting a master node and two data nodes"); - internalCluster().startMasterOnlyNode(); + public void testDataNodeRestartWithBusyClusterManagerDuringSnapshot() throws Exception { + logger.info("--> starting a cluster-manager node and two data nodes"); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNodes(2); final Path repoPath = randomRepoPath(); createRepository("test-repo", "mock", repoPath); @@ -1157,9 +1156,9 @@ public void testDataNodeRestartWithBusyMasterDuringSnapshot() throws Exception { final String dataNode = blockNodeWithIndex("test-repo", "test-idx"); logger.info("--> snapshot"); - ServiceDisruptionScheme disruption = new BusyMasterServiceDisruption(random(), Priority.HIGH); + ServiceDisruptionScheme disruption = new BusyClusterManagerServiceDisruption(random(), Priority.HIGH); setDisruptionScheme(disruption); - client(internalCluster().getMasterName()).admin() + client(internalCluster().getClusterManagerName()).admin() .cluster() .prepareCreateSnapshot("test-repo", "test-snap") .setWaitForCompletion(false) @@ -1200,8 +1199,8 @@ public void testDataNodeRestartWithBusyMasterDuringSnapshot() throws Exception { } public void testDataNodeRestartAfterShardSnapshotFailure() throws Exception { - logger.info("--> starting a master node and two data nodes"); - internalCluster().startMasterOnlyNode(); + logger.info("--> starting a cluster-manager node and two data nodes"); + internalCluster().startClusterManagerOnlyNode(); final List dataNodes = internalCluster().startDataOnlyNodes(2); final Path repoPath = randomRepoPath(); createRepository("test-repo", "mock", repoPath); @@ -1213,7 +1212,7 @@ public void testDataNodeRestartAfterShardSnapshotFailure() throws Exception { blockAllDataNodes("test-repo"); logger.info("--> snapshot"); - client(internalCluster().getMasterName()).admin() + client(internalCluster().getClusterManagerName()).admin() .cluster() .prepareCreateSnapshot("test-repo", "test-snap") .setWaitForCompletion(false) @@ -1323,7 +1322,7 @@ public void testRetentionLeasesClearedOnRestore() throws Exception { } public void testAbortWaitsOnDataNode() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNodeName = internalCluster().startDataOnlyNode(); final String indexName = "test-index"; createIndex(indexName); @@ -1375,7 +1374,7 @@ public void onRequestSent( } public void testPartialSnapshotAllShardsMissing() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final String dataNode = internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "fs"); @@ -1393,17 +1392,17 @@ public void testPartialSnapshotAllShardsMissing() throws Exception { * correctly by testing a snapshot name collision. */ public void testCreateSnapshotLegacyPath() throws Exception { - final String masterNode = internalCluster().startMasterOnlyNode(); + final String clusterManagerNode = internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoName = "test-repo"; createRepository(repoName, "fs"); createIndex("some-index"); - final SnapshotsService snapshotsService = internalCluster().getMasterNodeInstance(SnapshotsService.class); + final SnapshotsService snapshotsService = internalCluster().getClusterManagerNodeInstance(SnapshotsService.class); final Snapshot snapshot1 = PlainActionFuture.get( f -> snapshotsService.createSnapshotLegacy(new CreateSnapshotRequest(repoName, "snap-1"), f) ); - awaitNoMoreRunningOperations(masterNode); + awaitNoMoreRunningOperations(clusterManagerNode); final InvalidSnapshotNameException sne = expectThrows( InvalidSnapshotNameException.class, @@ -1425,7 +1424,7 @@ public void testCreateSnapshotLegacyPath() throws Exception { } public void testSnapshotDeleteRelocatingPrimaryIndex() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); final List dataNodes = internalCluster().startDataOnlyNodes(2); final String repoName = "test-repo"; createRepository(repoName, "fs"); @@ -1475,6 +1474,31 @@ public void testSnapshotDeleteRelocatingPrimaryIndex() throws Exception { logger.info("--> done"); } + public void testIndexDeletionDuringSnapshotCreationInQueue() throws Exception { + assertAcked(prepareCreate("test-idx", 1, indexSettingsNoReplicas(1))); + ensureGreen(); + indexRandomDocs("test-idx", 100); + createRepository("test-repo", "fs"); + createSnapshot("test-repo", "test-snap", Collections.singletonList("test-idx")); + + logger.info("--> create snapshot to be deleted and then delete"); + createSnapshot("test-repo", "test-snap-delete", Collections.singletonList("test-idx")); + clusterAdmin().prepareDeleteSnapshot("test-repo", "test-snap-delete").execute(); + + logger.info("--> create snapshot before index deletion during above snapshot deletion"); + clusterAdmin().prepareCreateSnapshot("test-repo", "test-snap-2") + .setWaitForCompletion(false) + .setPartial(true) + .setIndices("test-idx") + .get(); + + logger.info("delete index during snapshot creation"); + assertAcked(admin().indices().prepareDelete("test-idx")); + + clusterAdmin().prepareRestoreSnapshot("test-repo", "test-snap").get(); + ensureGreen("test-idx"); + } + private long calculateTotalFilesSize(List files) { return files.stream().mapToLong(f -> { try { diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/DeleteSnapshotIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/DeleteSnapshotIT.java new file mode 100644 index 0000000000000..864779f86caf3 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/DeleteSnapshotIT.java @@ -0,0 +1,351 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.snapshots; + +import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.client.Client; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.UUIDs; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.remotestore.RemoteStoreBaseIntegTestCase; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.hamcrest.Matchers.comparesEqualTo; +import static org.hamcrest.Matchers.is; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class DeleteSnapshotIT extends AbstractSnapshotIntegTestCase { + + private static final String REMOTE_REPO_NAME = "remote-store-repo-name"; + + public void testDeleteSnapshot() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used in the test"); + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + + final String snapshotRepoName = "snapshot-repo-name"; + final Path snapshotRepoPath = randomRepoPath(); + createRepository(snapshotRepoName, "fs", snapshotRepoPath); + + final String indexName = "index-1"; + createIndexWithRandomDocs(indexName, randomIntBetween(5, 10)); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + final String snapshot = "snapshot"; + createFullSnapshot(snapshotRepoName, snapshot); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == 0); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == 1); + + assertAcked(startDeleteSnapshot(snapshotRepoName, snapshot).get()); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == 0); + } + + public void testDeleteShallowCopySnapshot() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used in the test"); + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + + final String snapshotRepoName = "snapshot-repo-name"; + createRepository(snapshotRepoName, "fs", snapshotRepoSettingsForShallowCopy()); + + final String indexName = "index-1"; + createIndexWithRandomDocs(indexName, randomIntBetween(5, 10)); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + final String shallowSnapshot = "shallow-snapshot"; + createFullSnapshot(snapshotRepoName, shallowSnapshot); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == 1); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == 1); + + assertAcked(startDeleteSnapshot(snapshotRepoName, shallowSnapshot).get()); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == 0); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == 0); + } + + // Deleting multiple shallow copy snapshots as part of single delete call with repo having only shallow copy snapshots. + public void testDeleteMultipleShallowCopySnapshotsCase1() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used in the test"); + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + final Client clusterManagerClient = internalCluster().clusterManagerClient(); + ensureStableCluster(2); + + final String snapshotRepoName = "snapshot-repo-name"; + final Path snapshotRepoPath = randomRepoPath(); + createRepository(snapshotRepoName, "mock", snapshotRepoSettingsForShallowCopy(snapshotRepoPath)); + final String testIndex = "index-test"; + createIndexWithContent(testIndex); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + // Creating some shallow copy snapshots + int totalShallowCopySnapshotsCount = randomIntBetween(4, 10); + List shallowCopySnapshots = createNSnapshots(snapshotRepoName, totalShallowCopySnapshotsCount); + List snapshotsToBeDeleted = shallowCopySnapshots.subList(0, randomIntBetween(2, totalShallowCopySnapshotsCount)); + int tobeDeletedSnapshotsCount = snapshotsToBeDeleted.size(); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == totalShallowCopySnapshotsCount); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == totalShallowCopySnapshotsCount); + // Deleting subset of shallow copy snapshots + assertAcked( + clusterManagerClient.admin() + .cluster() + .prepareDeleteSnapshot(snapshotRepoName, snapshotsToBeDeleted.toArray(new String[0])) + .get() + ); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == totalShallowCopySnapshotsCount - tobeDeletedSnapshotsCount); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == totalShallowCopySnapshotsCount + - tobeDeletedSnapshotsCount); + } + + // Deleting multiple shallow copy snapshots as part of single delete call with both partial and full copy snapshot present in the repo + // And then deleting multiple full copy snapshots as part of single delete call with both partial and shallow copy snapshots present in + // the repo + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8610") + public void testDeleteMultipleShallowCopySnapshotsCase2() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used in the test"); + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + final String dataNode = internalCluster().startDataOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + ensureStableCluster(2); + final String clusterManagerNode = internalCluster().getClusterManagerName(); + + final String snapshotRepoName = "snapshot-repo-name"; + final Path snapshotRepoPath = randomRepoPath(); + createRepository(snapshotRepoName, "mock", snapshotRepoSettingsForShallowCopy(snapshotRepoPath)); + final String testIndex = "index-test"; + createIndexWithContent(testIndex); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + // Creating a partial shallow copy snapshot + final String snapshot = "snapshot"; + blockNodeWithIndex(snapshotRepoName, testIndex); + blockDataNode(snapshotRepoName, dataNode); + + final Client clusterManagerClient = internalCluster().clusterManagerClient(); + final ActionFuture snapshotFuture = clusterManagerClient.admin() + .cluster() + .prepareCreateSnapshot(snapshotRepoName, snapshot) + .setWaitForCompletion(true) + .execute(); + + awaitNumberOfSnapshotsInProgress(1); + waitForBlock(dataNode, snapshotRepoName, TimeValue.timeValueSeconds(30L)); + internalCluster().restartNode(dataNode); + assertThat(snapshotFuture.get().getSnapshotInfo().state(), is(SnapshotState.PARTIAL)); + + unblockAllDataNodes(snapshotRepoName); + + ensureStableCluster(2, clusterManagerNode); + + // Creating some shallow copy snapshots + int totalShallowCopySnapshotsCount = randomIntBetween(4, 10); + List shallowCopySnapshots = createNSnapshots(snapshotRepoName, totalShallowCopySnapshotsCount); + List shallowCopySnapshotsToBeDeleted = shallowCopySnapshots.subList(0, randomIntBetween(2, totalShallowCopySnapshotsCount)); + int tobeDeletedShallowCopySnapshotsCount = shallowCopySnapshotsToBeDeleted.size(); + totalShallowCopySnapshotsCount += 1; // Adding partial shallow snapshot here + // Updating the snapshot repository flag to disable shallow snapshots + createRepository(snapshotRepoName, "mock", snapshotRepoPath); + // Creating some full copy snapshots + int totalFullCopySnapshotsCount = randomIntBetween(4, 10); + List fullCopySnapshots = createNSnapshots(snapshotRepoName, totalFullCopySnapshotsCount); + List fullCopySnapshotsToBeDeleted = fullCopySnapshots.subList(0, randomIntBetween(2, totalFullCopySnapshotsCount)); + int tobeDeletedFullCopySnapshotsCount = fullCopySnapshotsToBeDeleted.size(); + + int totalSnapshotsCount = totalFullCopySnapshotsCount + totalShallowCopySnapshotsCount; + + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == totalShallowCopySnapshotsCount); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == totalSnapshotsCount); + // Deleting subset of shallow copy snapshots + assertAcked( + clusterManagerClient.admin() + .cluster() + .prepareDeleteSnapshot(snapshotRepoName, shallowCopySnapshotsToBeDeleted.toArray(new String[0])) + .get() + ); + totalSnapshotsCount -= tobeDeletedShallowCopySnapshotsCount; + totalShallowCopySnapshotsCount -= tobeDeletedShallowCopySnapshotsCount; + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == totalSnapshotsCount); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == totalShallowCopySnapshotsCount); + + // Deleting subset of full copy snapshots + assertAcked( + clusterManagerClient.admin() + .cluster() + .prepareDeleteSnapshot(snapshotRepoName, fullCopySnapshotsToBeDeleted.toArray(new String[0])) + .get() + ); + totalSnapshotsCount -= tobeDeletedFullCopySnapshotsCount; + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == totalSnapshotsCount); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == totalShallowCopySnapshotsCount); + } + + // Deleting subset of shallow and full copy snapshots as part of single delete call and then deleting all snapshots in the repo. + @AwaitsFix(bugUrl = "https://github.com/opensearch-project/OpenSearch/issues/8610") + public void testDeleteMultipleShallowCopySnapshotsCase3() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used in the test"); + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + final Client clusterManagerClient = internalCluster().clusterManagerClient(); + ensureStableCluster(2); + + final String snapshotRepoName = "snapshot-repo-name"; + final Path snapshotRepoPath = randomRepoPath(); + createRepository(snapshotRepoName, "mock", snapshotRepoSettingsForShallowCopy(snapshotRepoPath)); + + final String testIndex = "index-test"; + createIndexWithContent(testIndex); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + // Creating some shallow copy snapshots + int totalShallowCopySnapshotsCount = randomIntBetween(4, 10); + List shallowCopySnapshots = createNSnapshots(snapshotRepoName, totalShallowCopySnapshotsCount); + List shallowCopySnapshotsToBeDeleted = shallowCopySnapshots.subList(0, randomIntBetween(2, totalShallowCopySnapshotsCount)); + int tobeDeletedShallowCopySnapshotsCount = shallowCopySnapshotsToBeDeleted.size(); + // Updating the snapshot repository flag to disable shallow snapshots + createRepository(snapshotRepoName, "mock", snapshotRepoPath); + // Creating some full copy snapshots + int totalFullCopySnapshotsCount = randomIntBetween(4, 10); + List fullCopySnapshots = createNSnapshots(snapshotRepoName, totalFullCopySnapshotsCount); + List fullCopySnapshotsToBeDeleted = fullCopySnapshots.subList(0, randomIntBetween(2, totalFullCopySnapshotsCount)); + int tobeDeletedFullCopySnapshotsCount = fullCopySnapshotsToBeDeleted.size(); + + int totalSnapshotsCount = totalFullCopySnapshotsCount + totalShallowCopySnapshotsCount; + + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == totalShallowCopySnapshotsCount); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == totalSnapshotsCount); + // Deleting subset of shallow copy snapshots and full copy snapshots + assertAcked( + clusterManagerClient.admin() + .cluster() + .prepareDeleteSnapshot( + snapshotRepoName, + Stream.concat(shallowCopySnapshotsToBeDeleted.stream(), fullCopySnapshotsToBeDeleted.stream()).toArray(String[]::new) + ) + .get() + ); + totalSnapshotsCount -= (tobeDeletedShallowCopySnapshotsCount + tobeDeletedFullCopySnapshotsCount); + totalShallowCopySnapshotsCount -= tobeDeletedShallowCopySnapshotsCount; + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == totalSnapshotsCount); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == totalShallowCopySnapshotsCount); + + // Deleting all the remaining snapshots + assertAcked(clusterManagerClient.admin().cluster().prepareDeleteSnapshot(snapshotRepoName, "*").get()); + assert (getRepositoryData(snapshotRepoName).getSnapshotIds().size() == 0); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME).length == 0); + } + + public void testRemoteStoreCleanupForDeletedIndex() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used in the test"); + final Path remoteStoreRepoPath = randomRepoPath(); + internalCluster().startClusterManagerOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + internalCluster().startDataOnlyNode(remoteStoreClusterSettings(REMOTE_REPO_NAME, remoteStoreRepoPath)); + final Client clusterManagerClient = internalCluster().clusterManagerClient(); + ensureStableCluster(2); + + final String snapshotRepoName = "snapshot-repo-name"; + final Path snapshotRepoPath = randomRepoPath(); + createRepository(snapshotRepoName, "mock", snapshotRepoSettingsForShallowCopy(snapshotRepoPath)); + + final String testIndex = "index-test"; + createIndexWithContent(testIndex); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + indexRandomDocs(remoteStoreEnabledIndexName, randomIntBetween(5, 10)); + + String indexUUID = client().admin() + .indices() + .prepareGetSettings(remoteStoreEnabledIndexName) + .get() + .getSetting(remoteStoreEnabledIndexName, IndexMetadata.SETTING_INDEX_UUID); + + logger.info("--> create two remote index shallow snapshots"); + List shallowCopySnapshots = createNSnapshots(snapshotRepoName, 2); + + String[] lockFiles = getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME); + assert (lockFiles.length == 2) : "lock files are " + Arrays.toString(lockFiles); + + // delete the giremote store index + assertAcked(client().admin().indices().prepareDelete(remoteStoreEnabledIndexName)); + + logger.info("--> delete snapshot 1"); + AcknowledgedResponse deleteSnapshotResponse = clusterManagerClient.admin() + .cluster() + .prepareDeleteSnapshot(snapshotRepoName, shallowCopySnapshots.get(0)) + .get(); + assertAcked(deleteSnapshotResponse); + + lockFiles = getLockFilesInRemoteStore(remoteStoreEnabledIndexName, REMOTE_REPO_NAME, indexUUID); + assert (lockFiles.length == 1) : "lock files are " + Arrays.toString(lockFiles); + + logger.info("--> delete snapshot 2"); + deleteSnapshotResponse = clusterManagerClient.admin() + .cluster() + .prepareDeleteSnapshot(snapshotRepoName, shallowCopySnapshots.get(1)) + .get(); + assertAcked(deleteSnapshotResponse); + + Path indexPath = Path.of(String.valueOf(remoteStoreRepoPath), indexUUID); + // Delete is async. Give time for it + assertBusy(() -> { + try { + assertThat(RemoteStoreBaseIntegTestCase.getFileCount(indexPath), comparesEqualTo(0)); + } catch (Exception e) {} + }, 30, TimeUnit.SECONDS); + } + + private List createNSnapshots(String repoName, int count) { + final List snapshotNames = new ArrayList<>(count); + final String prefix = "snap-" + UUIDs.randomBase64UUID(random()).toLowerCase(Locale.ROOT) + "-"; + for (int i = 0; i < count; i++) { + final String name = prefix + i; + createFullSnapshot(repoName, name); + snapshotNames.add(name); + } + logger.info("--> created {} in [{}]", snapshotNames, repoName); + return snapshotNames; + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/MetadataLoadingDuringSnapshotRestoreIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/MetadataLoadingDuringSnapshotRestoreIT.java index 608a439b40fec..b19ba9f5862a7 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/MetadataLoadingDuringSnapshotRestoreIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/MetadataLoadingDuringSnapshotRestoreIT.java @@ -41,7 +41,8 @@ import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.indices.recovery.RecoverySettings; import org.opensearch.plugins.Plugin; @@ -49,7 +50,6 @@ import org.opensearch.repositories.RepositoriesService; import org.opensearch.repositories.Repository; import org.opensearch.repositories.RepositoryData; -import org.opensearch.rest.RestStatus; import org.opensearch.snapshots.mockstore.MockRepository; import java.io.IOException; @@ -198,8 +198,8 @@ private void assertIndexMetadataLoads(final String snapshot, final String index, } private CountingMockRepository getCountingMockRepository() { - String master = internalCluster().getMasterName(); - RepositoriesService repositoriesService = internalCluster().getInstance(RepositoriesService.class, master); + String clusterManager = internalCluster().getClusterManagerName(); + RepositoriesService repositoriesService = internalCluster().getInstance(RepositoriesService.class, clusterManager); Repository repository = repositoriesService.repository("repository"); assertThat(repository, instanceOf(CountingMockRepository.class)); return (CountingMockRepository) repository; diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/MultiClusterRepoAccessIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/MultiClusterRepoAccessIT.java index 2b2b656d5dd0e..1c46e37dea93a 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/MultiClusterRepoAccessIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/MultiClusterRepoAccessIT.java @@ -33,15 +33,15 @@ import org.opensearch.common.network.NetworkModule; import org.opensearch.common.settings.Settings; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.env.Environment; import org.opensearch.repositories.RepositoryException; import org.opensearch.snapshots.mockstore.MockRepository; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; import org.opensearch.test.InternalTestCluster; import org.opensearch.test.MockHttpTransport; import org.opensearch.test.NodeConfigurationSource; +import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.transport.MockTransportService; import org.opensearch.transport.nio.MockNioTransportPlugin; import org.junit.After; @@ -107,13 +107,13 @@ public void stopSecondCluster() throws IOException { } public void testConcurrentDeleteFromOtherCluster() throws InterruptedException { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); final String repoNameOnFirstCluster = "test-repo"; final String repoNameOnSecondCluster = randomBoolean() ? "test-repo" : "other-repo"; createRepository(repoNameOnFirstCluster, "fs", repoPath); - secondCluster.startMasterOnlyNode(); + secondCluster.startClusterManagerOnlyNode(); secondCluster.startDataOnlyNode(); secondCluster.client() .admin() diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/RemoteIndexSnapshotStatusApiIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/RemoteIndexSnapshotStatusApiIT.java new file mode 100644 index 0000000000000..8e2580aba1745 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/RemoteIndexSnapshotStatusApiIT.java @@ -0,0 +1,206 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.snapshots; + +import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; +import org.opensearch.action.admin.cluster.snapshots.status.SnapshotIndexShardStage; +import org.opensearch.action.admin.cluster.snapshots.status.SnapshotIndexShardStatus; +import org.opensearch.action.admin.cluster.snapshots.status.SnapshotStatus; +import org.opensearch.cluster.SnapshotsInProgress; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.settings.Settings; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.opensearch.threadpool.ThreadPool; +import org.junit.Before; + +import java.nio.file.Path; + +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class RemoteIndexSnapshotStatusApiIT extends AbstractSnapshotIntegTestCase { + + protected Path absolutePath; + final String remoteStoreRepoName = "remote-store-repo-name"; + + @Before + public void setup() { + absolutePath = randomRepoPath().toAbsolutePath(); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(ThreadPool.ESTIMATED_TIME_INTERVAL_SETTING.getKey(), 0) // We have tests that check by-timestamp order + .put(remoteStoreClusterSettings(remoteStoreRepoName, absolutePath)) + .build(); + } + + public void testStatusAPICallForShallowCopySnapshot() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used for the test"); + internalCluster().startClusterManagerOnlyNode(); + internalCluster().startDataOnlyNodes(2); + + final String snapshotRepoName = "snapshot-repo-name"; + createRepository(snapshotRepoName, "fs", snapshotRepoSettingsForShallowCopy()); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + ensureGreen(); + + logger.info("--> indexing some data"); + for (int i = 0; i < 100; i++) { + index(remoteStoreEnabledIndexName, "_doc", Integer.toString(i), "foo", "bar" + i); + } + refresh(); + + final String snapshot = "snapshot"; + createFullSnapshot(snapshotRepoName, snapshot); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length == 1); + + final SnapshotStatus snapshotStatus = getSnapshotStatus(snapshotRepoName, snapshot); + assertThat(snapshotStatus.getState(), is(SnapshotsInProgress.State.SUCCESS)); + + // Validating that the incremental file count and incremental file size is zero for shallow copy + final SnapshotIndexShardStatus shallowSnapshotShardState = stateFirstShard(snapshotStatus, remoteStoreEnabledIndexName); + assertThat(shallowSnapshotShardState.getStage(), is(SnapshotIndexShardStage.DONE)); + assertThat(shallowSnapshotShardState.getStats().getTotalFileCount(), greaterThan(0)); + assertThat(shallowSnapshotShardState.getStats().getTotalSize(), greaterThan(0L)); + assertThat(shallowSnapshotShardState.getStats().getIncrementalFileCount(), is(0)); + assertThat(shallowSnapshotShardState.getStats().getIncrementalSize(), is(0L)); + } + + public void testStatusAPIStatsForBackToBackShallowSnapshot() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used for the test"); + internalCluster().startClusterManagerOnlyNode(); + internalCluster().startDataOnlyNodes(2); + + final String snapshotRepoName = "snapshot-repo-name"; + createRepository(snapshotRepoName, "fs", snapshotRepoSettingsForShallowCopy()); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + ensureGreen(); + + logger.info("--> indexing some data"); + for (int i = 0; i < 100; i++) { + index(remoteStoreEnabledIndexName, "_doc", Integer.toString(i), "foo", "bar" + i); + } + refresh(); + + createFullSnapshot(snapshotRepoName, "test-snap-1"); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length == 1); + + SnapshotStatus snapshotStatus = getSnapshotStatus(snapshotRepoName, "test-snap-1"); + assertThat(snapshotStatus.getState(), is(SnapshotsInProgress.State.SUCCESS)); + + SnapshotIndexShardStatus shallowSnapshotShardState = stateFirstShard(snapshotStatus, remoteStoreEnabledIndexName); + assertThat(shallowSnapshotShardState.getStage(), is(SnapshotIndexShardStage.DONE)); + final int totalFileCount = shallowSnapshotShardState.getStats().getTotalFileCount(); + final long totalSize = shallowSnapshotShardState.getStats().getTotalSize(); + final int incrementalFileCount = shallowSnapshotShardState.getStats().getIncrementalFileCount(); + final long incrementalSize = shallowSnapshotShardState.getStats().getIncrementalSize(); + + createFullSnapshot(snapshotRepoName, "test-snap-2"); + assert (getLockFilesInRemoteStore(remoteStoreEnabledIndexName, remoteStoreRepoName).length == 2); + + snapshotStatus = getSnapshotStatus(snapshotRepoName, "test-snap-2"); + assertThat(snapshotStatus.getState(), is(SnapshotsInProgress.State.SUCCESS)); + shallowSnapshotShardState = stateFirstShard(snapshotStatus, remoteStoreEnabledIndexName); + assertThat(shallowSnapshotShardState.getStats().getTotalFileCount(), equalTo(totalFileCount)); + assertThat(shallowSnapshotShardState.getStats().getTotalSize(), equalTo(totalSize)); + assertThat(shallowSnapshotShardState.getStats().getIncrementalFileCount(), equalTo(incrementalFileCount)); + assertThat(shallowSnapshotShardState.getStats().getIncrementalSize(), equalTo(incrementalSize)); + } + + public void testStatusAPICallInProgressShallowSnapshot() throws Exception { + disableRepoConsistencyCheck("Remote store repository is being used for the test"); + internalCluster().startClusterManagerOnlyNode(); + internalCluster().startDataOnlyNodes(2); + + final String snapshotRepoName = "snapshot-repo-name"; + createRepository(snapshotRepoName, "mock", snapshotRepoSettingsForShallowCopy().put("block_on_data", true)); + + final String remoteStoreEnabledIndexName = "remote-index-1"; + final Settings remoteStoreEnabledIndexSettings = getRemoteStoreBackedIndexSettings(); + createIndex(remoteStoreEnabledIndexName, remoteStoreEnabledIndexSettings); + ensureGreen(); + + logger.info("--> indexing some data"); + for (int i = 0; i < 100; i++) { + index(remoteStoreEnabledIndexName, "_doc", Integer.toString(i), "foo", "bar" + i); + } + refresh(); + + logger.info("--> snapshot"); + ActionFuture createSnapshotResponseActionFuture = startFullSnapshot(snapshotRepoName, "test-snap"); + + logger.info("--> wait for data nodes to get blocked"); + awaitNumberOfSnapshotsInProgress(1); + assertEquals( + SnapshotsInProgress.State.STARTED, + client().admin() + .cluster() + .prepareSnapshotStatus(snapshotRepoName) + .setSnapshots("test-snap") + .get() + .getSnapshots() + .get(0) + .getState() + ); + + logger.info("--> unblock all data nodes"); + unblockAllDataNodes(snapshotRepoName); + + logger.info("--> wait for snapshot to finish"); + createSnapshotResponseActionFuture.actionGet(); + } + + private static SnapshotIndexShardStatus stateFirstShard(SnapshotStatus snapshotStatus, String indexName) { + return snapshotStatus.getIndices().get(indexName).getShards().get(0); + } + + private static SnapshotStatus getSnapshotStatus(String repoName, String snapshotName) { + try { + return client().admin().cluster().prepareSnapshotStatus(repoName).setSnapshots(snapshotName).get().getSnapshots().get(0); + } catch (SnapshotMissingException e) { + throw new AssertionError(e); + } + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/RepositoriesIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/RepositoriesIT.java index 662e97dd84fda..dd40c77ba918d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/RepositoriesIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/RepositoriesIT.java @@ -35,17 +35,20 @@ import org.opensearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.opensearch.action.admin.cluster.repositories.verify.VerifyRepositoryResponse; import org.opensearch.action.admin.cluster.state.ClusterStateResponse; +import org.opensearch.action.bulk.BulkRequest; +import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.Client; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.RepositoriesMetadata; import org.opensearch.cluster.metadata.RepositoryMetadata; -import org.opensearch.common.io.FileSystemUtils; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.util.FileSystemUtils; import org.opensearch.repositories.RepositoriesService; import org.opensearch.repositories.RepositoryException; import org.opensearch.repositories.RepositoryVerificationException; +import org.opensearch.repositories.blobstore.BlobStoreRepository; import org.opensearch.snapshots.mockstore.MockRepository; import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.threadpool.ThreadPool; @@ -71,7 +74,7 @@ public void testRepositoryCreation() throws Exception { logger.info("--> verify the repository"); int numberOfFiles = FileSystemUtils.files(location).length; VerifyRepositoryResponse verifyRepositoryResponse = client.admin().cluster().prepareVerifyRepository("test-repo-1").get(); - assertThat(verifyRepositoryResponse.getNodes().size(), equalTo(cluster().numDataAndMasterNodes())); + assertThat(verifyRepositoryResponse.getNodes().size(), equalTo(cluster().numDataAndClusterManagerNodes())); logger.info("--> verify that we didn't leave any files as a result of verification"); assertThat(FileSystemUtils.files(location).length, equalTo(numberOfFiles)); @@ -157,7 +160,7 @@ public void testResidualStaleIndicesAreDeletedByConsecutiveDelete() throws Excep createFullSnapshot(repositoryName, snapshotToBeDeletedLastName); // Create more snapshots to be deleted in bulk - int maxThreadsForSnapshotDeletion = internalCluster().getMasterNodeInstance(ThreadPool.class) + int maxThreadsForSnapshotDeletion = internalCluster().getClusterManagerNodeInstance(ThreadPool.class) .info(ThreadPool.Names.SNAPSHOT) .getMax(); for (int i = 1; i <= maxThreadsForSnapshotDeletion + 1; i++) { @@ -177,15 +180,15 @@ public void testResidualStaleIndicesAreDeletedByConsecutiveDelete() throws Excep // Make repository to throw exception when trying to delete stale indices // This will make sure stale indices stays in repository after snapshot delete - String masterNode = internalCluster().getMasterName(); - ((MockRepository) internalCluster().getInstance(RepositoriesService.class, masterNode).repository("test-repo")) + String clusterManagerNode = internalCluster().getClusterManagerName(); + ((MockRepository) internalCluster().getInstance(RepositoriesService.class, clusterManagerNode).repository("test-repo")) .setThrowExceptionWhileDelete(true); logger.info("--> delete the bulk of the snapshots"); client.admin().cluster().prepareDeleteSnapshot(repositoryName, bulkSnapshotsPattern).get(); // Make repository to work normally - ((MockRepository) internalCluster().getInstance(RepositoriesService.class, masterNode).repository("test-repo")) + ((MockRepository) internalCluster().getInstance(RepositoriesService.class, clusterManagerNode).repository("test-repo")) .setThrowExceptionWhileDelete(false); // This snapshot should delete last snapshot's residual stale indices as well @@ -328,4 +331,125 @@ public void testRepositoryVerification() throws Exception { assertThat(ex.getMessage(), containsString("is not shared")); } } + + public void testSnapshotShardBlobDelete() throws Exception { + Client client = client(); + Path repositoryPath = randomRepoPath(); + final String repositoryName = "test-repo"; + final String firstSnapshot = "first-snapshot"; + final String secondSnapshot = "second-snapshot"; + final String indexName = "test-idx"; + + logger.info("--> creating repository at {}", repositoryPath.toAbsolutePath()); + int maxShardBlobDeleteBatchSize = randomIntBetween(1, 1000); + createRepository( + "test-repo", + "mock", + Settings.builder() + .put("location", repositoryPath) + .put(BlobStoreRepository.MAX_SNAPSHOT_SHARD_BLOB_DELETE_BATCH_SIZE.getKey(), maxShardBlobDeleteBatchSize) + ); + + logger.info("--> creating index-0 and ingest data"); + createIndex(indexName); + ensureGreen(); + for (int j = 0; j < randomIntBetween(1, 1000); j++) { + index(indexName, "_doc", Integer.toString(j), "foo", "bar" + j); + } + refresh(); + + logger.info("--> creating first snapshot"); + createFullSnapshot(repositoryName, firstSnapshot); + + int numberOfFiles = numberOfFiles(repositoryPath); + + logger.info("--> adding some more documents to test index"); + for (int j = 0; j < randomIntBetween(100, 10000); ++j) { + final BulkRequest bulkRequest = new BulkRequest(); + for (int i = 0; i < randomIntBetween(100, 1000); ++i) { + bulkRequest.add(new IndexRequest(indexName).source("foo" + j, "bar" + i)); + } + client().bulk(bulkRequest).get(); + } + refresh(); + + logger.info("--> creating second snapshot"); + createFullSnapshot(repositoryName, secondSnapshot); + + // Delete second snapshot + logger.info("--> delete second snapshot"); + client.admin().cluster().prepareDeleteSnapshot(repositoryName, secondSnapshot).get(); + + logger.info("--> make sure that number of files is back to what it was when the first snapshot was made"); + assertFileCount(repositoryPath, numberOfFiles); + + logger.info("--> done"); + } + + public void testSnapshotShardBlobDeletionRepositoryThrowingError() throws Exception { + Client client = client(); + Path repositoryPath = randomRepoPath(); + final String repositoryName = "test-repo"; + final String firstSnapshot = "first-snapshot"; + final String secondSnapshot = "second-snapshot"; + final String indexName = "test-idx"; + + logger.info("--> creating repository at {}", repositoryPath.toAbsolutePath()); + int maxShardBlobDeleteBatchSize = randomIntBetween(1, 1000); + createRepository( + "test-repo", + "mock", + Settings.builder() + .put("location", repositoryPath) + .put(BlobStoreRepository.MAX_SNAPSHOT_SHARD_BLOB_DELETE_BATCH_SIZE.getKey(), maxShardBlobDeleteBatchSize) + ); + + logger.info("--> creating index-0 and ingest data"); + createIndex(indexName); + ensureGreen(); + for (int j = 0; j < randomIntBetween(1, 1000); j++) { + index(indexName, "_doc", Integer.toString(j), "foo", "bar" + j); + } + refresh(); + + logger.info("--> creating first snapshot"); + createFullSnapshot(repositoryName, firstSnapshot); + + logger.info("--> adding some more documents to test index"); + for (int j = 0; j < randomIntBetween(100, 1000); ++j) { + final BulkRequest bulkRequest = new BulkRequest(); + for (int i = 0; i < randomIntBetween(100, 1000); ++i) { + bulkRequest.add(new IndexRequest(indexName).source("foo" + j, "bar" + i)); + } + client().bulk(bulkRequest).get(); + } + refresh(); + + logger.info("--> creating second snapshot"); + createFullSnapshot(repositoryName, secondSnapshot); + + // Make repository to throw exception when trying to delete stale snapshot shard blobs + String clusterManagerNode = internalCluster().getMasterName(); + ((MockRepository) internalCluster().getInstance(RepositoriesService.class, clusterManagerNode).repository("test-repo")) + .setThrowExceptionWhileDelete(true); + + // Delete second snapshot + logger.info("--> delete second snapshot"); + client.admin().cluster().prepareDeleteSnapshot(repositoryName, secondSnapshot).get(); + + // Make repository to work normally + ((MockRepository) internalCluster().getInstance(RepositoriesService.class, clusterManagerNode).repository("test-repo")) + .setThrowExceptionWhileDelete(false); + + // This snapshot should delete last snapshot's residual stale shard blobs as well + logger.info("--> delete first snapshot"); + client.admin().cluster().prepareDeleteSnapshot(repositoryName, firstSnapshot).get(); + + // Expect two files to remain in the repository: + // (1) index-(N+1) + // (2) index-latest + assertFileCount(repositoryPath, 2); + + logger.info("--> done"); + } } diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/RepositoryFilterUserMetadataIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/RepositoryFilterUserMetadataIT.java index 9ee479cdd7fe0..0eb37703eb0f1 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/RepositoryFilterUserMetadataIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/RepositoryFilterUserMetadataIT.java @@ -32,14 +32,13 @@ package org.opensearch.snapshots; import org.apache.lucene.index.IndexCommit; - import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.snapshots.IndexShardSnapshotStatus; @@ -70,7 +69,7 @@ protected Collection> nodePlugins() { } public void testFilteredRepoMetadataIsUsed() { - final String masterName = internalCluster().getMasterName(); + final String clusterManagerName = internalCluster().getClusterManagerName(); final String repoName = "test-repo"; assertAcked( client().admin() @@ -78,7 +77,9 @@ public void testFilteredRepoMetadataIsUsed() { .preparePutRepository(repoName) .setType(MetadataFilteringPlugin.TYPE) .setSettings( - Settings.builder().put("location", randomRepoPath()).put(MetadataFilteringPlugin.MASTER_SETTING_VALUE, masterName) + Settings.builder() + .put("location", randomRepoPath()) + .put(MetadataFilteringPlugin.CLUSTER_MANAGER_SETTING_VALUE, clusterManagerName) ) ); createIndex("test-idx"); @@ -88,15 +89,18 @@ public void testFilteredRepoMetadataIsUsed() { .setWaitForCompletion(true) .get() .getSnapshotInfo(); - assertThat(snapshotInfo.userMetadata(), is(Collections.singletonMap(MetadataFilteringPlugin.MOCK_FILTERED_META, masterName))); + assertThat( + snapshotInfo.userMetadata(), + is(Collections.singletonMap(MetadataFilteringPlugin.MOCK_FILTERED_META, clusterManagerName)) + ); } - // Mock plugin that stores the name of the master node that started a snapshot in each snapshot's metadata + // Mock plugin that stores the name of the cluster-manager node that started a snapshot in each snapshot's metadata public static final class MetadataFilteringPlugin extends org.opensearch.plugins.Plugin implements RepositoryPlugin { private static final String MOCK_FILTERED_META = "mock_filtered_meta"; - private static final String MASTER_SETTING_VALUE = "initial_master"; + private static final String CLUSTER_MANAGER_SETTING_VALUE = "initial_cluster_manager"; private static final String TYPE = "mock_meta_filtering"; @@ -112,8 +116,8 @@ public Map getRepositories( metadata -> new FsRepository(metadata, env, namedXContentRegistry, clusterService, recoverySettings) { // Storing the initially expected metadata value here to verify that #filterUserMetadata is only called once on the - // initial master node starting the snapshot - private final String initialMetaValue = metadata.settings().get(MASTER_SETTING_VALUE); + // initial cluster-manager node starting the snapshot + private final String initialMetaValue = metadata.settings().get(CLUSTER_MANAGER_SETTING_VALUE); @Override public void finalizeSnapshot( diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/RestoreSnapshotIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/RestoreSnapshotIT.java index 3a7fdd4093657..7117818451e14 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/RestoreSnapshotIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/RestoreSnapshotIT.java @@ -32,23 +32,24 @@ package org.opensearch.snapshots; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.opensearch.action.admin.indices.template.delete.DeleteIndexTemplateRequestBuilder; import org.opensearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.opensearch.action.index.IndexRequestBuilder; import org.opensearch.client.Client; import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MappingMetadata; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.rest.RestStatus; import org.opensearch.indices.InvalidIndexNameException; import org.opensearch.repositories.RepositoriesService; -import org.opensearch.rest.RestStatus; import java.nio.file.Path; import java.util.Collections; @@ -80,7 +81,6 @@ import static org.hamcrest.Matchers.nullValue; public class RestoreSnapshotIT extends AbstractSnapshotIntegTestCase { - public void testParallelRestoreOperations() { String indexName1 = "testindex1"; String indexName2 = "testindex2"; @@ -973,4 +973,86 @@ public void testForbidDisableSoftDeletesDuringRestore() throws Exception { ); assertThat(restoreError.getMessage(), containsString("cannot disable setting [index.soft_deletes.enabled] on restore")); } + + public void testRestoreBalancedReplica() { + try { + createRepository("test-repo", "fs"); + DeleteIndexTemplateRequestBuilder deleteTemplate = client().admin().indices().prepareDeleteTemplate("random_index_template"); + assertAcked(deleteTemplate.execute().actionGet()); + + createIndex("test-index", Settings.builder().put("index.number_of_replicas", 0).build()); + createIndex(".system-index", Settings.builder().put("index.number_of_replicas", 0).build()); + ensureGreen(); + clusterAdmin().prepareCreateSnapshot("test-repo", "snapshot-0") + .setIndices("test-index", ".system-index") + .setWaitForCompletion(true) + .get(); + manageReplicaSettingForDefaultReplica(true); + + final IllegalArgumentException restoreError = expectThrows( + IllegalArgumentException.class, + () -> clusterAdmin().prepareRestoreSnapshot("test-repo", "snapshot-0") + .setRenamePattern("test-index") + .setRenameReplacement("new-index") + .setIndices("test-index") + .get() + ); + assertThat( + restoreError.getMessage(), + containsString("expected total copies needs to be a multiple of total awareness attributes [3]") + ); + + final IllegalArgumentException restoreError2 = expectThrows( + IllegalArgumentException.class, + () -> clusterAdmin().prepareRestoreSnapshot("test-repo", "snapshot-0") + .setRenamePattern("test-index") + .setRenameReplacement("new-index-2") + .setIndexSettings( + Settings.builder().put(SETTING_NUMBER_OF_REPLICAS, 1).put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-1").build() + ) + .setIndices("test-index") + .get() + ); + assertThat( + restoreError2.getMessage(), + containsString("expected max cap on auto expand to be a multiple of total awareness attributes [3]") + ); + + RestoreSnapshotResponse restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "snapshot-0") + .setRenamePattern(".system-index") + .setRenameReplacement(".system-index-restore-1") + .setWaitForCompletion(true) + .setIndices(".system-index") + .execute() + .actionGet(); + + assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0)); + + restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "snapshot-0") + .setRenamePattern("test-index") + .setRenameReplacement("new-index") + .setIndexSettings(Settings.builder().put("index.number_of_replicas", 2).build()) + .setWaitForCompletion(true) + .setIndices("test-index") + .execute() + .actionGet(); + + restoreSnapshotResponse = clusterAdmin().prepareRestoreSnapshot("test-repo", "snapshot-0") + .setRenamePattern("test-index") + .setRenameReplacement("new-index-3") + .setIndexSettings( + Settings.builder().put(SETTING_NUMBER_OF_REPLICAS, 0).put(IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS, "0-2").build() + ) + .setWaitForCompletion(true) + .setIndices("test-index") + .execute() + .actionGet(); + + assertThat(restoreSnapshotResponse.getRestoreInfo().totalShards(), greaterThan(0)); + } finally { + manageReplicaSettingForDefaultReplica(false); + randomIndexTemplate(); + } + } + } diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/SearchableSnapshotIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/SearchableSnapshotIT.java new file mode 100644 index 0000000000000..4478a3432e519 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/SearchableSnapshotIT.java @@ -0,0 +1,751 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.opensearch.snapshots; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; +import com.carrotsearch.randomizedtesting.generators.RandomPicks; + +import org.opensearch.action.admin.cluster.node.stats.NodeStats; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; +import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsGroup; +import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsRequest; +import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; +import org.opensearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest; +import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; +import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest; +import org.opensearch.action.admin.indices.settings.get.GetSettingsRequest; +import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequestBuilder; +import org.opensearch.action.index.IndexRequestBuilder; +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.client.Client; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.routing.GroupShardsIterator; +import org.opensearch.cluster.routing.ShardIterator; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.common.io.PathUtils; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.index.Index; +import org.opensearch.index.IndexModule; +import org.opensearch.index.IndexNotFoundException; +import org.opensearch.index.store.remote.file.CleanerDaemonThreadLeakFilter; +import org.opensearch.index.store.remote.filecache.FileCacheStats; +import org.opensearch.monitor.fs.FsInfo; +import org.opensearch.node.Node; +import org.opensearch.repositories.fs.FsRepository; +import org.hamcrest.MatcherAssert; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest.Metric.FS; +import static org.opensearch.core.common.util.CollectionUtils.iterableAsArrayList; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +@ThreadLeakFilters(filters = CleanerDaemonThreadLeakFilter.class) +public final class SearchableSnapshotIT extends AbstractSnapshotIntegTestCase { + + @Override + protected boolean addMockInternalEngine() { + return false; + } + + @Override + protected Settings.Builder randomRepositorySettings() { + final Settings.Builder settings = Settings.builder(); + settings.put("location", randomRepoPath()).put("compress", randomBoolean()); + settings.put(FsRepository.BASE_PATH_SETTING.getKey(), "my_base_path"); + return settings; + } + + private Settings.Builder chunkedRepositorySettings() { + final Settings.Builder settings = Settings.builder(); + settings.put("location", randomRepoPath()).put("compress", randomBoolean()); + settings.put("chunk_size", 2 << 23, ByteSizeUnit.BYTES); + return settings; + } + + /** + * Tests a happy path scenario for searchable snapshots by creating 2 indices, + * taking a snapshot, restoring them as searchable snapshots. + * Ensures availability of sufficient data nodes and search capable nodes. + */ + public void testCreateSearchableSnapshot() throws Exception { + final String snapshotName = "test-snap"; + final String repoName = "test-repo"; + final String indexName1 = "test-idx-1"; + final String restoredIndexName1 = indexName1 + "-copy"; + final String indexName2 = "test-idx-2"; + final String restoredIndexName2 = indexName2 + "-copy"; + final int numReplicasIndex1 = randomIntBetween(1, 4); + final int numReplicasIndex2 = randomIntBetween(0, 2); + final Client client = client(); + + internalCluster().ensureAtLeastNumDataNodes(Math.max(numReplicasIndex1, numReplicasIndex2) + 1); + createIndexWithDocsAndEnsureGreen(numReplicasIndex1, 100, indexName1); + createIndexWithDocsAndEnsureGreen(numReplicasIndex2, 100, indexName2); + + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName1, indexName2); + deleteIndicesAndEnsureGreen(client, indexName1, indexName2); + + internalCluster().ensureAtLeastNumSearchNodes(Math.max(numReplicasIndex1, numReplicasIndex2) + 1); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertRemoteSnapshotIndexSettings(client, restoredIndexName1, restoredIndexName2); + + assertDocCount(restoredIndexName1, 100L); + assertDocCount(restoredIndexName2, 100L); + assertIndexDirectoryDoesNotExist(restoredIndexName1, restoredIndexName2); + } + + public void testSnapshottingSearchableSnapshots() throws Exception { + final String repoName = "test-repo"; + final String indexName = "test-idx"; + final Client client = client(); + + // create an index, add data, snapshot it, then delete it + internalCluster().ensureAtLeastNumDataNodes(1); + createIndexWithDocsAndEnsureGreen(0, 100, indexName); + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, "initial-snapshot", repoName, indexName); + deleteIndicesAndEnsureGreen(client, indexName); + + // restore the index as a searchable snapshot + internalCluster().ensureAtLeastNumSearchNodes(1); + client.admin() + .cluster() + .prepareRestoreSnapshot(repoName, "initial-snapshot") + .setRenamePattern("(.+)") + .setRenameReplacement("$1-copy-0") + .setStorageType(RestoreSnapshotRequest.StorageType.REMOTE_SNAPSHOT) + .setWaitForCompletion(true) + .execute() + .actionGet(); + ensureGreen(); + assertDocCount(indexName + "-copy-0", 100L); + assertIndexDirectoryDoesNotExist(indexName + "-copy-0"); + + // Test that the searchable snapshot index can continue to be snapshotted and restored + for (int i = 0; i < 4; i++) { + final String repeatedSnapshotName = "test-repeated-snap-" + i; + takeSnapshot(client, repeatedSnapshotName, repoName); + deleteIndicesAndEnsureGreen(client, "_all"); + client.admin() + .cluster() + .prepareRestoreSnapshot(repoName, repeatedSnapshotName) + .setRenamePattern("([a-z-]+).*") + .setRenameReplacement("$1" + (i + 1)) + .setWaitForCompletion(true) + .execute() + .actionGet(); + ensureGreen(); + final String restoredIndexName = indexName + "-copy-" + (i + 1); + assertDocCount(restoredIndexName, 100L); + assertIndexDirectoryDoesNotExist(restoredIndexName); + } + // Assert all the snapshots exist. Note that AbstractSnapshotIntegTestCase::assertRepoConsistency + // will run after this test (and all others) and assert on the consistency of the data in the repo. + final GetSnapshotsResponse response = client.admin().cluster().prepareGetSnapshots(repoName).execute().actionGet(); + final Map> snapshotInfoMap = response.getSnapshots() + .stream() + .collect(Collectors.toMap(s -> s.snapshotId().getName(), SnapshotInfo::indices)); + assertEquals( + Map.of( + "initial-snapshot", + List.of("test-idx"), + "test-repeated-snap-0", + List.of("test-idx-copy-0"), + "test-repeated-snap-1", + List.of("test-idx-copy-1"), + "test-repeated-snap-2", + List.of("test-idx-copy-2"), + "test-repeated-snap-3", + List.of("test-idx-copy-3") + ), + snapshotInfoMap + ); + } + + /** + * Tests a chunked repository scenario for searchable snapshots by creating an index, + * taking a snapshot, restoring it as a searchable snapshot index. + */ + public void testCreateSearchableSnapshotWithChunks() throws Exception { + final int numReplicasIndex = randomIntBetween(1, 4); + final String indexName = "test-idx"; + final String restoredIndexName = indexName + "-copy"; + final String repoName = "test-repo"; + final String snapshotName = "test-snap"; + final Client client = client(); + + Settings.Builder repositorySettings = chunkedRepositorySettings(); + + internalCluster().ensureAtLeastNumSearchAndDataNodes(numReplicasIndex + 1); + createIndexWithDocsAndEnsureGreen(numReplicasIndex, 1000, indexName); + createRepositoryWithSettings(repositorySettings, repoName); + takeSnapshot(client, snapshotName, repoName, indexName); + + deleteIndicesAndEnsureGreen(client, indexName); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertRemoteSnapshotIndexSettings(client, restoredIndexName); + + assertDocCount(restoredIndexName, 1000L); + } + + /** + * Tests the functionality of remote shard allocation to + * ensure it can assign remote shards to a node with local shards given it has the + * search role capabilities. + */ + public void testSearchableSnapshotAllocationForLocalAndRemoteShardsOnSameNode() throws Exception { + final int numReplicasIndex = randomIntBetween(1, 4); + final String indexName = "test-idx"; + final String restoredIndexName = indexName + "-copy"; + final String repoName = "test-repo"; + final String snapshotName = "test-snap"; + final Client client = client(); + + internalCluster().ensureAtLeastNumSearchAndDataNodes(numReplicasIndex + 1); + createIndexWithDocsAndEnsureGreen(numReplicasIndex, 100, indexName); + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName); + + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertRemoteSnapshotIndexSettings(client, restoredIndexName); + + assertDocCount(restoredIndexName, 100L); + assertDocCount(indexName, 100L); + } + + /** + * Tests the functionality of remote shard allocation to + * ensure it can handle node drops for failover scenarios and the cluster gets back to a healthy state when + * nodes with search capabilities are added back to the cluster. + */ + public void testSearchableSnapshotAllocationForFailoverAndRecovery() throws Exception { + final int numReplicasIndex = 1; + final String indexName = "test-idx"; + final String restoredIndexName = indexName + "-copy"; + final String repoName = "test-repo"; + final String snapshotName = "test-snap"; + final Client client = client(); + + internalCluster().ensureAtLeastNumDataNodes(numReplicasIndex + 1); + createIndexWithDocsAndEnsureGreen(numReplicasIndex, 100, indexName); + + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName); + deleteIndicesAndEnsureGreen(client, indexName); + + internalCluster().ensureAtLeastNumSearchNodes(numReplicasIndex + 1); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertRemoteSnapshotIndexSettings(client, restoredIndexName); + assertDocCount(restoredIndexName, 100L); + + logger.info("--> stop a random search node"); + internalCluster().stopRandomSearchNode(); + ensureYellow(restoredIndexName); + assertDocCount(restoredIndexName, 100L); + + logger.info("--> stop the last search node"); + internalCluster().stopRandomSearchNode(); + ensureRed(restoredIndexName); + + logger.info("--> add 3 new search nodes"); + internalCluster().ensureAtLeastNumSearchNodes(numReplicasIndex + 2); + ensureGreen(restoredIndexName); + assertDocCount(restoredIndexName, 100); + + logger.info("--> stop a random search node"); + internalCluster().stopRandomSearchNode(); + ensureGreen(restoredIndexName); + assertDocCount(restoredIndexName, 100); + } + + /** + * Tests the functionality of index write block on a searchable snapshot index. + */ + public void testSearchableSnapshotIndexIsReadOnly() throws Exception { + final String indexName = "test-index"; + final String restoredIndexName = indexName + "-copy"; + final String repoName = "test-repo"; + final String snapshotName = "test-snap"; + final Client client = client(); + + createIndexWithDocsAndEnsureGreen(0, 100, indexName); + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName); + deleteIndicesAndEnsureGreen(client, indexName); + + internalCluster().ensureAtLeastNumSearchNodes(1); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertRemoteSnapshotIndexSettings(client, restoredIndexName); + + assertIndexingBlocked(restoredIndexName); + assertTrue(client.admin().indices().prepareDelete(restoredIndexName).get().isAcknowledged()); + assertThrows( + "Expect index to not exist", + IndexNotFoundException.class, + () -> client.admin().indices().prepareGetIndex().setIndices(restoredIndexName).execute().actionGet() + ); + } + + public void testDeleteSearchableSnapshotBackingIndexThrowsException() throws Exception { + final String indexName = "test-index"; + final Client client = client(); + final String repoName = "test-repo"; + final String snapshotName = "test-snap"; + createRepositoryWithSettings(null, repoName); + createIndexWithDocsAndEnsureGreen(0, 100, indexName); + takeSnapshot(client, snapshotName, repoName, indexName); + internalCluster().ensureAtLeastNumSearchNodes(1); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertThrows( + SnapshotInUseDeletionException.class, + () -> client().admin().cluster().deleteSnapshot(new DeleteSnapshotRequest(repoName, snapshotName)).actionGet() + ); + } + + public void testDeleteSearchableSnapshotBackingIndex() throws Exception { + final String indexName1 = "test-index1"; + final String indexName2 = "test-index2"; + final Client client = client(); + final String repoName = "test-repo"; + final String snapshotName1 = "test-snapshot1"; + final String snapshotName2 = "test-snap"; + createRepositoryWithSettings(null, repoName); + createIndexWithDocsAndEnsureGreen(0, 100, indexName1); + createIndexWithDocsAndEnsureGreen(0, 100, indexName2); + takeSnapshot(client, snapshotName1, repoName, indexName1); + takeSnapshot(client, snapshotName2, repoName, indexName2); + internalCluster().ensureAtLeastNumSearchNodes(1); + restoreSnapshotAndEnsureGreen(client, snapshotName2, repoName); + client().admin().cluster().deleteSnapshot(new DeleteSnapshotRequest(repoName, snapshotName1)).actionGet(); + } + + private void createIndexWithDocsAndEnsureGreen(int numReplicasIndex, int numOfDocs, String indexName) throws InterruptedException { + createIndex( + indexName, + Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, Integer.toString(numReplicasIndex)) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, "1") + .put(IndexModule.INDEX_STORE_TYPE_SETTING.getKey(), IndexModule.Type.FS.getSettingsKey()) + .build() + ); + ensureGreen(); + + indexRandomDocs(indexName, numOfDocs); + ensureGreen(); + } + + private void takeSnapshot(Client client, String snapshotName, String repoName, String... indices) { + logger.info("--> Take a snapshot"); + final CreateSnapshotResponse createSnapshotResponse = client.admin() + .cluster() + .prepareCreateSnapshot(repoName, snapshotName) + .setWaitForCompletion(true) + .setIndices(indices) + .get(); + + MatcherAssert.assertThat(createSnapshotResponse.getSnapshotInfo().successfulShards(), greaterThan(0)); + MatcherAssert.assertThat( + createSnapshotResponse.getSnapshotInfo().successfulShards(), + equalTo(createSnapshotResponse.getSnapshotInfo().totalShards()) + ); + } + + private void createRepositoryWithSettings(Settings.Builder repositorySettings, String repoName) { + logger.info("--> Create a repository"); + if (repositorySettings == null) { + createRepository(repoName, FsRepository.TYPE); + } else { + createRepository(repoName, FsRepository.TYPE, repositorySettings); + } + } + + private void deleteIndicesAndEnsureGreen(Client client, String... indices) { + assertTrue(client.admin().indices().prepareDelete(indices).get().isAcknowledged()); + ensureGreen(); + } + + private void restoreSnapshotAndEnsureGreen(Client client, String snapshotName, String repoName) { + logger.info("--> restore indices as 'remote_snapshot'"); + client.admin() + .cluster() + .prepareRestoreSnapshot(repoName, snapshotName) + .setRenamePattern("(.+)") + .setRenameReplacement("$1-copy") + .setStorageType(RestoreSnapshotRequest.StorageType.REMOTE_SNAPSHOT) + .setWaitForCompletion(true) + .execute() + .actionGet(); + ensureGreen(); + } + + private void assertRemoteSnapshotIndexSettings(Client client, String... snapshotIndexNames) { + GetSettingsResponse settingsResponse = client.admin() + .indices() + .getSettings(new GetSettingsRequest().indices(snapshotIndexNames)) + .actionGet(); + assertEquals(snapshotIndexNames.length, settingsResponse.getIndexToSettings().keySet().size()); + for (String snapshotIndexName : snapshotIndexNames) { + assertEquals( + IndexModule.Type.REMOTE_SNAPSHOT.getSettingsKey(), + settingsResponse.getSetting(snapshotIndexName, IndexModule.INDEX_STORE_TYPE_SETTING.getKey()) + ); + } + } + + private void assertIndexingBlocked(String index) { + try { + final IndexRequestBuilder builder = client().prepareIndex(index); + builder.setSource("foo", "bar"); + builder.execute().actionGet(); + fail("Expected operation to throw an exception"); + } catch (ClusterBlockException e) { + MatcherAssert.assertThat(e.blocks(), contains(IndexMetadata.REMOTE_READ_ONLY_ALLOW_DELETE)); + } + } + + public void testUpdateIndexSettings() throws InterruptedException { + final String indexName = "test-index"; + final String restoredIndexName = indexName + "-copy"; + final String repoName = "test-repo"; + final String snapshotName = "test-snap"; + final Client client = client(); + + createIndexWithDocsAndEnsureGreen(0, 100, indexName); + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName); + deleteIndicesAndEnsureGreen(client, indexName); + + internalCluster().ensureAtLeastNumSearchNodes(1); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertRemoteSnapshotIndexSettings(client, restoredIndexName); + + testUpdateIndexSettingsOnlyNotAllowedSettings(restoredIndexName); + testUpdateIndexSettingsOnlyAllowedSettings(restoredIndexName); + testUpdateIndexSettingsAtLeastOneNotAllowedSettings(restoredIndexName); + } + + private void testUpdateIndexSettingsOnlyNotAllowedSettings(String index) { + try { + final UpdateSettingsRequestBuilder builder = client().admin().indices().prepareUpdateSettings(index); + builder.setSettings(Map.of("index.refresh_interval", 10)); + builder.execute().actionGet(); + fail("Expected operation to throw an exception"); + } catch (ClusterBlockException e) { + MatcherAssert.assertThat(e.blocks(), contains(IndexMetadata.REMOTE_READ_ONLY_ALLOW_DELETE)); + } + } + + private void testUpdateIndexSettingsOnlyAllowedSettings(String index) { + final UpdateSettingsRequestBuilder builder = client().admin().indices().prepareUpdateSettings(index); + builder.setSettings(Map.of("index.max_result_window", 1000, "index.search.slowlog.threshold.query.warn", "10s")); + AcknowledgedResponse settingsResponse = builder.execute().actionGet(); + assertThat(settingsResponse, notNullValue()); + } + + private void testUpdateIndexSettingsAtLeastOneNotAllowedSettings(String index) { + try { + final UpdateSettingsRequestBuilder builder = client().admin().indices().prepareUpdateSettings(index); + builder.setSettings( + Map.of("index.max_result_window", 5000, "index.search.slowlog.threshold.query.warn", "15s", "index.refresh_interval", 10) + ); + builder.execute().actionGet(); + fail("Expected operation to throw an exception"); + } catch (ClusterBlockException e) { + MatcherAssert.assertThat(e.blocks(), contains(IndexMetadata.REMOTE_READ_ONLY_ALLOW_DELETE)); + } + } + + public void testFileCacheStats() throws Exception { + final String snapshotName = "test-snap"; + final String repoName = "test-repo"; + final String indexName1 = "test-idx-1"; + final Client client = client(); + final int numNodes = 2; + + internalCluster().ensureAtLeastNumDataNodes(numNodes); + createIndexWithDocsAndEnsureGreen(1, 100, indexName1); + + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName1); + deleteIndicesAndEnsureGreen(client, indexName1); + assertAllNodesFileCacheEmpty(); + + internalCluster().ensureAtLeastNumSearchNodes(numNodes); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertNodesFileCacheNonEmpty(numNodes); + } + + /** + * Tests file cache restore scenario for searchable snapshots by creating an index, + * taking a snapshot, restoring it as a searchable snapshot. + * It ensures file cache is restored post node restart. + */ + public void testFileCacheRestore() throws Exception { + final String snapshotName = "test-snap"; + final String repoName = "test-repo"; + final String indexName = "test-idx"; + final String restoredIndexName = indexName + "-copy"; + // Keeping the replicas to 0 for reproducible cache results as shards can get reassigned otherwise + final int numReplicasIndex = 0; + final Client client = client(); + + internalCluster().ensureAtLeastNumDataNodes(numReplicasIndex + 1); + createIndexWithDocsAndEnsureGreen(numReplicasIndex, 100, indexName); + + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName); + deleteIndicesAndEnsureGreen(client, indexName); + + internalCluster().ensureAtLeastNumSearchNodes(numReplicasIndex + 1); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertRemoteSnapshotIndexSettings(client, restoredIndexName); + + assertDocCount(restoredIndexName, 100L); + assertIndexDirectoryDoesNotExist(restoredIndexName); + + NodesStatsResponse preRestoreStats = client().admin().cluster().nodesStats(new NodesStatsRequest().all()).actionGet(); + for (NodeStats nodeStats : preRestoreStats.getNodes()) { + if (nodeStats.getNode().isSearchNode()) { + internalCluster().restartNode(nodeStats.getNode().getName()); + } + } + + NodesStatsResponse postRestoreStats = client().admin().cluster().nodesStats(new NodesStatsRequest().all()).actionGet(); + Map preRestoreStatsMap = preRestoreStats.getNodesMap(); + Map postRestoreStatsMap = postRestoreStats.getNodesMap(); + for (String node : postRestoreStatsMap.keySet()) { + NodeStats preRestoreStat = preRestoreStatsMap.get(node); + NodeStats postRestoreStat = postRestoreStatsMap.get(node); + if (preRestoreStat.getNode().isSearchNode()) { + assertEquals(preRestoreStat.getFileCacheStats().getUsed(), postRestoreStat.getFileCacheStats().getUsed()); + } + } + } + + /** + * Picks a shard out of the cluster state for each given index and asserts + * that the 'index' directory does not exist in the node's file system. + * This assertion is digging a bit into the implementation details to + * verify that the Lucene segment files are not copied from the snapshot + * repository to the node's local disk for a remote snapshot index. + */ + private void assertIndexDirectoryDoesNotExist(String... indexNames) { + final ClusterState state = client().admin().cluster().prepareState().get().getState(); + for (String indexName : indexNames) { + final Index index = state.metadata().index(indexName).getIndex(); + // Get the primary shards for the given index + final GroupShardsIterator shardIterators = state.getRoutingTable() + .activePrimaryShardsGrouped(new String[] { indexName }, false); + // Randomly pick one of the shards + final List iterators = iterableAsArrayList(shardIterators); + final ShardIterator shardIterator = RandomPicks.randomFrom(random(), iterators); + final ShardRouting shardRouting = shardIterator.nextOrNull(); + assertNotNull(shardRouting); + assertTrue(shardRouting.primary()); + assertTrue(shardRouting.assignedToNode()); + // Get the file system stats for the assigned node + final String nodeId = shardRouting.currentNodeId(); + final NodesStatsResponse nodeStats = client().admin().cluster().prepareNodesStats(nodeId).addMetric(FS.metricName()).get(); + for (FsInfo.Path info : nodeStats.getNodes().get(0).getFs()) { + // Build the expected path for the index data for a "normal" + // index and assert it does not exist + final String path = info.getPath(); + final Path file = PathUtils.get(path) + .resolve("indices") + .resolve(index.getUUID()) + .resolve(Integer.toString(shardRouting.getId())) + .resolve("index"); + MatcherAssert.assertThat("Expect file not to exist: " + file, Files.exists(file), is(false)); + } + } + } + + private void assertAllNodesFileCacheEmpty() { + NodesStatsResponse response = client().admin().cluster().nodesStats(new NodesStatsRequest().all()).actionGet(); + for (NodeStats stats : response.getNodes()) { + FileCacheStats fcstats = stats.getFileCacheStats(); + if (fcstats != null) { + assertTrue(isFileCacheEmpty(fcstats)); + } + } + } + + private void assertNodesFileCacheNonEmpty(int numNodes) { + NodesStatsResponse response = client().admin().cluster().nodesStats(new NodesStatsRequest().all()).actionGet(); + int nonEmptyFileCacheNodes = 0; + for (NodeStats stats : response.getNodes()) { + FileCacheStats fcStats = stats.getFileCacheStats(); + if (stats.getNode().isSearchNode()) { + if (!isFileCacheEmpty(fcStats)) { + nonEmptyFileCacheNodes++; + } + } else { + assertNull(fcStats); + } + + } + assertEquals(numNodes, nonEmptyFileCacheNodes); + } + + private boolean isFileCacheEmpty(FileCacheStats stats) { + return stats.getUsed().getBytes() == 0L && stats.getActive().getBytes() == 0L; + } + + public void testPruneFileCacheOnIndexDeletion() throws Exception { + final String snapshotName = "test-snap"; + final String repoName = "test-repo"; + final String indexName1 = "test-idx-1"; + final String restoredIndexName1 = indexName1 + "-copy"; + final Client client = client(); + final int numNodes = 2; + + internalCluster().ensureAtLeastNumSearchAndDataNodes(numNodes); + createIndexWithDocsAndEnsureGreen(1, 100, indexName1); + + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName1); + deleteIndicesAndEnsureGreen(client, indexName1); + + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertRemoteSnapshotIndexSettings(client, restoredIndexName1); + assertNodesFileCacheNonEmpty(numNodes); + + deleteIndicesAndEnsureGreen(client, restoredIndexName1); + assertAllNodesFileCacheEmpty(); + } + + /** + * Test scenario that checks the cache folder location on search nodes for the restored index on snapshot restoration + * and ensures the index folder is cleared on all nodes post index deletion + */ + public void testCacheIndexFilesClearedOnDelete() throws Exception { + final int numReplicas = randomIntBetween(1, 4); + final int numShards = numReplicas + 1; + final String indexName = "test-idx"; + final String restoredIndexName = indexName + "-copy"; + final String repoName = "test-repo"; + final String snapshotName = "test-snap"; + final Client client = client(); + + internalCluster().ensureAtLeastNumSearchAndDataNodes(numShards); + createIndexWithDocsAndEnsureGreen(numReplicas, 100, indexName); + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertDocCount(restoredIndexName, 100L); + assertRemoteSnapshotIndexSettings(client, restoredIndexName); + + // The index count will be 1 since there is only a single restored index "test-idx-copy" + assertCacheDirectoryReplicaAndIndexCount(numShards, 1); + + // The local cache files should be closed by deleting the restored index + deleteIndicesAndEnsureGreen(client, restoredIndexName); + + logger.info("--> validate cache file path is deleted"); + // The index count will be 0 since the only restored index "test-idx-copy" was deleted + assertCacheDirectoryReplicaAndIndexCount(numShards, 0); + logger.info("--> validated that the cache file path doesn't exist"); + } + + /** + * Test scenario that validates that the default search preference for searchable snapshot + * is primary shards + */ + public void testDefaultShardPreference() throws Exception { + final int numReplicas = 1; + final String indexName = "test-idx"; + final String restoredIndexName = indexName + "-copy"; + final String repoName = "test-repo"; + final String snapshotName = "test-snap"; + final Client client = client(); + + // Create an index, snapshot and restore as a searchable snapshot index + internalCluster().ensureAtLeastNumSearchAndDataNodes(numReplicas + 1); + createIndexWithDocsAndEnsureGreen(numReplicas, 100, indexName); + createRepositoryWithSettings(null, repoName); + takeSnapshot(client, snapshotName, repoName, indexName); + restoreSnapshotAndEnsureGreen(client, snapshotName, repoName); + assertDocCount(restoredIndexName, 100L); + assertRemoteSnapshotIndexSettings(client, restoredIndexName); + + // ClusterSearchShards API returns a list of shards that will be used + // when querying a particular index + ClusterSearchShardsGroup[] shardGroups = client.admin() + .cluster() + .searchShards(new ClusterSearchShardsRequest(restoredIndexName)) + .actionGet() + .getGroups(); + + // Ensure when no preferences are set (default preference), the only compatible shards are primary + for (ClusterSearchShardsGroup shardsGroup : shardGroups) { + assertEquals(1, shardsGroup.getShards().length); + assertTrue(shardsGroup.getShards()[0].primary()); + } + + // Ensure when preferences are set, all the compatible shards are returned + shardGroups = client.admin() + .cluster() + .searchShards(new ClusterSearchShardsRequest(restoredIndexName).preference("foo")) + .actionGet() + .getGroups(); + + // Ensures that the compatible shards are not just primaries + for (ClusterSearchShardsGroup shardsGroup : shardGroups) { + assertTrue(shardsGroup.getShards().length > 1); + boolean containsReplica = Arrays.stream(shardsGroup.getShards()) + .map(shardRouting -> !shardRouting.primary()) + .reduce(false, (s1, s2) -> s1 || s2); + assertTrue(containsReplica); + } + } + + /** + * Asserts the cache folder count to match the number of shards and the number of indices within the cache folder + * as provided. + * @param numCacheFolderCount total number of cache folders that should exist for the test case + * @param numIndexCount total number of index folder locations that should exist within the cache folder + */ + private void assertCacheDirectoryReplicaAndIndexCount(int numCacheFolderCount, int numIndexCount) throws IOException { + // Get the available NodeEnvironment instances + Iterable nodes = internalCluster().getInstances(Node.class); + + // Filter out search NodeEnvironment(s) since FileCache is initialized only on search nodes and + // collect the path for all the cache locations on search nodes. + List searchNodeFileCachePaths = StreamSupport.stream(nodes.spliterator(), false) + .filter(node -> node.fileCache() != null) + .map(node -> node.getNodeEnvironment().fileCacheNodePath().fileCachePath) + .collect(Collectors.toList()); + + // Walk through the cache directory on nodes + for (Path fileCachePath : searchNodeFileCachePaths) { + assertTrue(Files.exists(fileCachePath)); + assertTrue(Files.isDirectory(fileCachePath)); + try (Stream dataPathStream = Files.list(fileCachePath)) { + assertEquals(numIndexCount, dataPathStream.count()); + } + } + // Verifies if all the shards (primary and replica) have been deleted + assertEquals(numCacheFolderCount, searchNodeFileCachePaths.size()); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/SegmentReplicationSnapshotIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/SegmentReplicationSnapshotIT.java new file mode 100644 index 0000000000000..c2ce7e48f92d2 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/SegmentReplicationSnapshotIT.java @@ -0,0 +1,309 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.snapshots; + +import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; +import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequestBuilder; +import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; +import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; +import org.opensearch.action.admin.indices.settings.get.GetSettingsRequest; +import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.opensearch.action.search.SearchResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.index.Index; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.index.query.QueryBuilders; +import org.opensearch.indices.IndicesService; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.InternalTestCluster; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; +import static org.opensearch.indices.IndicesService.CLUSTER_SETTING_REPLICATION_TYPE; +import static org.opensearch.indices.replication.SegmentReplicationBaseIT.waitForSearchableDocs; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertHitCount; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SegmentReplicationSnapshotIT extends AbstractSnapshotIntegTestCase { + private static final String INDEX_NAME = "test-segrep-idx"; + private static final String RESTORED_INDEX_NAME = INDEX_NAME + "-restored"; + private static final int SHARD_COUNT = 1; + private static final int REPLICA_COUNT = 1; + private static final int DOC_COUNT = 1010; + + private static final String REPOSITORY_NAME = "test-segrep-repo"; + private static final String SNAPSHOT_NAME = "test-segrep-snapshot"; + + public Settings segRepEnableIndexSettings() { + return getShardSettings().put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT).build(); + } + + public Settings docRepEnableIndexSettings() { + return getShardSettings().put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT).build(); + } + + public Settings.Builder getShardSettings() { + return Settings.builder() + .put(super.indexSettings()) + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, SHARD_COUNT) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, REPLICA_COUNT); + } + + public Settings restoreIndexSegRepSettings() { + return Settings.builder().put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT).build(); + } + + public Settings restoreIndexDocRepSettings() { + return Settings.builder().put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT).build(); + } + + @Override + protected boolean addMockInternalEngine() { + return false; + } + + public void ingestData(int docCount, String indexName) throws Exception { + for (int i = 0; i < docCount; i++) { + client().prepareIndex(indexName).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + } + + // Start cluster with provided settings and return the node names as list + public List startClusterWithSettings(Settings indexSettings, int replicaCount) throws Exception { + // Start primary + final String primaryNode = internalCluster().startNode(); + List nodeNames = new ArrayList<>(); + nodeNames.add(primaryNode); + for (int i = 0; i < replicaCount; i++) { + nodeNames.add(internalCluster().startNode()); + } + createIndex(INDEX_NAME, indexSettings); + ensureGreen(INDEX_NAME); + // Ingest data + ingestData(DOC_COUNT, INDEX_NAME); + return nodeNames; + } + + public void createSnapshot() { + // Snapshot declaration + Path absolutePath = randomRepoPath().toAbsolutePath(); + // Create snapshot + createRepository(REPOSITORY_NAME, "fs", absolutePath); + CreateSnapshotResponse createSnapshotResponse = client().admin() + .cluster() + .prepareCreateSnapshot(REPOSITORY_NAME, SNAPSHOT_NAME) + .setWaitForCompletion(true) + .setIndices(INDEX_NAME) + .get(); + assertEquals(createSnapshotResponse.getSnapshotInfo().successfulShards(), createSnapshotResponse.getSnapshotInfo().totalShards()); + assertEquals(createSnapshotResponse.getSnapshotInfo().state(), SnapshotState.SUCCESS); + } + + public RestoreSnapshotResponse restoreSnapshotWithSettings(Settings indexSettings) { + RestoreSnapshotRequestBuilder builder = client().admin() + .cluster() + .prepareRestoreSnapshot(REPOSITORY_NAME, SNAPSHOT_NAME) + .setWaitForCompletion(false) + .setRenamePattern(INDEX_NAME) + .setRenameReplacement(RESTORED_INDEX_NAME); + if (indexSettings != null) { + builder.setIndexSettings(indexSettings); + } + return builder.get(); + } + + public void testRestoreOnSegRep() throws Exception { + // Start cluster with one primary and one replica node + startClusterWithSettings(segRepEnableIndexSettings(), 1); + createSnapshot(); + // Delete index + assertAcked(client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).get()); + assertFalse("index [" + INDEX_NAME + "] should have been deleted", indexExists(INDEX_NAME)); + + RestoreSnapshotResponse restoreSnapshotResponse = restoreSnapshotWithSettings(null); + + // Assertions + assertEquals(restoreSnapshotResponse.status(), RestStatus.ACCEPTED); + ensureGreen(RESTORED_INDEX_NAME); + GetSettingsResponse settingsResponse = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(RESTORED_INDEX_NAME)) + .get(); + assertEquals(settingsResponse.getSetting(RESTORED_INDEX_NAME, "index.replication.type"), "SEGMENT"); + SearchResponse resp = client().prepareSearch(RESTORED_INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).get(); + assertHitCount(resp, DOC_COUNT); + } + + public void testSnapshotOnSegRep_RestoreOnSegRepDuringIngestion() throws Exception { + List nodes = startClusterWithSettings(segRepEnableIndexSettings(), 1); + waitForSearchableDocs(INDEX_NAME, DOC_COUNT, nodes); + createSnapshot(); + // Delete index + assertAcked(client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).get()); + assertFalse("index [" + INDEX_NAME + "] should have been deleted", indexExists(INDEX_NAME)); + + RestoreSnapshotResponse restoreSnapshotResponse = restoreSnapshotWithSettings(null); + + // Assertions + assertEquals(restoreSnapshotResponse.status(), RestStatus.ACCEPTED); + assertBusy(() -> ensureGreen(RESTORED_INDEX_NAME), 60, TimeUnit.SECONDS); + final int docCountPostRestore = 1001; + final int totalDocCount = DOC_COUNT + docCountPostRestore; + for (int i = DOC_COUNT; i < totalDocCount; i++) { + client().prepareIndex(RESTORED_INDEX_NAME).setId(Integer.toString(i)).setSource("field", "value" + i).execute().actionGet(); + } + flushAndRefresh(RESTORED_INDEX_NAME); + assertBusy(() -> ensureGreen(RESTORED_INDEX_NAME), 60, TimeUnit.SECONDS); + waitForSearchableDocs(RESTORED_INDEX_NAME, totalDocCount, nodes); + GetSettingsResponse settingsResponse = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(RESTORED_INDEX_NAME)) + .get(); + assertEquals(settingsResponse.getSetting(RESTORED_INDEX_NAME, "index.replication.type"), "SEGMENT"); + SearchResponse resp = client().prepareSearch(RESTORED_INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).get(); + assertHitCount(resp, totalDocCount); + } + + public void testSnapshotOnDocRep_RestoreOnSegRep() throws Exception { + startClusterWithSettings(docRepEnableIndexSettings(), 1); + createSnapshot(); + // Delete index + assertAcked(client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).get()); + + RestoreSnapshotResponse restoreSnapshotResponse = restoreSnapshotWithSettings(restoreIndexSegRepSettings()); + + // Assertions + assertEquals(restoreSnapshotResponse.status(), RestStatus.ACCEPTED); + ensureGreen(RESTORED_INDEX_NAME); + GetSettingsResponse settingsResponse = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(RESTORED_INDEX_NAME)) + .get(); + assertEquals(settingsResponse.getSetting(RESTORED_INDEX_NAME, "index.replication.type"), "SEGMENT"); + + SearchResponse resp = client().prepareSearch(RESTORED_INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).get(); + assertHitCount(resp, DOC_COUNT); + } + + public void testSnapshotOnSegRep_RestoreOnDocRep() throws Exception { + // Start a cluster with one primary and one replica + startClusterWithSettings(segRepEnableIndexSettings(), 1); + createSnapshot(); + // Delete index + assertAcked(client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).get()); + + RestoreSnapshotResponse restoreSnapshotResponse = restoreSnapshotWithSettings(restoreIndexDocRepSettings()); + + // Assertions + assertEquals(restoreSnapshotResponse.status(), RestStatus.ACCEPTED); + ensureGreen(RESTORED_INDEX_NAME); + GetSettingsResponse settingsResponse = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(RESTORED_INDEX_NAME)) + .get(); + assertEquals(settingsResponse.getSetting(RESTORED_INDEX_NAME, "index.replication.type"), "DOCUMENT"); + SearchResponse resp = client().prepareSearch(RESTORED_INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).get(); + assertHitCount(resp, DOC_COUNT); + } + + public void testSnapshotOnDocRep_RestoreOnDocRep() throws Exception { + startClusterWithSettings(docRepEnableIndexSettings(), 1); + createSnapshot(); + // Delete index + assertAcked(client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).get()); + + RestoreSnapshotResponse restoreSnapshotResponse = restoreSnapshotWithSettings(restoreIndexDocRepSettings()); + + // Assertions + assertEquals(restoreSnapshotResponse.status(), RestStatus.ACCEPTED); + ensureGreen(RESTORED_INDEX_NAME); + GetSettingsResponse settingsResponse = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(RESTORED_INDEX_NAME)) + .get(); + assertEquals(settingsResponse.getSetting(RESTORED_INDEX_NAME, "index.replication.type"), "DOCUMENT"); + + SearchResponse resp = client().prepareSearch(RESTORED_INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).get(); + assertHitCount(resp, DOC_COUNT); + } + + public void testRestoreOnReplicaNode() throws Exception { + List nodeNames = startClusterWithSettings(segRepEnableIndexSettings(), 1); + final String primaryNode = nodeNames.get(0); + createSnapshot(); + // Delete index + assertAcked(client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).get()); + assertFalse("index [" + INDEX_NAME + "] should have been deleted", indexExists(INDEX_NAME)); + + // stop the primary node so that restoration happens on replica node + internalCluster().stopRandomNode(InternalTestCluster.nameFilter(primaryNode)); + + RestoreSnapshotResponse restoreSnapshotResponse = restoreSnapshotWithSettings(null); + + // Assertions + assertEquals(restoreSnapshotResponse.status(), RestStatus.ACCEPTED); + internalCluster().startNode(); + ensureGreen(RESTORED_INDEX_NAME); + GetSettingsResponse settingsResponse = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(RESTORED_INDEX_NAME)) + .get(); + assertEquals(settingsResponse.getSetting(RESTORED_INDEX_NAME, "index.replication.type"), "SEGMENT"); + SearchResponse resp = client().prepareSearch(RESTORED_INDEX_NAME).setQuery(QueryBuilders.matchAllQuery()).get(); + assertHitCount(resp, DOC_COUNT); + } + + public void testSnapshotRestoreOnIndexWithSegRepClusterSetting() throws Exception { + Settings settings = Settings.builder() + .put(super.featureFlagSettings()) + .put(CLUSTER_SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + + // Starting two nodes with primary and replica shards respectively. + final String primaryNode = internalCluster().startNode(settings); + prepareCreate( + INDEX_NAME, + Settings.builder() + // we want to override cluster replication setting by passing a index replication setting + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, REPLICA_COUNT) + ).get(); + ensureYellowAndNoInitializingShards(INDEX_NAME); + final String replicaNode = internalCluster().startNode(settings); + ensureGreen(INDEX_NAME); + + createSnapshot(); + // Delete index + assertAcked(client().admin().indices().delete(new DeleteIndexRequest(INDEX_NAME)).get()); + assertFalse("index [" + INDEX_NAME + "] should have been deleted", indexExists(INDEX_NAME)); + + RestoreSnapshotResponse restoreSnapshotResponse = restoreSnapshotWithSettings(null); + + // Assertions + assertEquals(restoreSnapshotResponse.status(), RestStatus.ACCEPTED); + ensureGreen(RESTORED_INDEX_NAME); + GetSettingsResponse settingsResponse = client().admin() + .indices() + .getSettings(new GetSettingsRequest().indices(RESTORED_INDEX_NAME).includeDefaults(true)) + .get(); + assertEquals(settingsResponse.getSetting(RESTORED_INDEX_NAME, SETTING_REPLICATION_TYPE), ReplicationType.DOCUMENT.toString()); + + // Verify index setting isSegRepEnabled. + Index index = resolveIndex(RESTORED_INDEX_NAME); + IndicesService indicesService = internalCluster().getInstance(IndicesService.class); + assertEquals(indicesService.indexService(index).getIndexSettings().isSegRepEnabled(), false); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/SharedClusterSnapshotRestoreIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/SharedClusterSnapshotRestoreIT.java index 88fcd075a563f..62cbf171e8146 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/SharedClusterSnapshotRestoreIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/SharedClusterSnapshotRestoreIT.java @@ -33,11 +33,9 @@ package org.opensearch.snapshots; import org.apache.lucene.util.BytesRef; - -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; +import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse; @@ -65,17 +63,18 @@ import org.opensearch.cluster.routing.ShardRoutingState; import org.opensearch.cluster.routing.UnassignedInfo; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Numbers; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.TimeValue; -import org.opensearch.index.Index; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.util.BytesRefUtils; import org.opensearch.index.IndexService; import org.opensearch.index.engine.Engine; import org.opensearch.index.engine.EngineTestCase; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.repositories.IndexId; import org.opensearch.repositories.RepositoriesService; @@ -588,7 +587,7 @@ public void testDataFileCorruptionDuringRestore() throws Exception { RestoreSnapshotResponse restoreSnapshotResponse = client.admin() .cluster() .prepareRestoreSnapshot("test-repo", "test-snap") - .setMasterNodeTimeout("30s") + .setClusterManagerNodeTimeout("30s") .setWaitForCompletion(true) .execute() .actionGet(); @@ -1916,7 +1915,7 @@ public void testSnapshotCanceledOnRemovedShard() throws Exception { waitForBlock(blockedNode, repo, TimeValue.timeValueSeconds(10)); logger.info("--> removing primary shard that is being snapshotted"); - ClusterState clusterState = internalCluster().clusterService(internalCluster().getMasterName()).state(); + ClusterState clusterState = internalCluster().clusterService(internalCluster().getClusterManagerName()).state(); IndexRoutingTable indexRoutingTable = clusterState.getRoutingTable().index(index); String nodeWithPrimary = clusterState.nodes().get(indexRoutingTable.shard(0).primaryShard().currentNodeId()).getName(); assertNotNull("should be at least one node with a primary shard", nodeWithPrimary); @@ -2368,13 +2367,13 @@ public void testIndexLatestFailuresIgnored() throws Exception { final String repoName = "test-repo"; final Path repoPath = randomRepoPath(); createRepository(repoName, "mock", repoPath); - final MockRepository repository = (MockRepository) internalCluster().getCurrentMasterNodeInstance(RepositoriesService.class) + final MockRepository repository = (MockRepository) internalCluster().getCurrentClusterManagerNodeInstance(RepositoriesService.class) .repository(repoName); repository.setFailOnIndexLatest(true); createFullSnapshot(repoName, "snapshot-1"); repository.setFailOnIndexLatest(false); createFullSnapshot(repoName, "snapshot-2"); - final long repoGenInIndexLatest = Numbers.bytesToLong( + final long repoGenInIndexLatest = BytesRefUtils.bytesToLong( new BytesRef(Files.readAllBytes(repoPath.resolve(BlobStoreRepository.INDEX_LATEST_BLOB))) ); assertEquals(getRepositoryData(repoName).getGenId(), repoGenInIndexLatest); @@ -2385,14 +2384,14 @@ public void testIndexLatestFailuresIgnored() throws Exception { Settings.builder().put("location", repoPath).put(BlobStoreRepository.SUPPORT_URL_REPO.getKey(), false) ); createFullSnapshot(repoName, "snapshot-3"); - final long repoGenInIndexLatest2 = Numbers.bytesToLong( + final long repoGenInIndexLatest2 = BytesRefUtils.bytesToLong( new BytesRef(Files.readAllBytes(repoPath.resolve(BlobStoreRepository.INDEX_LATEST_BLOB))) ); assertEquals("index.latest should not have been written to", repoGenInIndexLatest, repoGenInIndexLatest2); createRepository(repoName, "fs", repoPath); createFullSnapshot(repoName, "snapshot-4"); - final long repoGenInIndexLatest3 = Numbers.bytesToLong( + final long repoGenInIndexLatest3 = BytesRefUtils.bytesToLong( new BytesRef(Files.readAllBytes(repoPath.resolve(BlobStoreRepository.INDEX_LATEST_BLOB))) ); assertEquals(getRepositoryData(repoName).getGenId(), repoGenInIndexLatest3); diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotCustomPluginStateIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotCustomPluginStateIT.java index ea1635b1d8053..b2dcab61c05cb 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotCustomPluginStateIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotCustomPluginStateIT.java @@ -40,10 +40,10 @@ import org.opensearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.opensearch.action.ingest.DeletePipelineRequest; import org.opensearch.action.ingest.GetPipelineResponse; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.ingest.IngestTestPlugin; import org.opensearch.plugins.Plugin; import org.opensearch.script.MockScriptEngine; @@ -115,7 +115,7 @@ public void testIncludeGlobalState() throws Exception { .endArray() .endObject() ); - assertAcked(clusterAdmin().preparePutPipeline("barbaz", pipelineSource, XContentType.JSON).get()); + assertAcked(clusterAdmin().preparePutPipeline("barbaz", pipelineSource, MediaTypeRegistry.JSON).get()); } if (testScript) { @@ -125,7 +125,7 @@ public void testIncludeGlobalState() throws Exception { .setId("foobar") .setContent( new BytesArray("{\"script\": { \"lang\": \"" + MockScriptEngine.NAME + "\", \"source\": \"1\"} }"), - XContentType.JSON + MediaTypeRegistry.JSON ) ); } diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotShardsServiceIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotShardsServiceIT.java index 762656e251659..ef7c61205855e 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotShardsServiceIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotShardsServiceIT.java @@ -60,7 +60,7 @@ protected Collection> nodePlugins() { } public void testRetryPostingSnapshotStatusMessages() throws Exception { - internalCluster().startMasterOnlyNode(); + internalCluster().startClusterManagerOnlyNode(); internalCluster().startDataOnlyNode(); createRepository("test-repo", "mock"); @@ -83,7 +83,7 @@ public void testRetryPostingSnapshotStatusMessages() throws Exception { final SnapshotId snapshotId = getSnapshot("test-repo", "test-snap").snapshotId(); logger.info("--> start disrupting cluster"); - final NetworkDisruption networkDisruption = isolateMasterDisruption(NetworkDisruption.NetworkDelay.random(random())); + final NetworkDisruption networkDisruption = isolateClusterManagerDisruption(NetworkDisruption.NetworkDelay.random(random())); internalCluster().setDisruptionScheme(networkDisruption); networkDisruption.startDisrupting(); diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotStatusApisIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotStatusApisIT.java index 6c697439b241d..c574233d25051 100644 --- a/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotStatusApisIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/SnapshotStatusApisIT.java @@ -33,7 +33,6 @@ package org.opensearch.snapshots; import org.opensearch.Version; -import org.opensearch.action.ActionFuture; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotResponse; import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest; import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse; @@ -45,11 +44,12 @@ import org.opensearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse; import org.opensearch.client.Client; import org.opensearch.cluster.SnapshotsInProgress; -import org.opensearch.common.Strings; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; import org.opensearch.common.unit.TimeValue; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.unit.ByteSizeUnit; import org.opensearch.repositories.blobstore.BlobStoreRepository; import org.opensearch.threadpool.ThreadPool; @@ -110,6 +110,37 @@ public void testStatusApiConsistency() { assertEquals(snStatus.getStats().getTime(), snapshotInfo.endTime() - snapshotInfo.startTime()); } + public void testStatusAPICallForShallowCopySnapshot() { + disableRepoConsistencyCheck("Remote store repository is being used for the test"); + internalCluster().startClusterManagerOnlyNode(); + internalCluster().startDataOnlyNode(); + + final String snapshotRepoName = "snapshot-repo-name"; + createRepository(snapshotRepoName, "fs", snapshotRepoSettingsForShallowCopy()); + + final String indexName = "index-1"; + createIndex(indexName); + ensureGreen(); + logger.info("--> indexing some data"); + for (int i = 0; i < 100; i++) { + index(indexName, "_doc", Integer.toString(i), "foo", "bar" + i); + } + refresh(); + + final String snapshot = "snapshot"; + createFullSnapshot(snapshotRepoName, snapshot); + + final SnapshotStatus snapshotStatus = getSnapshotStatus(snapshotRepoName, snapshot); + assertThat(snapshotStatus.getState(), is(SnapshotsInProgress.State.SUCCESS)); + + final SnapshotIndexShardStatus snapshotShardState = stateFirstShard(snapshotStatus, indexName); + assertThat(snapshotShardState.getStage(), is(SnapshotIndexShardStage.DONE)); + assertThat(snapshotShardState.getStats().getTotalFileCount(), greaterThan(0)); + assertThat(snapshotShardState.getStats().getTotalSize(), greaterThan(0L)); + assertThat(snapshotShardState.getStats().getIncrementalFileCount(), greaterThan(0)); + assertThat(snapshotShardState.getStats().getIncrementalSize(), greaterThan(0L)); + } + public void testStatusAPICallInProgressSnapshot() throws Exception { createRepository("test-repo", "mock", Settings.builder().put("location", randomRepoPath()).put("block_on_data", true)); @@ -237,8 +268,8 @@ public void testCorrectCountsForDoneShards() throws Exception { blockDataNode(repoName, dataNodeOne); final String snapshotOne = "snap-1"; - // restarting a data node below so using a master client here - final ActionFuture responseSnapshotOne = internalCluster().masterClient() + // restarting a data node below so using a cluster-manager client here + final ActionFuture responseSnapshotOne = internalCluster().clusterManagerClient() .admin() .cluster() .prepareCreateSnapshot(repoName, snapshotOne) @@ -326,6 +357,47 @@ public void testSnapshotStatusOnFailedSnapshot() throws Exception { assertEquals(SnapshotsInProgress.State.FAILED, snapshotsStatusResponse.getSnapshots().get(0).getState()); } + public void testStatusAPICallInProgressShallowSnapshot() throws Exception { + internalCluster().startClusterManagerOnlyNode(); + internalCluster().startDataOnlyNode(); + + final String snapshotRepoName = "snapshot-repo-name"; + createRepository(snapshotRepoName, "mock", snapshotRepoSettingsForShallowCopy().put("block_on_data", true)); + + final String indexName = "index-1"; + createIndex(indexName); + ensureGreen(); + logger.info("--> indexing some data"); + for (int i = 0; i < 100; i++) { + index(indexName, "_doc", Integer.toString(i), "foo", "bar" + i); + } + refresh(); + + logger.info("--> snapshot"); + ActionFuture createSnapshotResponseActionFuture = startFullSnapshot(snapshotRepoName, "test-snap"); + + logger.info("--> wait for data nodes to get blocked"); + waitForBlockOnAnyDataNode(snapshotRepoName, TimeValue.timeValueMinutes(1)); + awaitNumberOfSnapshotsInProgress(1); + assertEquals( + SnapshotsInProgress.State.STARTED, + client().admin() + .cluster() + .prepareSnapshotStatus(snapshotRepoName) + .setSnapshots("test-snap") + .get() + .getSnapshots() + .get(0) + .getState() + ); + + logger.info("--> unblock all data nodes"); + unblockAllDataNodes(snapshotRepoName); + + logger.info("--> wait for snapshot to finish"); + createSnapshotResponseActionFuture.actionGet(); + } + public void testGetSnapshotsRequest() throws Exception { final String repositoryName = "test-repo"; final String indexName = "test-idx"; @@ -379,6 +451,17 @@ public void testGetSnapshotsRequest() throws Exception { .get(); assertEquals(1, getSnapshotsResponse.getSnapshots().size()); assertEquals("snap-on-empty-repo", getSnapshotsResponse.getSnapshots().get(0).snapshotId().getName()); + + // there is an in-progress snapshot, make sure we return empty result when getting a non-existing snapshot with setting + // ignore_unavailable to true + getSnapshotsResponse = client.admin() + .cluster() + .prepareGetSnapshots("test-repo") + .setIgnoreUnavailable(true) + .addSnapshots("non-existent-snapshot") + .get(); + assertEquals(0, getSnapshotsResponse.getSnapshots().size()); + unblockNode(repositoryName, initialBlockedNode); // unblock node admin().cluster().prepareDeleteSnapshot(repositoryName, "snap-on-empty-repo").get(); diff --git a/server/src/internalClusterTest/java/org/opensearch/snapshots/SystemRepositoryIT.java b/server/src/internalClusterTest/java/org/opensearch/snapshots/SystemRepositoryIT.java new file mode 100644 index 0000000000000..f50fc691fb232 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/snapshots/SystemRepositoryIT.java @@ -0,0 +1,71 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.snapshots; + +import org.opensearch.client.Client; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.repositories.RepositoryException; +import org.opensearch.repositories.fs.FsRepository; +import org.opensearch.test.OpenSearchIntegTestCase; +import org.junit.Before; + +import java.nio.file.Path; + +import static org.opensearch.remotestore.RemoteStoreBaseIntegTestCase.remoteStoreClusterSettings; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) +public class SystemRepositoryIT extends AbstractSnapshotIntegTestCase { + protected Path absolutePath; + final String systemRepoName = "system-repo-name"; + + @Before + public void setup() { + absolutePath = randomRepoPath().toAbsolutePath(); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + return Settings.builder() + .put(super.nodeSettings(nodeOrdinal)) + .put(remoteStoreClusterSettings(systemRepoName, absolutePath)) + .build(); + } + + public void testRestrictedSettingsCantBeUpdated() { + disableRepoConsistencyCheck("System repository is being used for the test"); + + internalCluster().startNode(); + final Client client = client(); + final Settings.Builder repoSettings = Settings.builder().put("location", randomRepoPath()); + + RepositoryException e = expectThrows( + RepositoryException.class, + () -> client.admin().cluster().preparePutRepository(systemRepoName).setType("mock").setSettings(repoSettings).get() + ); + assertEquals( + e.getMessage(), + "[system-repo-name] trying to modify an unmodifiable attribute type of system " + + "repository from current value [fs] to new value [mock]" + ); + } + + public void testSystemRepositoryNonRestrictedSettingsCanBeUpdated() { + disableRepoConsistencyCheck("System repository is being used for the test"); + + internalCluster().startNode(); + final Client client = client(); + final Settings.Builder repoSettings = Settings.builder().put("location", absolutePath).put("chunk_size", new ByteSizeValue(20)); + + assertAcked( + client.admin().cluster().preparePutRepository(systemRepoName).setType(FsRepository.TYPE).setSettings(repoSettings).get() + ); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/update/UpdateIT.java b/server/src/internalClusterTest/java/org/opensearch/update/UpdateIT.java index 16b0e8829b1a7..b46d27bafb2a5 100644 --- a/server/src/internalClusterTest/java/org/opensearch/update/UpdateIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/update/UpdateIT.java @@ -33,7 +33,6 @@ package org.opensearch.update; import org.opensearch.OpenSearchTimeoutException; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.admin.indices.alias.Alias; @@ -49,16 +48,17 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.index.MergePolicyConfig; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.index.MergePolicyProvider; import org.opensearch.index.engine.DocumentMissingException; import org.opensearch.index.engine.VersionConflictEngineException; import org.opensearch.plugins.Plugin; -import org.opensearch.rest.RestStatus; import org.opensearch.script.MockScriptPlugin; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; -import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.InternalSettingsPlugin; +import org.opensearch.test.OpenSearchIntegTestCase; import java.util.ArrayList; import java.util.Arrays; @@ -669,7 +669,7 @@ public void run() { public void testStressUpdateDeleteConcurrency() throws Exception { // We create an index with merging disabled so that deletes don't get merged away - assertAcked(prepareCreate("test").setSettings(Settings.builder().put(MergePolicyConfig.INDEX_MERGE_ENABLED, false))); + assertAcked(prepareCreate("test").setSettings(Settings.builder().put(MergePolicyProvider.INDEX_MERGE_ENABLED, false))); ensureGreen(); Script fieldIncScript = new Script(ScriptType.INLINE, UPDATE_SCRIPTS, FIELD_INC_SCRIPT, Collections.singletonMap("field", "field")); diff --git a/server/src/internalClusterTest/java/org/opensearch/update/UpdateNoopIT.java b/server/src/internalClusterTest/java/org/opensearch/update/UpdateNoopIT.java index 606a5fe1b7eca..8d66cf998cd7f 100644 --- a/server/src/internalClusterTest/java/org/opensearch/update/UpdateNoopIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/update/UpdateNoopIT.java @@ -34,9 +34,9 @@ import org.opensearch.action.update.UpdateRequestBuilder; import org.opensearch.action.update.UpdateResponse; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.test.OpenSearchIntegTestCase; import org.junit.Before; diff --git a/server/src/internalClusterTest/java/org/opensearch/validate/SimpleValidateQueryIT.java b/server/src/internalClusterTest/java/org/opensearch/validate/SimpleValidateQueryIT.java index 86a51e7367ade..e619b93cdd93b 100644 --- a/server/src/internalClusterTest/java/org/opensearch/validate/SimpleValidateQueryIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/validate/SimpleValidateQueryIT.java @@ -34,10 +34,10 @@ import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.validate.query.ValidateQueryResponse; import org.opensearch.client.Client; -import org.opensearch.common.bytes.BytesArray; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.Fuzziness; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.bytes.BytesArray; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.MoreLikeThisQueryBuilder.Item; @@ -48,7 +48,6 @@ import org.opensearch.test.OpenSearchIntegTestCase; import org.opensearch.test.OpenSearchIntegTestCase.ClusterScope; import org.opensearch.test.OpenSearchIntegTestCase.Scope; - import org.hamcrest.Matcher; import java.io.IOException; diff --git a/server/src/internalClusterTest/java/org/opensearch/versioning/ConcurrentDocumentOperationIT.java b/server/src/internalClusterTest/java/org/opensearch/versioning/ConcurrentDocumentOperationIT.java index e433a489ad572..9c0a1fa26098c 100644 --- a/server/src/internalClusterTest/java/org/opensearch/versioning/ConcurrentDocumentOperationIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/versioning/ConcurrentDocumentOperationIT.java @@ -32,9 +32,9 @@ package org.opensearch.versioning; -import org.opensearch.action.ActionListener; import org.opensearch.action.index.IndexResponse; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; import org.opensearch.test.OpenSearchIntegTestCase; import java.util.Map; @@ -77,9 +77,9 @@ public void onFailure(Exception e) { client().admin().indices().prepareRefresh().execute().actionGet(); logger.info("done indexing, check all have the same field value"); - Map masterSource = client().prepareGet("test", "1").execute().actionGet().getSourceAsMap(); + Map clusterManagerSource = client().prepareGet("test", "1").execute().actionGet().getSourceAsMap(); for (int i = 0; i < (cluster().size() * 5); i++) { - assertThat(client().prepareGet("test", "1").execute().actionGet().getSourceAsMap(), equalTo(masterSource)); + assertThat(client().prepareGet("test", "1").execute().actionGet().getSourceAsMap(), equalTo(clusterManagerSource)); } } } diff --git a/server/src/internalClusterTest/java/org/opensearch/versioning/ConcurrentSeqNoVersioningIT.java b/server/src/internalClusterTest/java/org/opensearch/versioning/ConcurrentSeqNoVersioningIT.java index 2194152284d37..c651689e21d3d 100644 --- a/server/src/internalClusterTest/java/org/opensearch/versioning/ConcurrentSeqNoVersioningIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/versioning/ConcurrentSeqNoVersioningIT.java @@ -38,15 +38,15 @@ import org.opensearch.cluster.coordination.LinearizabilityChecker; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.SuppressForbidden; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.InputStreamStreamInput; -import org.opensearch.common.io.stream.NamedWriteable; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.InputStreamStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteable; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.discovery.AbstractDisruptionTestCase; import org.opensearch.index.engine.VersionConflictEngineException; import org.opensearch.test.OpenSearchIntegTestCase; diff --git a/server/src/internalClusterTest/java/org/opensearch/versioning/SimpleVersioningIT.java b/server/src/internalClusterTest/java/org/opensearch/versioning/SimpleVersioningIT.java index 5898bba9762ad..8cd7b419f7989 100644 --- a/server/src/internalClusterTest/java/org/opensearch/versioning/SimpleVersioningIT.java +++ b/server/src/internalClusterTest/java/org/opensearch/versioning/SimpleVersioningIT.java @@ -32,7 +32,6 @@ package org.opensearch.versioning; import org.apache.lucene.tests.util.TestUtil; -import org.opensearch.action.ActionResponse; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.bulk.BulkResponse; @@ -43,6 +42,7 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionResponse; import org.opensearch.index.VersionType; import org.opensearch.index.engine.VersionConflictEngineException; import org.opensearch.test.OpenSearchIntegTestCase; diff --git a/server/src/main/java/org/apache/lucene/analysis/miscellaneous/DisableGraphAttribute.java b/server/src/main/java/org/apache/lucene/analysis/miscellaneous/DisableGraphAttribute.java index 01058afa6db96..089d4c1dcfac2 100644 --- a/server/src/main/java/org/apache/lucene/analysis/miscellaneous/DisableGraphAttribute.java +++ b/server/src/main/java/org/apache/lucene/analysis/miscellaneous/DisableGraphAttribute.java @@ -33,8 +33,8 @@ package org.apache.lucene.analysis.miscellaneous; import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.util.Attribute; import org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute; +import org.apache.lucene.util.Attribute; /** * This attribute can be used to indicate that the {@link PositionLengthAttribute} diff --git a/server/src/main/java/org/apache/lucene/queries/BinaryDocValuesRangeQuery.java b/server/src/main/java/org/apache/lucene/queries/BinaryDocValuesRangeQuery.java index a2c59de7832d4..963044a3f58d4 100644 --- a/server/src/main/java/org/apache/lucene/queries/BinaryDocValuesRangeQuery.java +++ b/server/src/main/java/org/apache/lucene/queries/BinaryDocValuesRangeQuery.java @@ -45,7 +45,7 @@ import org.apache.lucene.search.TwoPhaseIterator; import org.apache.lucene.search.Weight; import org.apache.lucene.util.BytesRef; -import org.opensearch.common.io.stream.BytesStreamInput; +import org.opensearch.core.common.io.stream.BytesStreamInput; import org.opensearch.index.mapper.RangeType; import java.io.IOException; diff --git a/server/src/main/java/org/apache/lucene/queries/BlendedTermQuery.java b/server/src/main/java/org/apache/lucene/queries/BlendedTermQuery.java index 3a36a6ff103e0..345be330f048c 100644 --- a/server/src/main/java/org/apache/lucene/queries/BlendedTermQuery.java +++ b/server/src/main/java/org/apache/lucene/queries/BlendedTermQuery.java @@ -35,13 +35,14 @@ import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermStates; import org.apache.lucene.index.TermState; +import org.apache.lucene.index.TermStates; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.DisjunctionMaxQuery; +import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.TermQuery; @@ -93,11 +94,12 @@ public BlendedTermQuery(Term[] terms, float[] boosts) { } @Override - public Query rewrite(IndexReader reader) throws IOException { - Query rewritten = super.rewrite(reader); + public Query rewrite(IndexSearcher searcher) throws IOException { + Query rewritten = super.rewrite(searcher); if (rewritten != this) { return rewritten; } + IndexReader reader = searcher.getIndexReader(); IndexReaderContext context = reader.getContext(); TermStates[] ctx = new TermStates[terms.length]; int[] docFreqs = new int[ctx.length]; diff --git a/server/src/main/java/org/apache/lucene/search/grouping/CollapseTopFieldDocs.java b/server/src/main/java/org/apache/lucene/search/grouping/CollapseTopFieldDocs.java index fe26c313d72b2..e93e5cdcc3f7b 100644 --- a/server/src/main/java/org/apache/lucene/search/grouping/CollapseTopFieldDocs.java +++ b/server/src/main/java/org/apache/lucene/search/grouping/CollapseTopFieldDocs.java @@ -39,7 +39,7 @@ import org.apache.lucene.search.TopFieldDocs; import org.apache.lucene.search.TotalHits; import org.apache.lucene.util.PriorityQueue; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.util.CollectionUtils; import java.util.ArrayList; import java.util.HashSet; diff --git a/server/src/main/java/org/apache/lucene/search/uhighlight/CustomUnifiedHighlighter.java b/server/src/main/java/org/apache/lucene/search/uhighlight/CustomUnifiedHighlighter.java index fb22eb583d9e1..cd4ee121f0f29 100644 --- a/server/src/main/java/org/apache/lucene/search/uhighlight/CustomUnifiedHighlighter.java +++ b/server/src/main/java/org/apache/lucene/search/uhighlight/CustomUnifiedHighlighter.java @@ -43,7 +43,6 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.uhighlight.UnifiedHighlighter.HighlightFlag; import org.apache.lucene.util.BytesRef; import org.opensearch.common.CheckedSupplier; import org.opensearch.common.Nullable; @@ -79,6 +78,7 @@ public class CustomUnifiedHighlighter extends UnifiedHighlighter { private final int noMatchSize; private final FieldHighlighter fieldHighlighter; private final int maxAnalyzedOffset; + private final Integer fieldMaxAnalyzedOffset; /** * Creates a new instance of {@link CustomUnifiedHighlighter} @@ -99,6 +99,7 @@ public class CustomUnifiedHighlighter extends UnifiedHighlighter { * @param fieldMatcher decides which terms should be highlighted * @param maxAnalyzedOffset if the field is more than this long we'll refuse to use the ANALYZED * offset source for it because it'd be super slow + * @param fieldMaxAnalyzedOffset this is used to limit the length of input that will be ANALYZED, this allows bigger fields to be partially highligthed */ public CustomUnifiedHighlighter( IndexSearcher searcher, @@ -113,7 +114,8 @@ public CustomUnifiedHighlighter( int noMatchSize, int maxPassages, Predicate fieldMatcher, - int maxAnalyzedOffset + int maxAnalyzedOffset, + Integer fieldMaxAnalyzedOffset ) throws IOException { super(searcher, analyzer); this.offsetSource = offsetSource; @@ -126,6 +128,7 @@ public CustomUnifiedHighlighter( this.setFieldMatcher(fieldMatcher); this.maxAnalyzedOffset = maxAnalyzedOffset; fieldHighlighter = getFieldHighlighter(field, query, extractTerms(query), maxPassages); + this.fieldMaxAnalyzedOffset = fieldMaxAnalyzedOffset; } /** @@ -141,7 +144,21 @@ public Snippet[] highlightField(LeafReader reader, int docId, CheckedSupplier maxAnalyzedOffset)) { + + if (fieldMaxAnalyzedOffset != null && fieldMaxAnalyzedOffset > maxAnalyzedOffset) { + throw new IllegalArgumentException( + "max_analyzer_offset has exceeded [" + + maxAnalyzedOffset + + "] - maximum allowed to be analyzed for highlighting. " + + "This maximum can be set by changing the [" + + IndexSettings.MAX_ANALYZED_OFFSET_SETTING.getKey() + + "] index level setting. " + + "For large texts, indexing with offsets or term vectors is recommended!" + ); + } + // if fieldMaxAnalyzedOffset is not defined + // and if this happens we should fallback to the previous behavior + if ((offsetSource == OffsetSource.ANALYSIS) && (fieldValueLength > maxAnalyzedOffset && fieldMaxAnalyzedOffset == null)) { throw new IllegalArgumentException( "The length of [" + field diff --git a/server/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java b/server/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java index ac688f15cda01..b8070de81bb3d 100644 --- a/server/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java +++ b/server/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java @@ -38,6 +38,7 @@ import org.apache.lucene.queries.spans.SpanTermQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; @@ -66,45 +67,45 @@ public CustomFieldQuery(Query query, IndexReader reader, boolean phraseHighlight } @Override - protected void flatten(Query sourceQuery, IndexReader reader, Collection flatQueries, float boost) throws IOException { + protected void flatten(Query sourceQuery, IndexSearcher searcher, Collection flatQueries, float boost) throws IOException { if (sourceQuery instanceof BoostQuery) { BoostQuery bq = (BoostQuery) sourceQuery; sourceQuery = bq.getQuery(); boost *= bq.getBoost(); - flatten(sourceQuery, reader, flatQueries, boost); + flatten(sourceQuery, searcher, flatQueries, boost); } else if (sourceQuery instanceof SpanTermQuery) { - super.flatten(new TermQuery(((SpanTermQuery) sourceQuery).getTerm()), reader, flatQueries, boost); + super.flatten(new TermQuery(((SpanTermQuery) sourceQuery).getTerm()), searcher, flatQueries, boost); } else if (sourceQuery instanceof ConstantScoreQuery) { - flatten(((ConstantScoreQuery) sourceQuery).getQuery(), reader, flatQueries, boost); + flatten(((ConstantScoreQuery) sourceQuery).getQuery(), searcher, flatQueries, boost); } else if (sourceQuery instanceof FunctionScoreQuery) { - flatten(((FunctionScoreQuery) sourceQuery).getSubQuery(), reader, flatQueries, boost); + flatten(((FunctionScoreQuery) sourceQuery).getSubQuery(), searcher, flatQueries, boost); } else if (sourceQuery instanceof MultiPhrasePrefixQuery) { - flatten(sourceQuery.rewrite(reader), reader, flatQueries, boost); + flatten(sourceQuery.rewrite(searcher), searcher, flatQueries, boost); } else if (sourceQuery instanceof MultiPhraseQuery) { MultiPhraseQuery q = ((MultiPhraseQuery) sourceQuery); - convertMultiPhraseQuery(0, new int[q.getTermArrays().length], q, q.getTermArrays(), q.getPositions(), reader, flatQueries); + convertMultiPhraseQuery(0, new int[q.getTermArrays().length], q, q.getTermArrays(), q.getPositions(), searcher, flatQueries); } else if (sourceQuery instanceof BlendedTermQuery) { final BlendedTermQuery blendedTermQuery = (BlendedTermQuery) sourceQuery; - flatten(blendedTermQuery.rewrite(reader), reader, flatQueries, boost); + flatten(blendedTermQuery.rewrite(searcher), searcher, flatQueries, boost); } else if (sourceQuery instanceof org.apache.lucene.queries.function.FunctionScoreQuery) { org.apache.lucene.queries.function.FunctionScoreQuery funcScoreQuery = (org.apache.lucene.queries.function.FunctionScoreQuery) sourceQuery; // flatten query with query boost - flatten(funcScoreQuery.getWrappedQuery(), reader, flatQueries, boost); + flatten(funcScoreQuery.getWrappedQuery(), searcher, flatQueries, boost); } else if (sourceQuery instanceof SynonymQuery) { // SynonymQuery should be handled by the parent class directly. // This statement should be removed when https://issues.apache.org/jira/browse/LUCENE-7484 is merged. SynonymQuery synQuery = (SynonymQuery) sourceQuery; for (Term term : synQuery.getTerms()) { - flatten(new TermQuery(term), reader, flatQueries, boost); + flatten(new TermQuery(term), searcher, flatQueries, boost); } } else if (sourceQuery instanceof OpenSearchToParentBlockJoinQuery) { Query childQuery = ((OpenSearchToParentBlockJoinQuery) sourceQuery).getChildQuery(); if (childQuery != null) { - flatten(childQuery, reader, flatQueries, boost); + flatten(childQuery, searcher, flatQueries, boost); } } else { - super.flatten(sourceQuery, reader, flatQueries, boost); + super.flatten(sourceQuery, searcher, flatQueries, boost); } } @@ -114,7 +115,7 @@ private void convertMultiPhraseQuery( MultiPhraseQuery orig, Term[][] terms, int[] pos, - IndexReader reader, + IndexSearcher searcher, Collection flatQueries ) throws IOException { if (currentPos == 0) { @@ -126,7 +127,7 @@ private void convertMultiPhraseQuery( if (numTerms > 16) { for (Term[] currentPosTerm : terms) { for (Term term : currentPosTerm) { - super.flatten(new TermQuery(term), reader, flatQueries, 1F); + super.flatten(new TermQuery(term), searcher, flatQueries, 1F); } } return; @@ -143,12 +144,12 @@ private void convertMultiPhraseQuery( queryBuilder.add(terms[i][termsIdx[i]], pos[i]); } Query query = queryBuilder.build(); - this.flatten(query, reader, flatQueries, 1F); + this.flatten(query, searcher, flatQueries, 1F); } else { Term[] t = terms[currentPos]; for (int i = 0; i < t.length; i++) { termsIdx[currentPos] = i; - convertMultiPhraseQuery(currentPos + 1, termsIdx, orig, terms, pos, reader, flatQueries); + convertMultiPhraseQuery(currentPos + 1, termsIdx, orig, terms, pos, searcher, flatQueries); } } } diff --git a/server/src/main/java/org/apache/lucene/util/SPIClassIterator.java b/server/src/main/java/org/apache/lucene/util/SPIClassIterator.java index 1480c9aeeb2d8..1dea54bcf0176 100644 --- a/server/src/main/java/org/apache/lucene/util/SPIClassIterator.java +++ b/server/src/main/java/org/apache/lucene/util/SPIClassIterator.java @@ -30,9 +30,9 @@ package org.apache.lucene.util; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; diff --git a/server/src/main/java/org/apache/lucene/util/packed/XPacked64.java b/server/src/main/java/org/apache/lucene/util/packed/XPacked64.java index d811b245606ba..4777b77cfbfed 100644 --- a/server/src/main/java/org/apache/lucene/util/packed/XPacked64.java +++ b/server/src/main/java/org/apache/lucene/util/packed/XPacked64.java @@ -29,11 +29,12 @@ */ package org.apache.lucene.util.packed; -import java.io.IOException; -import java.util.Arrays; import org.apache.lucene.store.DataInput; import org.apache.lucene.util.RamUsageEstimator; +import java.io.IOException; +import java.util.Arrays; + /** * Forked from Lucene 8.x; removed in Lucene 9.0 * diff --git a/server/src/main/java/org/apache/lucene/util/packed/XPacked64SingleBlock.java b/server/src/main/java/org/apache/lucene/util/packed/XPacked64SingleBlock.java index ef7644c32a843..0324522e9a68d 100644 --- a/server/src/main/java/org/apache/lucene/util/packed/XPacked64SingleBlock.java +++ b/server/src/main/java/org/apache/lucene/util/packed/XPacked64SingleBlock.java @@ -16,11 +16,12 @@ */ package org.apache.lucene.util.packed; -import java.io.IOException; -import java.util.Arrays; import org.apache.lucene.store.DataInput; import org.apache.lucene.util.RamUsageEstimator; +import java.io.IOException; +import java.util.Arrays; + /** * Forked from Lucene 8.x; removed in Lucene 9.0 * diff --git a/server/src/main/java/org/apache/lucene/util/packed/XPackedInts.java b/server/src/main/java/org/apache/lucene/util/packed/XPackedInts.java index 9a277a7b5f2f4..f94a4531a7db9 100644 --- a/server/src/main/java/org/apache/lucene/util/packed/XPackedInts.java +++ b/server/src/main/java/org/apache/lucene/util/packed/XPackedInts.java @@ -16,9 +16,6 @@ */ package org.apache.lucene.util.packed; -import java.io.EOFException; -import java.io.IOException; -import java.util.Arrays; import org.apache.lucene.codecs.CodecUtil; import org.apache.lucene.store.DataInput; import org.apache.lucene.store.DataOutput; @@ -32,6 +29,10 @@ import org.apache.lucene.util.packed.PackedInts.ReaderIterator; import org.apache.lucene.util.packed.PackedInts.Writer; +import java.io.EOFException; +import java.io.IOException; +import java.util.Arrays; + /** * Forked from Lucene 8.x; removed in Lucene 8.9 * diff --git a/server/src/main/java/org/opensearch/OpenSearchCorruptionException.java b/server/src/main/java/org/opensearch/OpenSearchCorruptionException.java index ecca2191295a4..eb012b209c7de 100644 --- a/server/src/main/java/org/opensearch/OpenSearchCorruptionException.java +++ b/server/src/main/java/org/opensearch/OpenSearchCorruptionException.java @@ -36,6 +36,8 @@ /** * This exception is thrown when OpenSearch detects * an inconsistency in one of it's persistent files. + * + * @opensearch.internal */ public class OpenSearchCorruptionException extends IOException { diff --git a/server/src/main/java/org/opensearch/OpenSearchException.java b/server/src/main/java/org/opensearch/OpenSearchException.java deleted file mode 100644 index 8d3e2569957f1..0000000000000 --- a/server/src/main/java/org/opensearch/OpenSearchException.java +++ /dev/null @@ -1,1741 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch; - -import org.opensearch.action.support.replication.ReplicationOperation; -import org.opensearch.cluster.action.shard.ShardStateAction; -import org.opensearch.common.CheckedFunction; -import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.logging.LoggerMessageFormat; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParseException; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.index.Index; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; -import org.opensearch.search.SearchException; -import org.opensearch.search.aggregations.MultiBucketConsumerService; -import org.opensearch.transport.TcpTransport; - -import java.io.IOException; -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.Set; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; -import static java.util.Collections.unmodifiableMap; -import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_UUID_NA_VALUE; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureFieldName; - -/** - * A base class for all opensearch exceptions. - */ -public class OpenSearchException extends RuntimeException implements ToXContentFragment, Writeable { - - private static final Version UNKNOWN_VERSION_ADDED = Version.fromId(0); - - /** - * Passed in the {@link Params} of {@link #generateThrowableXContent(XContentBuilder, Params, Throwable)} - * to control if the {@code caused_by} element should render. Unlike most parameters to {@code toXContent} methods this parameter is - * internal only and not available as a URL parameter. - */ - private static final String REST_EXCEPTION_SKIP_CAUSE = "rest.exception.cause.skip"; - /** - * Passed in the {@link Params} of {@link #generateThrowableXContent(XContentBuilder, Params, Throwable)} - * to control if the {@code stack_trace} element should render. Unlike most parameters to {@code toXContent} methods this parameter is - * internal only and not available as a URL parameter. Use the {@code error_trace} parameter instead. - */ - public static final String REST_EXCEPTION_SKIP_STACK_TRACE = "rest.exception.stacktrace.skip"; - public static final boolean REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT = true; - private static final boolean REST_EXCEPTION_SKIP_CAUSE_DEFAULT = false; - private static final String INDEX_METADATA_KEY = "opensearch.index"; - private static final String INDEX_METADATA_KEY_UUID = "opensearch.index_uuid"; - private static final String SHARD_METADATA_KEY = "opensearch.shard"; - private static final String RESOURCE_METADATA_TYPE_KEY = "opensearch.resource.type"; - private static final String RESOURCE_METADATA_ID_KEY = "opensearch.resource.id"; - - private static final String TYPE = "type"; - private static final String REASON = "reason"; - private static final String CAUSED_BY = "caused_by"; - private static final ParseField SUPPRESSED = new ParseField("suppressed"); - public static final String STACK_TRACE = "stack_trace"; - private static final String HEADER = "header"; - private static final String ERROR = "error"; - private static final String ROOT_CAUSE = "root_cause"; - - private static final Map> ID_TO_SUPPLIER; - private static final Map, OpenSearchExceptionHandle> CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE; - - private static final Pattern OS_METADATA = Pattern.compile("^opensearch\\."); - private static final Pattern ES_METADATA = Pattern.compile("^es\\."); - - private final Map> metadata = new HashMap<>(); - private final Map> headers = new HashMap<>(); - - /** - * Construct a OpenSearchException with the specified cause exception. - */ - public OpenSearchException(Throwable cause) { - super(cause); - } - - /** - * Construct a OpenSearchException with the specified detail message. - * - * The message can be parameterized using {} as placeholders for the given - * arguments - * - * @param msg the detail message - * @param args the arguments for the message - */ - public OpenSearchException(String msg, Object... args) { - super(LoggerMessageFormat.format(msg, args)); - } - - /** - * Construct a OpenSearchException with the specified detail message - * and nested exception. - * - * The message can be parameterized using {} as placeholders for the given - * arguments - * - * @param msg the detail message - * @param cause the nested exception - * @param args the arguments for the message - */ - public OpenSearchException(String msg, Throwable cause, Object... args) { - super(LoggerMessageFormat.format(msg, args), cause); - } - - public OpenSearchException(StreamInput in) throws IOException { - super(in.readOptionalString(), in.readException()); - readStackTrace(this, in); - headers.putAll(in.readMapOfLists(StreamInput::readString, StreamInput::readString)); - metadata.putAll(in.readMapOfLists(OpenSearchException::readAndReplace, StreamInput::readString)); - } - - private static String readAndReplace(StreamInput in) throws IOException { - String str = in.readString(); - return in.getVersion().onOrBefore(LegacyESVersion.V_7_10_2) ? ES_METADATA.matcher(str).replaceFirst("opensearch.") : str; - } - - private static void replaceAndWrite(StreamOutput out, String str) throws IOException { - out.writeString(out.getVersion().onOrBefore(LegacyESVersion.V_7_10_2) ? OS_METADATA.matcher(str).replaceFirst("es.") : str); - } - - /** - * Adds a new piece of metadata with the given key. - * If the provided key is already present, the corresponding metadata will be replaced - */ - public void addMetadata(String key, String... values) { - addMetadata(key, Arrays.asList(values)); - } - - /** - * Adds a new piece of metadata with the given key. - * If the provided key is already present, the corresponding metadata will be replaced - */ - public void addMetadata(String key, List values) { - // we need to enforce this otherwise bw comp doesn't work properly, as "opensearch." - // was the previous criteria to split headers in two sets - if (key.startsWith("opensearch.") == false) { - throw new IllegalArgumentException("exception metadata must start with [opensearch.], found [" + key + "] instead"); - } - this.metadata.put(key, values); - } - - /** - * Returns a set of all metadata keys on this exception - */ - public Set getMetadataKeys() { - return metadata.keySet(); - } - - /** - * Returns the list of metadata values for the given key or {@code null} if no metadata for the - * given key exists. - */ - public List getMetadata(String key) { - return metadata.get(key); - } - - protected Map> getMetadata() { - return metadata; - } - - /** - * Adds a new header with the given key. - * This method will replace existing header if a header with the same key already exists - */ - public void addHeader(String key, List value) { - // we need to enforce this otherwise bw comp doesn't work properly, as "opensearch." - // was the previous criteria to split headers in two sets - if (key.startsWith("opensearch.")) { - throw new IllegalArgumentException("exception headers must not start with [opensearch.], found [" + key + "] instead"); - } - this.headers.put(key, value); - } - - /** - * Adds a new header with the given key. - * This method will replace existing header if a header with the same key already exists - */ - public void addHeader(String key, String... value) { - addHeader(key, Arrays.asList(value)); - } - - /** - * Returns a set of all header keys on this exception - */ - public Set getHeaderKeys() { - return headers.keySet(); - } - - /** - * Returns the list of header values for the given key or {@code null} if no header for the - * given key exists. - */ - public List getHeader(String key) { - return headers.get(key); - } - - protected Map> getHeaders() { - return headers; - } - - /** - * Returns the rest status code associated with this exception. - */ - public RestStatus status() { - Throwable cause = unwrapCause(); - if (cause == this) { - return RestStatus.INTERNAL_SERVER_ERROR; - } else { - return ExceptionsHelper.status(cause); - } - } - - /** - * Unwraps the actual cause from the exception for cases when the exception is a - * {@link OpenSearchWrapperException}. - * - * @see ExceptionsHelper#unwrapCause(Throwable) - */ - public Throwable unwrapCause() { - return ExceptionsHelper.unwrapCause(this); - } - - /** - * Return the detail message, including the message from the nested exception - * if there is one. - */ - public String getDetailedMessage() { - if (getCause() != null) { - StringBuilder sb = new StringBuilder(); - sb.append(toString()).append("; "); - if (getCause() instanceof OpenSearchException) { - sb.append(((OpenSearchException) getCause()).getDetailedMessage()); - } else { - sb.append(getCause()); - } - return sb.toString(); - } else { - return super.toString(); - } - } - - /** - * Retrieve the innermost cause of this exception, if none, returns the current exception. - */ - public Throwable getRootCause() { - Throwable rootCause = this; - Throwable cause = getCause(); - while (cause != null && cause != rootCause) { - rootCause = cause; - cause = cause.getCause(); - } - return rootCause; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeOptionalString(this.getMessage()); - out.writeException(this.getCause()); - writeStackTraces(this, out, StreamOutput::writeException); - out.writeMapOfLists(headers, StreamOutput::writeString, StreamOutput::writeString); - out.writeMapOfLists(metadata, OpenSearchException::replaceAndWrite, StreamOutput::writeString); - } - - public static OpenSearchException readException(StreamInput input, int id) throws IOException { - CheckedFunction opensearchException = ID_TO_SUPPLIER.get(id); - if (opensearchException == null) { - if (id == 127 && input.getVersion().before(LegacyESVersion.V_7_5_0)) { - // was SearchContextException - return new SearchException(input); - } - throw new IllegalStateException("unknown exception for id: " + id); - } - return opensearchException.apply(input); - } - - /** - * Returns true iff the given class is a registered for an exception to be read. - */ - public static boolean isRegistered(Class exception, Version version) { - OpenSearchExceptionHandle openSearchExceptionHandle = CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE.get(exception); - if (openSearchExceptionHandle != null) { - return version.onOrAfter(openSearchExceptionHandle.versionAdded); - } - return false; - } - - static Set> getRegisteredKeys() { // for testing - return CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE.keySet(); - } - - /** - * Returns the serialization id the given exception. - */ - public static int getId(Class exception) { - return CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE.get(exception).id; - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - Throwable ex = ExceptionsHelper.unwrapCause(this); - if (ex != this) { - generateThrowableXContent(builder, params, this); - } else { - innerToXContent(builder, params, this, getExceptionName(), getMessage(), headers, metadata, getCause()); - } - return builder; - } - - protected static void innerToXContent( - XContentBuilder builder, - Params params, - Throwable throwable, - String type, - String message, - Map> headers, - Map> metadata, - Throwable cause - ) throws IOException { - builder.field(TYPE, type); - builder.field(REASON, message); - - for (Map.Entry> entry : metadata.entrySet()) { - headerToXContent(builder, entry.getKey().substring("opensearch.".length()), entry.getValue()); - } - - if (throwable instanceof OpenSearchException) { - OpenSearchException exception = (OpenSearchException) throwable; - exception.metadataToXContent(builder, params); - } - - if (params.paramAsBoolean(REST_EXCEPTION_SKIP_CAUSE, REST_EXCEPTION_SKIP_CAUSE_DEFAULT) == false) { - if (cause != null) { - builder.field(CAUSED_BY); - builder.startObject(); - generateThrowableXContent(builder, params, cause); - builder.endObject(); - } - } - - if (headers.isEmpty() == false) { - builder.startObject(HEADER); - for (Map.Entry> entry : headers.entrySet()) { - headerToXContent(builder, entry.getKey(), entry.getValue()); - } - builder.endObject(); - } - - if (params.paramAsBoolean(REST_EXCEPTION_SKIP_STACK_TRACE, REST_EXCEPTION_SKIP_STACK_TRACE_DEFAULT) == false) { - builder.field(STACK_TRACE, ExceptionsHelper.stackTrace(throwable)); - } - - Throwable[] allSuppressed = throwable.getSuppressed(); - if (allSuppressed.length > 0) { - builder.startArray(SUPPRESSED.getPreferredName()); - for (Throwable suppressed : allSuppressed) { - builder.startObject(); - generateThrowableXContent(builder, params, suppressed); - builder.endObject(); - } - builder.endArray(); - } - } - - private static void headerToXContent(XContentBuilder builder, String key, List values) throws IOException { - if (values != null && values.isEmpty() == false) { - if (values.size() == 1) { - builder.field(key, values.get(0)); - } else { - builder.startArray(key); - for (String value : values) { - builder.value(value); - } - builder.endArray(); - } - } - } - - /** - * Renders additional per exception information into the XContent - */ - protected void metadataToXContent(XContentBuilder builder, Params params) throws IOException {} - - /** - * Generate a {@link OpenSearchException} from a {@link XContentParser}. This does not - * return the original exception type (ie NodeClosedException for example) but just wraps - * the type, the reason and the cause of the exception. It also recursively parses the - * tree structure of the cause, returning it as a tree structure of {@link OpenSearchException} - * instances. - */ - public static OpenSearchException fromXContent(XContentParser parser) throws IOException { - XContentParser.Token token = parser.nextToken(); - ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser); - return innerFromXContent(parser, false); - } - - public static OpenSearchException innerFromXContent(XContentParser parser, boolean parseRootCauses) throws IOException { - XContentParser.Token token = parser.currentToken(); - ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser); - - String type = null, reason = null, stack = null; - OpenSearchException cause = null; - Map> metadata = new HashMap<>(); - Map> headers = new HashMap<>(); - List rootCauses = new ArrayList<>(); - List suppressed = new ArrayList<>(); - - for (; token == XContentParser.Token.FIELD_NAME; token = parser.nextToken()) { - String currentFieldName = parser.currentName(); - token = parser.nextToken(); - - if (token.isValue()) { - if (TYPE.equals(currentFieldName)) { - type = parser.text(); - } else if (REASON.equals(currentFieldName)) { - reason = parser.text(); - } else if (STACK_TRACE.equals(currentFieldName)) { - stack = parser.text(); - } else if (token == XContentParser.Token.VALUE_STRING) { - metadata.put(currentFieldName, Collections.singletonList(parser.text())); - } - } else if (token == XContentParser.Token.START_OBJECT) { - if (CAUSED_BY.equals(currentFieldName)) { - cause = fromXContent(parser); - } else if (HEADER.equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else { - List values = headers.getOrDefault(currentFieldName, new ArrayList<>()); - if (token == XContentParser.Token.VALUE_STRING) { - values.add(parser.text()); - } else if (token == XContentParser.Token.START_ARRAY) { - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token == XContentParser.Token.VALUE_STRING) { - values.add(parser.text()); - } else { - parser.skipChildren(); - } - } - } else if (token == XContentParser.Token.START_OBJECT) { - parser.skipChildren(); - } - headers.put(currentFieldName, values); - } - } - } else { - // Any additional metadata object added by the metadataToXContent method is ignored - // and skipped, so that the parser does not fail on unknown fields. The parser only - // support metadata key-pairs and metadata arrays of values. - parser.skipChildren(); - } - } else if (token == XContentParser.Token.START_ARRAY) { - if (parseRootCauses && ROOT_CAUSE.equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - rootCauses.add(fromXContent(parser)); - } - } else if (SUPPRESSED.match(currentFieldName, parser.getDeprecationHandler())) { - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - suppressed.add(fromXContent(parser)); - } - } else { - // Parse the array and add each item to the corresponding list of metadata. - // Arrays of objects are not supported yet and just ignored and skipped. - List values = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token == XContentParser.Token.VALUE_STRING) { - values.add(parser.text()); - } else { - parser.skipChildren(); - } - } - if (values.size() > 0) { - if (metadata.containsKey(currentFieldName)) { - values.addAll(metadata.get(currentFieldName)); - } - metadata.put(currentFieldName, values); - } - } - } - } - - OpenSearchException e = new OpenSearchException(buildMessage(type, reason, stack), cause); - for (Map.Entry> entry : metadata.entrySet()) { - // subclasses can print out additional metadata through the metadataToXContent method. Simple key-value pairs will be - // parsed back and become part of this metadata set, while objects and arrays are not supported when parsing back. - // Those key-value pairs become part of the metadata set and inherit the "opensearch." prefix as that is currently required - // by addMetadata. The prefix will get stripped out when printing metadata out so it will be effectively invisible. - // TODO move subclasses that print out simple metadata to using addMetadata directly and support also numbers and booleans. - // TODO rename metadataToXContent and have only SearchPhaseExecutionException use it, which prints out complex objects - e.addMetadata("opensearch." + entry.getKey(), entry.getValue()); - } - for (Map.Entry> header : headers.entrySet()) { - e.addHeader(header.getKey(), header.getValue()); - } - - // Adds root causes as suppressed exception. This way they are not lost - // after parsing and can be retrieved using getSuppressed() method. - for (OpenSearchException rootCause : rootCauses) { - e.addSuppressed(rootCause); - } - for (OpenSearchException s : suppressed) { - e.addSuppressed(s); - } - return e; - } - - /** - * Static toXContent helper method that renders {@link OpenSearchException} or {@link Throwable} instances - * as XContent, delegating the rendering to {@link #toXContent(XContentBuilder, Params)} - * or {@link #innerToXContent(XContentBuilder, Params, Throwable, String, String, Map, Map, Throwable)}. - * - * This method is usually used when the {@link Throwable} is rendered as a part of another XContent object, and its result can - * be parsed back using the {@link #fromXContent(XContentParser)} method. - */ - public static void generateThrowableXContent(XContentBuilder builder, Params params, Throwable t) throws IOException { - t = ExceptionsHelper.unwrapCause(t); - - if (t instanceof OpenSearchException) { - ((OpenSearchException) t).toXContent(builder, params); - } else { - innerToXContent(builder, params, t, getExceptionName(t), t.getMessage(), emptyMap(), emptyMap(), t.getCause()); - } - } - - /** - * Render any exception as a xcontent, encapsulated within a field or object named "error". The level of details that are rendered - * depends on the value of the "detailed" parameter: when it's false only a simple message based on the type and message of the - * exception is rendered. When it's true all detail are provided including guesses root causes, cause and potentially stack - * trace. - * - * This method is usually used when the {@link Exception} is rendered as a full XContent object, and its output can be parsed - * by the {@link #failureFromXContent(XContentParser)} method. - */ - public static void generateFailureXContent(XContentBuilder builder, Params params, @Nullable Exception e, boolean detailed) - throws IOException { - // No exception to render as an error - if (e == null) { - builder.field(ERROR, "unknown"); - return; - } - - // Render the exception with a simple message - if (detailed == false) { - String message = "No OpenSearchException found"; - Throwable t = e; - for (int counter = 0; counter < 10 && t != null; counter++) { - if (t instanceof OpenSearchException) { - message = t.getClass().getSimpleName() + "[" + t.getMessage() + "]"; - break; - } - t = t.getCause(); - } - builder.field(ERROR, message); - return; - } - - // Render the exception with all details - final OpenSearchException[] rootCauses = OpenSearchException.guessRootCauses(e); - builder.startObject(ERROR); - { - builder.startArray(ROOT_CAUSE); - for (OpenSearchException rootCause : rootCauses) { - builder.startObject(); - rootCause.toXContent(builder, new DelegatingMapParams(singletonMap(REST_EXCEPTION_SKIP_CAUSE, "true"), params)); - builder.endObject(); - } - builder.endArray(); - } - generateThrowableXContent(builder, params, e); - builder.endObject(); - } - - /** - * Parses the output of {@link #generateFailureXContent(XContentBuilder, Params, Exception, boolean)} - */ - public static OpenSearchException failureFromXContent(XContentParser parser) throws IOException { - XContentParser.Token token = parser.currentToken(); - ensureFieldName(parser, token, ERROR); - - token = parser.nextToken(); - if (token.isValue()) { - return new OpenSearchException(buildMessage("exception", parser.text(), null)); - } - - ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser); - token = parser.nextToken(); - - // Root causes are parsed in the innerFromXContent() and are added as suppressed exceptions. - return innerFromXContent(parser, true); - } - - /** - * Returns the root cause of this exception or multiple if different shards caused different exceptions - */ - public OpenSearchException[] guessRootCauses() { - final Throwable cause = getCause(); - if (cause != null && cause instanceof OpenSearchException) { - return ((OpenSearchException) cause).guessRootCauses(); - } - return new OpenSearchException[] { this }; - } - - /** - * Returns the root cause of this exception or multiple if different shards caused different exceptions. - * If the given exception is not an instance of {@link OpenSearchException} an empty array - * is returned. - */ - public static OpenSearchException[] guessRootCauses(Throwable t) { - Throwable ex = ExceptionsHelper.unwrapCause(t); - if (ex instanceof OpenSearchException) { - // OpenSearchException knows how to guess its own root cause - return ((OpenSearchException) ex).guessRootCauses(); - } - if (ex instanceof XContentParseException) { - /* - * We'd like to unwrap parsing exceptions to the inner-most - * parsing exception because that is generally the most interesting - * exception to return to the user. If that exception is caused by - * an OpenSearchException we'd like to keep unwrapping because - * ElasticserachExceptions tend to contain useful information for - * the user. - */ - Throwable cause = ex.getCause(); - if (cause != null) { - if (cause instanceof XContentParseException || cause instanceof OpenSearchException) { - return guessRootCauses(ex.getCause()); - } - } - } - return new OpenSearchException[] { new OpenSearchException(ex.getMessage(), ex) { - @Override - protected String getExceptionName() { - return getExceptionName(getCause()); - } - } }; - } - - protected String getExceptionName() { - return getExceptionName(this); - } - - /** - * Returns an underscore case name for the given exception. This method strips {@code OpenSearch} prefixes from exception names. - */ - public static String getExceptionName(Throwable ex) { - String simpleName = ex.getClass().getSimpleName(); - if (simpleName.startsWith("OpenSearch")) { - simpleName = simpleName.substring("OpenSearch".length()); - } - // TODO: do we really need to make the exception name in underscore casing? - return toUnderscoreCase(simpleName); - } - - static String buildMessage(String type, String reason, String stack) { - StringBuilder message = new StringBuilder("OpenSearch exception ["); - message.append(TYPE).append('=').append(type).append(", "); - message.append(REASON).append('=').append(reason); - if (stack != null) { - message.append(", ").append(STACK_TRACE).append('=').append(stack); - } - message.append(']'); - return message.toString(); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (metadata.containsKey(INDEX_METADATA_KEY)) { - builder.append(getIndex()); - if (metadata.containsKey(SHARD_METADATA_KEY)) { - builder.append('[').append(getShardId()).append(']'); - } - builder.append(' '); - } - return builder.append(ExceptionsHelper.detailedMessage(this).trim()).toString(); - } - - /** - * Deserializes stacktrace elements as well as suppressed exceptions from the given output stream and - * adds it to the given exception. - */ - public static T readStackTrace(T throwable, StreamInput in) throws IOException { - throwable.setStackTrace(in.readArray(i -> { - final String declaringClasss = i.readString(); - final String fileName = i.readOptionalString(); - final String methodName = i.readString(); - final int lineNumber = i.readVInt(); - return new StackTraceElement(declaringClasss, methodName, fileName, lineNumber); - }, StackTraceElement[]::new)); - - int numSuppressed = in.readVInt(); - for (int i = 0; i < numSuppressed; i++) { - throwable.addSuppressed(in.readException()); - } - return throwable; - } - - /** - * Serializes the given exceptions stacktrace elements as well as it's suppressed exceptions to the given output stream. - */ - public static T writeStackTraces(T throwable, StreamOutput out, Writer exceptionWriter) - throws IOException { - out.writeArray((o, v) -> { - o.writeString(v.getClassName()); - o.writeOptionalString(v.getFileName()); - o.writeString(v.getMethodName()); - o.writeVInt(v.getLineNumber()); - }, throwable.getStackTrace()); - out.writeArray(exceptionWriter, throwable.getSuppressed()); - return throwable; - } - - /** - * This is the list of Exceptions OpenSearch can throw over the wire or save into a corruption marker. Each value in the enum is a - * single exception tying the Class to an id for use of the encode side and the id back to a constructor for use on the decode side. As - * such its ok if the exceptions to change names so long as their constructor can still read the exception. Each exception is listed - * in id order below. If you want to remove an exception leave a tombstone comment and mark the id as null in - * ExceptionSerializationTests.testIds.ids. - */ - private enum OpenSearchExceptionHandle { - INDEX_SHARD_SNAPSHOT_FAILED_EXCEPTION( - org.opensearch.index.snapshots.IndexShardSnapshotFailedException.class, - org.opensearch.index.snapshots.IndexShardSnapshotFailedException::new, - 0, - UNKNOWN_VERSION_ADDED - ), - DFS_PHASE_EXECUTION_EXCEPTION( - org.opensearch.search.dfs.DfsPhaseExecutionException.class, - org.opensearch.search.dfs.DfsPhaseExecutionException::new, - 1, - UNKNOWN_VERSION_ADDED - ), - EXECUTION_CANCELLED_EXCEPTION( - org.opensearch.common.util.CancellableThreads.ExecutionCancelledException.class, - org.opensearch.common.util.CancellableThreads.ExecutionCancelledException::new, - 2, - UNKNOWN_VERSION_ADDED - ), - MASTER_NOT_DISCOVERED_EXCEPTION( - org.opensearch.discovery.MasterNotDiscoveredException.class, - org.opensearch.discovery.MasterNotDiscoveredException::new, - 3, - UNKNOWN_VERSION_ADDED - ), - OPENSEARCH_SECURITY_EXCEPTION( - org.opensearch.OpenSearchSecurityException.class, - org.opensearch.OpenSearchSecurityException::new, - 4, - UNKNOWN_VERSION_ADDED - ), - INDEX_SHARD_RESTORE_EXCEPTION( - org.opensearch.index.snapshots.IndexShardRestoreException.class, - org.opensearch.index.snapshots.IndexShardRestoreException::new, - 5, - UNKNOWN_VERSION_ADDED - ), - INDEX_CLOSED_EXCEPTION( - org.opensearch.indices.IndexClosedException.class, - org.opensearch.indices.IndexClosedException::new, - 6, - UNKNOWN_VERSION_ADDED - ), - BIND_HTTP_EXCEPTION( - org.opensearch.http.BindHttpException.class, - org.opensearch.http.BindHttpException::new, - 7, - UNKNOWN_VERSION_ADDED - ), - REDUCE_SEARCH_PHASE_EXCEPTION( - org.opensearch.action.search.ReduceSearchPhaseException.class, - org.opensearch.action.search.ReduceSearchPhaseException::new, - 8, - UNKNOWN_VERSION_ADDED - ), - NODE_CLOSED_EXCEPTION( - org.opensearch.node.NodeClosedException.class, - org.opensearch.node.NodeClosedException::new, - 9, - UNKNOWN_VERSION_ADDED - ), - SNAPSHOT_FAILED_ENGINE_EXCEPTION( - org.opensearch.index.engine.SnapshotFailedEngineException.class, - org.opensearch.index.engine.SnapshotFailedEngineException::new, - 10, - UNKNOWN_VERSION_ADDED - ), - SHARD_NOT_FOUND_EXCEPTION( - org.opensearch.index.shard.ShardNotFoundException.class, - org.opensearch.index.shard.ShardNotFoundException::new, - 11, - UNKNOWN_VERSION_ADDED - ), - CONNECT_TRANSPORT_EXCEPTION( - org.opensearch.transport.ConnectTransportException.class, - org.opensearch.transport.ConnectTransportException::new, - 12, - UNKNOWN_VERSION_ADDED - ), - NOT_SERIALIZABLE_TRANSPORT_EXCEPTION( - org.opensearch.transport.NotSerializableTransportException.class, - org.opensearch.transport.NotSerializableTransportException::new, - 13, - UNKNOWN_VERSION_ADDED - ), - RESPONSE_HANDLER_FAILURE_TRANSPORT_EXCEPTION( - org.opensearch.transport.ResponseHandlerFailureTransportException.class, - org.opensearch.transport.ResponseHandlerFailureTransportException::new, - 14, - UNKNOWN_VERSION_ADDED - ), - INDEX_CREATION_EXCEPTION( - org.opensearch.indices.IndexCreationException.class, - org.opensearch.indices.IndexCreationException::new, - 15, - UNKNOWN_VERSION_ADDED - ), - INDEX_NOT_FOUND_EXCEPTION( - org.opensearch.index.IndexNotFoundException.class, - org.opensearch.index.IndexNotFoundException::new, - 16, - UNKNOWN_VERSION_ADDED - ), - ILLEGAL_SHARD_ROUTING_STATE_EXCEPTION( - org.opensearch.cluster.routing.IllegalShardRoutingStateException.class, - org.opensearch.cluster.routing.IllegalShardRoutingStateException::new, - 17, - UNKNOWN_VERSION_ADDED - ), - BROADCAST_SHARD_OPERATION_FAILED_EXCEPTION( - org.opensearch.action.support.broadcast.BroadcastShardOperationFailedException.class, - org.opensearch.action.support.broadcast.BroadcastShardOperationFailedException::new, - 18, - UNKNOWN_VERSION_ADDED - ), - RESOURCE_NOT_FOUND_EXCEPTION( - org.opensearch.ResourceNotFoundException.class, - org.opensearch.ResourceNotFoundException::new, - 19, - UNKNOWN_VERSION_ADDED - ), - ACTION_TRANSPORT_EXCEPTION( - org.opensearch.transport.ActionTransportException.class, - org.opensearch.transport.ActionTransportException::new, - 20, - UNKNOWN_VERSION_ADDED - ), - OPENSEARCH_GENERATION_EXCEPTION( - org.opensearch.OpenSearchGenerationException.class, - org.opensearch.OpenSearchGenerationException::new, - 21, - UNKNOWN_VERSION_ADDED - ), - // 22 was CreateFailedEngineException - INDEX_SHARD_STARTED_EXCEPTION( - org.opensearch.index.shard.IndexShardStartedException.class, - org.opensearch.index.shard.IndexShardStartedException::new, - 23, - UNKNOWN_VERSION_ADDED - ), - SEARCH_CONTEXT_MISSING_EXCEPTION( - org.opensearch.search.SearchContextMissingException.class, - org.opensearch.search.SearchContextMissingException::new, - 24, - UNKNOWN_VERSION_ADDED - ), - GENERAL_SCRIPT_EXCEPTION( - org.opensearch.script.GeneralScriptException.class, - org.opensearch.script.GeneralScriptException::new, - 25, - UNKNOWN_VERSION_ADDED - ), - // 26 was BatchOperationException - SNAPSHOT_CREATION_EXCEPTION( - org.opensearch.snapshots.SnapshotCreationException.class, - org.opensearch.snapshots.SnapshotCreationException::new, - 27, - UNKNOWN_VERSION_ADDED - ), - // 28 was DeleteFailedEngineException, deprecated in 6.0, removed in 7.0 - DOCUMENT_MISSING_EXCEPTION( - org.opensearch.index.engine.DocumentMissingException.class, - org.opensearch.index.engine.DocumentMissingException::new, - 29, - UNKNOWN_VERSION_ADDED - ), - SNAPSHOT_EXCEPTION( - org.opensearch.snapshots.SnapshotException.class, - org.opensearch.snapshots.SnapshotException::new, - 30, - UNKNOWN_VERSION_ADDED - ), - INVALID_ALIAS_NAME_EXCEPTION( - org.opensearch.indices.InvalidAliasNameException.class, - org.opensearch.indices.InvalidAliasNameException::new, - 31, - UNKNOWN_VERSION_ADDED - ), - INVALID_INDEX_NAME_EXCEPTION( - org.opensearch.indices.InvalidIndexNameException.class, - org.opensearch.indices.InvalidIndexNameException::new, - 32, - UNKNOWN_VERSION_ADDED - ), - INDEX_PRIMARY_SHARD_NOT_ALLOCATED_EXCEPTION( - org.opensearch.indices.IndexPrimaryShardNotAllocatedException.class, - org.opensearch.indices.IndexPrimaryShardNotAllocatedException::new, - 33, - UNKNOWN_VERSION_ADDED - ), - TRANSPORT_EXCEPTION( - org.opensearch.transport.TransportException.class, - org.opensearch.transport.TransportException::new, - 34, - UNKNOWN_VERSION_ADDED - ), - OPENSEARCH_PARSE_EXCEPTION( - org.opensearch.OpenSearchParseException.class, - org.opensearch.OpenSearchParseException::new, - 35, - UNKNOWN_VERSION_ADDED - ), - SEARCH_EXCEPTION( - org.opensearch.search.SearchException.class, - org.opensearch.search.SearchException::new, - 36, - UNKNOWN_VERSION_ADDED - ), - MAPPER_EXCEPTION( - org.opensearch.index.mapper.MapperException.class, - org.opensearch.index.mapper.MapperException::new, - 37, - UNKNOWN_VERSION_ADDED - ), - INVALID_TYPE_NAME_EXCEPTION( - org.opensearch.indices.InvalidTypeNameException.class, - org.opensearch.indices.InvalidTypeNameException::new, - 38, - UNKNOWN_VERSION_ADDED - ), - SNAPSHOT_RESTORE_EXCEPTION( - org.opensearch.snapshots.SnapshotRestoreException.class, - org.opensearch.snapshots.SnapshotRestoreException::new, - 39, - UNKNOWN_VERSION_ADDED - ), - PARSING_EXCEPTION( - org.opensearch.common.ParsingException.class, - org.opensearch.common.ParsingException::new, - 40, - UNKNOWN_VERSION_ADDED - ), - INDEX_SHARD_CLOSED_EXCEPTION( - org.opensearch.index.shard.IndexShardClosedException.class, - org.opensearch.index.shard.IndexShardClosedException::new, - 41, - UNKNOWN_VERSION_ADDED - ), - RECOVER_FILES_RECOVERY_EXCEPTION( - org.opensearch.indices.recovery.RecoverFilesRecoveryException.class, - org.opensearch.indices.recovery.RecoverFilesRecoveryException::new, - 42, - UNKNOWN_VERSION_ADDED - ), - TRUNCATED_TRANSLOG_EXCEPTION( - org.opensearch.index.translog.TruncatedTranslogException.class, - org.opensearch.index.translog.TruncatedTranslogException::new, - 43, - UNKNOWN_VERSION_ADDED - ), - RECOVERY_FAILED_EXCEPTION( - org.opensearch.indices.recovery.RecoveryFailedException.class, - org.opensearch.indices.recovery.RecoveryFailedException::new, - 44, - UNKNOWN_VERSION_ADDED - ), - INDEX_SHARD_RELOCATED_EXCEPTION( - org.opensearch.index.shard.IndexShardRelocatedException.class, - org.opensearch.index.shard.IndexShardRelocatedException::new, - 45, - UNKNOWN_VERSION_ADDED - ), - NODE_SHOULD_NOT_CONNECT_EXCEPTION( - org.opensearch.transport.NodeShouldNotConnectException.class, - org.opensearch.transport.NodeShouldNotConnectException::new, - 46, - UNKNOWN_VERSION_ADDED - ), - // 47 used to be for IndexTemplateAlreadyExistsException which was deprecated in 5.1 removed in 6.0 - TRANSLOG_CORRUPTED_EXCEPTION( - org.opensearch.index.translog.TranslogCorruptedException.class, - org.opensearch.index.translog.TranslogCorruptedException::new, - 48, - UNKNOWN_VERSION_ADDED - ), - CLUSTER_BLOCK_EXCEPTION( - org.opensearch.cluster.block.ClusterBlockException.class, - org.opensearch.cluster.block.ClusterBlockException::new, - 49, - UNKNOWN_VERSION_ADDED - ), - FETCH_PHASE_EXECUTION_EXCEPTION( - org.opensearch.search.fetch.FetchPhaseExecutionException.class, - org.opensearch.search.fetch.FetchPhaseExecutionException::new, - 50, - UNKNOWN_VERSION_ADDED - ), - // 51 used to be for IndexShardAlreadyExistsException which was deprecated in 5.1 removed in 6.0 - VERSION_CONFLICT_ENGINE_EXCEPTION( - org.opensearch.index.engine.VersionConflictEngineException.class, - org.opensearch.index.engine.VersionConflictEngineException::new, - 52, - UNKNOWN_VERSION_ADDED - ), - ENGINE_EXCEPTION( - org.opensearch.index.engine.EngineException.class, - org.opensearch.index.engine.EngineException::new, - 53, - UNKNOWN_VERSION_ADDED - ), - // 54 was DocumentAlreadyExistsException, which is superseded by VersionConflictEngineException - NO_SUCH_NODE_EXCEPTION( - org.opensearch.action.NoSuchNodeException.class, - org.opensearch.action.NoSuchNodeException::new, - 55, - UNKNOWN_VERSION_ADDED - ), - SETTINGS_EXCEPTION( - org.opensearch.common.settings.SettingsException.class, - org.opensearch.common.settings.SettingsException::new, - 56, - UNKNOWN_VERSION_ADDED - ), - INDEX_TEMPLATE_MISSING_EXCEPTION( - org.opensearch.indices.IndexTemplateMissingException.class, - org.opensearch.indices.IndexTemplateMissingException::new, - 57, - UNKNOWN_VERSION_ADDED - ), - SEND_REQUEST_TRANSPORT_EXCEPTION( - org.opensearch.transport.SendRequestTransportException.class, - org.opensearch.transport.SendRequestTransportException::new, - 58, - UNKNOWN_VERSION_ADDED - ), - // 59 used to be OpenSearchRejectedExecutionException - // 60 used to be for EarlyTerminationException - // 61 used to be for RoutingValidationException - NOT_SERIALIZABLE_EXCEPTION_WRAPPER( - org.opensearch.common.io.stream.NotSerializableExceptionWrapper.class, - org.opensearch.common.io.stream.NotSerializableExceptionWrapper::new, - 62, - UNKNOWN_VERSION_ADDED - ), - ALIAS_FILTER_PARSING_EXCEPTION( - org.opensearch.indices.AliasFilterParsingException.class, - org.opensearch.indices.AliasFilterParsingException::new, - 63, - UNKNOWN_VERSION_ADDED - ), - // 64 was DeleteByQueryFailedEngineException, which was removed in 5.0 - GATEWAY_EXCEPTION( - org.opensearch.gateway.GatewayException.class, - org.opensearch.gateway.GatewayException::new, - 65, - UNKNOWN_VERSION_ADDED - ), - INDEX_SHARD_NOT_RECOVERING_EXCEPTION( - org.opensearch.index.shard.IndexShardNotRecoveringException.class, - org.opensearch.index.shard.IndexShardNotRecoveringException::new, - 66, - UNKNOWN_VERSION_ADDED - ), - HTTP_EXCEPTION(org.opensearch.http.HttpException.class, org.opensearch.http.HttpException::new, 67, UNKNOWN_VERSION_ADDED), - OPENSEARCH_EXCEPTION(OpenSearchException.class, OpenSearchException::new, 68, UNKNOWN_VERSION_ADDED), - SNAPSHOT_MISSING_EXCEPTION( - org.opensearch.snapshots.SnapshotMissingException.class, - org.opensearch.snapshots.SnapshotMissingException::new, - 69, - UNKNOWN_VERSION_ADDED - ), - PRIMARY_MISSING_ACTION_EXCEPTION( - org.opensearch.action.PrimaryMissingActionException.class, - org.opensearch.action.PrimaryMissingActionException::new, - 70, - UNKNOWN_VERSION_ADDED - ), - FAILED_NODE_EXCEPTION( - org.opensearch.action.FailedNodeException.class, - org.opensearch.action.FailedNodeException::new, - 71, - UNKNOWN_VERSION_ADDED - ), - SEARCH_PARSE_EXCEPTION( - org.opensearch.search.SearchParseException.class, - org.opensearch.search.SearchParseException::new, - 72, - UNKNOWN_VERSION_ADDED - ), - CONCURRENT_SNAPSHOT_EXECUTION_EXCEPTION( - org.opensearch.snapshots.ConcurrentSnapshotExecutionException.class, - org.opensearch.snapshots.ConcurrentSnapshotExecutionException::new, - 73, - UNKNOWN_VERSION_ADDED - ), - BLOB_STORE_EXCEPTION( - org.opensearch.common.blobstore.BlobStoreException.class, - org.opensearch.common.blobstore.BlobStoreException::new, - 74, - UNKNOWN_VERSION_ADDED - ), - INCOMPATIBLE_CLUSTER_STATE_VERSION_EXCEPTION( - org.opensearch.cluster.IncompatibleClusterStateVersionException.class, - org.opensearch.cluster.IncompatibleClusterStateVersionException::new, - 75, - UNKNOWN_VERSION_ADDED - ), - RECOVERY_ENGINE_EXCEPTION( - org.opensearch.index.engine.RecoveryEngineException.class, - org.opensearch.index.engine.RecoveryEngineException::new, - 76, - UNKNOWN_VERSION_ADDED - ), - UNCATEGORIZED_EXECUTION_EXCEPTION( - org.opensearch.common.util.concurrent.UncategorizedExecutionException.class, - org.opensearch.common.util.concurrent.UncategorizedExecutionException::new, - 77, - UNKNOWN_VERSION_ADDED - ), - TIMESTAMP_PARSING_EXCEPTION( - org.opensearch.action.TimestampParsingException.class, - org.opensearch.action.TimestampParsingException::new, - 78, - UNKNOWN_VERSION_ADDED - ), - ROUTING_MISSING_EXCEPTION( - org.opensearch.action.RoutingMissingException.class, - org.opensearch.action.RoutingMissingException::new, - 79, - UNKNOWN_VERSION_ADDED - ), - // 80 was IndexFailedEngineException, deprecated in 6.0, removed in 7.0 - INDEX_SHARD_RESTORE_FAILED_EXCEPTION( - org.opensearch.index.snapshots.IndexShardRestoreFailedException.class, - org.opensearch.index.snapshots.IndexShardRestoreFailedException::new, - 81, - UNKNOWN_VERSION_ADDED - ), - REPOSITORY_EXCEPTION( - org.opensearch.repositories.RepositoryException.class, - org.opensearch.repositories.RepositoryException::new, - 82, - UNKNOWN_VERSION_ADDED - ), - RECEIVE_TIMEOUT_TRANSPORT_EXCEPTION( - org.opensearch.transport.ReceiveTimeoutTransportException.class, - org.opensearch.transport.ReceiveTimeoutTransportException::new, - 83, - UNKNOWN_VERSION_ADDED - ), - NODE_DISCONNECTED_EXCEPTION( - org.opensearch.transport.NodeDisconnectedException.class, - org.opensearch.transport.NodeDisconnectedException::new, - 84, - UNKNOWN_VERSION_ADDED - ), - // 85 used to be for AlreadyExpiredException - AGGREGATION_EXECUTION_EXCEPTION( - org.opensearch.search.aggregations.AggregationExecutionException.class, - org.opensearch.search.aggregations.AggregationExecutionException::new, - 86, - UNKNOWN_VERSION_ADDED - ), - // 87 used to be for MergeMappingException - INVALID_INDEX_TEMPLATE_EXCEPTION( - org.opensearch.indices.InvalidIndexTemplateException.class, - org.opensearch.indices.InvalidIndexTemplateException::new, - 88, - UNKNOWN_VERSION_ADDED - ), - REFRESH_FAILED_ENGINE_EXCEPTION( - org.opensearch.index.engine.RefreshFailedEngineException.class, - org.opensearch.index.engine.RefreshFailedEngineException::new, - 90, - UNKNOWN_VERSION_ADDED - ), - AGGREGATION_INITIALIZATION_EXCEPTION( - org.opensearch.search.aggregations.AggregationInitializationException.class, - org.opensearch.search.aggregations.AggregationInitializationException::new, - 91, - UNKNOWN_VERSION_ADDED - ), - DELAY_RECOVERY_EXCEPTION( - org.opensearch.indices.recovery.DelayRecoveryException.class, - org.opensearch.indices.recovery.DelayRecoveryException::new, - 92, - UNKNOWN_VERSION_ADDED - ), - // 93 used to be for IndexWarmerMissingException - NO_NODE_AVAILABLE_EXCEPTION( - org.opensearch.client.transport.NoNodeAvailableException.class, - org.opensearch.client.transport.NoNodeAvailableException::new, - 94, - UNKNOWN_VERSION_ADDED - ), - INVALID_SNAPSHOT_NAME_EXCEPTION( - org.opensearch.snapshots.InvalidSnapshotNameException.class, - org.opensearch.snapshots.InvalidSnapshotNameException::new, - 96, - UNKNOWN_VERSION_ADDED - ), - ILLEGAL_INDEX_SHARD_STATE_EXCEPTION( - org.opensearch.index.shard.IllegalIndexShardStateException.class, - org.opensearch.index.shard.IllegalIndexShardStateException::new, - 97, - UNKNOWN_VERSION_ADDED - ), - INDEX_SHARD_SNAPSHOT_EXCEPTION( - org.opensearch.index.snapshots.IndexShardSnapshotException.class, - org.opensearch.index.snapshots.IndexShardSnapshotException::new, - 98, - UNKNOWN_VERSION_ADDED - ), - INDEX_SHARD_NOT_STARTED_EXCEPTION( - org.opensearch.index.shard.IndexShardNotStartedException.class, - org.opensearch.index.shard.IndexShardNotStartedException::new, - 99, - UNKNOWN_VERSION_ADDED - ), - SEARCH_PHASE_EXECUTION_EXCEPTION( - org.opensearch.action.search.SearchPhaseExecutionException.class, - org.opensearch.action.search.SearchPhaseExecutionException::new, - 100, - UNKNOWN_VERSION_ADDED - ), - ACTION_NOT_FOUND_TRANSPORT_EXCEPTION( - org.opensearch.transport.ActionNotFoundTransportException.class, - org.opensearch.transport.ActionNotFoundTransportException::new, - 101, - UNKNOWN_VERSION_ADDED - ), - TRANSPORT_SERIALIZATION_EXCEPTION( - org.opensearch.transport.TransportSerializationException.class, - org.opensearch.transport.TransportSerializationException::new, - 102, - UNKNOWN_VERSION_ADDED - ), - REMOTE_TRANSPORT_EXCEPTION( - org.opensearch.transport.RemoteTransportException.class, - org.opensearch.transport.RemoteTransportException::new, - 103, - UNKNOWN_VERSION_ADDED - ), - ENGINE_CREATION_FAILURE_EXCEPTION( - org.opensearch.index.engine.EngineCreationFailureException.class, - org.opensearch.index.engine.EngineCreationFailureException::new, - 104, - UNKNOWN_VERSION_ADDED - ), - ROUTING_EXCEPTION( - org.opensearch.cluster.routing.RoutingException.class, - org.opensearch.cluster.routing.RoutingException::new, - 105, - UNKNOWN_VERSION_ADDED - ), - INDEX_SHARD_RECOVERY_EXCEPTION( - org.opensearch.index.shard.IndexShardRecoveryException.class, - org.opensearch.index.shard.IndexShardRecoveryException::new, - 106, - UNKNOWN_VERSION_ADDED - ), - REPOSITORY_MISSING_EXCEPTION( - org.opensearch.repositories.RepositoryMissingException.class, - org.opensearch.repositories.RepositoryMissingException::new, - 107, - UNKNOWN_VERSION_ADDED - ), - DOCUMENT_SOURCE_MISSING_EXCEPTION( - org.opensearch.index.engine.DocumentSourceMissingException.class, - org.opensearch.index.engine.DocumentSourceMissingException::new, - 109, - UNKNOWN_VERSION_ADDED - ), - // 110 used to be FlushNotAllowedEngineException - NO_CLASS_SETTINGS_EXCEPTION( - org.opensearch.common.settings.NoClassSettingsException.class, - org.opensearch.common.settings.NoClassSettingsException::new, - 111, - UNKNOWN_VERSION_ADDED - ), - BIND_TRANSPORT_EXCEPTION( - org.opensearch.transport.BindTransportException.class, - org.opensearch.transport.BindTransportException::new, - 112, - UNKNOWN_VERSION_ADDED - ), - ALIASES_NOT_FOUND_EXCEPTION( - org.opensearch.rest.action.admin.indices.AliasesNotFoundException.class, - org.opensearch.rest.action.admin.indices.AliasesNotFoundException::new, - 113, - UNKNOWN_VERSION_ADDED - ), - INDEX_SHARD_RECOVERING_EXCEPTION( - org.opensearch.index.shard.IndexShardRecoveringException.class, - org.opensearch.index.shard.IndexShardRecoveringException::new, - 114, - UNKNOWN_VERSION_ADDED - ), - TRANSLOG_EXCEPTION( - org.opensearch.index.translog.TranslogException.class, - org.opensearch.index.translog.TranslogException::new, - 115, - UNKNOWN_VERSION_ADDED - ), - PROCESS_CLUSTER_EVENT_TIMEOUT_EXCEPTION( - org.opensearch.cluster.metadata.ProcessClusterEventTimeoutException.class, - org.opensearch.cluster.metadata.ProcessClusterEventTimeoutException::new, - 116, - UNKNOWN_VERSION_ADDED - ), - RETRY_ON_PRIMARY_EXCEPTION( - ReplicationOperation.RetryOnPrimaryException.class, - ReplicationOperation.RetryOnPrimaryException::new, - 117, - UNKNOWN_VERSION_ADDED - ), - OPENSEARCH_TIMEOUT_EXCEPTION( - org.opensearch.OpenSearchTimeoutException.class, - org.opensearch.OpenSearchTimeoutException::new, - 118, - UNKNOWN_VERSION_ADDED - ), - QUERY_PHASE_EXECUTION_EXCEPTION( - org.opensearch.search.query.QueryPhaseExecutionException.class, - org.opensearch.search.query.QueryPhaseExecutionException::new, - 119, - UNKNOWN_VERSION_ADDED - ), - REPOSITORY_VERIFICATION_EXCEPTION( - org.opensearch.repositories.RepositoryVerificationException.class, - org.opensearch.repositories.RepositoryVerificationException::new, - 120, - UNKNOWN_VERSION_ADDED - ), - INVALID_AGGREGATION_PATH_EXCEPTION( - org.opensearch.search.aggregations.InvalidAggregationPathException.class, - org.opensearch.search.aggregations.InvalidAggregationPathException::new, - 121, - UNKNOWN_VERSION_ADDED - ), - // 123 used to be IndexAlreadyExistsException and was renamed - RESOURCE_ALREADY_EXISTS_EXCEPTION( - ResourceAlreadyExistsException.class, - ResourceAlreadyExistsException::new, - 123, - UNKNOWN_VERSION_ADDED - ), - // 124 used to be Script.ScriptParseException - HTTP_REQUEST_ON_TRANSPORT_EXCEPTION( - TcpTransport.HttpRequestOnTransportException.class, - TcpTransport.HttpRequestOnTransportException::new, - 125, - UNKNOWN_VERSION_ADDED - ), - MAPPER_PARSING_EXCEPTION( - org.opensearch.index.mapper.MapperParsingException.class, - org.opensearch.index.mapper.MapperParsingException::new, - 126, - UNKNOWN_VERSION_ADDED - ), - // 127 used to be org.opensearch.search.SearchContextException - SEARCH_SOURCE_BUILDER_EXCEPTION( - org.opensearch.search.builder.SearchSourceBuilderException.class, - org.opensearch.search.builder.SearchSourceBuilderException::new, - 128, - UNKNOWN_VERSION_ADDED - ), - // 129 was EngineClosedException - NO_SHARD_AVAILABLE_ACTION_EXCEPTION( - org.opensearch.action.NoShardAvailableActionException.class, - org.opensearch.action.NoShardAvailableActionException::new, - 130, - UNKNOWN_VERSION_ADDED - ), - UNAVAILABLE_SHARDS_EXCEPTION( - org.opensearch.action.UnavailableShardsException.class, - org.opensearch.action.UnavailableShardsException::new, - 131, - UNKNOWN_VERSION_ADDED - ), - FLUSH_FAILED_ENGINE_EXCEPTION( - org.opensearch.index.engine.FlushFailedEngineException.class, - org.opensearch.index.engine.FlushFailedEngineException::new, - 132, - UNKNOWN_VERSION_ADDED - ), - CIRCUIT_BREAKING_EXCEPTION( - org.opensearch.common.breaker.CircuitBreakingException.class, - org.opensearch.common.breaker.CircuitBreakingException::new, - 133, - UNKNOWN_VERSION_ADDED - ), - NODE_NOT_CONNECTED_EXCEPTION( - org.opensearch.transport.NodeNotConnectedException.class, - org.opensearch.transport.NodeNotConnectedException::new, - 134, - UNKNOWN_VERSION_ADDED - ), - STRICT_DYNAMIC_MAPPING_EXCEPTION( - org.opensearch.index.mapper.StrictDynamicMappingException.class, - org.opensearch.index.mapper.StrictDynamicMappingException::new, - 135, - UNKNOWN_VERSION_ADDED - ), - RETRY_ON_REPLICA_EXCEPTION( - org.opensearch.action.support.replication.TransportReplicationAction.RetryOnReplicaException.class, - org.opensearch.action.support.replication.TransportReplicationAction.RetryOnReplicaException::new, - 136, - UNKNOWN_VERSION_ADDED - ), - TYPE_MISSING_EXCEPTION( - org.opensearch.indices.TypeMissingException.class, - org.opensearch.indices.TypeMissingException::new, - 137, - UNKNOWN_VERSION_ADDED - ), - FAILED_TO_COMMIT_CLUSTER_STATE_EXCEPTION( - org.opensearch.cluster.coordination.FailedToCommitClusterStateException.class, - org.opensearch.cluster.coordination.FailedToCommitClusterStateException::new, - 140, - UNKNOWN_VERSION_ADDED - ), - QUERY_SHARD_EXCEPTION( - org.opensearch.index.query.QueryShardException.class, - org.opensearch.index.query.QueryShardException::new, - 141, - UNKNOWN_VERSION_ADDED - ), - NO_LONGER_PRIMARY_SHARD_EXCEPTION( - ShardStateAction.NoLongerPrimaryShardException.class, - ShardStateAction.NoLongerPrimaryShardException::new, - 142, - UNKNOWN_VERSION_ADDED - ), - SCRIPT_EXCEPTION( - org.opensearch.script.ScriptException.class, - org.opensearch.script.ScriptException::new, - 143, - UNKNOWN_VERSION_ADDED - ), - NOT_MASTER_EXCEPTION( - org.opensearch.cluster.NotMasterException.class, - org.opensearch.cluster.NotMasterException::new, - 144, - UNKNOWN_VERSION_ADDED - ), - STATUS_EXCEPTION( - org.opensearch.OpenSearchStatusException.class, - org.opensearch.OpenSearchStatusException::new, - 145, - UNKNOWN_VERSION_ADDED - ), - TASK_CANCELLED_EXCEPTION( - org.opensearch.tasks.TaskCancelledException.class, - org.opensearch.tasks.TaskCancelledException::new, - 146, - UNKNOWN_VERSION_ADDED - ), - SHARD_LOCK_OBTAIN_FAILED_EXCEPTION( - org.opensearch.env.ShardLockObtainFailedException.class, - org.opensearch.env.ShardLockObtainFailedException::new, - 147, - UNKNOWN_VERSION_ADDED - ), - // 148 was UnknownNamedObjectException - TOO_MANY_BUCKETS_EXCEPTION( - MultiBucketConsumerService.TooManyBucketsException.class, - MultiBucketConsumerService.TooManyBucketsException::new, - 149, - UNKNOWN_VERSION_ADDED - ), - COORDINATION_STATE_REJECTED_EXCEPTION( - org.opensearch.cluster.coordination.CoordinationStateRejectedException.class, - org.opensearch.cluster.coordination.CoordinationStateRejectedException::new, - 150, - LegacyESVersion.V_7_0_0 - ), - SNAPSHOT_IN_PROGRESS_EXCEPTION( - org.opensearch.snapshots.SnapshotInProgressException.class, - org.opensearch.snapshots.SnapshotInProgressException::new, - 151, - UNKNOWN_VERSION_ADDED - ), - NO_SUCH_REMOTE_CLUSTER_EXCEPTION( - org.opensearch.transport.NoSuchRemoteClusterException.class, - org.opensearch.transport.NoSuchRemoteClusterException::new, - 152, - UNKNOWN_VERSION_ADDED - ), - RETENTION_LEASE_ALREADY_EXISTS_EXCEPTION( - org.opensearch.index.seqno.RetentionLeaseAlreadyExistsException.class, - org.opensearch.index.seqno.RetentionLeaseAlreadyExistsException::new, - 153, - UNKNOWN_VERSION_ADDED - ), - RETENTION_LEASE_NOT_FOUND_EXCEPTION( - org.opensearch.index.seqno.RetentionLeaseNotFoundException.class, - org.opensearch.index.seqno.RetentionLeaseNotFoundException::new, - 154, - UNKNOWN_VERSION_ADDED - ), - SHARD_NOT_IN_PRIMARY_MODE_EXCEPTION( - org.opensearch.index.shard.ShardNotInPrimaryModeException.class, - org.opensearch.index.shard.ShardNotInPrimaryModeException::new, - 155, - UNKNOWN_VERSION_ADDED - ), - RETENTION_LEASE_INVALID_RETAINING_SEQUENCE_NUMBER_EXCEPTION( - org.opensearch.index.seqno.RetentionLeaseInvalidRetainingSeqNoException.class, - org.opensearch.index.seqno.RetentionLeaseInvalidRetainingSeqNoException::new, - 156, - LegacyESVersion.V_7_5_0 - ), - INGEST_PROCESSOR_EXCEPTION( - org.opensearch.ingest.IngestProcessorException.class, - org.opensearch.ingest.IngestProcessorException::new, - 157, - LegacyESVersion.V_7_5_0 - ), - PEER_RECOVERY_NOT_FOUND_EXCEPTION( - org.opensearch.indices.recovery.PeerRecoveryNotFound.class, - org.opensearch.indices.recovery.PeerRecoveryNotFound::new, - 158, - LegacyESVersion.V_7_9_0 - ), - NODE_HEALTH_CHECK_FAILURE_EXCEPTION( - org.opensearch.cluster.coordination.NodeHealthCheckFailureException.class, - org.opensearch.cluster.coordination.NodeHealthCheckFailureException::new, - 159, - LegacyESVersion.V_7_9_0 - ), - NO_SEED_NODE_LEFT_EXCEPTION( - org.opensearch.transport.NoSeedNodeLeftException.class, - org.opensearch.transport.NoSeedNodeLeftException::new, - 160, - LegacyESVersion.V_7_10_0 - ); - - final Class exceptionClass; - final CheckedFunction constructor; - final int id; - final Version versionAdded; - - OpenSearchExceptionHandle( - Class exceptionClass, - CheckedFunction constructor, - int id, - Version versionAdded - ) { - // We need the exceptionClass because you can't dig it out of the constructor reliably. - this.exceptionClass = exceptionClass; - this.constructor = constructor; - this.versionAdded = versionAdded; - this.id = id; - } - } - - /** - * Returns an array of all registered handle IDs. These are the IDs for every registered - * exception. - * - * @return an array of all registered handle IDs - */ - static int[] ids() { - return Arrays.stream(OpenSearchExceptionHandle.values()).mapToInt(h -> h.id).toArray(); - } - - /** - * Returns an array of all registered pairs of handle IDs and exception classes. These pairs are - * provided for every registered exception. - * - * @return an array of all registered pairs of handle IDs and exception classes - */ - static Tuple>[] classes() { - @SuppressWarnings("unchecked") - final Tuple>[] ts = Arrays.stream(OpenSearchExceptionHandle.values()) - .map(h -> Tuple.tuple(h.id, h.exceptionClass)) - .toArray(Tuple[]::new); - return ts; - } - - static { - ID_TO_SUPPLIER = unmodifiableMap( - Arrays.stream(OpenSearchExceptionHandle.values()).collect(Collectors.toMap(e -> e.id, e -> e.constructor)) - ); - CLASS_TO_OPENSEARCH_EXCEPTION_HANDLE = unmodifiableMap( - Arrays.stream(OpenSearchExceptionHandle.values()).collect(Collectors.toMap(e -> e.exceptionClass, e -> e)) - ); - } - - public Index getIndex() { - List index = getMetadata(INDEX_METADATA_KEY); - if (index != null && index.isEmpty() == false) { - List index_uuid = getMetadata(INDEX_METADATA_KEY_UUID); - return new Index(index.get(0), index_uuid.get(0)); - } - - return null; - } - - public ShardId getShardId() { - List shard = getMetadata(SHARD_METADATA_KEY); - if (shard != null && shard.isEmpty() == false) { - return new ShardId(getIndex(), Integer.parseInt(shard.get(0))); - } - return null; - } - - public void setIndex(Index index) { - if (index != null) { - addMetadata(INDEX_METADATA_KEY, index.getName()); - addMetadata(INDEX_METADATA_KEY_UUID, index.getUUID()); - } - } - - public void setIndex(String index) { - if (index != null) { - setIndex(new Index(index, INDEX_UUID_NA_VALUE)); - } - } - - public void setShard(ShardId shardId) { - if (shardId != null) { - setIndex(shardId.getIndex()); - addMetadata(SHARD_METADATA_KEY, Integer.toString(shardId.id())); - } - } - - public void setResources(String type, String... id) { - assert type != null; - addMetadata(RESOURCE_METADATA_ID_KEY, id); - addMetadata(RESOURCE_METADATA_TYPE_KEY, type); - } - - public List getResourceId() { - return getMetadata(RESOURCE_METADATA_ID_KEY); - } - - public String getResourceType() { - List header = getMetadata(RESOURCE_METADATA_TYPE_KEY); - if (header != null && header.isEmpty() == false) { - assert header.size() == 1; - return header.get(0); - } - return null; - } - - // lower cases and adds underscores to transitions in a name - private static String toUnderscoreCase(String value) { - StringBuilder sb = new StringBuilder(); - boolean changed = false; - for (int i = 0; i < value.length(); i++) { - char c = value.charAt(i); - if (Character.isUpperCase(c)) { - if (!changed) { - // copy it over here - for (int j = 0; j < i; j++) { - sb.append(value.charAt(j)); - } - changed = true; - if (i == 0) { - sb.append(Character.toLowerCase(c)); - } else { - sb.append('_'); - sb.append(Character.toLowerCase(c)); - } - } else { - sb.append('_'); - sb.append(Character.toLowerCase(c)); - } - } else { - if (changed) { - sb.append(c); - } - } - } - if (!changed) { - return value; - } - return sb.toString(); - } - -} diff --git a/server/src/main/java/org/opensearch/OpenSearchGenerationException.java b/server/src/main/java/org/opensearch/OpenSearchGenerationException.java index 1f112aaf5a6e6..50b021d41123f 100644 --- a/server/src/main/java/org/opensearch/OpenSearchGenerationException.java +++ b/server/src/main/java/org/opensearch/OpenSearchGenerationException.java @@ -32,14 +32,14 @@ package org.opensearch; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** * A generic exception indicating failure to generate. * - * + * @opensearch.internal */ public class OpenSearchGenerationException extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/OpenSearchSecurityException.java b/server/src/main/java/org/opensearch/OpenSearchSecurityException.java index 35edb058605a7..f3b2022f03e38 100644 --- a/server/src/main/java/org/opensearch/OpenSearchSecurityException.java +++ b/server/src/main/java/org/opensearch/OpenSearchSecurityException.java @@ -31,13 +31,15 @@ package org.opensearch; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; /** * Generic security exception + * + * @opensearch.internal */ public class OpenSearchSecurityException extends OpenSearchStatusException { /** diff --git a/server/src/main/java/org/opensearch/OpenSearchServerException.java b/server/src/main/java/org/opensearch/OpenSearchServerException.java new file mode 100644 index 0000000000000..10c5beb46092f --- /dev/null +++ b/server/src/main/java/org/opensearch/OpenSearchServerException.java @@ -0,0 +1,1191 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch; + +import org.opensearch.crypto.CryptoRegistryException; + +import static org.opensearch.OpenSearchException.OpenSearchExceptionHandle; +import static org.opensearch.OpenSearchException.OpenSearchExceptionHandleRegistry.registerExceptionHandle; +import static org.opensearch.OpenSearchException.UNKNOWN_VERSION_ADDED; +import static org.opensearch.Version.V_2_10_0; +import static org.opensearch.Version.V_2_1_0; +import static org.opensearch.Version.V_2_3_0; +import static org.opensearch.Version.V_2_4_0; +import static org.opensearch.Version.V_2_5_0; +import static org.opensearch.Version.V_2_6_0; +import static org.opensearch.Version.V_2_7_0; + +/** + * Utility class to register server exceptions + * + * @opensearch.internal + */ +public final class OpenSearchServerException { + + private OpenSearchServerException() { + // no ctor: + } + + /** + * Setting a higher base exception id to avoid conflicts. + */ + private static final int CUSTOM_ELASTICSEARCH_EXCEPTIONS_BASE_ID = 10000; + + public static void registerExceptions() { + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.dfs.DfsPhaseExecutionException.class, + org.opensearch.search.dfs.DfsPhaseExecutionException::new, + 1, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.common.util.CancellableThreads.ExecutionCancelledException.class, + org.opensearch.common.util.CancellableThreads.ExecutionCancelledException::new, + 2, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.discovery.ClusterManagerNotDiscoveredException.class, + org.opensearch.discovery.ClusterManagerNotDiscoveredException::new, + 3, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.OpenSearchSecurityException.class, + org.opensearch.OpenSearchSecurityException::new, + 4, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.snapshots.IndexShardRestoreException.class, + org.opensearch.index.snapshots.IndexShardRestoreException::new, + 5, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.IndexClosedException.class, + org.opensearch.indices.IndexClosedException::new, + 6, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.http.BindHttpException.class, + org.opensearch.http.BindHttpException::new, + 7, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.search.ReduceSearchPhaseException.class, + org.opensearch.action.search.ReduceSearchPhaseException::new, + 8, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.node.NodeClosedException.class, + org.opensearch.node.NodeClosedException::new, + 9, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.SnapshotFailedEngineException.class, + org.opensearch.index.engine.SnapshotFailedEngineException::new, + 10, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.ShardNotFoundException.class, + org.opensearch.index.shard.ShardNotFoundException::new, + 11, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.ConnectTransportException.class, + org.opensearch.transport.ConnectTransportException::new, + 12, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.NotSerializableTransportException.class, + org.opensearch.transport.NotSerializableTransportException::new, + 13, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.ResponseHandlerFailureTransportException.class, + org.opensearch.transport.ResponseHandlerFailureTransportException::new, + 14, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.IndexCreationException.class, + org.opensearch.indices.IndexCreationException::new, + 15, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.IndexNotFoundException.class, + org.opensearch.index.IndexNotFoundException::new, + 16, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.routing.IllegalShardRoutingStateException.class, + org.opensearch.cluster.routing.IllegalShardRoutingStateException::new, + 17, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.support.broadcast.BroadcastShardOperationFailedException.class, + org.opensearch.action.support.broadcast.BroadcastShardOperationFailedException::new, + 18, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.ResourceNotFoundException.class, + org.opensearch.ResourceNotFoundException::new, + 19, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.ActionTransportException.class, + org.opensearch.transport.ActionTransportException::new, + 20, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.OpenSearchGenerationException.class, + org.opensearch.OpenSearchGenerationException::new, + 21, + UNKNOWN_VERSION_ADDED + ) + ); + // 22 was CreateFailedEngineException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.IndexShardStartedException.class, + org.opensearch.index.shard.IndexShardStartedException::new, + 23, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.SearchContextMissingException.class, + org.opensearch.search.SearchContextMissingException::new, + 24, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.script.GeneralScriptException.class, + org.opensearch.script.GeneralScriptException::new, + 25, + UNKNOWN_VERSION_ADDED + ) + ); + // 26 was BatchOperationException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.snapshots.SnapshotCreationException.class, + org.opensearch.snapshots.SnapshotCreationException::new, + 27, + UNKNOWN_VERSION_ADDED + ) + ); + // 28 was DeleteFailedEngineException, deprecated in 6.0, removed in 7.0 + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.DocumentMissingException.class, + org.opensearch.index.engine.DocumentMissingException::new, + 29, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.snapshots.SnapshotException.class, + org.opensearch.snapshots.SnapshotException::new, + 30, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.InvalidAliasNameException.class, + org.opensearch.indices.InvalidAliasNameException::new, + 31, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.InvalidIndexNameException.class, + org.opensearch.indices.InvalidIndexNameException::new, + 32, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.IndexPrimaryShardNotAllocatedException.class, + org.opensearch.indices.IndexPrimaryShardNotAllocatedException::new, + 33, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.TransportException.class, + org.opensearch.transport.TransportException::new, + 34, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.SearchException.class, + org.opensearch.search.SearchException::new, + 36, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.mapper.MapperException.class, + org.opensearch.index.mapper.MapperException::new, + 37, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.InvalidTypeNameException.class, + org.opensearch.indices.InvalidTypeNameException::new, + 38, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.snapshots.SnapshotRestoreException.class, + org.opensearch.snapshots.SnapshotRestoreException::new, + 39, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.IndexShardClosedException.class, + org.opensearch.index.shard.IndexShardClosedException::new, + 41, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.recovery.RecoverFilesRecoveryException.class, + org.opensearch.indices.recovery.RecoverFilesRecoveryException::new, + 42, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.translog.TruncatedTranslogException.class, + org.opensearch.index.translog.TruncatedTranslogException::new, + 43, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.recovery.RecoveryFailedException.class, + org.opensearch.indices.recovery.RecoveryFailedException::new, + 44, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.IndexShardRelocatedException.class, + org.opensearch.index.shard.IndexShardRelocatedException::new, + 45, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.NodeShouldNotConnectException.class, + org.opensearch.transport.NodeShouldNotConnectException::new, + 46, + UNKNOWN_VERSION_ADDED + ) + ); + // 47 used to be for IndexTemplateAlreadyExistsException which was deprecated in 5.1 removed in 6.0 + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.translog.TranslogCorruptedException.class, + org.opensearch.index.translog.TranslogCorruptedException::new, + 48, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.block.ClusterBlockException.class, + org.opensearch.cluster.block.ClusterBlockException::new, + 49, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.fetch.FetchPhaseExecutionException.class, + org.opensearch.search.fetch.FetchPhaseExecutionException::new, + 50, + UNKNOWN_VERSION_ADDED + ) + ); + // 51 used to be for IndexShardAlreadyExistsException which was deprecated in 5.1 removed in 6.0 + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.VersionConflictEngineException.class, + org.opensearch.index.engine.VersionConflictEngineException::new, + 52, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.EngineException.class, + org.opensearch.index.engine.EngineException::new, + 53, + UNKNOWN_VERSION_ADDED + ) + ); + // 54 was DocumentAlreadyExistsException, which is superseded by VersionConflictEngineException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.NoSuchNodeException.class, + org.opensearch.action.NoSuchNodeException::new, + 55, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.common.settings.SettingsException.class, + org.opensearch.common.settings.SettingsException::new, + 56, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.IndexTemplateMissingException.class, + org.opensearch.indices.IndexTemplateMissingException::new, + 57, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.SendRequestTransportException.class, + org.opensearch.transport.SendRequestTransportException::new, + 58, + UNKNOWN_VERSION_ADDED + ) + ); + // 59 used to be OpenSearchRejectedExecutionException + // 60 used to be for EarlyTerminationException + // 61 used to be for RoutingValidationException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.AliasFilterParsingException.class, + org.opensearch.indices.AliasFilterParsingException::new, + 63, + UNKNOWN_VERSION_ADDED + ) + ); + // 64 was DeleteByQueryFailedEngineException, which was removed in 5.0 + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.gateway.GatewayException.class, + org.opensearch.gateway.GatewayException::new, + 65, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.IndexShardNotRecoveringException.class, + org.opensearch.index.shard.IndexShardNotRecoveringException::new, + 66, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.http.HttpException.class, + org.opensearch.http.HttpException::new, + 67, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.OpenSearchException.class, + org.opensearch.OpenSearchException::new, + 68, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.snapshots.SnapshotMissingException.class, + org.opensearch.snapshots.SnapshotMissingException::new, + 69, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.PrimaryMissingActionException.class, + org.opensearch.action.PrimaryMissingActionException::new, + 70, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.SearchParseException.class, + org.opensearch.search.SearchParseException::new, + 72, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.FailedNodeException.class, + org.opensearch.action.FailedNodeException::new, + 71, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.snapshots.ConcurrentSnapshotExecutionException.class, + org.opensearch.snapshots.ConcurrentSnapshotExecutionException::new, + 73, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.common.blobstore.BlobStoreException.class, + org.opensearch.common.blobstore.BlobStoreException::new, + 74, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.IncompatibleClusterStateVersionException.class, + org.opensearch.cluster.IncompatibleClusterStateVersionException::new, + 75, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.RecoveryEngineException.class, + org.opensearch.index.engine.RecoveryEngineException::new, + 76, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.common.util.concurrent.UncategorizedExecutionException.class, + org.opensearch.common.util.concurrent.UncategorizedExecutionException::new, + 77, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.TimestampParsingException.class, + org.opensearch.action.TimestampParsingException::new, + 78, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.RoutingMissingException.class, + org.opensearch.action.RoutingMissingException::new, + 79, + UNKNOWN_VERSION_ADDED + ) + ); + // 80 was IndexFailedEngineException, deprecated in 6.0, removed in 7.0 + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.snapshots.IndexShardRestoreFailedException.class, + org.opensearch.index.snapshots.IndexShardRestoreFailedException::new, + 81, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.repositories.RepositoryException.class, + org.opensearch.repositories.RepositoryException::new, + 82, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.ReceiveTimeoutTransportException.class, + org.opensearch.transport.ReceiveTimeoutTransportException::new, + 83, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.NodeDisconnectedException.class, + org.opensearch.transport.NodeDisconnectedException::new, + 84, + UNKNOWN_VERSION_ADDED + ) + ); + // 85 used to be for AlreadyExpiredException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.aggregations.AggregationExecutionException.class, + org.opensearch.search.aggregations.AggregationExecutionException::new, + 86, + UNKNOWN_VERSION_ADDED + ) + ); + // 87 used to be for MergeMappingException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.InvalidIndexTemplateException.class, + org.opensearch.indices.InvalidIndexTemplateException::new, + 88, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.RefreshFailedEngineException.class, + org.opensearch.index.engine.RefreshFailedEngineException::new, + 90, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.aggregations.AggregationInitializationException.class, + org.opensearch.search.aggregations.AggregationInitializationException::new, + 91, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.recovery.DelayRecoveryException.class, + org.opensearch.indices.recovery.DelayRecoveryException::new, + 92, + UNKNOWN_VERSION_ADDED + ) + ); + // 93 used to be for IndexWarmerMissingException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.client.transport.NoNodeAvailableException.class, + org.opensearch.client.transport.NoNodeAvailableException::new, + 94, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.snapshots.InvalidSnapshotNameException.class, + org.opensearch.snapshots.InvalidSnapshotNameException::new, + 96, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.IllegalIndexShardStateException.class, + org.opensearch.index.shard.IllegalIndexShardStateException::new, + 97, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.core.index.snapshots.IndexShardSnapshotException.class, + org.opensearch.core.index.snapshots.IndexShardSnapshotException::new, + 98, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.IndexShardNotStartedException.class, + org.opensearch.index.shard.IndexShardNotStartedException::new, + 99, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.search.SearchPhaseExecutionException.class, + org.opensearch.action.search.SearchPhaseExecutionException::new, + 100, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.ActionNotFoundTransportException.class, + org.opensearch.transport.ActionNotFoundTransportException::new, + 101, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.TransportSerializationException.class, + org.opensearch.transport.TransportSerializationException::new, + 102, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.RemoteTransportException.class, + org.opensearch.transport.RemoteTransportException::new, + 103, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.EngineCreationFailureException.class, + org.opensearch.index.engine.EngineCreationFailureException::new, + 104, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.routing.RoutingException.class, + org.opensearch.cluster.routing.RoutingException::new, + 105, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.IndexShardRecoveryException.class, + org.opensearch.index.shard.IndexShardRecoveryException::new, + 106, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.repositories.RepositoryMissingException.class, + org.opensearch.repositories.RepositoryMissingException::new, + 107, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.DocumentSourceMissingException.class, + org.opensearch.index.engine.DocumentSourceMissingException::new, + 109, + UNKNOWN_VERSION_ADDED + ) + ); + // 110 used to be FlushNotAllowedEngineException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.common.settings.NoClassSettingsException.class, + org.opensearch.common.settings.NoClassSettingsException::new, + 111, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.BindTransportException.class, + org.opensearch.transport.BindTransportException::new, + 112, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.rest.action.admin.indices.AliasesNotFoundException.class, + org.opensearch.rest.action.admin.indices.AliasesNotFoundException::new, + 113, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.IndexShardRecoveringException.class, + org.opensearch.index.shard.IndexShardRecoveringException::new, + 114, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.translog.TranslogException.class, + org.opensearch.index.translog.TranslogException::new, + 115, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.metadata.ProcessClusterEventTimeoutException.class, + org.opensearch.cluster.metadata.ProcessClusterEventTimeoutException::new, + 116, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.support.replication.ReplicationOperation.RetryOnPrimaryException.class, + org.opensearch.action.support.replication.ReplicationOperation.RetryOnPrimaryException::new, + 117, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.OpenSearchTimeoutException.class, + org.opensearch.OpenSearchTimeoutException::new, + 118, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.query.QueryPhaseExecutionException.class, + org.opensearch.search.query.QueryPhaseExecutionException::new, + 119, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.repositories.RepositoryVerificationException.class, + org.opensearch.repositories.RepositoryVerificationException::new, + 120, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.aggregations.InvalidAggregationPathException.class, + org.opensearch.search.aggregations.InvalidAggregationPathException::new, + 121, + UNKNOWN_VERSION_ADDED + ) + ); + // 123 used to be IndexAlreadyExistsException and was renamed + registerExceptionHandle( + new OpenSearchExceptionHandle( + ResourceAlreadyExistsException.class, + ResourceAlreadyExistsException::new, + 123, + UNKNOWN_VERSION_ADDED + ) + ); + // 124 used to be Script.ScriptParseException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.TcpTransport.HttpRequestOnTransportException.class, + org.opensearch.transport.TcpTransport.HttpRequestOnTransportException::new, + 125, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.mapper.MapperParsingException.class, + org.opensearch.index.mapper.MapperParsingException::new, + 126, + UNKNOWN_VERSION_ADDED + ) + ); + // 127 used to be org.opensearch.search.SearchContextException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.builder.SearchSourceBuilderException.class, + org.opensearch.search.builder.SearchSourceBuilderException::new, + 128, + UNKNOWN_VERSION_ADDED + ) + ); + // 129 was EngineClosedException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.NoShardAvailableActionException.class, + org.opensearch.action.NoShardAvailableActionException::new, + 130, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.UnavailableShardsException.class, + org.opensearch.action.UnavailableShardsException::new, + 131, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.engine.FlushFailedEngineException.class, + org.opensearch.index.engine.FlushFailedEngineException::new, + 132, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.NodeNotConnectedException.class, + org.opensearch.transport.NodeNotConnectedException::new, + 134, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.mapper.StrictDynamicMappingException.class, + org.opensearch.index.mapper.StrictDynamicMappingException::new, + 135, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.action.support.replication.TransportReplicationAction.RetryOnReplicaException.class, + org.opensearch.action.support.replication.TransportReplicationAction.RetryOnReplicaException::new, + 136, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.TypeMissingException.class, + org.opensearch.indices.TypeMissingException::new, + 137, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.coordination.FailedToCommitClusterStateException.class, + org.opensearch.cluster.coordination.FailedToCommitClusterStateException::new, + 140, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.query.QueryShardException.class, + org.opensearch.index.query.QueryShardException::new, + 141, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.action.shard.ShardStateAction.NoLongerPrimaryShardException.class, + org.opensearch.cluster.action.shard.ShardStateAction.NoLongerPrimaryShardException::new, + 142, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.script.ScriptException.class, + org.opensearch.script.ScriptException::new, + 143, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.NotClusterManagerException.class, + org.opensearch.cluster.NotClusterManagerException::new, + 144, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.OpenSearchStatusException.class, + org.opensearch.OpenSearchStatusException::new, + 145, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.env.ShardLockObtainFailedException.class, + org.opensearch.env.ShardLockObtainFailedException::new, + 147, + UNKNOWN_VERSION_ADDED + ) + ); + // 148 was UnknownNamedObjectException + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.aggregations.MultiBucketConsumerService.TooManyBucketsException.class, + org.opensearch.search.aggregations.MultiBucketConsumerService.TooManyBucketsException::new, + 149, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.coordination.CoordinationStateRejectedException.class, + org.opensearch.cluster.coordination.CoordinationStateRejectedException::new, + 150, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.snapshots.SnapshotInProgressException.class, + org.opensearch.snapshots.SnapshotInProgressException::new, + 151, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.NoSuchRemoteClusterException.class, + org.opensearch.transport.NoSuchRemoteClusterException::new, + 152, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.seqno.RetentionLeaseAlreadyExistsException.class, + org.opensearch.index.seqno.RetentionLeaseAlreadyExistsException::new, + 153, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.seqno.RetentionLeaseNotFoundException.class, + org.opensearch.index.seqno.RetentionLeaseNotFoundException::new, + 154, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.ShardNotInPrimaryModeException.class, + org.opensearch.index.shard.ShardNotInPrimaryModeException::new, + 155, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.seqno.RetentionLeaseInvalidRetainingSeqNoException.class, + org.opensearch.index.seqno.RetentionLeaseInvalidRetainingSeqNoException::new, + 156, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.ingest.IngestProcessorException.class, + org.opensearch.ingest.IngestProcessorException::new, + 157, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.recovery.PeerRecoveryNotFound.class, + org.opensearch.indices.recovery.PeerRecoveryNotFound::new, + 158, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.coordination.NodeHealthCheckFailureException.class, + org.opensearch.cluster.coordination.NodeHealthCheckFailureException::new, + 159, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.transport.NoSeedNodeLeftException.class, + org.opensearch.transport.NoSeedNodeLeftException::new, + 160, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.indices.replication.common.ReplicationFailedException.class, + org.opensearch.indices.replication.common.ReplicationFailedException::new, + 161, + V_2_1_0 + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.index.shard.PrimaryShardClosedException.class, + org.opensearch.index.shard.PrimaryShardClosedException::new, + 162, + V_2_3_0 + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.decommission.DecommissioningFailedException.class, + org.opensearch.cluster.decommission.DecommissioningFailedException::new, + 163, + V_2_4_0 + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.decommission.NodeDecommissionedException.class, + org.opensearch.cluster.decommission.NodeDecommissionedException::new, + 164, + V_2_4_0 + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.service.ClusterManagerThrottlingException.class, + org.opensearch.cluster.service.ClusterManagerThrottlingException::new, + 165, + Version.V_2_5_0 + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.snapshots.SnapshotInUseDeletionException.class, + org.opensearch.snapshots.SnapshotInUseDeletionException::new, + 166, + UNKNOWN_VERSION_ADDED + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.routing.UnsupportedWeightedRoutingStateException.class, + org.opensearch.cluster.routing.UnsupportedWeightedRoutingStateException::new, + 167, + V_2_5_0 + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.routing.PreferenceBasedSearchNotAllowedException.class, + org.opensearch.cluster.routing.PreferenceBasedSearchNotAllowedException::new, + 168, + V_2_6_0 + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.routing.NodeWeighedAwayException.class, + org.opensearch.cluster.routing.NodeWeighedAwayException::new, + 169, + V_2_6_0 + ) + ); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.search.pipeline.SearchPipelineProcessingException.class, + org.opensearch.search.pipeline.SearchPipelineProcessingException::new, + 170, + V_2_7_0 + ) + ); + registerExceptionHandle(new OpenSearchExceptionHandle(CryptoRegistryException.class, CryptoRegistryException::new, 171, V_2_10_0)); + registerExceptionHandle( + new OpenSearchExceptionHandle( + org.opensearch.cluster.block.IndexCreateBlockException.class, + org.opensearch.cluster.block.IndexCreateBlockException::new, + CUSTOM_ELASTICSEARCH_EXCEPTIONS_BASE_ID + 1, + V_2_6_0 + ) + ); + } +} diff --git a/server/src/main/java/org/opensearch/OpenSearchStatusException.java b/server/src/main/java/org/opensearch/OpenSearchStatusException.java index 274eac8f8b2ad..8061057465498 100644 --- a/server/src/main/java/org/opensearch/OpenSearchStatusException.java +++ b/server/src/main/java/org/opensearch/OpenSearchStatusException.java @@ -32,15 +32,17 @@ package org.opensearch; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; /** * Exception who's {@link RestStatus} is arbitrary rather than derived. Used, for example, by reindex-from-remote to wrap remote exceptions * that contain a status. + * + * @opensearch.internal */ public class OpenSearchStatusException extends OpenSearchException { private final RestStatus status; diff --git a/server/src/main/java/org/opensearch/OpenSearchTimeoutException.java b/server/src/main/java/org/opensearch/OpenSearchTimeoutException.java index 6b20e145e500b..3b1d7086d0584 100644 --- a/server/src/main/java/org/opensearch/OpenSearchTimeoutException.java +++ b/server/src/main/java/org/opensearch/OpenSearchTimeoutException.java @@ -32,14 +32,14 @@ package org.opensearch; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** * The same as {@link java.util.concurrent.TimeoutException} simply a runtime one. * - * + * @opensearch.internal */ public class OpenSearchTimeoutException extends OpenSearchException { public OpenSearchTimeoutException(StreamInput in) throws IOException { diff --git a/server/src/main/java/org/opensearch/ResourceAlreadyExistsException.java b/server/src/main/java/org/opensearch/ResourceAlreadyExistsException.java index e6b5177538fa9..9566244062626 100644 --- a/server/src/main/java/org/opensearch/ResourceAlreadyExistsException.java +++ b/server/src/main/java/org/opensearch/ResourceAlreadyExistsException.java @@ -32,12 +32,17 @@ package org.opensearch; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.Index; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; +/** + * Exception when Resources already exists + * + * @opensearch.internal + */ public class ResourceAlreadyExistsException extends OpenSearchException { public ResourceAlreadyExistsException(Index index) { diff --git a/server/src/main/java/org/opensearch/ResourceNotFoundException.java b/server/src/main/java/org/opensearch/ResourceNotFoundException.java index 373493aeb53b1..0a89fa3667542 100644 --- a/server/src/main/java/org/opensearch/ResourceNotFoundException.java +++ b/server/src/main/java/org/opensearch/ResourceNotFoundException.java @@ -31,13 +31,15 @@ package org.opensearch; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; /** * Generic ResourceNotFoundException corresponding to the {@link RestStatus#NOT_FOUND} status code + * + * @opensearch.internal */ public class ResourceNotFoundException extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/SpecialPermission.java b/server/src/main/java/org/opensearch/SpecialPermission.java index 00ae1528d4ef2..8a694d4543f32 100644 --- a/server/src/main/java/org/opensearch/SpecialPermission.java +++ b/server/src/main/java/org/opensearch/SpecialPermission.java @@ -68,6 +68,8 @@ * ... * ); * + * + * @opensearch.internal */ public final class SpecialPermission extends BasicPermission { diff --git a/server/src/main/java/org/opensearch/action/ActionListenerResponseHandler.java b/server/src/main/java/org/opensearch/action/ActionListenerResponseHandler.java index 83ac650bf48af..1ed774b8f8f9a 100644 --- a/server/src/main/java/org/opensearch/action/ActionListenerResponseHandler.java +++ b/server/src/main/java/org/opensearch/action/ActionListenerResponseHandler.java @@ -32,12 +32,13 @@ package org.opensearch.action; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportException; -import org.opensearch.transport.TransportResponse; +import org.opensearch.transport.TransportResponseHandler; import java.io.IOException; import java.util.Objects; @@ -45,6 +46,8 @@ /** * A simple base class for action response listeners, defaulting to using the SAME executor (as its * very common on response handlers). + * + * @opensearch.internal */ public class ActionListenerResponseHandler implements TransportResponseHandler { diff --git a/server/src/main/java/org/opensearch/action/ActionModule.java b/server/src/main/java/org/opensearch/action/ActionModule.java index 8e31aa23d88cf..7b0b725c88f64 100644 --- a/server/src/main/java/org/opensearch/action/ActionModule.java +++ b/server/src/main/java/org/opensearch/action/ActionModule.java @@ -40,6 +40,12 @@ import org.opensearch.action.admin.cluster.configuration.ClearVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction; import org.opensearch.action.admin.cluster.configuration.TransportClearVotingConfigExclusionsAction; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.TransportDeleteDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.TransportGetDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.TransportDecommissionAction; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.health.TransportClusterHealthAction; import org.opensearch.action.admin.cluster.node.hotthreads.NodesHotThreadsAction; @@ -61,6 +67,10 @@ import org.opensearch.action.admin.cluster.node.usage.TransportNodesUsageAction; import org.opensearch.action.admin.cluster.remote.RemoteInfoAction; import org.opensearch.action.admin.cluster.remote.TransportRemoteInfoAction; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreAction; +import org.opensearch.action.admin.cluster.remotestore.restore.TransportRestoreRemoteStoreAction; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsAction; +import org.opensearch.action.admin.cluster.remotestore.stats.TransportRemoteStoreStatsAction; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryAction; import org.opensearch.action.admin.cluster.repositories.cleanup.TransportCleanupRepositoryAction; import org.opensearch.action.admin.cluster.repositories.delete.DeleteRepositoryAction; @@ -77,6 +87,12 @@ import org.opensearch.action.admin.cluster.settings.TransportClusterUpdateSettingsAction; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsAction; import org.opensearch.action.admin.cluster.shards.TransportClusterSearchShardsAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.TransportDeleteWeightedRoutingAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.TransportGetWeightedRoutingAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterAddWeightedRoutingAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.TransportAddWeightedRoutingAction; import org.opensearch.action.admin.cluster.snapshots.clone.CloneSnapshotAction; import org.opensearch.action.admin.cluster.snapshots.clone.TransportCloneSnapshotAction; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotAction; @@ -108,8 +124,6 @@ import org.opensearch.action.admin.indices.alias.IndicesAliasesAction; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; import org.opensearch.action.admin.indices.alias.TransportIndicesAliasesAction; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistAction; -import org.opensearch.action.admin.indices.alias.exists.TransportAliasesExistAction; import org.opensearch.action.admin.indices.alias.get.GetAliasesAction; import org.opensearch.action.admin.indices.alias.get.TransportGetAliasesAction; import org.opensearch.action.admin.indices.analyze.AnalyzeAction; @@ -137,8 +151,6 @@ import org.opensearch.action.admin.indices.delete.TransportDeleteIndexAction; import org.opensearch.action.admin.indices.exists.indices.IndicesExistsAction; import org.opensearch.action.admin.indices.exists.indices.TransportIndicesExistsAction; -import org.opensearch.action.admin.indices.exists.types.TransportTypesExistsAction; -import org.opensearch.action.admin.indices.exists.types.TypesExistsAction; import org.opensearch.action.admin.indices.flush.FlushAction; import org.opensearch.action.admin.indices.flush.TransportFlushAction; import org.opensearch.action.admin.indices.forcemerge.ForceMergeAction; @@ -163,11 +175,15 @@ import org.opensearch.action.admin.indices.recovery.TransportRecoveryAction; import org.opensearch.action.admin.indices.refresh.RefreshAction; import org.opensearch.action.admin.indices.refresh.TransportRefreshAction; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsAction; +import org.opensearch.action.admin.indices.replication.TransportSegmentReplicationStatsAction; import org.opensearch.action.admin.indices.resolve.ResolveIndexAction; import org.opensearch.action.admin.indices.rollover.RolloverAction; import org.opensearch.action.admin.indices.rollover.TransportRolloverAction; import org.opensearch.action.admin.indices.segments.IndicesSegmentsAction; +import org.opensearch.action.admin.indices.segments.PitSegmentsAction; import org.opensearch.action.admin.indices.segments.TransportIndicesSegmentsAction; +import org.opensearch.action.admin.indices.segments.TransportPitSegmentsAction; import org.opensearch.action.admin.indices.settings.get.GetSettingsAction; import org.opensearch.action.admin.indices.settings.get.TransportGetSettingsAction; import org.opensearch.action.admin.indices.settings.put.TransportUpdateSettingsAction; @@ -236,10 +252,22 @@ import org.opensearch.action.main.MainAction; import org.opensearch.action.main.TransportMainAction; import org.opensearch.action.search.ClearScrollAction; +import org.opensearch.action.search.CreatePitAction; +import org.opensearch.action.search.DeletePitAction; +import org.opensearch.action.search.DeleteSearchPipelineAction; +import org.opensearch.action.search.DeleteSearchPipelineTransportAction; +import org.opensearch.action.search.GetAllPitsAction; +import org.opensearch.action.search.GetSearchPipelineAction; +import org.opensearch.action.search.GetSearchPipelineTransportAction; import org.opensearch.action.search.MultiSearchAction; +import org.opensearch.action.search.PutSearchPipelineAction; +import org.opensearch.action.search.PutSearchPipelineTransportAction; import org.opensearch.action.search.SearchAction; import org.opensearch.action.search.SearchScrollAction; import org.opensearch.action.search.TransportClearScrollAction; +import org.opensearch.action.search.TransportCreatePitAction; +import org.opensearch.action.search.TransportDeletePitAction; +import org.opensearch.action.search.TransportGetAllPitsAction; import org.opensearch.action.search.TransportMultiSearchAction; import org.opensearch.action.search.TransportSearchAction; import org.opensearch.action.search.TransportSearchScrollAction; @@ -265,15 +293,24 @@ import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.indices.breaker.CircuitBreakerService; +import org.opensearch.extensions.ExtensionsManager; +import org.opensearch.extensions.action.ExtensionProxyAction; +import org.opensearch.extensions.action.ExtensionProxyTransportAction; +import org.opensearch.extensions.rest.RestInitializeExtensionAction; +import org.opensearch.extensions.rest.RestSendToExtensionAction; +import org.opensearch.identity.IdentityService; import org.opensearch.index.seqno.RetentionLeaseActions; import org.opensearch.indices.SystemIndices; -import org.opensearch.indices.breaker.CircuitBreakerService; import org.opensearch.persistent.CompletionPersistentTaskAction; import org.opensearch.persistent.RemovePersistentTaskAction; import org.opensearch.persistent.StartPersistentTaskAction; import org.opensearch.persistent.UpdatePersistentTaskStatusAction; import org.opensearch.plugins.ActionPlugin; import org.opensearch.plugins.ActionPlugin.ActionHandler; +import org.opensearch.rest.NamedRoute; import org.opensearch.rest.RestController; import org.opensearch.rest.RestHandler; import org.opensearch.rest.RestHeaderDefinition; @@ -285,17 +322,23 @@ import org.opensearch.rest.action.admin.cluster.RestClearVotingConfigExclusionsAction; import org.opensearch.rest.action.admin.cluster.RestCloneSnapshotAction; import org.opensearch.rest.action.admin.cluster.RestClusterAllocationExplainAction; +import org.opensearch.rest.action.admin.cluster.RestClusterDeleteWeightedRoutingAction; import org.opensearch.rest.action.admin.cluster.RestClusterGetSettingsAction; +import org.opensearch.rest.action.admin.cluster.RestClusterGetWeightedRoutingAction; import org.opensearch.rest.action.admin.cluster.RestClusterHealthAction; +import org.opensearch.rest.action.admin.cluster.RestClusterPutWeightedRoutingAction; import org.opensearch.rest.action.admin.cluster.RestClusterRerouteAction; import org.opensearch.rest.action.admin.cluster.RestClusterSearchShardsAction; import org.opensearch.rest.action.admin.cluster.RestClusterStateAction; import org.opensearch.rest.action.admin.cluster.RestClusterStatsAction; import org.opensearch.rest.action.admin.cluster.RestClusterUpdateSettingsAction; import org.opensearch.rest.action.admin.cluster.RestCreateSnapshotAction; +import org.opensearch.rest.action.admin.cluster.RestDecommissionAction; +import org.opensearch.rest.action.admin.cluster.RestDeleteDecommissionStateAction; import org.opensearch.rest.action.admin.cluster.RestDeleteRepositoryAction; import org.opensearch.rest.action.admin.cluster.RestDeleteSnapshotAction; import org.opensearch.rest.action.admin.cluster.RestDeleteStoredScriptAction; +import org.opensearch.rest.action.admin.cluster.RestGetDecommissionStateAction; import org.opensearch.rest.action.admin.cluster.RestGetRepositoriesAction; import org.opensearch.rest.action.admin.cluster.RestGetScriptContextAction; import org.opensearch.rest.action.admin.cluster.RestGetScriptLanguageAction; @@ -312,6 +355,8 @@ import org.opensearch.rest.action.admin.cluster.RestPutStoredScriptAction; import org.opensearch.rest.action.admin.cluster.RestReloadSecureSettingsAction; import org.opensearch.rest.action.admin.cluster.RestRemoteClusterInfoAction; +import org.opensearch.rest.action.admin.cluster.RestRemoteStoreStatsAction; +import org.opensearch.rest.action.admin.cluster.RestRestoreRemoteStoreAction; import org.opensearch.rest.action.admin.cluster.RestRestoreSnapshotAction; import org.opensearch.rest.action.admin.cluster.RestSnapshotsStatusAction; import org.opensearch.rest.action.admin.cluster.RestVerifyRepositoryAction; @@ -369,12 +414,14 @@ import org.opensearch.rest.action.cat.RestAllocationAction; import org.opensearch.rest.action.cat.RestCatAction; import org.opensearch.rest.action.cat.RestCatRecoveryAction; +import org.opensearch.rest.action.cat.RestCatSegmentReplicationAction; +import org.opensearch.rest.action.cat.RestClusterManagerAction; import org.opensearch.rest.action.cat.RestFielddataAction; import org.opensearch.rest.action.cat.RestHealthAction; import org.opensearch.rest.action.cat.RestIndicesAction; -import org.opensearch.rest.action.cat.RestMasterAction; import org.opensearch.rest.action.cat.RestNodeAttrsAction; import org.opensearch.rest.action.cat.RestNodesAction; +import org.opensearch.rest.action.cat.RestPitSegmentsAction; import org.opensearch.rest.action.cat.RestPluginsAction; import org.opensearch.rest.action.cat.RestRepositoriesAction; import org.opensearch.rest.action.cat.RestSegmentsAction; @@ -400,8 +447,14 @@ import org.opensearch.rest.action.ingest.RestSimulatePipelineAction; import org.opensearch.rest.action.search.RestClearScrollAction; import org.opensearch.rest.action.search.RestCountAction; +import org.opensearch.rest.action.search.RestCreatePitAction; +import org.opensearch.rest.action.search.RestDeletePitAction; +import org.opensearch.rest.action.search.RestDeleteSearchPipelineAction; import org.opensearch.rest.action.search.RestExplainAction; +import org.opensearch.rest.action.search.RestGetAllPitsAction; +import org.opensearch.rest.action.search.RestGetSearchPipelineAction; import org.opensearch.rest.action.search.RestMultiSearchAction; +import org.opensearch.rest.action.search.RestPutSearchPipelineAction; import org.opensearch.rest.action.search.RestSearchAction; import org.opensearch.rest.action.search.RestSearchScrollAction; import org.opensearch.tasks.Task; @@ -410,9 +463,12 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.function.UnaryOperator; @@ -420,9 +476,12 @@ import java.util.stream.Stream; import static java.util.Collections.unmodifiableMap; +import static java.util.Objects.requireNonNull; /** * Builds and binds the generic action map, all {@link TransportAction}s, and {@link ActionFilters}. + * + * @opensearch.internal */ public class ActionModule extends AbstractModule { @@ -434,7 +493,17 @@ public class ActionModule extends AbstractModule { private final ClusterSettings clusterSettings; private final SettingsFilter settingsFilter; private final List actionPlugins; + // The unmodifiable map containing OpenSearch and Plugin actions + // This is initialized at node bootstrap and contains same-JVM actions + // It will be wrapped in the Dynamic Action Registry but otherwise + // remains unchanged from its prior purpose, and registered actions + // will remain accessible. private final Map> actions; + // A dynamic action registry which includes the above immutable actions + // and also registers dynamic actions which may be unregistered. Usually + // associated with remote action execution on extensions, possibly in + // a different JVM and possibly on a different server. + private final DynamicActionRegistry dynamicActionRegistry; private final ActionFilters actionFilters; private final AutoCreateIndex autoCreateIndex; private final DestructiveOperations destructiveOperations; @@ -442,6 +511,7 @@ public class ActionModule extends AbstractModule { private final RequestValidators mappingRequestValidators; private final RequestValidators indicesAliasesRequestRequestValidators; private final ThreadPool threadPool; + private final ExtensionsManager extensionsManager; public ActionModule( Settings settings, @@ -454,7 +524,9 @@ public ActionModule( NodeClient nodeClient, CircuitBreakerService circuitBreakerService, UsageService usageService, - SystemIndices systemIndices + SystemIndices systemIndices, + IdentityService identityService, + ExtensionsManager extensionsManager ) { this.settings = settings; this.indexNameExpressionResolver = indexNameExpressionResolver; @@ -463,8 +535,10 @@ public ActionModule( this.settingsFilter = settingsFilter; this.actionPlugins = actionPlugins; this.threadPool = threadPool; + this.extensionsManager = extensionsManager; actions = setupActions(actionPlugins); actionFilters = setupActionFilters(actionPlugins); + dynamicActionRegistry = new DynamicActionRegistry(); autoCreateIndex = new AutoCreateIndex(settings, clusterSettings, indexNameExpressionResolver, systemIndices); destructiveOperations = new DestructiveOperations(settings, clusterSettings); Set headers = Stream.concat( @@ -489,7 +563,7 @@ public ActionModule( actionPlugins.stream().flatMap(p -> p.indicesAliasesRequestValidators().stream()).collect(Collectors.toList()) ); - restController = new RestController(headers, restWrapper, nodeClient, circuitBreakerService, usageService); + restController = new RestController(headers, restWrapper, nodeClient, circuitBreakerService, usageService, identityService); } public Map> getActions() { @@ -521,6 +595,7 @@ public void reg actions.register(NodesInfoAction.INSTANCE, TransportNodesInfoAction.class); actions.register(RemoteInfoAction.INSTANCE, TransportRemoteInfoAction.class); actions.register(NodesStatsAction.INSTANCE, TransportNodesStatsAction.class); + actions.register(RemoteStoreStatsAction.INSTANCE, TransportRemoteStoreStatsAction.class); actions.register(NodesUsageAction.INSTANCE, TransportNodesUsageAction.class); actions.register(NodesHotThreadsAction.INSTANCE, TransportNodesHotThreadsAction.class); actions.register(ListTasksAction.INSTANCE, TransportListTasksAction.class); @@ -549,6 +624,9 @@ public void reg actions.register(RestoreSnapshotAction.INSTANCE, TransportRestoreSnapshotAction.class); actions.register(SnapshotsStatusAction.INSTANCE, TransportSnapshotsStatusAction.class); + actions.register(ClusterAddWeightedRoutingAction.INSTANCE, TransportAddWeightedRoutingAction.class); + actions.register(ClusterGetWeightedRoutingAction.INSTANCE, TransportGetWeightedRoutingAction.class); + actions.register(ClusterDeleteWeightedRoutingAction.INSTANCE, TransportDeleteWeightedRoutingAction.class); actions.register(IndicesStatsAction.INSTANCE, TransportIndicesStatsAction.class); actions.register(IndicesSegmentsAction.INSTANCE, TransportIndicesSegmentsAction.class); actions.register(IndicesShardStoresAction.INSTANCE, TransportIndicesShardStoresAction.class); @@ -560,7 +638,6 @@ public void reg actions.register(OpenIndexAction.INSTANCE, TransportOpenIndexAction.class); actions.register(CloseIndexAction.INSTANCE, TransportCloseIndexAction.class); actions.register(IndicesExistsAction.INSTANCE, TransportIndicesExistsAction.class); - actions.register(TypesExistsAction.INSTANCE, TransportTypesExistsAction.class); actions.register(AddIndexBlockAction.INSTANCE, TransportAddIndexBlockAction.class); actions.register(GetMappingsAction.INSTANCE, TransportGetMappingsAction.class); actions.register( @@ -593,7 +670,6 @@ public void reg actions.register(UpgradeSettingsAction.INSTANCE, TransportUpgradeSettingsAction.class); actions.register(ClearIndicesCacheAction.INSTANCE, TransportClearIndicesCacheAction.class); actions.register(GetAliasesAction.INSTANCE, TransportGetAliasesAction.class); - actions.register(AliasesExistAction.INSTANCE, TransportAliasesExistAction.class); actions.register(GetSettingsAction.INSTANCE, TransportGetSettingsAction.class); actions.register(IndexAction.INSTANCE, TransportIndexAction.class); @@ -614,6 +690,7 @@ public void reg actions.register(ExplainAction.INSTANCE, TransportExplainAction.class); actions.register(ClearScrollAction.INSTANCE, TransportClearScrollAction.class); actions.register(RecoveryAction.INSTANCE, TransportRecoveryAction.class); + actions.register(SegmentReplicationStatsAction.INSTANCE, TransportSegmentReplicationStatsAction.class); actions.register(NodesReloadSecureSettingsAction.INSTANCE, TransportNodesReloadSecureSettingsAction.class); actions.register(AutoCreateAction.INSTANCE, AutoCreateAction.TransportAction.class); @@ -661,6 +738,30 @@ public void reg actions.register(DeleteDanglingIndexAction.INSTANCE, TransportDeleteDanglingIndexAction.class); actions.register(FindDanglingIndexAction.INSTANCE, TransportFindDanglingIndexAction.class); + // Remote Store + actions.register(RestoreRemoteStoreAction.INSTANCE, TransportRestoreRemoteStoreAction.class); + + // point in time actions + actions.register(CreatePitAction.INSTANCE, TransportCreatePitAction.class); + actions.register(DeletePitAction.INSTANCE, TransportDeletePitAction.class); + actions.register(PitSegmentsAction.INSTANCE, TransportPitSegmentsAction.class); + actions.register(GetAllPitsAction.INSTANCE, TransportGetAllPitsAction.class); + + if (FeatureFlags.isEnabled(FeatureFlags.EXTENSIONS)) { + // ExtensionProxyAction + actions.register(ExtensionProxyAction.INSTANCE, ExtensionProxyTransportAction.class); + } + + // Decommission actions + actions.register(DecommissionAction.INSTANCE, TransportDecommissionAction.class); + actions.register(GetDecommissionStateAction.INSTANCE, TransportGetDecommissionStateAction.class); + actions.register(DeleteDecommissionStateAction.INSTANCE, TransportDeleteDecommissionStateAction.class); + + // Search Pipelines + actions.register(PutSearchPipelineAction.INSTANCE, PutSearchPipelineTransportAction.class); + actions.register(GetSearchPipelineAction.INSTANCE, GetSearchPipelineTransportAction.class); + actions.register(DeleteSearchPipelineAction.INSTANCE, DeleteSearchPipelineTransportAction.class); + return unmodifiableMap(actions.getRegistry()); } @@ -724,6 +825,10 @@ public void initRestHandlers(Supplier nodesInCluster) { registerHandler.accept(new RestOpenIndexAction()); registerHandler.accept(new RestAddIndexBlockAction()); + registerHandler.accept(new RestClusterPutWeightedRoutingAction()); + registerHandler.accept(new RestClusterGetWeightedRoutingAction()); + registerHandler.accept(new RestClusterDeleteWeightedRoutingAction()); + registerHandler.accept(new RestUpdateSettingsAction()); registerHandler.accept(new RestGetSettingsAction()); @@ -812,8 +917,9 @@ public void initRestHandlers(Supplier nodesInCluster) { // CAT API registerHandler.accept(new RestAllocationAction()); + registerHandler.accept(new RestCatSegmentReplicationAction()); registerHandler.accept(new RestShardsAction()); - registerHandler.accept(new RestMasterAction()); + registerHandler.accept(new RestClusterManagerAction()); registerHandler.accept(new RestNodesAction()); registerHandler.accept(new RestTasksAction(nodesInCluster)); registerHandler.accept(new RestIndicesAction()); @@ -832,6 +938,24 @@ public void initRestHandlers(Supplier nodesInCluster) { registerHandler.accept(new RestRepositoriesAction()); registerHandler.accept(new RestSnapshotAction()); registerHandler.accept(new RestTemplatesAction()); + + // Point in time API + registerHandler.accept(new RestCreatePitAction()); + registerHandler.accept(new RestDeletePitAction()); + registerHandler.accept(new RestGetAllPitsAction(nodesInCluster)); + registerHandler.accept(new RestPitSegmentsAction(nodesInCluster)); + registerHandler.accept(new RestDeleteDecommissionStateAction()); + + // Search pipelines API + registerHandler.accept(new RestPutSearchPipelineAction()); + registerHandler.accept(new RestGetSearchPipelineAction()); + registerHandler.accept(new RestDeleteSearchPipelineAction()); + + // Extensions API + if (FeatureFlags.isEnabled(FeatureFlags.EXTENSIONS)) { + registerHandler.accept(new RestInitializeExtensionAction(extensionsManager)); + } + for (ActionPlugin plugin : actionPlugins) { for (RestHandler handler : plugin.getRestHandlers( settings, @@ -846,6 +970,10 @@ public void initRestHandlers(Supplier nodesInCluster) { } } registerHandler.accept(new RestCatAction(catActions)); + registerHandler.accept(new RestDecommissionAction()); + registerHandler.accept(new RestGetDecommissionStateAction()); + registerHandler.accept(new RestRemoteStoreStatsAction()); + registerHandler.accept(new RestRestoreRemoteStoreAction()); } @Override @@ -876,13 +1004,166 @@ protected void configure() { bind(supportAction).asEagerSingleton(); } } + + // register dynamic ActionType -> transportAction Map used by NodeClient + bind(DynamicActionRegistry.class).toInstance(dynamicActionRegistry); } public ActionFilters getActionFilters() { return actionFilters; } + public DynamicActionRegistry getDynamicActionRegistry() { + return dynamicActionRegistry; + } + public RestController getRestController() { return restController; } + + /** + * The DynamicActionRegistry maintains a registry mapping {@link ActionType} instances to {@link TransportAction} instances. + *

    + * This class is modeled after {@link NamedRegistry} but provides both register and unregister capabilities. + * + * @opensearch.internal + */ + public static class DynamicActionRegistry { + // This is the unmodifiable actions map created during node bootstrap, which + // will continue to link ActionType and TransportAction pairs from core and plugin + // action handler registration. + private Map actions = Collections.emptyMap(); + // A dynamic registry to add or remove ActionType / TransportAction pairs + // at times other than node bootstrap. + private final Map, TransportAction> registry = new ConcurrentHashMap<>(); + + // A dynamic registry to add or remove Route / RestSendToExtensionAction pairs + // at times other than node bootstrap. + private final Map routeRegistry = new ConcurrentHashMap<>(); + + private final Set registeredActionNames = new ConcurrentSkipListSet<>(); + + /** + * Register the immutable actions in the registry. + * + * @param actions The injected map of {@link ActionType} to {@link TransportAction} + */ + public void registerUnmodifiableActionMap(Map actions) { + this.actions = actions; + for (ActionType action : actions.keySet()) { + registeredActionNames.add(action.name()); + } + } + + /** + * Add a dynamic action to the registry. + * + * @param action The action instance to add + * @param transportAction The corresponding instance of transportAction to execute + */ + public void registerDynamicAction(ActionType action, TransportAction transportAction) { + requireNonNull(action, "action is required"); + requireNonNull(transportAction, "transportAction is required"); + if (actions.containsKey(action) || registry.putIfAbsent(action, transportAction) != null) { + throw new IllegalArgumentException("action [" + action.name() + "] already registered"); + } + registeredActionNames.add(action.name()); + } + + /** + * Remove a dynamic action from the registry. + * + * @param action The action to remove + */ + public void unregisterDynamicAction(ActionType action) { + requireNonNull(action, "action is required"); + if (registry.remove(action) == null) { + throw new IllegalArgumentException("action [" + action.name() + "] was not registered"); + } + registeredActionNames.remove(action.name()); + } + + /** + * Checks to see if an action is registered provided an action name + * + * @param actionName The name of the action to check + */ + public boolean isActionRegistered(String actionName) { + return registeredActionNames.contains(actionName); + } + + /** + * Gets the {@link TransportAction} instance corresponding to the {@link ActionType} instance. + * + * @param action The {@link ActionType}. + * @return the corresponding {@link TransportAction} if it is registered, null otherwise. + */ + @SuppressWarnings("unchecked") + public TransportAction get(ActionType action) { + if (actions.containsKey(action)) { + return actions.get(action); + } + return registry.get(action); + } + + /** + * Adds a dynamic route to the registry. + * + * @param route The route instance to add + * @param action The corresponding instance of RestSendToExtensionAction to execute + */ + public void registerDynamicRoute(NamedRoute route, RestSendToExtensionAction action) { + requireNonNull(route, "route is required"); + requireNonNull(action, "action is required"); + + String routeName = route.name(); + requireNonNull(routeName, "route name is required"); + if (isActionRegistered(routeName)) { + throw new IllegalArgumentException("route [" + route + "] already registered"); + } + + Set actionNames = route.actionNames(); + if (!Collections.disjoint(actionNames, registeredActionNames)) { + Set alreadyRegistered = new HashSet<>(registeredActionNames); + alreadyRegistered.retainAll(actionNames); + String acts = String.join(", ", alreadyRegistered); + throw new IllegalArgumentException( + "action" + (alreadyRegistered.size() > 1 ? "s [" : " [") + acts + "] already registered" + ); + } + + if (routeRegistry.containsKey(route)) { + throw new IllegalArgumentException("route [" + route + "] already registered"); + } + routeRegistry.put(route, action); + registeredActionNames.add(routeName); + registeredActionNames.addAll(actionNames); + } + + /** + * Remove a dynamic route from the registry. + * + * @param route The route to remove + */ + public void unregisterDynamicRoute(NamedRoute route) { + requireNonNull(route, "route is required"); + if (routeRegistry.remove(route) == null) { + throw new IllegalArgumentException("action [" + route + "] was not registered"); + } + + registeredActionNames.remove(route.name()); + registeredActionNames.removeAll(route.actionNames()); + } + + /** + * Gets the {@link RestSendToExtensionAction} instance corresponding to the {@link RestHandler.Route} instance. + * + * @param route The {@link RestHandler.Route}. + * @return the corresponding {@link RestSendToExtensionAction} if it is registered, null otherwise. + */ + @SuppressWarnings("unchecked") + public RestSendToExtensionAction get(RestHandler.Route route) { + return routeRegistry.get(route); + } + } } diff --git a/server/src/main/java/org/opensearch/action/ActionRequest.java b/server/src/main/java/org/opensearch/action/ActionRequest.java index d000155c55b7d..80511a7ded4f6 100644 --- a/server/src/main/java/org/opensearch/action/ActionRequest.java +++ b/server/src/main/java/org/opensearch/action/ActionRequest.java @@ -32,12 +32,17 @@ package org.opensearch.action; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.transport.TransportRequest; import java.io.IOException; +/** + * Base action request + * + * @opensearch.internal + */ public abstract class ActionRequest extends TransportRequest { public ActionRequest() { diff --git a/server/src/main/java/org/opensearch/action/ActionRequestBuilder.java b/server/src/main/java/org/opensearch/action/ActionRequestBuilder.java index 7a81d07c6927a..765d0004d744c 100644 --- a/server/src/main/java/org/opensearch/action/ActionRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/ActionRequestBuilder.java @@ -33,10 +33,18 @@ package org.opensearch.action; import org.opensearch.client.OpenSearchClient; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; import java.util.Objects; +/** + * Base Action Request Builder + * + * @opensearch.internal + */ public abstract class ActionRequestBuilder { protected final ActionType action; diff --git a/server/src/main/java/org/opensearch/action/ActionRequestValidationException.java b/server/src/main/java/org/opensearch/action/ActionRequestValidationException.java index 7ada8cdc77f14..d7da932c4dfc2 100644 --- a/server/src/main/java/org/opensearch/action/ActionRequestValidationException.java +++ b/server/src/main/java/org/opensearch/action/ActionRequestValidationException.java @@ -34,4 +34,9 @@ import org.opensearch.common.ValidationException; +/** + * Base exception for an action request validation + * + * @opensearch.internal + */ public class ActionRequestValidationException extends ValidationException {} diff --git a/server/src/main/java/org/opensearch/action/ActionRunnable.java b/server/src/main/java/org/opensearch/action/ActionRunnable.java index 84b04214e79ee..c8228739ae58e 100644 --- a/server/src/main/java/org/opensearch/action/ActionRunnable.java +++ b/server/src/main/java/org/opensearch/action/ActionRunnable.java @@ -36,10 +36,13 @@ import org.opensearch.common.CheckedRunnable; import org.opensearch.common.CheckedSupplier; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.action.ActionListener; /** * Base class for {@link Runnable}s that need to call {@link ActionListener#onFailure(Exception)} in case an uncaught * exception or error is thrown while the actual action is run. + * + * @opensearch.internal */ public abstract class ActionRunnable extends AbstractRunnable { diff --git a/server/src/main/java/org/opensearch/action/ActionType.java b/server/src/main/java/org/opensearch/action/ActionType.java index fbc9524ce4e52..b9798507705d4 100644 --- a/server/src/main/java/org/opensearch/action/ActionType.java +++ b/server/src/main/java/org/opensearch/action/ActionType.java @@ -32,12 +32,16 @@ package org.opensearch.action; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.transport.TransportRequestOptions; /** * A generic action. Should strive to make it a singleton. + * + * @opensearch.internal */ public class ActionType { @@ -61,7 +65,7 @@ public String name() { } /** - * Get a reader that can create a new instance of the class from a {@link org.opensearch.common.io.stream.StreamInput} + * Get a reader that can create a new instance of the class from a {@link StreamInput} */ public Writeable.Reader getResponseReader() { return responseReader; diff --git a/server/src/main/java/org/opensearch/action/AliasesRequest.java b/server/src/main/java/org/opensearch/action/AliasesRequest.java index a33db646cebba..4c5d5628b1aac 100644 --- a/server/src/main/java/org/opensearch/action/AliasesRequest.java +++ b/server/src/main/java/org/opensearch/action/AliasesRequest.java @@ -37,6 +37,8 @@ * one or more indices and one or more aliases. Meant to be used for aliases management requests (e.g. add/remove alias, * get aliases) that hold aliases and indices in separate fields. * Allows to retrieve which indices and aliases the action relates to. + * + * @opensearch.internal */ public interface AliasesRequest extends IndicesRequest.Replaceable { diff --git a/server/src/main/java/org/opensearch/action/CompositeIndicesRequest.java b/server/src/main/java/org/opensearch/action/CompositeIndicesRequest.java index 25ef0d220beec..1ec2350e713df 100644 --- a/server/src/main/java/org/opensearch/action/CompositeIndicesRequest.java +++ b/server/src/main/java/org/opensearch/action/CompositeIndicesRequest.java @@ -37,5 +37,7 @@ * multiple sub-requests which relate to one or more indices. A composite request is executed by its own transport action class * (e.g. {@link org.opensearch.action.search.TransportMultiSearchAction}), which goes through all sub-requests and delegates their * execution to the appropriate transport action (e.g. {@link org.opensearch.action.search.TransportSearchAction}) for each single item. + * + * @opensearch.internal */ public interface CompositeIndicesRequest {} diff --git a/server/src/main/java/org/opensearch/action/DocWriteRequest.java b/server/src/main/java/org/opensearch/action/DocWriteRequest.java index 11d645435c71c..e5979e93d7040 100644 --- a/server/src/main/java/org/opensearch/action/DocWriteRequest.java +++ b/server/src/main/java/org/opensearch/action/DocWriteRequest.java @@ -32,16 +32,17 @@ package org.opensearch.action; import org.apache.lucene.util.Accountable; +import org.apache.lucene.util.UnicodeUtil; import org.opensearch.action.delete.DeleteRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.update.UpdateRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.uid.Versions; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.VersionType; -import org.opensearch.index.shard.ShardId; import java.io.IOException; import java.util.Locale; @@ -53,6 +54,8 @@ /** * Generic interface to group ActionRequest, which perform writes to a single document * Action requests implementing this can be part of {@link org.opensearch.action.bulk.BulkRequest} + * + * @opensearch.internal */ public interface DocWriteRequest extends IndicesRequest, Accountable { @@ -247,6 +250,25 @@ static DocWriteRequest readDocumentRequest(@Nullable ShardId shardId, StreamI return docWriteRequest; } + /** + * Validates whether the doc id length is under the limit. + * @param id DocId to verify + * @param validationException containing all the validation errors. + * @return validationException + */ + static ActionRequestValidationException validateDocIdLength(String id, ActionRequestValidationException validationException) { + if (id != null) { + int docIdLength = UnicodeUtil.calcUTF16toUTF8Length(id, 0, id.length()); + if (docIdLength > 512) { + return addValidationError( + "id [" + id + "] is too long, must be no longer than 512 bytes but was: " + docIdLength, + validationException + ); + } + } + return validationException; + } + /** write a document write (index/delete/update) request*/ static void writeDocumentRequest(StreamOutput out, DocWriteRequest request) throws IOException { if (request instanceof IndexRequest) { diff --git a/server/src/main/java/org/opensearch/action/DocWriteResponse.java b/server/src/main/java/org/opensearch/action/DocWriteResponse.java index 587f93ed09f52..afdb1d3a0bdd9 100644 --- a/server/src/main/java/org/opensearch/action/DocWriteResponse.java +++ b/server/src/main/java/org/opensearch/action/DocWriteResponse.java @@ -38,18 +38,18 @@ import org.opensearch.action.support.replication.ReplicationResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.index.Index; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.IndexSettings; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.seqno.SequenceNumbers; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -57,12 +57,14 @@ import java.util.Locale; import java.util.Objects; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; import static org.opensearch.index.seqno.SequenceNumbers.UNASSIGNED_PRIMARY_TERM; import static org.opensearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO; /** * A base class for the response of a write operation that involves a single doc + * + * @opensearch.internal */ public abstract class DocWriteResponse extends ReplicationResponse implements WriteResponse, StatusToXContentObject { @@ -389,6 +391,8 @@ protected static void parseInnerToXContent(XContentParser parser, Builder contex * Base class of all {@link DocWriteResponse} builders. These {@link DocWriteResponse.Builder} are used during * xcontent parsing to temporarily store the parsed values, then the {@link Builder#build()} method is called to * instantiate the appropriate {@link DocWriteResponse} with the parsed values. + * + * @opensearch.internal */ public abstract static class Builder { diff --git a/server/src/main/java/org/opensearch/action/FailedNodeException.java b/server/src/main/java/org/opensearch/action/FailedNodeException.java index b4927d0b40a6a..cebcbd38741a0 100644 --- a/server/src/main/java/org/opensearch/action/FailedNodeException.java +++ b/server/src/main/java/org/opensearch/action/FailedNodeException.java @@ -33,12 +33,17 @@ package org.opensearch.action; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; +/** + * Base exception for a failed node + * + * @opensearch.internal + */ public class FailedNodeException extends OpenSearchException { private final String nodeId; diff --git a/server/src/main/java/org/opensearch/action/IndicesRequest.java b/server/src/main/java/org/opensearch/action/IndicesRequest.java index 18da6ed0f62b2..7e4c2f5076cda 100644 --- a/server/src/main/java/org/opensearch/action/IndicesRequest.java +++ b/server/src/main/java/org/opensearch/action/IndicesRequest.java @@ -39,6 +39,8 @@ * one or more indices. Allows to retrieve which indices the action relates to. * In case of internal requests originated during the distributed execution of an external request, * they will still return the indices that the original request related to. + * + * @opensearch.internal */ public interface IndicesRequest { @@ -62,6 +64,11 @@ default boolean includeDataStreams() { return false; } + /** + * Replaceable interface. + * + * @opensearch.internal + */ interface Replaceable extends IndicesRequest { /** * Sets the indices that the action relates to. diff --git a/server/src/main/java/org/opensearch/action/LatchedActionListener.java b/server/src/main/java/org/opensearch/action/LatchedActionListener.java index 4a79cfd898a85..c2400d3b59850 100644 --- a/server/src/main/java/org/opensearch/action/LatchedActionListener.java +++ b/server/src/main/java/org/opensearch/action/LatchedActionListener.java @@ -32,11 +32,15 @@ package org.opensearch.action; +import org.opensearch.core.action.ActionListener; + import java.util.concurrent.CountDownLatch; /** * An action listener that allows passing in a {@link CountDownLatch} that * will be counted down after onResponse or onFailure is called + * + * @opensearch.internal */ public class LatchedActionListener implements ActionListener { diff --git a/server/src/main/java/org/opensearch/action/ListenableActionFuture.java b/server/src/main/java/org/opensearch/action/ListenableActionFuture.java index 9c8510a06173c..1679ec804643e 100644 --- a/server/src/main/java/org/opensearch/action/ListenableActionFuture.java +++ b/server/src/main/java/org/opensearch/action/ListenableActionFuture.java @@ -32,10 +32,13 @@ package org.opensearch.action; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.core.action.ActionListener; + /** * An {@link ActionFuture} that listeners can be added to. * - * + * @opensearch.internal */ public interface ListenableActionFuture extends ActionFuture { diff --git a/server/src/main/java/org/opensearch/action/NoShardAvailableActionException.java b/server/src/main/java/org/opensearch/action/NoShardAvailableActionException.java index 4e1571d8007f6..d90ddeeba69af 100644 --- a/server/src/main/java/org/opensearch/action/NoShardAvailableActionException.java +++ b/server/src/main/java/org/opensearch/action/NoShardAvailableActionException.java @@ -33,12 +33,17 @@ package org.opensearch.action; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; +/** + * Base exception for no shard available + * + * @opensearch.internal + */ public class NoShardAvailableActionException extends OpenSearchException { public NoShardAvailableActionException(ShardId shardId) { diff --git a/server/src/main/java/org/opensearch/action/NoSuchNodeException.java b/server/src/main/java/org/opensearch/action/NoSuchNodeException.java index 898a3d124111c..58fa110973b2c 100644 --- a/server/src/main/java/org/opensearch/action/NoSuchNodeException.java +++ b/server/src/main/java/org/opensearch/action/NoSuchNodeException.java @@ -32,10 +32,15 @@ package org.opensearch.action; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Base exception for no node found + * + * @opensearch.internal + */ public class NoSuchNodeException extends FailedNodeException { public NoSuchNodeException(String nodeId) { diff --git a/server/src/main/java/org/opensearch/action/OriginalIndices.java b/server/src/main/java/org/opensearch/action/OriginalIndices.java index 759585fb499a0..1e24c64bc60fc 100644 --- a/server/src/main/java/org/opensearch/action/OriginalIndices.java +++ b/server/src/main/java/org/opensearch/action/OriginalIndices.java @@ -33,14 +33,16 @@ package org.opensearch.action; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Arrays; /** * Used to keep track of original indices within internal (e.g. shard level) requests + * + * @opensearch.internal */ public final class OriginalIndices implements IndicesRequest { diff --git a/server/src/main/java/org/opensearch/action/PrimaryMissingActionException.java b/server/src/main/java/org/opensearch/action/PrimaryMissingActionException.java index 57ae5056d6cd9..959a2ee655fe1 100644 --- a/server/src/main/java/org/opensearch/action/PrimaryMissingActionException.java +++ b/server/src/main/java/org/opensearch/action/PrimaryMissingActionException.java @@ -33,10 +33,15 @@ package org.opensearch.action; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Base exception for a missing action on a primary + * + * @opensearch.internal + */ public class PrimaryMissingActionException extends OpenSearchException { public PrimaryMissingActionException(String message) { diff --git a/server/src/main/java/org/opensearch/action/RealtimeRequest.java b/server/src/main/java/org/opensearch/action/RealtimeRequest.java index 37865a6cbff8d..0ceb79ec903d9 100644 --- a/server/src/main/java/org/opensearch/action/RealtimeRequest.java +++ b/server/src/main/java/org/opensearch/action/RealtimeRequest.java @@ -35,6 +35,8 @@ /** * Indicates that a request can execute in realtime (reads from the translog). * All {@link ActionRequest} that are realtime should implement this interface. + * + * @opensearch.internal */ public interface RealtimeRequest { diff --git a/server/src/main/java/org/opensearch/action/RequestValidators.java b/server/src/main/java/org/opensearch/action/RequestValidators.java index 44245c0906882..5cdf20616afc5 100644 --- a/server/src/main/java/org/opensearch/action/RequestValidators.java +++ b/server/src/main/java/org/opensearch/action/RequestValidators.java @@ -33,11 +33,16 @@ package org.opensearch.action; import org.opensearch.cluster.ClusterState; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import java.util.Collection; import java.util.Optional; +/** + * Validates transport requests + * + * @opensearch.internal + */ public class RequestValidators { private final Collection> validators; diff --git a/server/src/main/java/org/opensearch/action/RoutingMissingException.java b/server/src/main/java/org/opensearch/action/RoutingMissingException.java index 4f34a7847da4d..f99e86820bbb2 100644 --- a/server/src/main/java/org/opensearch/action/RoutingMissingException.java +++ b/server/src/main/java/org/opensearch/action/RoutingMissingException.java @@ -33,14 +33,19 @@ package org.opensearch.action; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; import org.opensearch.index.mapper.MapperService; -import org.opensearch.rest.RestStatus; import java.io.IOException; import java.util.Objects; +/** + * Base exception for a missing routing + * + * @opensearch.internal + */ public class RoutingMissingException extends OpenSearchException { private final String type; diff --git a/server/src/main/java/org/opensearch/action/StepListener.java b/server/src/main/java/org/opensearch/action/StepListener.java index 9547494c753c2..5701d20db90b8 100644 --- a/server/src/main/java/org/opensearch/action/StepListener.java +++ b/server/src/main/java/org/opensearch/action/StepListener.java @@ -33,9 +33,11 @@ package org.opensearch.action; import org.opensearch.common.CheckedConsumer; -import org.opensearch.common.util.concurrent.OpenSearchExecutors; import org.opensearch.common.util.concurrent.FutureUtils; import org.opensearch.common.util.concurrent.ListenableFuture; +import org.opensearch.common.util.concurrent.OpenSearchExecutors; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.NotifyOnceListener; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -61,6 +63,8 @@ * }, flowListener::onFailure); * } * } + * + * @opensearch.internal */ public final class StepListener extends NotifyOnceListener { diff --git a/server/src/main/java/org/opensearch/action/TaskOperationFailure.java b/server/src/main/java/org/opensearch/action/TaskOperationFailure.java index 09895e5f46db4..0930bd2741810 100644 --- a/server/src/main/java/org/opensearch/action/TaskOperationFailure.java +++ b/server/src/main/java/org/opensearch/action/TaskOperationFailure.java @@ -32,26 +32,28 @@ package org.opensearch.action; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.OpenSearchException; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * Information about task operation failures * * The class is final due to serialization limitations + * + * @opensearch.internal */ public final class TaskOperationFailure implements Writeable, ToXContentFragment { private static final String TASK_ID = "task_id"; diff --git a/server/src/main/java/org/opensearch/action/ThreadingModel.java b/server/src/main/java/org/opensearch/action/ThreadingModel.java index 93e96836941e4..11c61152e5107 100644 --- a/server/src/main/java/org/opensearch/action/ThreadingModel.java +++ b/server/src/main/java/org/opensearch/action/ThreadingModel.java @@ -32,6 +32,11 @@ package org.opensearch.action; +/** + * Threading model + * + * @opensearch.internal + */ public enum ThreadingModel { NONE((byte) 0), OPERATION((byte) 1), diff --git a/server/src/main/java/org/opensearch/action/TimestampParsingException.java b/server/src/main/java/org/opensearch/action/TimestampParsingException.java index f6a0207f22db7..25e22a6dfb6c3 100644 --- a/server/src/main/java/org/opensearch/action/TimestampParsingException.java +++ b/server/src/main/java/org/opensearch/action/TimestampParsingException.java @@ -33,11 +33,16 @@ package org.opensearch.action; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Base exception for a failed timestamp parse + * + * @opensearch.internal + */ public class TimestampParsingException extends OpenSearchException { private final String timestamp; diff --git a/server/src/main/java/org/opensearch/action/TransportActionNodeProxy.java b/server/src/main/java/org/opensearch/action/TransportActionNodeProxy.java index 8c6838b186fca..1088a3a4f8679 100644 --- a/server/src/main/java/org/opensearch/action/TransportActionNodeProxy.java +++ b/server/src/main/java/org/opensearch/action/TransportActionNodeProxy.java @@ -34,11 +34,15 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; import org.opensearch.transport.TransportRequestOptions; import org.opensearch.transport.TransportService; /** * A generic proxy that will execute the given action against a specific node. + * + * @opensearch.internal */ public class TransportActionNodeProxy { diff --git a/server/src/main/java/org/opensearch/action/UnavailableShardsException.java b/server/src/main/java/org/opensearch/action/UnavailableShardsException.java index a6050ae9a3c75..706d9bb38ed13 100644 --- a/server/src/main/java/org/opensearch/action/UnavailableShardsException.java +++ b/server/src/main/java/org/opensearch/action/UnavailableShardsException.java @@ -34,12 +34,17 @@ import org.opensearch.OpenSearchException; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; +/** + * Base exception for shards unavailable + * + * @opensearch.internal + */ public class UnavailableShardsException extends OpenSearchException { public UnavailableShardsException(@Nullable ShardId shardId, String message, Object... args) { diff --git a/server/src/main/java/org/opensearch/action/ValidateActions.java b/server/src/main/java/org/opensearch/action/ValidateActions.java index 134a880c7a972..1d1591ae3049b 100644 --- a/server/src/main/java/org/opensearch/action/ValidateActions.java +++ b/server/src/main/java/org/opensearch/action/ValidateActions.java @@ -32,6 +32,11 @@ package org.opensearch.action; +/** + * Validates transport actions + * + * @opensearch.internal + */ public class ValidateActions { public static ActionRequestValidationException addValidationError(String error, ActionRequestValidationException validationException) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainAction.java index 4c95d1e336b3a..0e99513a8fc7e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainAction.java @@ -36,6 +36,8 @@ /** * ActionType for explaining shard allocation for a shard in the cluster + * + * @opensearch.internal */ public class ClusterAllocationExplainAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java index 1a055fa0d14c2..625aa91e6ea7f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainRequest.java @@ -33,13 +33,13 @@ package org.opensearch.action.admin.cluster.allocation; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; @@ -47,8 +47,10 @@ /** * A request to explain the allocation of a shard in the cluster + * + * @opensearch.internal */ -public class ClusterAllocationExplainRequest extends MasterNodeRequest { +public class ClusterAllocationExplainRequest extends ClusterManagerNodeRequest { private static final ObjectParser PARSER = new ObjectParser<>("cluster/allocation/explain"); static { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainRequestBuilder.java index 78a22cd59b284..d85cb3929873d 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainRequestBuilder.java @@ -32,13 +32,15 @@ package org.opensearch.action.admin.cluster.allocation; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; /** * Builder for requests to explain the allocation of a shard in the cluster + * + * @opensearch.internal */ -public class ClusterAllocationExplainRequestBuilder extends MasterNodeOperationRequestBuilder< +public class ClusterAllocationExplainRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< ClusterAllocationExplainRequest, ClusterAllocationExplainResponse, ClusterAllocationExplainRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainResponse.java index 2fc72ccb13e28..0eeedb9af0ab7 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplainResponse.java @@ -32,14 +32,16 @@ package org.opensearch.action.admin.cluster.allocation; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * Explanation response for a shard in the cluster + * + * @opensearch.internal */ public class ClusterAllocationExplainResponse extends ActionResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplanation.java b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplanation.java index e9505c7fc83c2..f4705a21f5014 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplanation.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/ClusterAllocationExplanation.java @@ -40,12 +40,12 @@ import org.opensearch.cluster.routing.allocation.AllocationDecision; import org.opensearch.cluster.routing.allocation.ShardAllocationDecision; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.time.Instant; @@ -57,6 +57,8 @@ * A {@code ClusterAllocationExplanation} is an explanation of why a shard is unassigned, * or if it is not unassigned, then which nodes it could possibly be relocated to. * It is an immutable class. + * + * @opensearch.internal */ public final class ClusterAllocationExplanation implements ToXContentObject, Writeable { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java index 95fbe42384238..737155b13a708 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/TransportClusterAllocationExplainAction.java @@ -34,9 +34,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterInfo; import org.opensearch.cluster.ClusterInfoService; import org.opensearch.cluster.ClusterState; @@ -54,7 +53,8 @@ import org.opensearch.cluster.routing.allocation.decider.AllocationDeciders; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.snapshots.SnapshotsInfoService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -64,9 +64,11 @@ /** * The {@code TransportClusterAllocationExplainAction} is responsible for actually executing the explanation of a shard's allocation on the - * master node in the cluster. + * cluster-manager node in the cluster. + * + * @opensearch.internal */ -public class TransportClusterAllocationExplainAction extends TransportMasterNodeAction< +public class TransportClusterAllocationExplainAction extends TransportClusterManagerNodeAction< ClusterAllocationExplainRequest, ClusterAllocationExplainResponse> { @@ -123,7 +125,7 @@ protected ClusterBlockException checkBlock(ClusterAllocationExplainRequest reque } @Override - protected void masterOperation( + protected void clusterManagerOperation( final ClusterAllocationExplainRequest request, final ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/allocation/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/package-info.java new file mode 100644 index 0000000000000..ef38d39ede82a --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/allocation/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster allocation transport handlers. */ +package org.opensearch.action.admin.cluster.allocation; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsAction.java index 963a5d567c0bc..4a4f28b360801 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsAction.java @@ -33,7 +33,12 @@ import org.opensearch.action.ActionType; -public class AddVotingConfigExclusionsAction extends ActionType { +/** + * Transport endpoint for adding exclusions to voting config + * + * @opensearch.internal + */ +public final class AddVotingConfigExclusionsAction extends ActionType { public static final AddVotingConfigExclusionsAction INSTANCE = new AddVotingConfigExclusionsAction(); public static final String NAME = "cluster:admin/voting_config/add_exclusions"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java index 99291742145f0..2fd4557ebc7ee 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsRequest.java @@ -33,16 +33,16 @@ import org.opensearch.LegacyESVersion; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Arrays; @@ -54,10 +54,12 @@ import java.util.stream.StreamSupport; /** - * A request to add voting config exclusions for certain master-eligible nodes, and wait for these nodes to be removed from the voting + * A request to add voting config exclusions for certain cluster-manager-eligible nodes, and wait for these nodes to be removed from the voting * configuration. + * + * @opensearch.internal */ -public class AddVotingConfigExclusionsRequest extends MasterNodeRequest { +public class AddVotingConfigExclusionsRequest extends ClusterManagerNodeRequest { public static final String DEPRECATION_MESSAGE = "nodeDescription is deprecated and will be removed, use nodeIds or nodeNames instead"; private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(AddVotingConfigExclusionsRequest.class); private final String[] nodeDescriptions; @@ -66,7 +68,7 @@ public class AddVotingConfigExclusionsRequest extends MasterNodeRequest resolveVotingConfigExclusions(ClusterState currentSta if (nodeDescriptions.length >= 1) { newVotingConfigExclusions = Arrays.stream(allNodes.resolveNodes(nodeDescriptions)) .map(allNodes::get) - .filter(DiscoveryNode::isMasterNode) + .filter(DiscoveryNode::isClusterManagerNode) .map(VotingConfigExclusion::new) .collect(Collectors.toSet()); if (newVotingConfigExclusions.isEmpty()) { throw new IllegalArgumentException( - "add voting config exclusions request for " + Arrays.asList(nodeDescriptions) + " matched no master-eligible nodes" + "add voting config exclusions request for " + + Arrays.asList(nodeDescriptions) + + " matched no cluster-manager-eligible nodes" ); } } else if (nodeIds.length >= 1) { for (String nodeId : nodeIds) { if (allNodes.nodeExists(nodeId)) { DiscoveryNode discoveryNode = allNodes.get(nodeId); - if (discoveryNode.isMasterNode()) { + if (discoveryNode.isClusterManagerNode()) { newVotingConfigExclusions.add(new VotingConfigExclusion(discoveryNode)); } } else { @@ -158,7 +162,7 @@ Set resolveVotingConfigExclusions(ClusterState currentSta for (String nodeName : nodeNames) { if (existingNodes.containsKey(nodeName)) { DiscoveryNode discoveryNode = existingNodes.get(nodeName); - if (discoveryNode.isMasterNode()) { + if (discoveryNode.isClusterManagerNode()) { newVotingConfigExclusions.add(new VotingConfigExclusion(discoveryNode)); } } else { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsResponse.java index 0493de7c439de..7855f9643c4ce 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/AddVotingConfigExclusionsResponse.java @@ -31,17 +31,19 @@ package org.opensearch.action.admin.cluster.configuration; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; /** * A response to {@link AddVotingConfigExclusionsRequest} indicating that voting config exclusions have been added for the requested nodes * and these nodes have been removed from the voting configuration. + * + * @opensearch.internal */ public class AddVotingConfigExclusionsResponse extends ActionResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsAction.java index ce31abceb6738..e0fcad8f8ccd8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsAction.java @@ -33,6 +33,11 @@ import org.opensearch.action.ActionType; +/** + * Transport endpoint for clearing exclusions to voting config + * + * @opensearch.internal + */ public class ClearVotingConfigExclusionsAction extends ActionType { public static final ClearVotingConfigExclusionsAction INSTANCE = new ClearVotingConfigExclusionsAction(); public static final String NAME = "cluster:admin/voting_config/clear_exclusions"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsRequest.java index 9ccccc88f3365..eda0175c90728 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsRequest.java @@ -32,18 +32,20 @@ package org.opensearch.action.admin.cluster.configuration; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * A request to clear the voting config exclusions from the cluster state, optionally waiting for these nodes to be removed from the * cluster first. + * + * @opensearch.internal */ -public class ClearVotingConfigExclusionsRequest extends MasterNodeRequest { +public class ClearVotingConfigExclusionsRequest extends ClusterManagerNodeRequest { private boolean waitForRemoval = true; private TimeValue timeout = TimeValue.timeValueSeconds(30); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsResponse.java index ed07f33b6c6a6..eb2ccaa7f5390 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/ClearVotingConfigExclusionsResponse.java @@ -31,17 +31,19 @@ package org.opensearch.action.admin.cluster.configuration; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; /** * A response to {@link ClearVotingConfigExclusionsRequest} indicating that voting config exclusions have been cleared from the * cluster state. + * + * @opensearch.internal */ public class ClearVotingConfigExclusionsResponse extends ActionResponse implements ToXContentObject { public ClearVotingConfigExclusionsResponse() {} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java index 30ba799db044a..f578925e54ce7 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/TransportAddVotingConfigExclusionsAction.java @@ -35,28 +35,26 @@ import org.apache.logging.log4j.Logger; import org.opensearch.OpenSearchException; import org.opensearch.OpenSearchTimeoutException; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateObserver.Listener; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; -import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.threadpool.ThreadPool.Names; import org.opensearch.transport.TransportService; @@ -66,7 +64,15 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -public class TransportAddVotingConfigExclusionsAction extends TransportMasterNodeAction< +import static org.opensearch.action.admin.cluster.configuration.VotingConfigExclusionsHelper.addExclusionAndGetState; +import static org.opensearch.action.admin.cluster.configuration.VotingConfigExclusionsHelper.resolveVotingConfigExclusionsAndCheckMaximum; + +/** + * Transport endpoint action for adding exclusions to voting config + * + * @opensearch.internal + */ +public class TransportAddVotingConfigExclusionsAction extends TransportClusterManagerNodeAction< AddVotingConfigExclusionsRequest, AddVotingConfigExclusionsResponse> { @@ -121,7 +127,7 @@ protected AddVotingConfigExclusionsResponse read(StreamInput in) throws IOExcept } @Override - protected void masterOperation( + protected void clusterManagerOperation( AddVotingConfigExclusionsRequest request, ClusterState state, ActionListener listener @@ -139,13 +145,7 @@ public ClusterState execute(ClusterState currentState) { assert resolvedExclusions == null : resolvedExclusions; final int finalMaxVotingConfigExclusions = TransportAddVotingConfigExclusionsAction.this.maxVotingConfigExclusions; resolvedExclusions = resolveVotingConfigExclusionsAndCheckMaximum(request, currentState, finalMaxVotingConfigExclusions); - - final CoordinationMetadata.Builder builder = CoordinationMetadata.builder(currentState.coordinationMetadata()); - resolvedExclusions.forEach(builder::addVotingConfigExclusion); - final Metadata newMetadata = Metadata.builder(currentState.metadata()).coordinationMetadata(builder.build()).build(); - final ClusterState newState = ClusterState.builder(currentState).metadata(newMetadata).build(); - assert newState.getVotingConfigExclusions().size() <= finalMaxVotingConfigExclusions; - return newState; + return addExclusionAndGetState(currentState, resolvedExclusions, finalMaxVotingConfigExclusions); } @Override @@ -208,18 +208,6 @@ public void onTimeout(TimeValue timeout) { }); } - private static Set resolveVotingConfigExclusionsAndCheckMaximum( - AddVotingConfigExclusionsRequest request, - ClusterState state, - int maxVotingConfigExclusions - ) { - return request.resolveVotingConfigExclusionsAndCheckMaximum( - state, - maxVotingConfigExclusions, - MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.getKey() - ); - } - @Override protected ClusterBlockException checkBlock(AddVotingConfigExclusionsRequest request, ClusterState state) { return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsAction.java index 31a1d07608071..c3c08b9636518 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/TransportClearVotingConfigExclusionsAction.java @@ -35,24 +35,22 @@ import org.apache.logging.log4j.Logger; import org.opensearch.OpenSearchException; import org.opensearch.OpenSearchTimeoutException; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateObserver.Listener; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; -import org.opensearch.cluster.coordination.CoordinationMetadata; import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.threadpool.ThreadPool.Names; import org.opensearch.transport.TransportService; @@ -60,7 +58,14 @@ import java.io.IOException; import java.util.function.Predicate; -public class TransportClearVotingConfigExclusionsAction extends TransportMasterNodeAction< +import static org.opensearch.action.admin.cluster.configuration.VotingConfigExclusionsHelper.clearExclusionsAndGetState; + +/** + * Transport endpoint action for clearing exclusions to voting config + * + * @opensearch.internal + */ +public class TransportClearVotingConfigExclusionsAction extends TransportClusterManagerNodeAction< ClearVotingConfigExclusionsRequest, ClearVotingConfigExclusionsResponse> { @@ -96,7 +101,7 @@ protected ClearVotingConfigExclusionsResponse read(StreamInput in) throws IOExce } @Override - protected void masterOperation( + protected void clusterManagerOperation( ClearVotingConfigExclusionsRequest request, ClusterState initialState, ActionListener listener @@ -161,13 +166,7 @@ private void submitClearVotingConfigExclusionsTask( clusterService.submitStateUpdateTask("clear-voting-config-exclusions", new ClusterStateUpdateTask(Priority.URGENT) { @Override public ClusterState execute(ClusterState currentState) { - final CoordinationMetadata newCoordinationMetadata = CoordinationMetadata.builder(currentState.coordinationMetadata()) - .clearVotingConfigExclusions() - .build(); - final Metadata newMetadata = Metadata.builder(currentState.metadata()) - .coordinationMetadata(newCoordinationMetadata) - .build(); - return ClusterState.builder(currentState).metadata(newMetadata).build(); + return clearExclusionsAndGetState(currentState); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/VotingConfigExclusionsHelper.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/VotingConfigExclusionsHelper.java new file mode 100644 index 0000000000000..5cc4bd2f831d7 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/VotingConfigExclusionsHelper.java @@ -0,0 +1,81 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.configuration; + +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.coordination.CoordinationMetadata; +import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; +import org.opensearch.cluster.metadata.Metadata; + +import java.util.Set; + +import static org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction.MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING; + +/** + * Static helper utilities for voting config exclusions cluster state updates + * + * @opensearch.internal + */ +public class VotingConfigExclusionsHelper { + + /** + * Static helper to update current state with given resolved exclusions + * + * @param currentState current cluster state + * @param resolvedExclusions resolved exclusions from the request + * @param finalMaxVotingConfigExclusions max exclusions that be added + * @return newly formed cluster state + */ + public static ClusterState addExclusionAndGetState( + ClusterState currentState, + Set resolvedExclusions, + int finalMaxVotingConfigExclusions + ) { + final CoordinationMetadata.Builder builder = CoordinationMetadata.builder(currentState.coordinationMetadata()); + resolvedExclusions.forEach(builder::addVotingConfigExclusion); + final Metadata newMetadata = Metadata.builder(currentState.metadata()).coordinationMetadata(builder.build()).build(); + final ClusterState newState = ClusterState.builder(currentState).metadata(newMetadata).build(); + assert newState.getVotingConfigExclusions().size() <= finalMaxVotingConfigExclusions; + return newState; + } + + /** + * Resolves the exclusion from the request and throws IAE if no nodes matched or maximum exceeded + * + * @param request AddVotingConfigExclusionsRequest request + * @param state current cluster state + * @param maxVotingConfigExclusions max number of exclusion acceptable + * @return set of VotingConfigExclusion + */ + public static Set resolveVotingConfigExclusionsAndCheckMaximum( + AddVotingConfigExclusionsRequest request, + ClusterState state, + int maxVotingConfigExclusions + ) { + return request.resolveVotingConfigExclusionsAndCheckMaximum( + state, + maxVotingConfigExclusions, + MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.getKey() + ); + } + + /** + * Clears Voting config exclusion from the given cluster state + * + * @param currentState current cluster state + * @return newly formed cluster state after clearing voting config exclusions + */ + public static ClusterState clearExclusionsAndGetState(ClusterState currentState) { + final CoordinationMetadata newCoordinationMetadata = CoordinationMetadata.builder(currentState.coordinationMetadata()) + .clearVotingConfigExclusions() + .build(); + final Metadata newMetadata = Metadata.builder(currentState.metadata()).coordinationMetadata(newCoordinationMetadata).build(); + return ClusterState.builder(currentState).metadata(newMetadata).build(); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/configuration/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/package-info.java new file mode 100644 index 0000000000000..5543e7a6bde2f --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/configuration/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster configuration transport handlers. */ +package org.opensearch.action.admin.cluster.configuration; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/crypto/CryptoSettings.java b/server/src/main/java/org/opensearch/action/admin/cluster/crypto/CryptoSettings.java new file mode 100644 index 0000000000000..bd783b349bed4 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/crypto/CryptoSettings.java @@ -0,0 +1,181 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.crypto; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Map; + +import static org.opensearch.action.ValidateActions.addValidationError; +import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; +import static org.opensearch.common.settings.Settings.readSettingsFromStream; +import static org.opensearch.common.settings.Settings.writeSettingsToStream; + +/** + * Crypto settings supplied during a put repository request + * + * @opensearch.internal + */ +public class CryptoSettings implements Writeable, ToXContentObject { + private String keyProviderName; + private String keyProviderType; + private Settings settings = EMPTY_SETTINGS; + + public CryptoSettings(StreamInput in) throws IOException { + keyProviderName = in.readString(); + keyProviderType = in.readString(); + settings = readSettingsFromStream(in); + } + + public CryptoSettings(String keyProviderName) { + this.keyProviderName = keyProviderName; + } + + /** + * Validate settings supplied in put repository request. + * @return Exception in case validation fails. + */ + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (keyProviderName == null) { + validationException = addValidationError("key_provider_name is missing", validationException); + } + if (keyProviderType == null) { + validationException = addValidationError("key_provider_type is missing", validationException); + } + return validationException; + } + + /** + * Returns key provider name + * @return keyProviderName + */ + public String getKeyProviderName() { + return keyProviderName; + } + + /** + * Returns key provider type + * @return keyProviderType + */ + public String getKeyProviderType() { + return keyProviderType; + } + + /** + * Returns crypto settings + * @return settings + */ + public Settings getSettings() { + return settings; + } + + /** + * Constructs a new crypto settings with provided key provider name. + * @param keyProviderName Name of the key provider + */ + public CryptoSettings keyProviderName(String keyProviderName) { + this.keyProviderName = keyProviderName; + return this; + } + + /** + * Constructs a new crypto settings with provided key provider type. + * @param keyProviderType Type of key provider to be used in encryption. + */ + public CryptoSettings keyProviderType(String keyProviderType) { + this.keyProviderType = keyProviderType; + return this; + } + + /** + * Sets the encryption settings + * + * @param settings for encryption + * @return this request + */ + public CryptoSettings settings(Settings.Builder settings) { + this.settings = settings.build(); + return this; + } + + /** + * Sets the encryption settings. + * + * @param source encryption settings in json or yaml format + * @param xContentType the content type of the source + * @return this request + */ + public CryptoSettings settings(String source, XContentType xContentType) { + this.settings = Settings.builder().loadFromSource(source, xContentType).build(); + return this; + } + + /** + * Sets the encryption settings. + * + * @param source encryption settings + * @return this request + */ + public CryptoSettings settings(Map source) { + this.settings = Settings.builder().loadFromMap(source).build(); + return this; + } + + /** + * Parses crypto settings definition. + * + * @param cryptoDefinition crypto settings definition + */ + public CryptoSettings(Map cryptoDefinition) { + for (Map.Entry entry : cryptoDefinition.entrySet()) { + if (entry.getKey().equals("key_provider_name")) { + keyProviderName(entry.getValue().toString()); + } else if (entry.getKey().equals("key_provider_type")) { + keyProviderType(entry.getValue().toString()); + } else if (entry.getKey().equals("settings")) { + if (!(entry.getValue() instanceof Map)) { + throw new IllegalArgumentException("Malformed settings section in crypto settings, should include an inner object"); + } + @SuppressWarnings("unchecked") + Map sub = (Map) entry.getValue(); + settings(sub); + } + } + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(keyProviderName); + out.writeString(keyProviderType); + writeSettingsToStream(settings, out); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("key_provider_name", keyProviderName); + builder.field("key_provider_type", keyProviderType); + + builder.startObject("settings"); + settings.toXContent(builder, params); + builder.endObject(); + + builder.endObject(); + return builder; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/crypto/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/crypto/package-info.java new file mode 100644 index 0000000000000..bb9375c20c87e --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/crypto/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Crypto client request and settings handlers. + */ +package org.opensearch.action.admin.cluster.crypto; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateAction.java new file mode 100644 index 0000000000000..3aff666d388be --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateAction.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.opensearch.action.ActionType; + +/** + * Delete decommission state action. + * + * @opensearch.internal + */ +public class DeleteDecommissionStateAction extends ActionType { + public static final DeleteDecommissionStateAction INSTANCE = new DeleteDecommissionStateAction(); + public static final String NAME = "cluster:admin/decommission/awareness/delete"; + + private DeleteDecommissionStateAction() { + super(NAME, DeleteDecommissionStateResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateRequest.java new file mode 100644 index 0000000000000..79b7381801da6 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateRequest.java @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Request for deleting decommission request. + * + * @opensearch.internal + */ +public class DeleteDecommissionStateRequest extends ClusterManagerNodeRequest { + + public DeleteDecommissionStateRequest() {} + + public DeleteDecommissionStateRequest(StreamInput in) throws IOException { + super(in); + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateRequestBuilder.java new file mode 100644 index 0000000000000..08f194c53f18e --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateRequestBuilder.java @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; + +/** + * Builder for Delete decommission request. + * + * @opensearch.internal + */ +public class DeleteDecommissionStateRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< + DeleteDecommissionStateRequest, + DeleteDecommissionStateResponse, + DeleteDecommissionStateRequestBuilder> { + + public DeleteDecommissionStateRequestBuilder(OpenSearchClient client, DeleteDecommissionStateAction action) { + super(client, action, new DeleteDecommissionStateRequest()); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateResponse.java new file mode 100644 index 0000000000000..3d0404c25373b --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/DeleteDecommissionStateResponse.java @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Response returned after deletion of decommission request. + * + * @opensearch.internal + */ +public class DeleteDecommissionStateResponse extends AcknowledgedResponse { + + public DeleteDecommissionStateResponse(StreamInput in) throws IOException { + super(in); + } + + public DeleteDecommissionStateResponse(boolean acknowledged) { + super(acknowledged); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionStateAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionStateAction.java new file mode 100644 index 0000000000000..8901375a4095a --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/TransportDeleteDecommissionStateAction.java @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.delete; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.decommission.DecommissionService; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Transport action for delete decommission. + * + * @opensearch.internal + */ +public class TransportDeleteDecommissionStateAction extends TransportClusterManagerNodeAction< + DeleteDecommissionStateRequest, + DeleteDecommissionStateResponse> { + + private static final Logger logger = LogManager.getLogger(TransportDeleteDecommissionStateAction.class); + private final DecommissionService decommissionService; + + @Inject + public TransportDeleteDecommissionStateAction( + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver, + DecommissionService decommissionService + ) { + super( + DeleteDecommissionStateAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + DeleteDecommissionStateRequest::new, + indexNameExpressionResolver + ); + this.decommissionService = decommissionService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected DeleteDecommissionStateResponse read(StreamInput in) throws IOException { + return new DeleteDecommissionStateResponse(in); + } + + @Override + protected ClusterBlockException checkBlock(DeleteDecommissionStateRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } + + @Override + protected void clusterManagerOperation( + DeleteDecommissionStateRequest request, + ClusterState state, + ActionListener listener + ) { + logger.info("Received delete decommission Request [{}]", request); + this.decommissionService.startRecommissionAction(listener); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/package-info.java new file mode 100644 index 0000000000000..c2cfc03baa45e --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/delete/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Delete decommission transport handlers. */ +package org.opensearch.action.admin.cluster.decommission.awareness.delete; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateAction.java new file mode 100644 index 0000000000000..72fd1a26cb860 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateAction.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.action.ActionType; + +/** + * Get decommission action + * + * @opensearch.internal + */ +public class GetDecommissionStateAction extends ActionType { + + public static final GetDecommissionStateAction INSTANCE = new GetDecommissionStateAction(); + public static final String NAME = "cluster:admin/decommission/awareness/get"; + + private GetDecommissionStateAction() { + super(NAME, GetDecommissionStateResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequest.java new file mode 100644 index 0000000000000..15c7e165fb62f --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequest.java @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Get Decommissioned attribute request + * + * @opensearch.internal + */ +public class GetDecommissionStateRequest extends ClusterManagerNodeReadRequest { + + private String attributeName; + + public GetDecommissionStateRequest() {} + + /** + * Constructs a new get decommission state request with given attribute name + * + * @param attributeName name of the attribute + */ + public GetDecommissionStateRequest(String attributeName) { + this.attributeName = attributeName; + } + + public GetDecommissionStateRequest(StreamInput in) throws IOException { + super(in); + attributeName = in.readString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(attributeName); + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (attributeName == null || Strings.isEmpty(attributeName)) { + validationException = addValidationError("attribute name is missing", validationException); + } + return validationException; + } + + /** + * Sets attribute name + * + * @param attributeName attribute name + * @return this request + */ + public GetDecommissionStateRequest attributeName(String attributeName) { + this.attributeName = attributeName; + return this; + } + + /** + * Returns attribute name + * + * @return attributeName name of attribute + */ + public String attributeName() { + return this.attributeName; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequestBuilder.java new file mode 100644 index 0000000000000..e766e9c674ff7 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateRequestBuilder.java @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; + +/** + * Get decommission request builder + * + * @opensearch.internal + */ +public class GetDecommissionStateRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< + GetDecommissionStateRequest, + GetDecommissionStateResponse, + GetDecommissionStateRequestBuilder> { + + /** + * Creates new get decommissioned attributes request builder + */ + public GetDecommissionStateRequestBuilder(OpenSearchClient client, GetDecommissionStateAction action) { + super(client, action, new GetDecommissionStateRequest()); + } + + /** + * @param attributeName name of attribute + * @return current object + */ + public GetDecommissionStateRequestBuilder setAttributeName(String attributeName) { + request.attributeName(attributeName); + return this; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java new file mode 100644 index 0000000000000..bbcbd7013c299 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/GetDecommissionStateResponse.java @@ -0,0 +1,121 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.cluster.decommission.DecommissionStatus; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Locale; +import java.util.Objects; + +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * Response for decommission status + * + * @opensearch.internal + */ +public class GetDecommissionStateResponse extends ActionResponse implements ToXContentObject { + + private String attributeValue; + private DecommissionStatus status; + + GetDecommissionStateResponse() { + this(null, null); + } + + GetDecommissionStateResponse(String attributeValue, DecommissionStatus status) { + this.attributeValue = attributeValue; + this.status = status; + } + + GetDecommissionStateResponse(StreamInput in) throws IOException { + // read decommissioned attribute and status only if it is present + if (in.readBoolean()) { + this.attributeValue = in.readString(); + this.status = DecommissionStatus.fromString(in.readString()); + } + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + // if decommissioned attribute value is null or status is null then mark its absence + if (attributeValue == null || status == null) { + out.writeBoolean(false); + } else { + out.writeBoolean(true); + out.writeString(attributeValue); + out.writeString(status.status()); + } + } + + public String getAttributeValue() { + return attributeValue; + } + + public DecommissionStatus getDecommissionStatus() { + return status; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + if (attributeValue != null && status != null) { + builder.field(attributeValue, status); + } + builder.endObject(); + return builder; + } + + public static GetDecommissionStateResponse fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + XContentParser.Token token; + String attributeValue = null; + DecommissionStatus status = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + attributeValue = parser.currentName(); + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new OpenSearchParseException("failed to parse status of decommissioning, expected string but found unknown type"); + } + status = DecommissionStatus.fromString(parser.text().toLowerCase(Locale.ROOT)); + } else { + throw new OpenSearchParseException( + "failed to parse decommission state, expected [{}] but found [{}]", + XContentParser.Token.FIELD_NAME, + token + ); + } + } + return new GetDecommissionStateResponse(attributeValue, status); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GetDecommissionStateResponse that = (GetDecommissionStateResponse) o; + if (!Objects.equals(attributeValue, that.attributeValue)) { + return false; + } + return Objects.equals(status, that.status); + } + + @Override + public int hashCode() { + return Objects.hash(attributeValue, status); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java new file mode 100644 index 0000000000000..22feb4d99297a --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/TransportGetDecommissionStateAction.java @@ -0,0 +1,89 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.get; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Transport action for getting decommission status + * + * @opensearch.internal + */ +public class TransportGetDecommissionStateAction extends TransportClusterManagerNodeReadAction< + GetDecommissionStateRequest, + GetDecommissionStateResponse> { + + @Inject + public TransportGetDecommissionStateAction( + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + GetDecommissionStateAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + GetDecommissionStateRequest::new, + indexNameExpressionResolver + ); + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected GetDecommissionStateResponse read(StreamInput in) throws IOException { + return new GetDecommissionStateResponse(in); + } + + @Override + protected void clusterManagerOperation( + GetDecommissionStateRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().decommissionAttributeMetadata(); + if (decommissionAttributeMetadata != null + && request.attributeName().equals(decommissionAttributeMetadata.decommissionAttribute().attributeName())) { + listener.onResponse( + new GetDecommissionStateResponse( + decommissionAttributeMetadata.decommissionAttribute().attributeValue(), + decommissionAttributeMetadata.status() + ) + ); + } else { + listener.onResponse(new GetDecommissionStateResponse()); + } + } + + @Override + protected ClusterBlockException checkBlock(GetDecommissionStateRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/package-info.java new file mode 100644 index 0000000000000..5b88e91cf4f9d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handlers for getting status of decommission request */ +package org.opensearch.action.admin.cluster.decommission.awareness.get; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/package-info.java new file mode 100644 index 0000000000000..e1260e638c91d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Decommission transport handlers. */ +package org.opensearch.action.admin.cluster.decommission.awareness; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionAction.java new file mode 100644 index 0000000000000..56678650f6e35 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionAction.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.ActionType; + +/** + * Register decommission action + * + * @opensearch.internal + */ +public final class DecommissionAction extends ActionType { + public static final DecommissionAction INSTANCE = new DecommissionAction(); + public static final String NAME = "cluster:admin/decommission/awareness/put"; + + private DecommissionAction() { + super(NAME, DecommissionResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequest.java new file mode 100644 index 0000000000000..18f14db3c2b8d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequest.java @@ -0,0 +1,154 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Registers a decommission request with decommission attribute and timeout + * + * @opensearch.internal + */ +public class DecommissionRequest extends ClusterManagerNodeRequest { + + public static final TimeValue DEFAULT_NODE_DRAINING_TIMEOUT = TimeValue.timeValueSeconds(120); + + private DecommissionAttribute decommissionAttribute; + private String requestID; + private TimeValue delayTimeout = DEFAULT_NODE_DRAINING_TIMEOUT; + + // holder for no_delay param. To avoid draining time timeout. + private boolean noDelay = false; + + public DecommissionRequest() {} + + public DecommissionRequest(DecommissionAttribute decommissionAttribute) { + this.decommissionAttribute = decommissionAttribute; + } + + public DecommissionRequest(StreamInput in) throws IOException { + super(in); + decommissionAttribute = new DecommissionAttribute(in); + this.delayTimeout = in.readTimeValue(); + this.noDelay = in.readBoolean(); + this.requestID = in.readOptionalString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + decommissionAttribute.writeTo(out); + out.writeTimeValue(delayTimeout); + out.writeBoolean(noDelay); + out.writeOptionalString(requestID); + } + + /** + * Sets decommission attribute for decommission request + * + * @param decommissionAttribute attribute key-value that needs to be decommissioned + * @return this request + */ + public DecommissionRequest setDecommissionAttribute(DecommissionAttribute decommissionAttribute) { + this.decommissionAttribute = decommissionAttribute; + return this; + } + + /** + * @return Returns the decommission attribute key-value + */ + public DecommissionAttribute getDecommissionAttribute() { + return this.decommissionAttribute; + } + + public DecommissionRequest setDelayTimeout(TimeValue delayTimeout) { + this.delayTimeout = delayTimeout; + return this; + } + + public TimeValue getDelayTimeout() { + return this.delayTimeout; + } + + public DecommissionRequest setNoDelay(boolean noDelay) { + if (noDelay) { + this.delayTimeout = TimeValue.ZERO; + } + this.noDelay = noDelay; + return this; + } + + public boolean isNoDelay() { + return noDelay; + } + + /** + * Sets id for decommission request + * + * @param requestID uuid for request + * @return this request + */ + public DecommissionRequest setRequestID(String requestID) { + this.requestID = requestID; + return this; + } + + /** + * @return Returns id of decommission request + */ + public String requestID() { + return requestID; + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (decommissionAttribute == null) { + validationException = addValidationError("decommission attribute is missing", validationException); + return validationException; + } + if (decommissionAttribute.attributeName() == null || Strings.isEmpty(decommissionAttribute.attributeName())) { + validationException = addValidationError("attribute name is missing", validationException); + } + if (decommissionAttribute.attributeValue() == null || Strings.isEmpty(decommissionAttribute.attributeValue())) { + validationException = addValidationError("attribute value is missing", validationException); + } + // This validation should not fail since we are not allowing delay timeout to be set externally. + // Still keeping it for double check. + if (noDelay && delayTimeout.getSeconds() > 0) { + final String validationMessage = "Invalid decommission request. no_delay is true and delay_timeout is set to " + + delayTimeout.getSeconds() + + "] Seconds"; + validationException = addValidationError(validationMessage, validationException); + } + return validationException; + } + + @Override + public String toString() { + return "DecommissionRequest{" + + "decommissionAttribute=" + + decommissionAttribute + + ", delayTimeout=" + + delayTimeout + + ", noDelay=" + + noDelay + + '}'; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java new file mode 100644 index 0000000000000..c3591fff54885 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionRequestBuilder.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.ActionType; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; +import org.opensearch.cluster.decommission.DecommissionAttribute; +import org.opensearch.common.unit.TimeValue; + +/** + * Register decommission request builder + * + * @opensearch.internal + */ +public class DecommissionRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< + DecommissionRequest, + DecommissionResponse, + DecommissionRequestBuilder> { + + public DecommissionRequestBuilder(OpenSearchClient client, ActionType action, DecommissionRequest request) { + super(client, action, request); + } + + /** + * @param decommissionAttribute decommission attribute + * @return current object + */ + public DecommissionRequestBuilder setDecommissionedAttribute(DecommissionAttribute decommissionAttribute) { + request.setDecommissionAttribute(decommissionAttribute); + return this; + } + + public DecommissionRequestBuilder setDelayTimeOut(TimeValue delayTimeOut) { + request.setDelayTimeout(delayTimeOut); + return this; + } + + public DecommissionRequestBuilder setNoDelay(boolean noDelay) { + request.setNoDelay(noDelay); + return this; + } + + /** + * Sets request id for decommission request + * + * @param requestID for decommission request + * @return current object + */ + public DecommissionRequestBuilder requestID(String requestID) { + request.setRequestID(requestID); + return this; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponse.java new file mode 100644 index 0000000000000..13c1775b005b3 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/DecommissionResponse.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; + +import java.io.IOException; + +/** + * Response for decommission request + * + * @opensearch.internal + */ +public class DecommissionResponse extends AcknowledgedResponse implements ToXContentObject { + + public DecommissionResponse(StreamInput in) throws IOException { + super(in); + } + + public DecommissionResponse(boolean acknowledged) { + super(acknowledged); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java new file mode 100644 index 0000000000000..0b1fd380ffdda --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/TransportDecommissionAction.java @@ -0,0 +1,81 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.decommission.awareness.put; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.decommission.DecommissionService; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Transport action for registering decommission + * + * @opensearch.internal + */ +public class TransportDecommissionAction extends TransportClusterManagerNodeAction { + + private static final Logger logger = LogManager.getLogger(TransportDecommissionAction.class); + private final DecommissionService decommissionService; + + @Inject + public TransportDecommissionAction( + TransportService transportService, + ClusterService clusterService, + DecommissionService decommissionService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + DecommissionAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + DecommissionRequest::new, + indexNameExpressionResolver + ); + this.decommissionService = decommissionService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected DecommissionResponse read(StreamInput in) throws IOException { + return new DecommissionResponse(in); + } + + @Override + protected ClusterBlockException checkBlock(DecommissionRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } + + @Override + protected void clusterManagerOperation(DecommissionRequest request, ClusterState state, ActionListener listener) + throws Exception { + logger.info("starting awareness attribute [{}] decommissioning", request.getDecommissionAttribute().toString()); + decommissionService.startDecommissionAction(request, listener); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/package-info.java new file mode 100644 index 0000000000000..c361f4b95a484 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/decommission/awareness/put/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handlers for putting a new decommission request */ +package org.opensearch.action.admin.cluster.decommission.awareness.put; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthAction.java index a37c0b0f1cb88..5c712b801f927 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport endpoint action for obtaining cluster health + * + * @opensearch.internal + */ public class ClusterHealthAction extends ActionType { public static final ClusterHealthAction INSTANCE = new ClusterHealthAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthRequest.java index 204a182edafab..585434db46104 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthRequest.java @@ -33,24 +33,33 @@ package org.opensearch.action.admin.cluster.health; import org.opensearch.LegacyESVersion; +import org.opensearch.Version; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Priority; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; import java.util.concurrent.TimeUnit; -public class ClusterHealthRequest extends MasterNodeReadRequest implements IndicesRequest.Replaceable { +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Transport request for requesting cluster health + * + * @opensearch.internal + */ +public class ClusterHealthRequest extends ClusterManagerNodeReadRequest implements IndicesRequest.Replaceable { private String[] indices; + private String awarenessAttribute; private IndicesOptions indicesOptions = IndicesOptions.lenientExpandHidden(); private TimeValue timeout = new TimeValue(30, TimeUnit.SECONDS); private ClusterHealthStatus waitForStatus; @@ -59,6 +68,7 @@ public class ClusterHealthRequest extends MasterNodeReadRequesttrue if local information is to be returned only when local node is also commissioned + * false to not check local node if commissioned or not for a local request + */ + public final boolean ensureNodeWeighedIn() { + return ensureNodeWeighedIn; + } + @Override public ActionRequestValidationException validate() { + if (level.equals(Level.AWARENESS_ATTRIBUTES) && indices.length > 0) { + return addValidationError("awareness_attribute is not a supported parameter with index health", null); + } else if (!level.equals(Level.AWARENESS_ATTRIBUTES) && awarenessAttribute != null) { + return addValidationError("level=awareness_attributes is required with awareness_attribute parameter", null); + } + if (ensureNodeWeighedIn && local == false) { + return addValidationError("not a local request to ensure local node commissioned or weighed in", null); + } return null; } + /** + * The level of the health request. + * + * @opensearch.internal + */ public enum Level { CLUSTER, INDICES, - SHARDS + SHARDS, + AWARENESS_ATTRIBUTES } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthRequestBuilder.java index 86fee83130bb3..cca9d35d8aa6f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthRequestBuilder.java @@ -34,13 +34,18 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Priority; import org.opensearch.common.unit.TimeValue; -public class ClusterHealthRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Builder for requesting cluster health + * + * @opensearch.internal + */ +public class ClusterHealthRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< ClusterHealthRequest, ClusterHealthResponse, ClusterHealthRequestBuilder> { @@ -146,4 +151,22 @@ public ClusterHealthRequestBuilder setWaitForEvents(Priority waitForEvents) { request.waitForEvents(waitForEvents); return this; } + + public ClusterHealthRequestBuilder setAwarenessAttribute(String awarenessAttribute) { + request.setAwarenessAttribute(awarenessAttribute); + return this; + } + + public ClusterHealthRequestBuilder setLevel(String level) { + request.setLevel(level); + return this; + } + + /** + * Specifies if the local request should ensure that the local node is commissioned + */ + public final ClusterHealthRequestBuilder setEnsureNodeWeighedIn(boolean ensureNodeCommissioned) { + request.ensureNodeWeighedIn(ensureNodeCommissioned); + return this; + } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthResponse.java index d9094e307fff1..fb68012502116 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/health/ClusterHealthResponse.java @@ -32,22 +32,26 @@ package org.opensearch.action.admin.cluster.health; -import org.opensearch.action.ActionResponse; +import org.opensearch.Version; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.awarenesshealth.ClusterAwarenessHealth; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.health.ClusterIndexHealth; import org.opensearch.cluster.health.ClusterStateHealth; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; @@ -57,15 +61,21 @@ import java.util.Objects; import static java.util.Collections.emptyMap; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +/** + * Transport response for Cluster Health + * + * @opensearch.internal + */ public class ClusterHealthResponse extends ActionResponse implements StatusToXContentObject { private static final String CLUSTER_NAME = "cluster_name"; private static final String STATUS = "status"; private static final String TIMED_OUT = "timed_out"; private static final String NUMBER_OF_NODES = "number_of_nodes"; private static final String NUMBER_OF_DATA_NODES = "number_of_data_nodes"; + @Deprecated private static final String DISCOVERED_MASTER = "discovered_master"; private static final String DISCOVERED_CLUSTER_MANAGER = "discovered_cluster_manager"; private static final String NUMBER_OF_PENDING_TASKS = "number_of_pending_tasks"; @@ -90,7 +100,8 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo // ClusterStateHealth fields int numberOfNodes = (int) parsedObjects[i++]; int numberOfDataNodes = (int) parsedObjects[i++]; - boolean hasDiscoveredMaster = (boolean) parsedObjects[i++]; + boolean hasDiscoveredMaster = Boolean.TRUE.equals(parsedObjects[i++]); + boolean hasDiscoveredClusterManager = Boolean.TRUE.equals(parsedObjects[i++]); int activeShards = (int) parsedObjects[i++]; int relocatingShards = (int) parsedObjects[i++]; int activePrimaryShards = (int) parsedObjects[i++]; @@ -118,7 +129,7 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo unassignedShards, numberOfNodes, numberOfDataNodes, - hasDiscoveredMaster, + hasDiscoveredClusterManager || hasDiscoveredMaster, activeShardsPercent, status, indices @@ -151,7 +162,8 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo // ClusterStateHealth fields PARSER.declareInt(constructorArg(), new ParseField(NUMBER_OF_NODES)); PARSER.declareInt(constructorArg(), new ParseField(NUMBER_OF_DATA_NODES)); - PARSER.declareBoolean(constructorArg(), new ParseField(DISCOVERED_MASTER)); + PARSER.declareBoolean(optionalConstructorArg(), new ParseField(DISCOVERED_MASTER)); + PARSER.declareBoolean(optionalConstructorArg(), new ParseField(DISCOVERED_CLUSTER_MANAGER)); PARSER.declareInt(constructorArg(), new ParseField(ACTIVE_SHARDS)); PARSER.declareInt(constructorArg(), new ParseField(RELOCATING_SHARDS)); PARSER.declareInt(constructorArg(), new ParseField(ACTIVE_PRIMARY_SHARDS)); @@ -179,6 +191,7 @@ public class ClusterHealthResponse extends ActionResponse implements StatusToXCo private boolean timedOut = false; private ClusterStateHealth clusterStateHealth; private ClusterHealthStatus clusterHealthStatus; + private ClusterAwarenessHealth clusterAwarenessHealth; public ClusterHealthResponse() {} @@ -192,6 +205,11 @@ public ClusterHealthResponse(StreamInput in) throws IOException { numberOfInFlightFetch = in.readInt(); delayedUnassignedShards = in.readInt(); taskMaxWaitingTime = in.readTimeValue(); + if (in.getVersion().onOrAfter(Version.V_2_5_0)) { + if (in.readBoolean()) { + clusterAwarenessHealth = new ClusterAwarenessHealth(in); + } + } } /** needed for plugins BWC */ @@ -217,6 +235,30 @@ public ClusterHealthResponse( this.clusterHealthStatus = clusterStateHealth.getStatus(); } + // Awareness Attribute health + public ClusterHealthResponse( + String clusterName, + ClusterState clusterState, + ClusterSettings clusterSettings, + String[] concreteIndices, + String awarenessAttributeName, + int numberOfPendingTasks, + int numberOfInFlightFetch, + int delayedUnassignedShards, + TimeValue taskMaxWaitingTime + ) { + this( + clusterName, + concreteIndices, + clusterState, + numberOfPendingTasks, + numberOfInFlightFetch, + delayedUnassignedShards, + taskMaxWaitingTime + ); + this.clusterAwarenessHealth = new ClusterAwarenessHealth(clusterState, clusterSettings, awarenessAttributeName); + } + /** * For XContent Parser and serialization tests */ @@ -276,8 +318,14 @@ public int getNumberOfDataNodes() { return clusterStateHealth.getNumberOfDataNodes(); } + public boolean hasDiscoveredClusterManager() { + return clusterStateHealth.hasDiscoveredClusterManager(); + } + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #hasDiscoveredClusterManager()} */ + @Deprecated public boolean hasDiscoveredMaster() { - return clusterStateHealth.hasDiscoveredMaster(); + return hasDiscoveredClusterManager(); } public int getNumberOfPendingTasks() { @@ -343,6 +391,10 @@ public double getActiveShardsPercent() { return clusterStateHealth.getActiveShardsPercent(); } + public ClusterAwarenessHealth getClusterAwarenessHealth() { + return clusterAwarenessHealth; + } + public static ClusterHealthResponse readResponseFrom(StreamInput in) throws IOException { return new ClusterHealthResponse(in); } @@ -357,11 +409,19 @@ public void writeTo(StreamOutput out) throws IOException { out.writeInt(numberOfInFlightFetch); out.writeInt(delayedUnassignedShards); out.writeTimeValue(taskMaxWaitingTime); + if (out.getVersion().onOrAfter(Version.V_2_5_0)) { + if (clusterAwarenessHealth != null) { + out.writeBoolean(true); + clusterAwarenessHealth.writeTo(out); + } else { + out.writeBoolean(false); + } + } } @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override @@ -377,8 +437,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(TIMED_OUT, isTimedOut()); builder.field(NUMBER_OF_NODES, getNumberOfNodes()); builder.field(NUMBER_OF_DATA_NODES, getNumberOfDataNodes()); - builder.field(DISCOVERED_MASTER, hasDiscoveredMaster()); // the field will be removed in a future major version - builder.field(DISCOVERED_CLUSTER_MANAGER, hasDiscoveredMaster()); + builder.field(DISCOVERED_MASTER, hasDiscoveredClusterManager()); // the field will be removed in a future major version + builder.field(DISCOVERED_CLUSTER_MANAGER, hasDiscoveredClusterManager()); builder.field(ACTIVE_PRIMARY_SHARDS, getActivePrimaryShards()); builder.field(ACTIVE_SHARDS, getActiveShards()); builder.field(RELOCATING_SHARDS, getRelocatingShards()); @@ -392,6 +452,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws String level = params.param("level", "cluster"); boolean outputIndices = "indices".equals(level) || "shards".equals(level); + boolean outputAwarenessHealth = "awareness_attributes".equals(level); if (outputIndices) { builder.startObject(INDICES); @@ -400,6 +461,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } builder.endObject(); } + + if (outputAwarenessHealth && clusterAwarenessHealth != null) { + clusterAwarenessHealth.toXContent(builder, params); + } + builder.endObject(); return builder; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/health/TransportClusterHealthAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/health/TransportClusterHealthAction.java index 6855803ba6c45..1cc357a4c20f4 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/health/TransportClusterHealthAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/health/TransportClusterHealthAction.java @@ -35,28 +35,35 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.LocalClusterUpdateTask; -import org.opensearch.cluster.NotMasterException; +import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.coordination.Coordinator; +import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.ProcessClusterEventTimeoutException; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.NodeWeighedAwayException; import org.opensearch.cluster.routing.UnassignedInfo; +import org.opensearch.cluster.routing.WeightedRoutingUtils; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.discovery.ClusterManagerNotDiscoveredException; +import org.opensearch.discovery.Discovery; import org.opensearch.index.IndexNotFoundException; import org.opensearch.node.NodeClosedException; import org.opensearch.tasks.Task; @@ -67,11 +74,17 @@ import java.util.function.Consumer; import java.util.function.Predicate; -public class TransportClusterHealthAction extends TransportMasterNodeReadAction { +/** + * Transport action for obtaining Cluster Health + * + * @opensearch.internal + */ +public class TransportClusterHealthAction extends TransportClusterManagerNodeReadAction { private static final Logger logger = LogManager.getLogger(TransportClusterHealthAction.class); private final AllocationService allocationService; + private final Discovery discovery; @Inject public TransportClusterHealthAction( @@ -80,7 +93,8 @@ public TransportClusterHealthAction( ThreadPool threadPool, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, - AllocationService allocationService + AllocationService allocationService, + Discovery discovery ) { super( ClusterHealthAction.NAME, @@ -93,6 +107,7 @@ public TransportClusterHealthAction( indexNameExpressionResolver ); this.allocationService = allocationService; + this.discovery = discovery; } @Override @@ -113,19 +128,28 @@ protected ClusterBlockException checkBlock(ClusterHealthRequest request, Cluster } @Override - protected final void masterOperation(ClusterHealthRequest request, ClusterState state, ActionListener listener) - throws Exception { + protected final void clusterManagerOperation( + ClusterHealthRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { logger.warn("attempt to execute a cluster health operation without a task"); throw new UnsupportedOperationException("task parameter is required for this operation"); } @Override - protected void masterOperation( + protected void clusterManagerOperation( final Task task, final ClusterHealthRequest request, final ClusterState unusedState, final ActionListener listener ) { + if (request.ensureNodeWeighedIn() + && discovery instanceof Coordinator + && ((Coordinator) discovery).localNodeCommissioned() == false) { + listener.onFailure(new NodeDecommissionedException("local node is decommissioned")); + return; + } final int waitCount = getWaitCount(request); @@ -216,13 +240,14 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } @Override - public void onNoLongerMaster(String source) { + public void onNoLongerClusterManager(String source) { logger.trace( - "stopped being master while waiting for events with priority [{}]. retrying.", + "stopped being cluster-manager while waiting for events with priority [{}]. retrying.", request.waitForEvents() ); - // TransportMasterNodeAction implements the retry logic, which is triggered by passing a NotMasterException - listener.onFailure(new NotMasterException("no longer master. source: [" + source + "]")); + // TransportClusterManagerNodeAction implements the retry logic, + // which is triggered by passing a NotClusterManagerException + listener.onFailure(new NotClusterManagerException("no longer cluster-manager. source: [" + source + "]")); } @Override @@ -254,7 +279,25 @@ private void executeHealth( final Predicate validationPredicate = newState -> validateRequest(request, newState, waitCount); if (validationPredicate.test(currentState)) { - listener.onResponse(getResponse(request, currentState, waitCount, TimeoutState.OK)); + ClusterHealthResponse clusterHealthResponse = getResponse(request, currentState, waitCount, TimeoutState.OK); + if (request.ensureNodeWeighedIn()) { + if (clusterHealthResponse.hasDiscoveredClusterManager() == false) { + listener.onFailure(new ClusterManagerNotDiscoveredException("cluster-manager not discovered")); + return; + } else { + DiscoveryNode localNode = currentState.getNodes().getLocalNode(); + // TODO: make this check more generic, check for node role instead + if (localNode.isDataNode()) { + assert request.local() == true : "local node request false for request for local node weighed in"; + boolean weighedAway = WeightedRoutingUtils.isWeighedAway(localNode.getId(), currentState); + if (weighedAway) { + listener.onFailure(new NodeWeighedAwayException("local node is weighed away")); + return; + } + } + } + } + listener.onResponse(clusterHealthResponse); } else { final ClusterStateObserver observer = new ClusterStateObserver( currentState, @@ -310,9 +353,9 @@ private boolean validateRequest(final ClusterHealthRequest request, ClusterState ClusterHealthResponse response = clusterHealth( request, clusterState, - clusterService.getMasterService().numberOfPendingTasks(), + clusterService.getClusterManagerService().numberOfPendingTasks(), allocationService.getNumberOfInFlightFetches(), - clusterService.getMasterService().getMaxTaskWaitTime() + clusterService.getClusterManagerService().getMaxTaskWaitTime() ); return prepareResponse(request, response, clusterState, indexNameExpressionResolver) == waitCount; } @@ -332,9 +375,9 @@ private ClusterHealthResponse getResponse( ClusterHealthResponse response = clusterHealth( request, clusterState, - clusterService.getMasterService().numberOfPendingTasks(), + clusterService.getClusterManagerService().numberOfPendingTasks(), allocationService.getNumberOfInFlightFetches(), - clusterService.getMasterService().getMaxTaskWaitTime() + clusterService.getClusterManagerService().getMaxTaskWaitTime() ); int readyCounter = prepareResponse(request, response, clusterState, indexNameExpressionResolver); boolean valid = (readyCounter == waitFor); @@ -448,6 +491,22 @@ private ClusterHealthResponse clusterHealth( } String[] concreteIndices; + if (request.level().equals(ClusterHealthRequest.Level.AWARENESS_ATTRIBUTES)) { + String awarenessAttribute = request.getAwarenessAttribute(); + concreteIndices = clusterState.getMetadata().getConcreteAllIndices(); + return new ClusterHealthResponse( + clusterState.getClusterName().value(), + clusterState, + clusterService.getClusterSettings(), + concreteIndices, + awarenessAttribute, + numberOfPendingTasks, + numberOfInFlightFetch, + UnassignedInfo.getNumberOfDelayedUnassigned(clusterState), + pendingTaskTimeInQueue + ); + } + try { concreteIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, request); } catch (IndexNotFoundException e) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/health/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/health/package-info.java new file mode 100644 index 0000000000000..7512ea5cdaafb --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/health/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Health transport handlers. */ +package org.opensearch.action.admin.cluster.health; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodeHotThreads.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodeHotThreads.java index 7d873bd22719d..9c33d023f832d 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodeHotThreads.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodeHotThreads.java @@ -34,11 +34,16 @@ import org.opensearch.action.support.nodes.BaseNodeResponse; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport for OpenSearch Hot Threads + * + * @opensearch.internal + */ public class NodeHotThreads extends BaseNodeResponse { private String hotThreads; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsAction.java index 1a09bccf0b34e..64689b2e3bc6d 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for requesting OpenSearch Hot Threads + * + * @opensearch.internal + */ public class NodesHotThreadsAction extends ActionType { public static final NodesHotThreadsAction INSTANCE = new NodesHotThreadsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java index 5f5c566e7b6d2..39e912a995f35 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequest.java @@ -33,13 +33,18 @@ package org.opensearch.action.admin.cluster.node.hotthreads; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.concurrent.TimeUnit; +/** + * Transport request for OpenSearch Hot Threads + * + * @opensearch.internal + */ public class NodesHotThreadsRequest extends BaseNodesRequest { int threads = 3; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequestBuilder.java index 54a99de61c854..3639439dd3fb8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsRequestBuilder.java @@ -36,6 +36,11 @@ import org.opensearch.client.OpenSearchClient; import org.opensearch.common.unit.TimeValue; +/** + * Builder class for requesting OpenSearch Hot Threads + * + * @opensearch.internal + */ public class NodesHotThreadsRequestBuilder extends NodesOperationRequestBuilder< NodesHotThreadsRequest, NodesHotThreadsResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsResponse.java index 62c73101eddbb..5af9ce50a4bfe 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/NodesHotThreadsResponse.java @@ -35,12 +35,17 @@ import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.List; +/** + * Transport response for OpenSearch Hot Threads + * + * @opensearch.internal + */ public class NodesHotThreadsResponse extends BaseNodesResponse { public NodesHotThreadsResponse(StreamInput in) throws IOException { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/TransportNodesHotThreadsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/TransportNodesHotThreadsAction.java index 4c4519c890d90..942fec7a9f41e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/TransportNodesHotThreadsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/TransportNodesHotThreadsAction.java @@ -39,8 +39,8 @@ import org.opensearch.action.support.nodes.TransportNodesAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.monitor.jvm.HotThreads; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -48,6 +48,11 @@ import java.io.IOException; import java.util.List; +/** + * Transport action for OpenSearch Hot Threads + * + * @opensearch.internal + */ public class TransportNodesHotThreadsAction extends TransportNodesAction< NodesHotThreadsRequest, NodesHotThreadsResponse, @@ -107,6 +112,11 @@ protected NodeHotThreads nodeOperation(NodeRequest request) { } } + /** + * Inner node request + * + * @opensearch.internal + */ public static class NodeRequest extends BaseNodeRequest { NodesHotThreadsRequest request; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/package-info.java new file mode 100644 index 0000000000000..a7e6b7c897fe5 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/hotthreads/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Hot threads at node level transport handlers. */ +package org.opensearch.action.admin.cluster.node.hotthreads; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodeInfo.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodeInfo.java index ddef959e66473..c9016b8bddec7 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodeInfo.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodeInfo.java @@ -38,17 +38,18 @@ import org.opensearch.action.support.nodes.BaseNodeResponse; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.service.ReportingService; import org.opensearch.http.HttpInfo; import org.opensearch.ingest.IngestInfo; import org.opensearch.monitor.jvm.JvmInfo; import org.opensearch.monitor.os.OsInfo; import org.opensearch.monitor.process.ProcessInfo; -import org.opensearch.node.ReportingService; import org.opensearch.search.aggregations.support.AggregationInfo; +import org.opensearch.search.pipeline.SearchPipelineInfo; import org.opensearch.threadpool.ThreadPoolInfo; import org.opensearch.transport.TransportInfo; @@ -58,6 +59,8 @@ /** * Node information (static, does not change over time). + * + * @opensearch.internal */ public class NodeInfo extends BaseNodeResponse { @@ -79,8 +82,8 @@ public class NodeInfo extends BaseNodeResponse { public NodeInfo(StreamInput in) throws IOException { super(in); - version = Version.readVersion(in); - build = Build.readBuild(in); + version = in.readVersion(); + build = in.readBuild(); if (in.readBoolean()) { totalIndexingBuffer = new ByteSizeValue(in.readLong()); } else { @@ -100,6 +103,9 @@ public NodeInfo(StreamInput in) throws IOException { if (in.getVersion().onOrAfter(LegacyESVersion.V_7_10_0)) { addInfoIfNonNull(AggregationInfo.class, in.readOptionalWriteable(AggregationInfo::new)); } + if (in.getVersion().onOrAfter(Version.V_2_7_0)) { + addInfoIfNonNull(SearchPipelineInfo.class, in.readOptionalWriteable(SearchPipelineInfo::new)); + } } public NodeInfo( @@ -116,7 +122,8 @@ public NodeInfo( @Nullable PluginsAndModules plugins, @Nullable IngestInfo ingest, @Nullable AggregationInfo aggsInfo, - @Nullable ByteSizeValue totalIndexingBuffer + @Nullable ByteSizeValue totalIndexingBuffer, + @Nullable SearchPipelineInfo searchPipelineInfo ) { super(node); this.version = version; @@ -131,6 +138,7 @@ public NodeInfo( addInfoIfNonNull(PluginsAndModules.class, plugins); addInfoIfNonNull(IngestInfo.class, ingest); addInfoIfNonNull(AggregationInfo.class, aggsInfo); + addInfoIfNonNull(SearchPipelineInfo.class, searchPipelineInfo); this.totalIndexingBuffer = totalIndexingBuffer; } @@ -201,7 +209,7 @@ public void writeTo(StreamOutput out) throws IOException { } else { out.writeVInt(version.id); } - Build.writeBuild(build, out); + out.writeBuild(build); if (totalIndexingBuffer == null) { out.writeBoolean(false); } else { @@ -225,5 +233,121 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getVersion().onOrAfter(LegacyESVersion.V_7_10_0)) { out.writeOptionalWriteable(getInfo(AggregationInfo.class)); } + if (out.getVersion().onOrAfter(Version.V_2_7_0)) { + out.writeOptionalWriteable(getInfo(SearchPipelineInfo.class)); + } + } + + public static NodeInfo.Builder builder(Version version, Build build, DiscoveryNode node) { + return new Builder(version, build, node); + } + + /** + * Builder class to accommodate new Info types being added to NodeInfo. + */ + public static class Builder { + private final Version version; + private final Build build; + private final DiscoveryNode node; + + private Builder(Version version, Build build, DiscoveryNode node) { + this.version = version; + this.build = build; + this.node = node; + } + + private Settings settings; + private OsInfo os; + private ProcessInfo process; + private JvmInfo jvm; + private ThreadPoolInfo threadPool; + private TransportInfo transport; + private HttpInfo http; + private PluginsAndModules plugins; + private IngestInfo ingest; + private AggregationInfo aggsInfo; + private ByteSizeValue totalIndexingBuffer; + private SearchPipelineInfo searchPipelineInfo; + + public Builder setSettings(Settings settings) { + this.settings = settings; + return this; + } + + public Builder setOs(OsInfo os) { + this.os = os; + return this; + } + + public Builder setProcess(ProcessInfo process) { + this.process = process; + return this; + } + + public Builder setJvm(JvmInfo jvm) { + this.jvm = jvm; + return this; + } + + public Builder setThreadPool(ThreadPoolInfo threadPool) { + this.threadPool = threadPool; + return this; + } + + public Builder setTransport(TransportInfo transport) { + this.transport = transport; + return this; + } + + public Builder setHttp(HttpInfo http) { + this.http = http; + return this; + } + + public Builder setPlugins(PluginsAndModules plugins) { + this.plugins = plugins; + return this; + } + + public Builder setIngest(IngestInfo ingest) { + this.ingest = ingest; + return this; + } + + public Builder setAggsInfo(AggregationInfo aggsInfo) { + this.aggsInfo = aggsInfo; + return this; + } + + public Builder setTotalIndexingBuffer(ByteSizeValue totalIndexingBuffer) { + this.totalIndexingBuffer = totalIndexingBuffer; + return this; + } + + public Builder setSearchPipelineInfo(SearchPipelineInfo searchPipelineInfo) { + this.searchPipelineInfo = searchPipelineInfo; + return this; + } + + public NodeInfo build() { + return new NodeInfo( + version, + build, + node, + settings, + os, + process, + jvm, + threadPool, + transport, + http, + plugins, + ingest, + aggsInfo, + totalIndexingBuffer, + searchPipelineInfo + ); + } + } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoAction.java index 6850cbee55412..ea0c9d9d7f3c9 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for OpenSearch Node Information + * + * @opensearch.internal + */ public class NodesInfoAction extends ActionType { public static final NodesInfoAction INSTANCE = new NodesInfoAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoRequest.java index 4024c9b0c12a9..e65027171fbb6 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoRequest.java @@ -34,8 +34,8 @@ import org.opensearch.LegacyESVersion; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Arrays; @@ -47,6 +47,8 @@ /** * A request to get node (cluster) level information. + * + * @opensearch.internal */ public class NodesInfoRequest extends BaseNodesRequest { @@ -197,7 +199,8 @@ public enum Metric { PLUGINS("plugins"), INGEST("ingest"), AGGREGATIONS("aggregations"), - INDICES("indices"); + INDICES("indices"), + SEARCH_PIPELINES("search_pipelines"); private String metricName; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoRequestBuilder.java index 179239cac5257..76ef75b77a1cf 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.support.nodes.NodesOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport action for OpenSearch Node Information + * + * @opensearch.internal + */ public class NodesInfoRequestBuilder extends NodesOperationRequestBuilder { public NodesInfoRequestBuilder(OpenSearchClient client, NodesInfoAction action) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoResponse.java index a483a2fa1f845..5b4444053a8b7 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/NodesInfoResponse.java @@ -36,19 +36,19 @@ import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.node.DiscoveryNodeRole; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.http.HttpInfo; import org.opensearch.ingest.IngestInfo; import org.opensearch.monitor.jvm.JvmInfo; import org.opensearch.monitor.os.OsInfo; import org.opensearch.monitor.process.ProcessInfo; import org.opensearch.search.aggregations.support.AggregationInfo; +import org.opensearch.search.pipeline.SearchPipelineInfo; import org.opensearch.threadpool.ThreadPoolInfo; import org.opensearch.transport.TransportInfo; @@ -56,6 +56,11 @@ import java.util.List; import java.util.Map; +/** + * Transport response for OpenSearch Node Information + * + * @opensearch.internal + */ public class NodesInfoResponse extends BaseNodesResponse implements ToXContentFragment { public NodesInfoResponse(StreamInput in) throws IOException { @@ -142,6 +147,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (nodeInfo.getInfo(AggregationInfo.class) != null) { nodeInfo.getInfo(AggregationInfo.class).toXContent(builder, params); } + if (nodeInfo.getInfo(SearchPipelineInfo.class) != null) { + nodeInfo.getInfo(SearchPipelineInfo.class).toXContent(builder, params); + } builder.endObject(); } @@ -156,7 +164,7 @@ public String toString() { builder.startObject(); toXContent(builder, EMPTY_PARAMS); builder.endObject(); - return Strings.toString(builder); + return builder.toString(); } catch (IOException e) { return "{ \"error\" : \"" + e.getMessage() + "\"}"; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/PluginsAndModules.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/PluginsAndModules.java index a51a0ab7b6ea6..13f7211d48e9a 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/PluginsAndModules.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/PluginsAndModules.java @@ -32,10 +32,10 @@ package org.opensearch.action.admin.cluster.node.info; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.node.ReportingService; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.service.ReportingService; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.plugins.PluginInfo; import java.io.IOException; @@ -46,6 +46,8 @@ /** * Information about plugins and modules + * + * @opensearch.internal */ public class PluginsAndModules implements ReportingService.Info { private final List plugins; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/TransportNodesInfoAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/TransportNodesInfoAction.java index 52f53f4c1368e..f38ebe35a52d2 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/TransportNodesInfoAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/TransportNodesInfoAction.java @@ -38,8 +38,8 @@ import org.opensearch.action.support.nodes.TransportNodesAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.node.NodeService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -48,6 +48,11 @@ import java.util.List; import java.util.Set; +/** + * Transport action for OpenSearch Node Information + * + * @opensearch.internal + */ public class TransportNodesInfoAction extends TransportNodesAction< NodesInfoRequest, NodesInfoResponse, @@ -112,10 +117,16 @@ protected NodeInfo nodeOperation(NodeInfoRequest nodeRequest) { metrics.contains(NodesInfoRequest.Metric.PLUGINS.metricName()), metrics.contains(NodesInfoRequest.Metric.INGEST.metricName()), metrics.contains(NodesInfoRequest.Metric.AGGREGATIONS.metricName()), - metrics.contains(NodesInfoRequest.Metric.INDICES.metricName()) + metrics.contains(NodesInfoRequest.Metric.INDICES.metricName()), + metrics.contains(NodesInfoRequest.Metric.SEARCH_PIPELINES.metricName()) ); } + /** + * Inner Node Info Request + * + * @opensearch.internal + */ public static class NodeInfoRequest extends BaseNodeRequest { NodesInfoRequest request; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/info/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/package-info.java new file mode 100644 index 0000000000000..4203adc8ed2d6 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/info/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node Info transport handlers. */ +package org.opensearch.action.admin.cluster.node.info; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/LivenessRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/LivenessRequest.java index c75e020c269eb..e4d9267123862 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/LivenessRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/LivenessRequest.java @@ -33,13 +33,15 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** * Transport level private response for the transport handler registered under * {@value TransportLivenessAction#NAME} + * + * @opensearch.internal */ public final class LivenessRequest extends ActionRequest { public LivenessRequest() {} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/LivenessResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/LivenessResponse.java index 1737d868f0afb..c164962cadcdc 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/LivenessResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/LivenessResponse.java @@ -32,17 +32,19 @@ package org.opensearch.action.admin.cluster.node.liveness; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * Transport level private response for the transport handler registered under * {@value TransportLivenessAction#NAME} + * + * @opensearch.internal */ public final class LivenessResponse extends ActionResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/TransportLivenessAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/TransportLivenessAction.java index 83f945ab85557..8a075cbee6e90 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/TransportLivenessAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/TransportLivenessAction.java @@ -40,6 +40,11 @@ import org.opensearch.transport.TransportRequestHandler; import org.opensearch.transport.TransportService; +/** + * Transport action for OpenSearch Node Liveness + * + * @opensearch.internal + */ public final class TransportLivenessAction implements TransportRequestHandler { private final ClusterService clusterService; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/package-info.java new file mode 100644 index 0000000000000..97dae1e4a4ef4 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/liveness/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node liveness transport handlers. */ +package org.opensearch.action.admin.cluster.node.liveness; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/package-info.java new file mode 100644 index 0000000000000..f92091587eb04 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node level transport handlers. */ +package org.opensearch.action.admin.cluster.node; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsAction.java index 252c4491bc56c..0f7c847582349 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for reloading OpenSearch Secure Settings + * + * @opensearch.internal + */ public class NodesReloadSecureSettingsAction extends ActionType { public static final NodesReloadSecureSettingsAction INSTANCE = new NodesReloadSecureSettingsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsRequest.java index 5a6c4c2ae7c83..415331a1b33bd 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsRequest.java @@ -34,21 +34,21 @@ import org.opensearch.LegacyESVersion; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.io.stream.StreamInput; - -import java.io.IOException; - import org.opensearch.common.CharArrays; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.settings.SecureString; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.settings.SecureString; +import java.io.IOException; import java.util.Arrays; /** * Request for a reload secure settings action + * + * @opensearch.internal */ public class NodesReloadSecureSettingsRequest extends BaseNodesRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsRequestBuilder.java index 8bb67f5042893..2f2162947aeea 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsRequestBuilder.java @@ -34,10 +34,12 @@ import org.opensearch.action.support.nodes.NodesOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.settings.SecureString; +import org.opensearch.core.common.settings.SecureString; /** * Builder for the reload secure settings nodes request + * + * @opensearch.internal */ public class NodesReloadSecureSettingsRequestBuilder extends NodesOperationRequestBuilder< NodesReloadSecureSettingsRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsResponse.java index 9dd8596b86260..6c250a8daaf3e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/NodesReloadSecureSettingsResponse.java @@ -38,17 +38,19 @@ import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + import java.io.IOException; import java.util.List; /** * The response for the reload secure settings action + * + * @opensearch.internal */ public class NodesReloadSecureSettingsResponse extends BaseNodesResponse implements @@ -97,12 +99,17 @@ public String toString() { builder.startObject(); toXContent(builder, EMPTY_PARAMS); builder.endObject(); - return Strings.toString(builder); + return builder.toString(); } catch (final IOException e) { return "{ \"error\" : \"" + e.getMessage() + "\"}"; } } + /** + * Inner Node Response + * + * @opensearch.internal + */ public static class NodeResponse extends BaseNodeResponse { private Exception reloadException = null; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/TransportNodesReloadSecureSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/TransportNodesReloadSecureSettingsAction.java index e51ba62c804d7..467636ce922e9 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/TransportNodesReloadSecureSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/TransportNodesReloadSecureSettingsAction.java @@ -34,9 +34,8 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionListener; +import org.opensearch.OpenSearchException; import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.nodes.BaseNodeRequest; @@ -44,11 +43,12 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.KeyStoreWrapper; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; import org.opensearch.plugins.PluginsService; import org.opensearch.plugins.ReloadablePlugin; @@ -60,6 +60,11 @@ import java.util.ArrayList; import java.util.List; +/** + * Transport action for reloading OpenSearch Secure Settings + * + * @opensearch.internal + */ public class TransportNodesReloadSecureSettingsAction extends TransportNodesAction< NodesReloadSecureSettingsRequest, NodesReloadSecureSettingsResponse, @@ -178,6 +183,11 @@ protected NodesReloadSecureSettingsResponse.NodeResponse nodeOperation(NodeReque } } + /** + * Inner Node Request + * + * @opensearch.internal + */ public static class NodeRequest extends BaseNodeRequest { NodesReloadSecureSettingsRequest request; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/package-info.java new file mode 100644 index 0000000000000..a2ddf57796a4a --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/reload/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node reloading secured settings transport handlers. */ +package org.opensearch.action.admin.cluster.node.reload; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodeStats.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodeStats.java index dd2c649e07c28..ce8033e971b44 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodeStats.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodeStats.java @@ -37,17 +37,20 @@ import org.opensearch.action.support.nodes.BaseNodeResponse; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; +import org.opensearch.cluster.routing.WeightedRoutingStats; +import org.opensearch.cluster.service.ClusterManagerThrottlingStats; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.indices.breaker.AllCircuitBreakerStats; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.discovery.DiscoveryStats; import org.opensearch.http.HttpStats; import org.opensearch.index.stats.IndexingPressureStats; import org.opensearch.index.stats.ShardIndexingPressureStats; +import org.opensearch.index.store.remote.filecache.FileCacheStats; import org.opensearch.indices.NodeIndicesStats; -import org.opensearch.indices.breaker.AllCircuitBreakerStats; import org.opensearch.ingest.IngestStats; import org.opensearch.monitor.fs.FsInfo; import org.opensearch.monitor.jvm.JvmStats; @@ -56,6 +59,9 @@ import org.opensearch.node.AdaptiveSelectionStats; import org.opensearch.script.ScriptCacheStats; import org.opensearch.script.ScriptStats; +import org.opensearch.search.backpressure.stats.SearchBackpressureStats; +import org.opensearch.search.pipeline.SearchPipelineStats; +import org.opensearch.tasks.TaskCancellationStats; import org.opensearch.threadpool.ThreadPoolStats; import org.opensearch.transport.TransportStats; @@ -64,6 +70,8 @@ /** * Node statistics (dynamic, changes depending on when created). + * + * @opensearch.internal */ public class NodeStats extends BaseNodeResponse implements ToXContentFragment { @@ -117,6 +125,24 @@ public class NodeStats extends BaseNodeResponse implements ToXContentFragment { @Nullable private ShardIndexingPressureStats shardIndexingPressureStats; + @Nullable + private SearchBackpressureStats searchBackpressureStats; + + @Nullable + private ClusterManagerThrottlingStats clusterManagerThrottlingStats; + + @Nullable + private WeightedRoutingStats weightedRoutingStats; + + @Nullable + private FileCacheStats fileCacheStats; + + @Nullable + private TaskCancellationStats taskCancellationStats; + + @Nullable + private SearchPipelineStats searchPipelineStats; + public NodeStats(StreamInput in) throws IOException { super(in); timestamp = in.readVLong(); @@ -154,6 +180,37 @@ public NodeStats(StreamInput in) throws IOException { shardIndexingPressureStats = null; } + if (in.getVersion().onOrAfter(Version.V_2_4_0)) { + searchBackpressureStats = in.readOptionalWriteable(SearchBackpressureStats::new); + } else { + searchBackpressureStats = null; + } + + if (in.getVersion().onOrAfter(Version.V_2_6_0)) { + clusterManagerThrottlingStats = in.readOptionalWriteable(ClusterManagerThrottlingStats::new); + } else { + clusterManagerThrottlingStats = null; + } + if (in.getVersion().onOrAfter(Version.V_2_6_0)) { + weightedRoutingStats = in.readOptionalWriteable(WeightedRoutingStats::new); + } else { + weightedRoutingStats = null; + } + if (in.getVersion().onOrAfter(Version.V_2_7_0)) { + fileCacheStats = in.readOptionalWriteable(FileCacheStats::new); + } else { + fileCacheStats = null; + } + if (in.getVersion().onOrAfter(Version.V_2_9_0)) { + taskCancellationStats = in.readOptionalWriteable(TaskCancellationStats::new); + } else { + taskCancellationStats = null; + } + if (in.getVersion().onOrAfter(Version.V_2_9_0)) { + searchPipelineStats = in.readOptionalWriteable(SearchPipelineStats::new); + } else { + searchPipelineStats = null; + } } public NodeStats( @@ -174,7 +231,13 @@ public NodeStats( @Nullable AdaptiveSelectionStats adaptiveSelectionStats, @Nullable ScriptCacheStats scriptCacheStats, @Nullable IndexingPressureStats indexingPressureStats, - @Nullable ShardIndexingPressureStats shardIndexingPressureStats + @Nullable ShardIndexingPressureStats shardIndexingPressureStats, + @Nullable SearchBackpressureStats searchBackpressureStats, + @Nullable ClusterManagerThrottlingStats clusterManagerThrottlingStats, + @Nullable WeightedRoutingStats weightedRoutingStats, + @Nullable FileCacheStats fileCacheStats, + @Nullable TaskCancellationStats taskCancellationStats, + @Nullable SearchPipelineStats searchPipelineStats ) { super(node); this.timestamp = timestamp; @@ -194,6 +257,12 @@ public NodeStats( this.scriptCacheStats = scriptCacheStats; this.indexingPressureStats = indexingPressureStats; this.shardIndexingPressureStats = shardIndexingPressureStats; + this.searchBackpressureStats = searchBackpressureStats; + this.clusterManagerThrottlingStats = clusterManagerThrottlingStats; + this.weightedRoutingStats = weightedRoutingStats; + this.fileCacheStats = fileCacheStats; + this.taskCancellationStats = taskCancellationStats; + this.searchPipelineStats = searchPipelineStats; } public long getTimestamp() { @@ -303,6 +372,34 @@ public ShardIndexingPressureStats getShardIndexingPressureStats() { return shardIndexingPressureStats; } + @Nullable + public SearchBackpressureStats getSearchBackpressureStats() { + return searchBackpressureStats; + } + + @Nullable + public ClusterManagerThrottlingStats getClusterManagerThrottlingStats() { + return clusterManagerThrottlingStats; + } + + public WeightedRoutingStats getWeightedRoutingStats() { + return weightedRoutingStats; + } + + public FileCacheStats getFileCacheStats() { + return fileCacheStats; + } + + @Nullable + public TaskCancellationStats getTaskCancellationStats() { + return taskCancellationStats; + } + + @Nullable + public SearchPipelineStats getSearchPipelineStats() { + return searchPipelineStats; + } + @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); @@ -334,6 +431,24 @@ public void writeTo(StreamOutput out) throws IOException { if (out.getVersion().onOrAfter(Version.V_1_2_0)) { out.writeOptionalWriteable(shardIndexingPressureStats); } + if (out.getVersion().onOrAfter(Version.V_2_4_0)) { + out.writeOptionalWriteable(searchBackpressureStats); + } + if (out.getVersion().onOrAfter(Version.V_2_6_0)) { + out.writeOptionalWriteable(clusterManagerThrottlingStats); + } + if (out.getVersion().onOrAfter(Version.V_2_6_0)) { + out.writeOptionalWriteable(weightedRoutingStats); + } + if (out.getVersion().onOrAfter(Version.V_2_7_0)) { + out.writeOptionalWriteable(fileCacheStats); + } + if (out.getVersion().onOrAfter(Version.V_2_9_0)) { + out.writeOptionalWriteable(taskCancellationStats); + } + if (out.getVersion().onOrAfter(Version.V_2_9_0)) { + out.writeOptionalWriteable(searchPipelineStats); + } } @Override @@ -406,6 +521,25 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (getShardIndexingPressureStats() != null) { getShardIndexingPressureStats().toXContent(builder, params); } + if (getSearchBackpressureStats() != null) { + getSearchBackpressureStats().toXContent(builder, params); + } + if (getClusterManagerThrottlingStats() != null) { + getClusterManagerThrottlingStats().toXContent(builder, params); + } + if (getWeightedRoutingStats() != null) { + getWeightedRoutingStats().toXContent(builder, params); + } + if (getFileCacheStats() != null) { + getFileCacheStats().toXContent(builder, params); + } + if (getTaskCancellationStats() != null) { + getTaskCancellationStats().toXContent(builder, params); + } + if (getSearchPipelineStats() != null) { + getSearchPipelineStats().toXContent(builder, params); + } + return builder; } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsAction.java index 77bb141966651..fc039778f05af 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for obtaining OpenSearch Node Stats + * + * @opensearch.internal + */ public class NodesStatsAction extends ActionType { public static final NodesStatsAction INSTANCE = new NodesStatsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequest.java index 2ebf2ca424c27..5022427628647 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequest.java @@ -35,8 +35,9 @@ import org.opensearch.LegacyESVersion; import org.opensearch.action.admin.indices.stats.CommonStatsFlags; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + import java.io.IOException; import java.util.Arrays; import java.util.HashSet; @@ -47,6 +48,8 @@ /** * A request to get node (cluster) level stats. + * + * @opensearch.internal */ public class NodesStatsRequest extends BaseNodesRequest { @@ -235,7 +238,13 @@ public enum Metric { ADAPTIVE_SELECTION("adaptive_selection"), SCRIPT_CACHE("script_cache"), INDEXING_PRESSURE("indexing_pressure"), - SHARD_INDEXING_PRESSURE("shard_indexing_pressure"); + SHARD_INDEXING_PRESSURE("shard_indexing_pressure"), + SEARCH_BACKPRESSURE("search_backpressure"), + CLUSTER_MANAGER_THROTTLING("cluster_manager_throttling"), + WEIGHTED_ROUTING_STATS("weighted_routing"), + FILE_CACHE_STATS("file_cache"), + TASK_CANCELLATION("task_cancellation"), + SEARCH_PIPELINE("search_pipeline"); private String metricName; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequestBuilder.java index 40d6946823727..e382278f5ddb8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsRequestBuilder.java @@ -36,6 +36,11 @@ import org.opensearch.action.support.nodes.NodesOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport builder for obtaining OpenSearch Node Stats + * + * @opensearch.internal + */ public class NodesStatsRequestBuilder extends NodesOperationRequestBuilder< NodesStatsRequest, NodesStatsResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsResponse.java index 8c84775e356c4..8fbc52ad8a3f8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/NodesStatsResponse.java @@ -35,16 +35,20 @@ import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.List; +/** + * Transport response for obtaining OpenSearch Node Stats + * + * @opensearch.internal + */ public class NodesStatsResponse extends BaseNodesResponse implements ToXContentFragment { public NodesStatsResponse(StreamInput in) throws IOException { @@ -87,7 +91,7 @@ public String toString() { builder.startObject(); toXContent(builder, EMPTY_PARAMS); builder.endObject(); - return Strings.toString(builder); + return builder.toString(); } catch (IOException e) { return "{ \"error\" : \"" + e.getMessage() + "\"}"; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/TransportNodesStatsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/TransportNodesStatsAction.java index b7ea714de4eb4..2c1a61ea6e3b2 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/TransportNodesStatsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/TransportNodesStatsAction.java @@ -38,8 +38,8 @@ import org.opensearch.action.support.nodes.TransportNodesAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.node.NodeService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -48,6 +48,11 @@ import java.util.List; import java.util.Set; +/** + * Transport action for obtaining OpenSearch Node Stats + * + * @opensearch.internal + */ public class TransportNodesStatsAction extends TransportNodesAction< NodesStatsRequest, NodesStatsResponse, @@ -113,10 +118,21 @@ protected NodeStats nodeOperation(NodeStatsRequest nodeStatsRequest) { NodesStatsRequest.Metric.ADAPTIVE_SELECTION.containedIn(metrics), NodesStatsRequest.Metric.SCRIPT_CACHE.containedIn(metrics), NodesStatsRequest.Metric.INDEXING_PRESSURE.containedIn(metrics), - NodesStatsRequest.Metric.SHARD_INDEXING_PRESSURE.containedIn(metrics) + NodesStatsRequest.Metric.SHARD_INDEXING_PRESSURE.containedIn(metrics), + NodesStatsRequest.Metric.SEARCH_BACKPRESSURE.containedIn(metrics), + NodesStatsRequest.Metric.CLUSTER_MANAGER_THROTTLING.containedIn(metrics), + NodesStatsRequest.Metric.WEIGHTED_ROUTING_STATS.containedIn(metrics), + NodesStatsRequest.Metric.FILE_CACHE_STATS.containedIn(metrics), + NodesStatsRequest.Metric.TASK_CANCELLATION.containedIn(metrics), + NodesStatsRequest.Metric.SEARCH_PIPELINE.containedIn(metrics) ); } + /** + * Inner Node Stats Request + * + * @opensearch.internal + */ public static class NodeStatsRequest extends BaseNodeRequest { NodesStatsRequest request; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/package-info.java new file mode 100644 index 0000000000000..14efa77f066d6 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/stats/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node Stats transport handlers. */ +package org.opensearch.action.admin.cluster.node.stats; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksAction.java index 6d6098adaa92c..d4f6dc39c93f7 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksAction.java @@ -36,6 +36,8 @@ /** * ActionType for cancelling running tasks + * + * @opensearch.internal */ public class CancelTasksAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksRequest.java index ea7e225051b80..8d2e1ac5eca42 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksRequest.java @@ -34,8 +34,8 @@ import org.opensearch.LegacyESVersion; import org.opensearch.action.support.tasks.BaseTasksRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.tasks.CancellableTask; import org.opensearch.tasks.Task; @@ -44,6 +44,8 @@ /** * A request to cancel tasks + * + * @opensearch.internal */ public class CancelTasksRequest extends BaseTasksRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksRequestBuilder.java index c0686f166c859..ee19e8b104603 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksRequestBuilder.java @@ -37,6 +37,8 @@ /** * Builder for the request to cancel tasks running on the specified nodes + * + * @opensearch.internal */ public class CancelTasksRequestBuilder extends TasksRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksResponse.java index 056463b85fe80..979489999cc6e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/CancelTasksResponse.java @@ -35,11 +35,12 @@ import org.opensearch.OpenSearchException; import org.opensearch.action.TaskOperationFailure; import org.opensearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.tasks.TaskInfo; import java.io.IOException; @@ -47,6 +48,8 @@ /** * Returns the list of tasks that were cancelled + * + * @opensearch.internal */ public class CancelTasksResponse extends ListTasksResponse { @@ -78,6 +81,6 @@ public static CancelTasksResponse fromXContent(XContentParser parser) { @Override public String toString() { - return Strings.toString(this, true, true); + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/TransportCancelTasksAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/TransportCancelTasksAction.java index 963ebe24ae8e2..634ce1e2d4095 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/TransportCancelTasksAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/TransportCancelTasksAction.java @@ -33,13 +33,13 @@ package org.opensearch.action.admin.cluster.node.tasks.cancel; import org.opensearch.ResourceNotFoundException; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.TaskOperationFailure; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.tasks.TransportTasksAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; import org.opensearch.tasks.CancellableTask; import org.opensearch.tasks.TaskInfo; import org.opensearch.threadpool.ThreadPool; @@ -54,6 +54,8 @@ *

    * For a task to be cancellable it has to return an instance of * {@link CancellableTask} from {@link TransportRequest#createTask} + * + * @opensearch.internal */ public class TransportCancelTasksAction extends TransportTasksAction { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/package-info.java new file mode 100644 index 0000000000000..898aa26d16792 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/cancel/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handler for Cancelling Node Tasks */ +package org.opensearch.action.admin.cluster.node.tasks.cancel; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskAction.java index 4297e911d6bde..3b429d61c31b3 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskAction.java @@ -36,6 +36,8 @@ /** * ActionType for retrieving a list of currently running tasks + * + * @opensearch.internal */ public class GetTaskAction extends ActionType { public static final String TASKS_ORIGIN = "tasks"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskRequest.java index a8fee2dc22d30..82902c55b1b0a 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskRequest.java @@ -34,10 +34,10 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskId; import java.io.IOException; @@ -45,6 +45,8 @@ /** * A request to get node tasks + * + * @opensearch.internal */ public class GetTaskRequest extends ActionRequest { private TaskId taskId = TaskId.EMPTY_TASK_ID; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskRequestBuilder.java index d7d0be125bee3..dd429da82ebf8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskRequestBuilder.java @@ -35,10 +35,12 @@ import org.opensearch.action.ActionRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.unit.TimeValue; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.tasks.TaskId; /** * Builder for the request to retrieve the list of tasks running on the specified nodes + * + * @opensearch.internal */ public class GetTaskRequestBuilder extends ActionRequestBuilder { public GetTaskRequestBuilder(OpenSearchClient client, GetTaskAction action) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskResponse.java index 32d7062449a71..9d4e6da8c7e62 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/GetTaskResponse.java @@ -32,12 +32,13 @@ package org.opensearch.action.admin.cluster.node.tasks.get; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.tasks.TaskResult; import java.io.IOException; @@ -46,6 +47,8 @@ /** * Returns the list of tasks currently running on the nodes + * + * @opensearch.internal */ public class GetTaskResponse extends ActionResponse implements ToXContentObject { @@ -82,6 +85,6 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/TransportGetTaskAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/TransportGetTaskAction.java index 80049b5e30fdf..8d82827f4ee50 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/TransportGetTaskAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/TransportGetTaskAction.java @@ -32,10 +32,9 @@ package org.opensearch.action.admin.cluster.node.tasks.get; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; +import org.opensearch.OpenSearchException; import org.opensearch.ResourceNotFoundException; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionListenerResponseHandler; import org.opensearch.action.get.GetRequest; import org.opensearch.action.get.GetResponse; @@ -48,12 +47,13 @@ import org.opensearch.common.inject.Inject; import org.opensearch.common.util.concurrent.AbstractRunnable; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.IndexNotFoundException; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskInfo; import org.opensearch.tasks.TaskResult; import org.opensearch.tasks.TaskResultsService; @@ -74,6 +74,8 @@ *

  • Look up the task and return it if it exists *
  • If it doesn't then look up the task from the results index * + * + * @opensearch.internal */ public class TransportGetTaskAction extends HandledTransportAction { private final ThreadPool threadPool; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/package-info.java new file mode 100644 index 0000000000000..0eb044c2a29eb --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handlers for getting Node Tasks. */ +package org.opensearch.action.admin.cluster.node.tasks.get; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksAction.java index 76c07cd570622..7c8b83ad9c913 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksAction.java @@ -36,6 +36,8 @@ /** * ActionType for retrieving a list of currently running tasks + * + * @opensearch.internal */ public class ListTasksAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksRequest.java index 4f1a5765d1eea..070b93c788ef0 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksRequest.java @@ -33,13 +33,15 @@ package org.opensearch.action.admin.cluster.node.tasks.list; import org.opensearch.action.support.tasks.BaseTasksRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * A request to get node tasks + * + * @opensearch.internal */ public class ListTasksRequest extends BaseTasksRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksRequestBuilder.java index dc10fb99e90b9..45beb0dd899b5 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksRequestBuilder.java @@ -37,6 +37,8 @@ /** * Builder for the request to retrieve the list of tasks running on the specified nodes + * + * @opensearch.internal */ public class ListTasksRequestBuilder extends TasksRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java index db0b1491fd693..693566391fb25 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java @@ -38,16 +38,17 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; import org.opensearch.common.TriFunction; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.tasks.TaskInfo; import java.io.IOException; @@ -58,10 +59,12 @@ import java.util.Map; import java.util.stream.Collectors; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Returns the list of tasks currently running on the nodes + * + * @opensearch.internal */ public class ListTasksResponse extends BaseTasksResponse implements ToXContentObject { private static final String TASKS = "tasks"; @@ -259,6 +262,6 @@ public static ListTasksResponse fromXContent(XContentParser parser) { @Override public String toString() { - return Strings.toString(this, true, true); + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/TaskGroup.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/TaskGroup.java index 6d1d96bfd7869..0ca114ae0ed5c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/TaskGroup.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/TaskGroup.java @@ -32,8 +32,8 @@ package org.opensearch.action.admin.cluster.node.tasks.list; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.tasks.TaskInfo; import java.io.IOException; @@ -44,6 +44,8 @@ /** * Information about a currently running task and all its subtasks. + * + * @opensearch.internal */ public class TaskGroup implements ToXContentObject { @@ -60,6 +62,11 @@ public static Builder builder(TaskInfo taskInfo) { return new Builder(taskInfo); } + /** + * Builder for the Task Group + * + * @opensearch.internal + */ public static class Builder { private TaskInfo taskInfo; private List childTasks; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/TransportListTasksAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/TransportListTasksAction.java index b7875c5f99774..1c543e60c46e0 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/TransportListTasksAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/TransportListTasksAction.java @@ -32,7 +32,6 @@ package org.opensearch.action.admin.cluster.node.tasks.list; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.TaskOperationFailure; import org.opensearch.action.support.ActionFilters; @@ -40,8 +39,10 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; import org.opensearch.tasks.Task; import org.opensearch.tasks.TaskInfo; +import org.opensearch.tasks.TaskResourceTrackingService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -50,6 +51,11 @@ import static org.opensearch.common.unit.TimeValue.timeValueSeconds; +/** + * Transport action for listing tasks currently running on the nodes + * + * @opensearch.internal + */ public class TransportListTasksAction extends TransportTasksAction { public static long waitForCompletionTimeout(TimeValue timeout) { if (timeout == null) { @@ -60,8 +66,15 @@ public static long waitForCompletionTimeout(TimeValue timeout) { private static final TimeValue DEFAULT_WAIT_FOR_COMPLETION_TIMEOUT = timeValueSeconds(30); + private final TaskResourceTrackingService taskResourceTrackingService; + @Inject - public TransportListTasksAction(ClusterService clusterService, TransportService transportService, ActionFilters actionFilters) { + public TransportListTasksAction( + ClusterService clusterService, + TransportService transportService, + ActionFilters actionFilters, + TaskResourceTrackingService taskResourceTrackingService + ) { super( ListTasksAction.NAME, clusterService, @@ -72,6 +85,7 @@ public TransportListTasksAction(ClusterService clusterService, TransportService TaskInfo::new, ThreadPool.Names.MANAGEMENT ); + this.taskResourceTrackingService = taskResourceTrackingService; } @Override @@ -101,6 +115,8 @@ protected void processTasks(ListTasksRequest request, Consumer operation) } taskManager.waitForTaskCompletion(task, timeoutNanos); }); + } else { + operation = operation.andThen(taskResourceTrackingService::refreshResourceStats); } super.processTasks(request, operation); } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/package-info.java new file mode 100644 index 0000000000000..df84d7fe19d02 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/list/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handlers for listing node tasks. */ +package org.opensearch.action.admin.cluster.node.tasks.list; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/package-info.java new file mode 100644 index 0000000000000..9d809646460f2 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/tasks/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node tasks transport handlers. */ +package org.opensearch.action.admin.cluster.node.tasks; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodeUsage.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodeUsage.java index 2094861185b50..8170d3631d029 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodeUsage.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodeUsage.java @@ -35,14 +35,19 @@ import org.opensearch.LegacyESVersion; import org.opensearch.action.support.nodes.BaseNodeResponse; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Map; +/** + * Concrete class for collecting OpenSearch telemetry + * + * @opensearch.internal + */ public class NodeUsage extends BaseNodeResponse implements ToXContentFragment { private final long timestamp; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageAction.java index 2a3c6be6e0bbe..8a3aec7679709 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for collecting OpenSearch telemetry + * + * @opensearch.internal + */ public class NodesUsageAction extends ActionType { public static final NodesUsageAction INSTANCE = new NodesUsageAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageRequest.java index 0a94f2a9bfca7..d1845861fc4e8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageRequest.java @@ -34,11 +34,16 @@ import org.opensearch.LegacyESVersion; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport request for collecting OpenSearch telemetry + * + * @opensearch.internal + */ public class NodesUsageRequest extends BaseNodesRequest { private boolean restActions; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageRequestBuilder.java index fefccf4368230..7d1823b59dc04 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageRequestBuilder.java @@ -36,6 +36,11 @@ import org.opensearch.action.support.nodes.NodesOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport builder for collecting OpenSearch telemetry + * + * @opensearch.internal + */ public class NodesUsageRequestBuilder extends NodesOperationRequestBuilder< NodesUsageRequest, NodesUsageResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageResponse.java index d3e5aeea72193..575d88c10317c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/NodesUsageResponse.java @@ -35,12 +35,11 @@ import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.List; @@ -48,6 +47,8 @@ /** * The response for the nodes usage api which contains the individual usage * statistics for all nodes queried. + * + * @opensearch.internal */ public class NodesUsageResponse extends BaseNodesResponse implements ToXContentFragment { @@ -91,7 +92,7 @@ public String toString() { builder.startObject(); toXContent(builder, EMPTY_PARAMS); builder.endObject(); - return Strings.toString(builder); + return builder.toString(); } catch (IOException e) { return "{ \"error\" : \"" + e.getMessage() + "\"}"; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/TransportNodesUsageAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/TransportNodesUsageAction.java index 207a5eb41c85d..091d99da3424a 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/TransportNodesUsageAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/TransportNodesUsageAction.java @@ -38,8 +38,8 @@ import org.opensearch.action.support.nodes.TransportNodesAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.search.aggregations.support.AggregationUsageService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -49,6 +49,11 @@ import java.util.List; import java.util.Map; +/** + * Transport action for collecting OpenSearch telemetry + * + * @opensearch.internal + */ public class TransportNodesUsageAction extends TransportNodesAction< NodesUsageRequest, NodesUsageResponse, @@ -107,6 +112,11 @@ protected NodeUsage nodeOperation(NodeUsageRequest nodeUsageRequest) { return new NodeUsage(clusterService.localNode(), System.currentTimeMillis(), sinceTime, restUsage, aggsUsage); } + /** + * Inner Node Usage Request + * + * @opensearch.internal + */ public static class NodeUsageRequest extends BaseNodeRequest { NodesUsageRequest request; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/package-info.java new file mode 100644 index 0000000000000..37943b23dbe10 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/node/usage/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node Usage transport handlers. */ +package org.opensearch.action.admin.cluster.node.usage; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoAction.java index 099b3abc7438c..55f75a142a53c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for remote monitoring + * + * @opensearch.internal + */ public final class RemoteInfoAction extends ActionType { public static final String NAME = "cluster:monitor/remote/info"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoRequest.java index c140a639ac3ae..be219b15875df 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoRequest.java @@ -34,10 +34,15 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Transport request for remote monitoring + * + * @opensearch.internal + */ public final class RemoteInfoRequest extends ActionRequest { public RemoteInfoRequest() {} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoRequestBuilder.java index 5fceced383731..03fd2a3f778ec 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport builder for remote monitoring + * + * @opensearch.internal + */ public final class RemoteInfoRequestBuilder extends ActionRequestBuilder { public RemoteInfoRequestBuilder(OpenSearchClient client, RemoteInfoAction action) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoResponse.java index 415e71e6d1010..2f47de4f6a5fe 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remote/RemoteInfoResponse.java @@ -32,12 +32,12 @@ package org.opensearch.action.admin.cluster.remote; -import org.opensearch.action.ActionResponse; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.transport.RemoteConnectionInfo; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; @@ -45,6 +45,11 @@ import java.util.Collections; import java.util.List; +/** + * Transport response for remote monitoring + * + * @opensearch.internal + */ public final class RemoteInfoResponse extends ActionResponse implements ToXContentObject { private List infos; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remote/TransportRemoteInfoAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/remote/TransportRemoteInfoAction.java index 10b1917b0c94b..cb7c965069987 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/remote/TransportRemoteInfoAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remote/TransportRemoteInfoAction.java @@ -32,17 +32,22 @@ package org.opensearch.action.admin.cluster.remote; -import org.opensearch.action.ActionListener; import org.opensearch.action.search.SearchTransportService; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; import org.opensearch.tasks.Task; import org.opensearch.transport.RemoteClusterService; import org.opensearch.transport.TransportService; import static java.util.stream.Collectors.toList; +/** + * Transport action for remote monitoring + * + * @opensearch.internal + */ public final class TransportRemoteInfoAction extends HandledTransportAction { private final RemoteClusterService remoteClusterService; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remote/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/remote/package-info.java new file mode 100644 index 0000000000000..e5be56ff1d139 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remote/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Remote Node Information transport handlers. */ +package org.opensearch.action.admin.cluster.remote; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreAction.java new file mode 100644 index 0000000000000..46b1bc14e8537 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreAction.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.restore; + +import org.opensearch.action.ActionType; + +/** + * Restore remote store action + * + * @opensearch.internal + */ +public final class RestoreRemoteStoreAction extends ActionType { + + public static final RestoreRemoteStoreAction INSTANCE = new RestoreRemoteStoreAction(); + public static final String NAME = "cluster:admin/remotestore/restore"; + + private RestoreRemoteStoreAction() { + super(NAME, RestoreRemoteStoreResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreRequest.java new file mode 100644 index 0000000000000..6b3d04c639bc9 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreRequest.java @@ -0,0 +1,211 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.restore; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Restore remote store request + * + * @opensearch.internal + */ +public class RestoreRemoteStoreRequest extends ClusterManagerNodeRequest implements ToXContentObject { + + private String[] indices = Strings.EMPTY_ARRAY; + private Boolean waitForCompletion = false; + private Boolean restoreAllShards = false; + + public RestoreRemoteStoreRequest() {} + + public RestoreRemoteStoreRequest(StreamInput in) throws IOException { + super(in); + indices = in.readStringArray(); + waitForCompletion = in.readOptionalBoolean(); + restoreAllShards = in.readOptionalBoolean(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeStringArray(indices); + out.writeOptionalBoolean(waitForCompletion); + out.writeOptionalBoolean(restoreAllShards); + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (indices == null || indices.length == 0) { + validationException = addValidationError("indices are missing", validationException); + } + return validationException; + } + + /** + * Sets the list of indices that should be restored from the remote store + *

    + * The list of indices supports multi-index syntax. For example: "+test*" ,"-test42" will index all indices with + * prefix "test" except index "test42". Aliases are not supported. An empty list or {"_all"} will restore all open + * indices in the cluster. + * + * @param indices list of indices + * @return this request + */ + public RestoreRemoteStoreRequest indices(String... indices) { + this.indices = indices; + return this; + } + + /** + * Sets the list of indices that should be restored from the remote store + *

    + * The list of indices supports multi-index syntax. For example: "+test*" ,"-test42" will index all indices with + * prefix "test" except index "test42". Aliases are not supported. An empty list or {"_all"} will restore all open + * indices in the cluster. + * + * @param indices list of indices + * @return this request + */ + public RestoreRemoteStoreRequest indices(List indices) { + this.indices = indices.toArray(new String[indices.size()]); + return this; + } + + /** + * Returns list of indices that should be restored from the remote store + */ + public String[] indices() { + return indices; + } + + /** + * If this parameter is set to true the operation will wait for completion of restore process before returning. + * + * @param waitForCompletion if true the operation will wait for completion + * @return this request + */ + public RestoreRemoteStoreRequest waitForCompletion(boolean waitForCompletion) { + this.waitForCompletion = waitForCompletion; + return this; + } + + /** + * Returns wait for completion setting + * + * @return true if the operation will wait for completion + */ + public boolean waitForCompletion() { + return waitForCompletion; + } + + /** + * Set the value for restoreAllShards, denoting whether to restore all shards or only unassigned shards + * + * @param restoreAllShards If true, the operation will restore all the shards of the given indices. + * If false, the operation will restore only the unassigned shards of the given indices. + * @return this request + */ + public RestoreRemoteStoreRequest restoreAllShards(boolean restoreAllShards) { + this.restoreAllShards = restoreAllShards; + return this; + } + + /** + * Returns restoreAllShards setting + * + * @return true if the operation will restore all the shards of the given indices + */ + public boolean restoreAllShards() { + return restoreAllShards; + } + + /** + * Parses restore definition + * + * @param source restore definition + * @return this request + */ + @SuppressWarnings("unchecked") + public RestoreRemoteStoreRequest source(Map source) { + for (Map.Entry entry : source.entrySet()) { + String name = entry.getKey(); + if (name.equals("indices")) { + if (entry.getValue() instanceof String) { + indices(Strings.splitStringByCommaToArray((String) entry.getValue())); + } else if (entry.getValue() instanceof ArrayList) { + indices((ArrayList) entry.getValue()); + } else { + throw new IllegalArgumentException("malformed indices section, should be an array of strings"); + } + } else { + if (IndicesOptions.isIndicesOptions(name) == false) { + throw new IllegalArgumentException("Unknown parameter " + name); + } + } + } + return this; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.startArray("indices"); + for (String index : indices) { + builder.value(index); + } + builder.endArray(); + builder.endObject(); + return builder; + } + + @Override + public String getDescription() { + return "remote_store"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RestoreRemoteStoreRequest that = (RestoreRemoteStoreRequest) o; + return waitForCompletion == that.waitForCompletion + && restoreAllShards == that.restoreAllShards + && Arrays.equals(indices, that.indices); + } + + @Override + public int hashCode() { + int result = Objects.hash(waitForCompletion, restoreAllShards); + result = 31 * result + Arrays.hashCode(indices); + return result; + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this); + } + +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreResponse.java new file mode 100644 index 0000000000000..66908889b0641 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreResponse.java @@ -0,0 +1,126 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.restore; + +import org.opensearch.common.Nullable; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.snapshots.RestoreInfo; + +import java.io.IOException; +import java.util.Objects; + +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; + +/** + * Contains information about remote store restores + * + * @opensearch.internal + */ +public final class RestoreRemoteStoreResponse extends ActionResponse implements ToXContentObject { + + @Nullable + private final RestoreInfo restoreInfo; + + public RestoreRemoteStoreResponse(@Nullable RestoreInfo restoreInfo) { + this.restoreInfo = restoreInfo; + } + + public RestoreRemoteStoreResponse(StreamInput in) throws IOException { + super(in); + restoreInfo = RestoreInfo.readOptionalRestoreInfo(in); + } + + /** + * Returns restore information if remote store restore was completed before this method returned, null otherwise + * + * @return restore information or null + */ + public RestoreInfo getRestoreInfo() { + return restoreInfo; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeOptionalWriteable(restoreInfo); + } + + public RestStatus status() { + if (restoreInfo == null) { + return RestStatus.ACCEPTED; + } + return restoreInfo.status(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + if (restoreInfo != null) { + builder.field("remote_store"); + restoreInfo.toXContent(builder, params); + } else { + builder.field("accepted", true); + } + builder.endObject(); + return builder; + } + + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "restore_remote_store", + true, + v -> { + RestoreInfo restoreInfo = (RestoreInfo) v[0]; + Boolean accepted = (Boolean) v[1]; + assert (accepted == null && restoreInfo != null) || (accepted != null && accepted && restoreInfo == null) : "accepted: [" + + accepted + + "], restoreInfo: [" + + restoreInfo + + "]"; + return new RestoreRemoteStoreResponse(restoreInfo); + } + ); + + static { + PARSER.declareObject( + optionalConstructorArg(), + (parser, context) -> RestoreInfo.fromXContent(parser), + new ParseField("remote_store") + ); + PARSER.declareBoolean(optionalConstructorArg(), new ParseField("accepted")); + } + + public static RestoreRemoteStoreResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RestoreRemoteStoreResponse that = (RestoreRemoteStoreResponse) o; + return Objects.equals(restoreInfo, that.restoreInfo); + } + + @Override + public int hashCode() { + return Objects.hash(restoreInfo); + } + + @Override + public String toString() { + return "RestoreRemoteStoreResponse{" + "restoreInfo=" + restoreInfo + '}'; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/TransportRestoreRemoteStoreAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/TransportRestoreRemoteStoreAction.java new file mode 100644 index 0000000000000..2b0e5c74cad53 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/TransportRestoreRemoteStoreAction.java @@ -0,0 +1,100 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.restore; + +import org.opensearch.action.admin.cluster.snapshots.restore.RestoreClusterStateListener; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.index.recovery.RemoteStoreRestoreService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Transport action for restore remote store operation + * + * @opensearch.internal + */ +public final class TransportRestoreRemoteStoreAction extends TransportClusterManagerNodeAction< + RestoreRemoteStoreRequest, + RestoreRemoteStoreResponse> { + private final RemoteStoreRestoreService restoreService; + + @Inject + public TransportRestoreRemoteStoreAction( + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + RemoteStoreRestoreService restoreService, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + RestoreRemoteStoreAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + RestoreRemoteStoreRequest::new, + indexNameExpressionResolver + ); + this.restoreService = restoreService; + } + + @Override + protected String executor() { + return ThreadPool.Names.GENERIC; + } + + @Override + protected RestoreRemoteStoreResponse read(StreamInput in) throws IOException { + return new RestoreRemoteStoreResponse(in); + } + + @Override + protected ClusterBlockException checkBlock(RestoreRemoteStoreRequest request, ClusterState state) { + // Restoring a remote store might change the global state and create/change an index, + // so we need to check for METADATA_WRITE and WRITE blocks + ClusterBlockException blockException = state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + if (blockException != null) { + return blockException; + } + return state.blocks().globalBlockedException(ClusterBlockLevel.WRITE); + + } + + @Override + protected void clusterManagerOperation( + final RestoreRemoteStoreRequest request, + final ClusterState state, + final ActionListener listener + ) { + restoreService.restore(request, ActionListener.delegateFailure(listener, (delegatedListener, restoreCompletionResponse) -> { + if (restoreCompletionResponse.getRestoreInfo() == null && request.waitForCompletion()) { + RestoreClusterStateListener.createAndRegisterListener( + clusterService, + restoreCompletionResponse, + delegatedListener, + RestoreRemoteStoreResponse::new + ); + } else { + delegatedListener.onResponse(new RestoreRemoteStoreResponse(restoreCompletionResponse.getRestoreInfo())); + } + })); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/package-info.java new file mode 100644 index 0000000000000..10348f5ccfe6e --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Restore remote store transport handler. */ +package org.opensearch.action.admin.cluster.remotestore.restore; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStats.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStats.java new file mode 100644 index 0000000000000..f292fcec7ccac --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStats.java @@ -0,0 +1,393 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.stats; + +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.index.remote.RemoteSegmentTransferTracker; +import org.opensearch.index.remote.RemoteTranslogTransferTracker; + +import java.io.IOException; + +/** + * Encapsulates all remote store stats + * + * @opensearch.internal + */ +public class RemoteStoreStats implements Writeable, ToXContentFragment { + /** + * Stats related to Remote Segment Store operations + */ + private final RemoteSegmentTransferTracker.Stats remoteSegmentShardStats; + + /** + * Stats related to Remote Translog Store operations + */ + private final RemoteTranslogTransferTracker.Stats remoteTranslogShardStats; + private final ShardRouting shardRouting; + + RemoteStoreStats( + RemoteSegmentTransferTracker.Stats remoteSegmentUploadShardStats, + RemoteTranslogTransferTracker.Stats remoteTranslogShardStats, + ShardRouting shardRouting + ) { + this.remoteSegmentShardStats = remoteSegmentUploadShardStats; + this.remoteTranslogShardStats = remoteTranslogShardStats; + this.shardRouting = shardRouting; + } + + RemoteStoreStats(StreamInput in) throws IOException { + remoteSegmentShardStats = in.readOptionalWriteable(RemoteSegmentTransferTracker.Stats::new); + remoteTranslogShardStats = in.readOptionalWriteable(RemoteTranslogTransferTracker.Stats::new); + this.shardRouting = new ShardRouting(in); + } + + public RemoteSegmentTransferTracker.Stats getSegmentStats() { + return remoteSegmentShardStats; + } + + public ShardRouting getShardRouting() { + return shardRouting; + } + + public RemoteTranslogTransferTracker.Stats getTranslogStats() { + return remoteTranslogShardStats; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + buildShardRouting(builder); + + builder.startObject(Fields.SEGMENT); + builder.startObject(SubFields.DOWNLOAD); + // Ensuring that we are not showing 0 metrics to the user + if (remoteSegmentShardStats.directoryFileTransferTrackerStats.transferredBytesStarted != 0) { + buildSegmentDownloadStats(builder); + } + builder.endObject(); // segment.download + builder.startObject(SubFields.UPLOAD); + // Ensuring that we are not showing 0 metrics to the user + if (remoteSegmentShardStats.totalUploadsStarted != 0) { + buildSegmentUploadStats(builder); + } + builder.endObject(); // segment.upload + builder.endObject(); // segment + + builder.startObject(Fields.TRANSLOG); + builder.startObject(SubFields.UPLOAD); + // Ensuring that we are not showing 0 metrics to the user + if (remoteTranslogShardStats.totalUploadsStarted > 0) { + buildTranslogUploadStats(builder); + } + builder.endObject(); // translog.upload + builder.startObject(SubFields.DOWNLOAD); + // Ensuring that we are not showing 0 metrics to the user + if (remoteTranslogShardStats.totalDownloadsSucceeded > 0) { + buildTranslogDownloadStats(builder); + } + builder.endObject(); // translog.download + builder.endObject(); // translog + + return builder.endObject(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeOptionalWriteable(remoteSegmentShardStats); + out.writeOptionalWriteable(remoteTranslogShardStats); + shardRouting.writeTo(out); + } + + private void buildTranslogUploadStats(XContentBuilder builder) throws IOException { + builder.field(UploadStatsFields.LAST_SUCCESSFUL_UPLOAD_TIMESTAMP, remoteTranslogShardStats.lastSuccessfulUploadTimestamp); + + builder.startObject(UploadStatsFields.TOTAL_UPLOADS); + builder.field(SubFields.STARTED, remoteTranslogShardStats.totalUploadsStarted) + .field(SubFields.FAILED, remoteTranslogShardStats.totalUploadsFailed) + .field(SubFields.SUCCEEDED, remoteTranslogShardStats.totalUploadsSucceeded); + builder.endObject(); + + builder.startObject(UploadStatsFields.TOTAL_UPLOAD_SIZE); + builder.field(SubFields.STARTED_BYTES, remoteTranslogShardStats.uploadBytesStarted) + .field(SubFields.FAILED_BYTES, remoteTranslogShardStats.uploadBytesFailed) + .field(SubFields.SUCCEEDED_BYTES, remoteTranslogShardStats.uploadBytesSucceeded); + builder.endObject(); + + builder.field(UploadStatsFields.TOTAL_UPLOAD_TIME_IN_MILLIS, remoteTranslogShardStats.totalUploadTimeInMillis); + + builder.startObject(UploadStatsFields.UPLOAD_SIZE_IN_BYTES); + builder.field(SubFields.MOVING_AVG, remoteTranslogShardStats.uploadBytesMovingAverage); + builder.endObject(); + + builder.startObject(UploadStatsFields.UPLOAD_SPEED_IN_BYTES_PER_SEC); + builder.field(SubFields.MOVING_AVG, remoteTranslogShardStats.uploadBytesPerSecMovingAverage); + builder.endObject(); + + builder.startObject(UploadStatsFields.UPLOAD_TIME_IN_MILLIS); + builder.field(SubFields.MOVING_AVG, remoteTranslogShardStats.uploadTimeMovingAverage); + builder.endObject(); + } + + private void buildTranslogDownloadStats(XContentBuilder builder) throws IOException { + builder.field(DownloadStatsFields.LAST_SUCCESSFUL_DOWNLOAD_TIMESTAMP, remoteTranslogShardStats.lastSuccessfulDownloadTimestamp); + + builder.startObject(DownloadStatsFields.TOTAL_DOWNLOADS); + builder.field(SubFields.SUCCEEDED, remoteTranslogShardStats.totalDownloadsSucceeded); + builder.endObject(); + + builder.startObject(DownloadStatsFields.TOTAL_DOWNLOAD_SIZE); + builder.field(SubFields.SUCCEEDED_BYTES, remoteTranslogShardStats.downloadBytesSucceeded); + builder.endObject(); + + builder.field(DownloadStatsFields.TOTAL_DOWNLOAD_TIME_IN_MILLIS, remoteTranslogShardStats.totalDownloadTimeInMillis); + + builder.startObject(DownloadStatsFields.DOWNLOAD_SIZE_IN_BYTES); + builder.field(SubFields.MOVING_AVG, remoteTranslogShardStats.downloadBytesMovingAverage); + builder.endObject(); + + builder.startObject(DownloadStatsFields.DOWNLOAD_SPEED_IN_BYTES_PER_SEC); + builder.field(SubFields.MOVING_AVG, remoteTranslogShardStats.downloadBytesPerSecMovingAverage); + builder.endObject(); + + builder.startObject(DownloadStatsFields.DOWNLOAD_TIME_IN_MILLIS); + builder.field(SubFields.MOVING_AVG, remoteTranslogShardStats.downloadTimeMovingAverage); + builder.endObject(); + } + + private void buildSegmentUploadStats(XContentBuilder builder) throws IOException { + builder.field(UploadStatsFields.LOCAL_REFRESH_TIMESTAMP, remoteSegmentShardStats.localRefreshClockTimeMs) + .field(UploadStatsFields.REMOTE_REFRESH_TIMESTAMP, remoteSegmentShardStats.remoteRefreshClockTimeMs) + .field(UploadStatsFields.REFRESH_TIME_LAG_IN_MILLIS, remoteSegmentShardStats.refreshTimeLagMs) + .field(UploadStatsFields.REFRESH_LAG, remoteSegmentShardStats.localRefreshNumber - remoteSegmentShardStats.remoteRefreshNumber) + .field(UploadStatsFields.BYTES_LAG, remoteSegmentShardStats.bytesLag) + .field(UploadStatsFields.BACKPRESSURE_REJECTION_COUNT, remoteSegmentShardStats.rejectionCount) + .field(UploadStatsFields.CONSECUTIVE_FAILURE_COUNT, remoteSegmentShardStats.consecutiveFailuresCount); + builder.startObject(UploadStatsFields.TOTAL_UPLOADS) + .field(SubFields.STARTED, remoteSegmentShardStats.totalUploadsStarted) + .field(SubFields.SUCCEEDED, remoteSegmentShardStats.totalUploadsSucceeded) + .field(SubFields.FAILED, remoteSegmentShardStats.totalUploadsFailed); + builder.endObject(); + builder.startObject(UploadStatsFields.TOTAL_UPLOAD_SIZE) + .field(SubFields.STARTED_BYTES, remoteSegmentShardStats.uploadBytesStarted) + .field(SubFields.SUCCEEDED_BYTES, remoteSegmentShardStats.uploadBytesSucceeded) + .field(SubFields.FAILED_BYTES, remoteSegmentShardStats.uploadBytesFailed); + builder.endObject(); + builder.startObject(UploadStatsFields.REMOTE_REFRESH_SIZE_IN_BYTES) + .field(SubFields.LAST_SUCCESSFUL, remoteSegmentShardStats.lastSuccessfulRemoteRefreshBytes) + .field(SubFields.MOVING_AVG, remoteSegmentShardStats.uploadBytesMovingAverage); + builder.endObject(); + builder.startObject(UploadStatsFields.UPLOAD_SPEED_IN_BYTES_PER_SEC) + .field(SubFields.MOVING_AVG, remoteSegmentShardStats.uploadBytesPerSecMovingAverage); + builder.endObject(); + builder.startObject(UploadStatsFields.REMOTE_REFRESH_LATENCY_IN_MILLIS) + .field(SubFields.MOVING_AVG, remoteSegmentShardStats.uploadTimeMovingAverage); + builder.endObject(); + } + + private void buildSegmentDownloadStats(XContentBuilder builder) throws IOException { + builder.field( + DownloadStatsFields.LAST_SYNC_TIMESTAMP, + remoteSegmentShardStats.directoryFileTransferTrackerStats.lastTransferTimestampMs + ); + builder.startObject(DownloadStatsFields.TOTAL_DOWNLOAD_SIZE) + .field(SubFields.STARTED_BYTES, remoteSegmentShardStats.directoryFileTransferTrackerStats.transferredBytesStarted) + .field(SubFields.SUCCEEDED_BYTES, remoteSegmentShardStats.directoryFileTransferTrackerStats.transferredBytesSucceeded) + .field(SubFields.FAILED_BYTES, remoteSegmentShardStats.directoryFileTransferTrackerStats.transferredBytesFailed); + builder.endObject(); + builder.startObject(DownloadStatsFields.DOWNLOAD_SIZE_IN_BYTES) + .field(SubFields.LAST_SUCCESSFUL, remoteSegmentShardStats.directoryFileTransferTrackerStats.lastSuccessfulTransferInBytes) + .field(SubFields.MOVING_AVG, remoteSegmentShardStats.directoryFileTransferTrackerStats.transferredBytesMovingAverage); + builder.endObject(); + builder.startObject(DownloadStatsFields.DOWNLOAD_SPEED_IN_BYTES_PER_SEC) + .field(SubFields.MOVING_AVG, remoteSegmentShardStats.directoryFileTransferTrackerStats.transferredBytesPerSecMovingAverage); + builder.endObject(); + } + + private void buildShardRouting(XContentBuilder builder) throws IOException { + builder.startObject(Fields.ROUTING); + builder.field(RoutingFields.STATE, shardRouting.state()); + builder.field(RoutingFields.PRIMARY, shardRouting.primary()); + builder.field(RoutingFields.NODE_ID, shardRouting.currentNodeId()); + builder.endObject(); + } + + /** + * Fields for remote store stats response + */ + static final class Fields { + static final String ROUTING = "routing"; + static final String SEGMENT = "segment"; + static final String TRANSLOG = "translog"; + } + + static final class RoutingFields { + static final String STATE = "state"; + static final String PRIMARY = "primary"; + static final String NODE_ID = "node"; + } + + /** + * Fields for remote store stats response + */ + public static final class UploadStatsFields { + /** + * Lag in terms of bytes b/w local and remote store + */ + static final String BYTES_LAG = "bytes_lag"; + + /** + * No of refresh remote store is lagging behind local + */ + static final String REFRESH_LAG = "refresh_lag"; + + /** + * Time in millis remote refresh is behind local refresh + */ + static final String REFRESH_TIME_LAG_IN_MILLIS = "refresh_time_lag_in_millis"; + + /** + * Last successful local refresh timestamp in milliseconds + */ + static final String LOCAL_REFRESH_TIMESTAMP = "local_refresh_timestamp_in_millis"; + + /** + * Last successful remote refresh timestamp in milliseconds + */ + static final String REMOTE_REFRESH_TIMESTAMP = "remote_refresh_timestamp_in_millis"; + + /** + * Total write rejections due to remote store backpressure kick in + */ + static final String BACKPRESSURE_REJECTION_COUNT = "backpressure_rejection_count"; + + /** + * No of consecutive remote refresh failures without a single success since the first failures + */ + static final String CONSECUTIVE_FAILURE_COUNT = "consecutive_failure_count"; + + /** + * Represents the size of new data to be uploaded as part of a refresh + */ + static final String REMOTE_REFRESH_SIZE_IN_BYTES = "remote_refresh_size_in_bytes"; + + /** + * Time taken by a single remote refresh + */ + static final String REMOTE_REFRESH_LATENCY_IN_MILLIS = "remote_refresh_latency_in_millis"; + + /** + * Timestamp of last successful remote store upload + */ + static final String LAST_SUCCESSFUL_UPLOAD_TIMESTAMP = "last_successful_upload_timestamp"; + + /** + * Count of files uploaded to remote store + */ + public static final String TOTAL_UPLOADS = "total_uploads"; + + /** + * Represents the total uploads to remote store in bytes + */ + public static final String TOTAL_UPLOAD_SIZE = "total_upload_size"; + + /** + * Total time spent on remote store uploads + */ + static final String TOTAL_UPLOAD_TIME_IN_MILLIS = "total_upload_time_in_millis"; + + /** + * Represents the size of new data to be transferred as part of a remote store upload + */ + static final String UPLOAD_SIZE_IN_BYTES = "upload_size_in_bytes"; + + /** + * Represents the speed of remote store uploads in bytes per sec + */ + static final String UPLOAD_SPEED_IN_BYTES_PER_SEC = "upload_speed_in_bytes_per_sec"; + + /** + * Time taken by a remote store upload + */ + static final String UPLOAD_TIME_IN_MILLIS = "upload_time_in_millis"; + } + + static final class DownloadStatsFields { + /** + * Epoch timestamp of the last successful download + */ + public static final String LAST_SUCCESSFUL_DOWNLOAD_TIMESTAMP = "last_successful_download_timestamp"; + + /** + * Last successful sync from remote in milliseconds + */ + static final String LAST_SYNC_TIMESTAMP = "last_sync_timestamp"; + + /** + * Count of files downloaded from remote store + */ + public static final String TOTAL_DOWNLOADS = "total_downloads"; + + /** + * Total time spent in downloads from remote store + */ + public static final String TOTAL_DOWNLOAD_TIME_IN_MILLIS = "total_download_time_in_millis"; + + /** + * Total bytes of files downloaded from the remote store + */ + static final String TOTAL_DOWNLOAD_SIZE = "total_download_size"; + + /** + * Average size of a file downloaded from the remote store + */ + static final String DOWNLOAD_SIZE_IN_BYTES = "download_size_in_bytes"; + + /** + * Average speed (in bytes/sec) of a remote store download + */ + static final String DOWNLOAD_SPEED_IN_BYTES_PER_SEC = "download_speed_in_bytes_per_sec"; + + /** + * Average time spent on a remote store download + */ + public static final String DOWNLOAD_TIME_IN_MILLIS = "download_time_in_millis"; + } + + /** + * Reusable sub fields for {@link UploadStatsFields} and {@link DownloadStatsFields} + */ + public static final class SubFields { + public static final String STARTED = "started"; + public static final String SUCCEEDED = "succeeded"; + public static final String FAILED = "failed"; + + public static final String STARTED_BYTES = "started_bytes"; + public static final String SUCCEEDED_BYTES = "succeeded_bytes"; + public static final String FAILED_BYTES = "failed_bytes"; + + static final String DOWNLOAD = "download"; + public static final String UPLOAD = "upload"; + + /** + * Moving avg over last N values stat + */ + static final String MOVING_AVG = "moving_avg"; + + /** + * Most recent successful attempt stat + */ + static final String LAST_SUCCESSFUL = "last_successful"; + } + +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsAction.java new file mode 100644 index 0000000000000..98c6e863b7b61 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsAction.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.stats; + +import org.opensearch.action.ActionType; + +/** + * Remote store stats action + * + * @opensearch.internal + */ +public class RemoteStoreStatsAction extends ActionType { + + public static final RemoteStoreStatsAction INSTANCE = new RemoteStoreStatsAction(); + public static final String NAME = "cluster:monitor/_remotestore/stats"; + + private RemoteStoreStatsAction() { + super(NAME, RemoteStoreStatsResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsRequest.java new file mode 100644 index 0000000000000..f09cf79c5154c --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsRequest.java @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.stats; + +import org.opensearch.action.support.broadcast.BroadcastRequest; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Encapsulates all remote store stats + * + * @opensearch.internal + */ +public class RemoteStoreStatsRequest extends BroadcastRequest { + + private String[] shards; + private boolean local = false; + + public RemoteStoreStatsRequest() { + super((String[]) null); + shards = new String[0]; + } + + public RemoteStoreStatsRequest(StreamInput in) throws IOException { + super(in); + shards = in.readStringArray(); + local = in.readBoolean(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeStringArray(shards); + out.writeBoolean(local); + } + + public RemoteStoreStatsRequest shards(String... shards) { + this.shards = shards; + return this; + } + + public String[] shards() { + return this.shards; + } + + public void local(boolean local) { + this.local = local; + } + + public boolean local() { + return local; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsRequestBuilder.java new file mode 100644 index 0000000000000..c31e4a1fd6178 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsRequestBuilder.java @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.stats; + +import org.opensearch.action.support.broadcast.BroadcastOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; +import org.opensearch.common.unit.TimeValue; + +/** + * Builder for RemoteStoreStatsRequest + * + * @opensearch.internal + */ +public class RemoteStoreStatsRequestBuilder extends BroadcastOperationRequestBuilder< + RemoteStoreStatsRequest, + RemoteStoreStatsResponse, + RemoteStoreStatsRequestBuilder> { + + public RemoteStoreStatsRequestBuilder(OpenSearchClient client, RemoteStoreStatsAction action) { + super(client, action, new RemoteStoreStatsRequest()); + } + + /** + * Sets timeout of request. + */ + public final RemoteStoreStatsRequestBuilder setTimeout(TimeValue timeout) { + request.timeout(timeout); + return this; + } + + /** + * Sets shards preference of request. + */ + public final RemoteStoreStatsRequestBuilder setShards(String... shards) { + request.shards(shards); + return this; + } + + /** + * Sets local shards preference of request. + */ + public final RemoteStoreStatsRequestBuilder setLocal(boolean local) { + request.local(local); + return this; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsResponse.java new file mode 100644 index 0000000000000..63a4c97d695b7 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/RemoteStoreStatsResponse.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.stats; + +import org.opensearch.action.support.broadcast.BroadcastResponse; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Remote Store stats response + * + * @opensearch.internal + */ +public class RemoteStoreStatsResponse extends BroadcastResponse { + + private final RemoteStoreStats[] remoteStoreStats; + + public RemoteStoreStatsResponse(StreamInput in) throws IOException { + super(in); + remoteStoreStats = in.readArray(RemoteStoreStats::new, RemoteStoreStats[]::new); + } + + public RemoteStoreStatsResponse( + RemoteStoreStats[] remoteStoreStats, + int totalShards, + int successfulShards, + int failedShards, + List shardFailures + ) { + super(totalShards, successfulShards, failedShards, shardFailures); + this.remoteStoreStats = remoteStoreStats; + } + + public RemoteStoreStats[] getRemoteStoreStats() { + return this.remoteStoreStats; + } + + public Map>> groupByIndexAndShards() { + Map>> indexWiseStats = new HashMap<>(); + for (RemoteStoreStats shardStat : remoteStoreStats) { + indexWiseStats.computeIfAbsent(shardStat.getShardRouting().getIndexName(), k -> new HashMap<>()) + .computeIfAbsent(shardStat.getShardRouting().getId(), k -> new ArrayList<>()) + .add(shardStat); + } + return indexWiseStats; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeArray(remoteStoreStats); + } + + @Override + protected void addCustomXContentFields(XContentBuilder builder, Params params) throws IOException { + Map>> indexWiseStats = groupByIndexAndShards(); + builder.startObject(Fields.INDICES); + for (String indexName : indexWiseStats.keySet()) { + builder.startObject(indexName); + builder.startObject(Fields.SHARDS); + for (int shardId : indexWiseStats.get(indexName).keySet()) { + builder.startArray(Integer.toString(shardId)); + for (RemoteStoreStats shardStat : indexWiseStats.get(indexName).get(shardId)) { + shardStat.toXContent(builder, params); + } + builder.endArray(); + } + builder.endObject(); + builder.endObject(); + } + builder.endObject(); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this, true, false); + } + + static final class Fields { + static final String SHARDS = "shards"; + static final String INDICES = "indices"; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/TransportRemoteStoreStatsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/TransportRemoteStoreStatsAction.java new file mode 100644 index 0000000000000..bd8db4a160bf6 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/TransportRemoteStoreStatsAction.java @@ -0,0 +1,168 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.remotestore.stats; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.routing.PlainShardsIterator; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.ShardsIterator; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.index.IndexService; +import org.opensearch.index.remote.RemoteSegmentTransferTracker; +import org.opensearch.index.remote.RemoteStoreStatsTrackerFactory; +import org.opensearch.index.remote.RemoteTranslogTransferTracker; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.index.shard.ShardNotFoundException; +import org.opensearch.indices.IndicesService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Encapsulates all remote store stats + * + * @opensearch.internal + */ +public class TransportRemoteStoreStatsAction extends TransportBroadcastByNodeAction< + RemoteStoreStatsRequest, + RemoteStoreStatsResponse, + RemoteStoreStats> { + + private final IndicesService indicesService; + + private final RemoteStoreStatsTrackerFactory remoteStoreStatsTrackerFactory; + + @Inject + public TransportRemoteStoreStatsAction( + ClusterService clusterService, + TransportService transportService, + IndicesService indicesService, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver, + RemoteStoreStatsTrackerFactory remoteStoreStatsTrackerFactory + ) { + super( + RemoteStoreStatsAction.NAME, + clusterService, + transportService, + actionFilters, + indexNameExpressionResolver, + RemoteStoreStatsRequest::new, + ThreadPool.Names.MANAGEMENT + ); + this.indicesService = indicesService; + this.remoteStoreStatsTrackerFactory = remoteStoreStatsTrackerFactory; + } + + /** + * Status goes across *all* shards. + */ + @Override + protected ShardsIterator shards(ClusterState clusterState, RemoteStoreStatsRequest request, String[] concreteIndices) { + final List newShardRoutings = new ArrayList<>(); + if (request.shards().length > 0) { + clusterState.routingTable().allShards(concreteIndices).getShardRoutings().forEach(shardRouting -> { + if (Arrays.asList(request.shards()).contains(Integer.toString(shardRouting.shardId().id()))) { + newShardRoutings.add(shardRouting); + } + }); + } else { + newShardRoutings.addAll(clusterState.routingTable().allShards(concreteIndices).getShardRoutings()); + } + return new PlainShardsIterator( + newShardRoutings.stream() + .filter( + shardRouting -> !request.local() + || (shardRouting.currentNodeId() == null + || shardRouting.currentNodeId().equals(clusterState.getNodes().getLocalNodeId())) + ) + .filter( + shardRouting -> Boolean.parseBoolean( + clusterState.getMetadata().index(shardRouting.index()).getSettings().get(IndexMetadata.SETTING_REMOTE_STORE_ENABLED) + ) + ) + .collect(Collectors.toList()) + ); + } + + @Override + protected ClusterBlockException checkGlobalBlock(ClusterState state, RemoteStoreStatsRequest request) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } + + @Override + protected ClusterBlockException checkRequestBlock(ClusterState state, RemoteStoreStatsRequest request, String[] concreteIndices) { + return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_READ, concreteIndices); + } + + @Override + protected RemoteStoreStats readShardResult(StreamInput in) throws IOException { + return new RemoteStoreStats(in); + } + + @Override + protected RemoteStoreStatsResponse newResponse( + RemoteStoreStatsRequest request, + int totalShards, + int successfulShards, + int failedShards, + List responses, + List shardFailures, + ClusterState clusterState + ) { + return new RemoteStoreStatsResponse( + responses.toArray(new RemoteStoreStats[0]), + totalShards, + successfulShards, + failedShards, + shardFailures + ); + } + + @Override + protected RemoteStoreStatsRequest readRequestFrom(StreamInput in) throws IOException { + return new RemoteStoreStatsRequest(in); + } + + @Override + protected RemoteStoreStats shardOperation(RemoteStoreStatsRequest request, ShardRouting shardRouting) { + IndexService indexService = indicesService.indexServiceSafe(shardRouting.shardId().getIndex()); + IndexShard indexShard = indexService.getShard(shardRouting.shardId().id()); + // if we don't have the routing entry yet, we need it stats wise, we treat it as if the shard is not ready yet + if (indexShard.routingEntry() == null) { + throw new ShardNotFoundException(indexShard.shardId()); + } + + RemoteSegmentTransferTracker remoteSegmentTransferTracker = remoteStoreStatsTrackerFactory.getRemoteSegmentTransferTracker( + indexShard.shardId() + ); + assert Objects.nonNull(remoteSegmentTransferTracker); + RemoteTranslogTransferTracker remoteTranslogTransferTracker = remoteStoreStatsTrackerFactory.getRemoteTranslogTransferTracker( + indexShard.shardId() + ); + assert Objects.nonNull(remoteTranslogTransferTracker); + + return new RemoteStoreStats(remoteSegmentTransferTracker.stats(), remoteTranslogTransferTracker.stats(), indexShard.routingEntry()); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/package-info.java new file mode 100644 index 0000000000000..80bd96e3245d9 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/stats/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Remote store stats transport handler. */ +package org.opensearch.action.admin.cluster.remotestore.stats; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryAction.java index 4feb755c3e5a1..0ec1281648cdb 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryAction.java @@ -33,6 +33,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for cleaning up snapshot repositories + * + * @opensearch.internal + */ public final class CleanupRepositoryAction extends ActionType { public static final CleanupRepositoryAction INSTANCE = new CleanupRepositoryAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryRequest.java index d1fc914201390..7e3f4cd95fc72 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryRequest.java @@ -33,13 +33,18 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport request for cleaning up snapshot repositories + * + * @opensearch.internal + */ public class CleanupRepositoryRequest extends AcknowledgedRequest { private String repository; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryRequestBuilder.java index b5db8d7eca669..95c4fb372572f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryRequestBuilder.java @@ -32,10 +32,15 @@ package org.opensearch.action.admin.cluster.repositories.cleanup; import org.opensearch.action.ActionType; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -public class CleanupRepositoryRequestBuilder extends MasterNodeOperationRequestBuilder< +/** + * Transport builder for cleaning up snapshot repositories + * + * @opensearch.internal + */ +public class CleanupRepositoryRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< CleanupRepositoryRequest, CleanupRepositoryResponse, CleanupRepositoryRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryResponse.java index e6bb65e732082..99af862c769e8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/CleanupRepositoryResponse.java @@ -31,18 +31,23 @@ package org.opensearch.action.admin.cluster.repositories.cleanup; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.repositories.RepositoryCleanupResult; import java.io.IOException; +/** + * Transport response for cleaning up snapshot repositories + * + * @opensearch.internal + */ public final class CleanupRepositoryResponse extends ActionResponse implements ToXContentObject { private static final ObjectParser PARSER = new ObjectParser<>( diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java index b7b9da675a385..fa22daf2ca038 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/TransportCleanupRepositoryAction.java @@ -36,11 +36,10 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.StepListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.RepositoryCleanupInProgress; @@ -53,7 +52,9 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.index.store.lockmanager.RemoteStoreLockManagerFactory; import org.opensearch.repositories.RepositoriesService; import org.opensearch.repositories.Repository; import org.opensearch.repositories.RepositoryCleanupResult; @@ -73,17 +74,21 @@ *

      *
    1. Check that there are no running repository cleanup, snapshot create, or snapshot delete actions * and add an entry for the repository that is to be cleaned up to {@link RepositoryCleanupInProgress}
    2. - *
    3. Run cleanup actions on the repository. Note, these are executed exclusively on the master node. + *
    4. Run cleanup actions on the repository. Note, these are executed exclusively on the cluster-manager node. * For the precise operations execute see {@link BlobStoreRepository#cleanup}
    5. *
    6. Remove the entry in {@link RepositoryCleanupInProgress} in the first step.
    7. *
    * - * On master failover during the cleanup operation it is simply removed from the cluster state. This is safe because the logic in + * On cluster-manager failover during the cleanup operation it is simply removed from the cluster state. This is safe because the logic in * {@link BlobStoreRepository#cleanup} ensures that the repository state id has not changed between creation of the cluster state entry * and any delete/write operations. TODO: This will not work if we also want to clean up at the shard level as those will involve writes * as well as deletes. + * + * @opensearch.internal */ -public final class TransportCleanupRepositoryAction extends TransportMasterNodeAction { +public final class TransportCleanupRepositoryAction extends TransportClusterManagerNodeAction< + CleanupRepositoryRequest, + CleanupRepositoryResponse> { private static final Logger logger = LogManager.getLogger(TransportCleanupRepositoryAction.class); @@ -93,6 +98,8 @@ public final class TransportCleanupRepositoryAction extends TransportMasterNodeA private final SnapshotsService snapshotsService; + private final RemoteStoreLockManagerFactory remoteStoreLockManagerFactory; + @Override protected String executor() { return ThreadPool.Names.SAME; @@ -119,24 +126,25 @@ public TransportCleanupRepositoryAction( ); this.repositoriesService = repositoriesService; this.snapshotsService = snapshotsService; - // We add a state applier that will remove any dangling repository cleanup actions on master failover. + this.remoteStoreLockManagerFactory = new RemoteStoreLockManagerFactory(() -> repositoriesService); + // We add a state applier that will remove any dangling repository cleanup actions on cluster-manager failover. // This is safe to do since cleanups will increment the repository state id before executing any operations to prevent concurrent // operations from corrupting the repository. This is the same safety mechanism used by snapshot deletes. - if (DiscoveryNode.isMasterNode(clusterService.getSettings())) { + if (DiscoveryNode.isClusterManagerNode(clusterService.getSettings())) { addClusterStateApplier(clusterService); } } private static void addClusterStateApplier(ClusterService clusterService) { clusterService.addStateApplier(event -> { - if (event.localNodeMaster() && event.previousState().nodes().isLocalNodeElectedMaster() == false) { + if (event.localNodeClusterManager() && event.previousState().nodes().isLocalNodeElectedClusterManager() == false) { final RepositoryCleanupInProgress repositoryCleanupInProgress = event.state() .custom(RepositoryCleanupInProgress.TYPE, RepositoryCleanupInProgress.EMPTY); if (repositoryCleanupInProgress.hasCleanupInProgress() == false) { return; } clusterService.submitStateUpdateTask( - "clean up repository cleanup task after master failover", + "clean up repository cleanup task after cluster-manager failover", new ClusterStateUpdateTask() { @Override public ClusterState execute(ClusterState currentState) { @@ -170,7 +178,7 @@ protected CleanupRepositoryResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation( + protected void clusterManagerOperation( CleanupRepositoryRequest request, ClusterState state, ActionListener listener @@ -277,6 +285,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS l -> blobStoreRepository.cleanup( repositoryStateId, snapshotsService.minCompatibleVersion(newState.nodes().getMinNodeVersion(), repositoryData, null), + remoteStoreLockManagerFactory, ActionListener.wrap(result -> after(null, result), e -> after(e, null)) ) ) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/package-info.java new file mode 100644 index 0000000000000..1a9c0f2ce113b --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/cleanup/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handler for cleaning up a snapshot repository. */ +package org.opensearch.action.admin.cluster.repositories.cleanup; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryAction.java index 3e6cbcbae8a8f..2031e4f7a716f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryAction.java @@ -37,6 +37,8 @@ /** * Unregister repository action + * + * @opensearch.internal */ public class DeleteRepositoryAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryRequest.java index 610b985eb0484..352a3772e039b 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryRequest.java @@ -34,8 +34,8 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -45,6 +45,8 @@ * Unregister repository request. *

    * The unregister repository command just unregisters the repository. No data is getting deleted from the repository. + * + * @opensearch.internal */ public class DeleteRepositoryRequest extends AcknowledgedRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryRequestBuilder.java index 3a390dd1df2ab..ffef8d5b41979 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/DeleteRepositoryRequestBuilder.java @@ -38,6 +38,8 @@ /** * Builder for unregister repository request + * + * @opensearch.internal */ public class DeleteRepositoryRequestBuilder extends AcknowledgedRequestBuilder< DeleteRepositoryRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java index c7369cabbc75d..3d779befe474e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/TransportDeleteRepositoryAction.java @@ -32,17 +32,17 @@ package org.opensearch.action.admin.cluster.repositories.delete; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.repositories.RepositoriesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -51,8 +51,10 @@ /** * Transport action for unregister repository operation + * + * @opensearch.internal */ -public class TransportDeleteRepositoryAction extends TransportMasterNodeAction { +public class TransportDeleteRepositoryAction extends TransportClusterManagerNodeAction { private final RepositoriesService repositoriesService; @@ -93,7 +95,7 @@ protected ClusterBlockException checkBlock(DeleteRepositoryRequest request, Clus } @Override - protected void masterOperation( + protected void clusterManagerOperation( final DeleteRepositoryRequest request, ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/package-info.java new file mode 100644 index 0000000000000..db9d5e7f8ceea --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/delete/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handler for deleting a snapshot repository. */ +package org.opensearch.action.admin.cluster.repositories.delete; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesAction.java index 86bc21ef8fd41..8d0fa4dc5010c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesAction.java @@ -36,6 +36,8 @@ /** * Get repositories action + * + * @opensearch.internal */ public class GetRepositoriesAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesRequest.java index 02b42d82e6702..ac975e917e056 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesRequest.java @@ -33,10 +33,10 @@ package org.opensearch.action.admin.cluster.repositories.get; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -44,8 +44,10 @@ /** * Get repository request + * + * @opensearch.internal */ -public class GetRepositoriesRequest extends MasterNodeReadRequest { +public class GetRepositoriesRequest extends ClusterManagerNodeReadRequest { private String[] repositories = Strings.EMPTY_ARRAY; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesRequestBuilder.java index 8c4555ce54f1e..4b93aff4c25bc 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesRequestBuilder.java @@ -32,14 +32,16 @@ package org.opensearch.action.admin.cluster.repositories.get; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.util.ArrayUtils; /** * Get repository request builder + * + * @opensearch.internal */ -public class GetRepositoriesRequestBuilder extends MasterNodeReadOperationRequestBuilder< +public class GetRepositoriesRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< GetRepositoriesRequest, GetRepositoriesResponse, GetRepositoriesRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java index ffb78f9622228..098a0e60142e7 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java @@ -32,23 +32,25 @@ package org.opensearch.action.admin.cluster.repositories.get; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.RepositoriesMetadata; import org.opensearch.cluster.metadata.RepositoryMetadata; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collections; import java.util.List; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * Get repositories response + * + * @opensearch.internal */ public class GetRepositoriesResponse extends ActionResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/TransportGetRepositoriesAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/TransportGetRepositoriesAction.java index d3a66671b585b..c7d784dbc96e7 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/TransportGetRepositoriesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/TransportGetRepositoriesAction.java @@ -32,9 +32,8 @@ package org.opensearch.action.admin.cluster.repositories.get; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -44,8 +43,9 @@ import org.opensearch.cluster.metadata.RepositoryMetadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.regex.Regex; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.repositories.RepositoryMissingException; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -59,8 +59,10 @@ /** * Transport action for get repositories operation + * + * @opensearch.internal */ -public class TransportGetRepositoriesAction extends TransportMasterNodeReadAction { +public class TransportGetRepositoriesAction extends TransportClusterManagerNodeReadAction { @Inject public TransportGetRepositoriesAction( @@ -97,7 +99,7 @@ protected ClusterBlockException checkBlock(GetRepositoriesRequest request, Clust } @Override - protected void masterOperation( + protected void clusterManagerOperation( final GetRepositoriesRequest request, ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/package-info.java new file mode 100644 index 0000000000000..9f053ae11d8bc --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handler for getting a snapshot repository. */ +package org.opensearch.action.admin.cluster.repositories.get; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/package-info.java new file mode 100644 index 0000000000000..75eb53cb11748 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Repository transport handlers. */ +package org.opensearch.action.admin.cluster.repositories; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryAction.java index 35bcdb6444ed7..c2f90d869d873 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryAction.java @@ -37,6 +37,8 @@ /** * Register repository action + * + * @opensearch.internal */ public class PutRepositoryAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryRequest.java index a3561f6b3052a..582f73f335b49 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryRequest.java @@ -32,28 +32,32 @@ package org.opensearch.action.admin.cluster.repositories.put; +import org.opensearch.Version; import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.admin.cluster.crypto.CryptoSettings; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Map; import static org.opensearch.action.ValidateActions.addValidationError; +import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; import static org.opensearch.common.settings.Settings.readSettingsFromStream; import static org.opensearch.common.settings.Settings.writeSettingsToStream; -import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; /** * Register repository request. *

    * Registers a repository with given name, type and settings. If the repository with the same name already * exists in the cluster, the new repository will replace the existing repository. + * + * @opensearch.internal */ public class PutRepositoryRequest extends AcknowledgedRequest implements ToXContentObject { @@ -65,12 +69,18 @@ public class PutRepositoryRequest extends AcknowledgedRequest repositoryDefinition) { @SuppressWarnings("unchecked") Map sub = (Map) entry.getValue(); settings(sub); + } else if (name.equals("crypto_settings")) { + if (!(entry.getValue() instanceof Map)) { + throw new IllegalArgumentException("Malformed encryption_settings section, should include an inner object"); + } + @SuppressWarnings("unchecked") + Map sub = (Map) entry.getValue(); + CryptoSettings cryptoSettings = new CryptoSettings(sub); + cryptoSettings(cryptoSettings); } } return this; @@ -234,6 +275,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(type); writeSettingsToStream(settings, out); out.writeBoolean(verify); + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + out.writeOptionalWriteable(cryptoSettings); + } } @Override @@ -247,6 +291,13 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endObject(); builder.field("verify", verify); + + if (cryptoSettings != null) { + builder.startObject("crypto_settings"); + cryptoSettings.toXContent(builder, params); + builder.endObject(); + } + builder.endObject(); return builder; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryRequestBuilder.java index 09a33533c8889..cf649ee6b4cbf 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/PutRepositoryRequestBuilder.java @@ -32,6 +32,7 @@ package org.opensearch.action.admin.cluster.repositories.put; +import org.opensearch.action.admin.cluster.crypto.CryptoSettings; import org.opensearch.action.support.master.AcknowledgedRequestBuilder; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.OpenSearchClient; @@ -42,6 +43,8 @@ /** * Register repository request builder + * + * @opensearch.internal */ public class PutRepositoryRequestBuilder extends AcknowledgedRequestBuilder< PutRepositoryRequest, @@ -139,4 +142,15 @@ public PutRepositoryRequestBuilder setVerify(boolean verify) { request.verify(verify); return this; } + + /** + * Sets the repository encryption settings + * + * @param cryptoSettings repository crypto settings builder + * @return this builder + */ + public PutRepositoryRequestBuilder setEncryptionSettings(CryptoSettings cryptoSettings) { + request.cryptoSettings(cryptoSettings); + return this; + } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/TransportPutRepositoryAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/TransportPutRepositoryAction.java index c91f492209bdd..1eadab6b1352e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/TransportPutRepositoryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/TransportPutRepositoryAction.java @@ -32,17 +32,17 @@ package org.opensearch.action.admin.cluster.repositories.put; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.repositories.RepositoriesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -51,8 +51,10 @@ /** * Transport action for register repository operation + * + * @opensearch.internal */ -public class TransportPutRepositoryAction extends TransportMasterNodeAction { +public class TransportPutRepositoryAction extends TransportClusterManagerNodeAction { private final RepositoriesService repositoriesService; @@ -93,12 +95,12 @@ protected ClusterBlockException checkBlock(PutRepositoryRequest request, Cluster } @Override - protected void masterOperation( + protected void clusterManagerOperation( final PutRepositoryRequest request, ClusterState state, final ActionListener listener ) { - repositoriesService.registerRepository( + repositoriesService.registerOrUpdateRepository( request, ActionListener.delegateFailure( listener, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/package-info.java new file mode 100644 index 0000000000000..e5b6305416174 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/put/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handlers for putting a new snapshot repository */ +package org.opensearch.action.admin.cluster.repositories.put; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/TransportVerifyRepositoryAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/TransportVerifyRepositoryAction.java index 2c727ece7f130..d717d2200902f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/TransportVerifyRepositoryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/TransportVerifyRepositoryAction.java @@ -32,9 +32,8 @@ package org.opensearch.action.admin.cluster.repositories.verify; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -42,7 +41,8 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.repositories.RepositoriesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -51,8 +51,10 @@ /** * Transport action for verifying repository operation + * + * @opensearch.internal */ -public class TransportVerifyRepositoryAction extends TransportMasterNodeAction { +public class TransportVerifyRepositoryAction extends TransportClusterManagerNodeAction { private final RepositoriesService repositoriesService; @@ -93,7 +95,7 @@ protected ClusterBlockException checkBlock(VerifyRepositoryRequest request, Clus } @Override - protected void masterOperation( + protected void clusterManagerOperation( final VerifyRepositoryRequest request, ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryAction.java index 98239c9da0f80..10b82f2d5c0f0 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryAction.java @@ -36,6 +36,8 @@ /** * Verify repository action + * + * @opensearch.internal */ public class VerifyRepositoryAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryRequest.java index 12788a421c75f..b84161e716f5d 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryRequest.java @@ -34,8 +34,8 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -43,6 +43,8 @@ /** * Verify repository request. + * + * @opensearch.internal */ public class VerifyRepositoryRequest extends AcknowledgedRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryRequestBuilder.java index db01ac2268b9a..c405fb9bc12cd 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryRequestBuilder.java @@ -32,13 +32,15 @@ package org.opensearch.action.admin.cluster.repositories.verify; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; /** * Builder for verify repository request + * + * @opensearch.internal */ -public class VerifyRepositoryRequestBuilder extends MasterNodeOperationRequestBuilder< +public class VerifyRepositoryRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< VerifyRepositoryRequest, VerifyRepositoryResponse, VerifyRepositoryRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryResponse.java index f31189f3073e0..d7af3478bdac3 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/VerifyRepositoryResponse.java @@ -32,17 +32,18 @@ package org.opensearch.action.admin.cluster.repositories.verify; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; @@ -52,12 +53,19 @@ /** * Verify repository response + * + * @opensearch.internal */ public class VerifyRepositoryResponse extends ActionResponse implements ToXContentObject { static final String NODES = "nodes"; static final String NAME = "name"; + /** + * Inner Node View + * + * @opensearch.internal + */ public static class NodeView implements Writeable, ToXContentObject { private static final ObjectParser.NamedObjectParser PARSER; static { @@ -188,7 +196,7 @@ public static VerifyRepositoryResponse fromXContent(XContentParser parser) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/package-info.java new file mode 100644 index 0000000000000..236e7a6b26369 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/repositories/verify/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handler for verifying a snapshot repository. */ +package org.opensearch.action.admin.cluster.repositories.verify; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteAction.java index f52169911e3a9..d6a09f47cbf12 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for rerouting allocation commands + * + * @opensearch.internal + */ public class ClusterRerouteAction extends ActionType { public static final ClusterRerouteAction INSTANCE = new ClusterRerouteAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteRequest.java index ddef4f62c5298..a6addce14787d 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteRequest.java @@ -36,14 +36,16 @@ import org.opensearch.action.support.master.AcknowledgedRequest; import org.opensearch.cluster.routing.allocation.command.AllocationCommand; import org.opensearch.cluster.routing.allocation.command.AllocationCommands; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; /** * Request to submit cluster reroute allocation commands + * + * @opensearch.internal */ public class ClusterRerouteRequest extends AcknowledgedRequest { private AllocationCommands commands = new AllocationCommands(); @@ -161,12 +163,12 @@ public boolean equals(Object obj) { && Objects.equals(explain, other.explain) && Objects.equals(timeout, other.timeout) && Objects.equals(retryFailed, other.retryFailed) - && Objects.equals(masterNodeTimeout, other.masterNodeTimeout); + && Objects.equals(clusterManagerNodeTimeout, other.clusterManagerNodeTimeout); } @Override public int hashCode() { // Override equals and hashCode for testing - return Objects.hash(commands, dryRun, explain, timeout, retryFailed, masterNodeTimeout); + return Objects.hash(commands, dryRun, explain, timeout, retryFailed, clusterManagerNodeTimeout); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteRequestBuilder.java index 8e0116bb3646e..01d52cb43320d 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteRequestBuilder.java @@ -38,6 +38,8 @@ /** * Builder for a cluster reroute request + * + * @opensearch.internal */ public class ClusterRerouteRequestBuilder extends AcknowledgedRequestBuilder< ClusterRerouteRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteResponse.java index da4cfb4333b3d..a62029218ca25 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/ClusterRerouteResponse.java @@ -35,16 +35,18 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.routing.allocation.RoutingExplanations; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; /** * Response returned after a cluster reroute request + * + * @opensearch.internal */ public class ClusterRerouteResponse extends AcknowledgedResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/TransportClusterRerouteAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/TransportClusterRerouteAction.java index c0e3d30fdb702..134583a56f489 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/TransportClusterRerouteAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/TransportClusterRerouteAction.java @@ -36,13 +36,12 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionListenerResponseHandler; import org.opensearch.action.admin.indices.shards.IndicesShardStoresAction; import org.opensearch.action.admin.indices.shards.IndicesShardStoresRequest; import org.opensearch.action.admin.indices.shards.IndicesShardStoresResponse; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.AckedClusterStateUpdateTask; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -54,13 +53,14 @@ import org.opensearch.cluster.routing.allocation.command.AbstractAllocateAllocationCommand; import org.opensearch.cluster.routing.allocation.command.AllocateStalePrimaryAllocationCommand; import org.opensearch.cluster.routing.allocation.command.AllocationCommand; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenIntMap; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -70,11 +70,17 @@ import java.util.List; import java.util.Map; -public class TransportClusterRerouteAction extends TransportMasterNodeAction { +/** + * Transport action for rerouting cluster allocation commands + * + * @opensearch.internal + */ +public class TransportClusterRerouteAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportClusterRerouteAction.class); private final AllocationService allocationService; + private static ClusterManagerTaskThrottler.ThrottlingKey clusterRerouteTaskKey; @Inject public TransportClusterRerouteAction( @@ -95,6 +101,8 @@ public TransportClusterRerouteAction( indexNameExpressionResolver ); this.allocationService = allocationService; + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + clusterRerouteTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.CLUSTER_REROUTE_API_KEY, true); } @Override @@ -114,7 +122,7 @@ protected ClusterRerouteResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation( + protected void clusterManagerOperation( final ClusterRerouteRequest request, final ClusterState state, final ActionListener listener @@ -143,12 +151,11 @@ private void verifyThenSubmitUpdate( IndicesShardStoresAction.NAME, new IndicesShardStoresRequest().indices(stalePrimaryAllocations.keySet().toArray(Strings.EMPTY_ARRAY)), new ActionListenerResponseHandler<>(ActionListener.wrap(response -> { - ImmutableOpenMap>> status = response - .getStoreStatuses(); + final Map>> status = response.getStoreStatuses(); Exception e = null; for (Map.Entry> entry : stalePrimaryAllocations.entrySet()) { final String index = entry.getKey(); - final ImmutableOpenIntMap> indexStatus = status.get(index); + final Map> indexStatus = status.get(index); if (indexStatus == null) { // The index in the stale primary allocation request was green and hence filtered out by the store status // request. We ignore it here since the relevant exception will be thrown by the reroute action later on. @@ -209,6 +216,11 @@ private void submitStateUpdate(final ClusterRerouteRequest request, final Action ); } + /** + * Inner Reroute Response Acknowledged the Cluster State Update + * + * @opensearch.internal + */ static class ClusterRerouteResponseAckedClusterStateUpdateTask extends AckedClusterStateUpdateTask { private final ClusterRerouteRequest request; @@ -231,6 +243,11 @@ static class ClusterRerouteResponseAckedClusterStateUpdateTask extends AckedClus this.allocationService = allocationService; } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return clusterRerouteTaskKey; + } + @Override protected ClusterRerouteResponse newResponse(boolean acknowledged) { return new ClusterRerouteResponse(acknowledged, clusterStateToSend, explanations); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/reroute/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/package-info.java new file mode 100644 index 0000000000000..af26371347c0d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/reroute/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster reroute transport handlers. */ +package org.opensearch.action.admin.cluster.reroute; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterGetSettingsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterGetSettingsRequest.java index b490f172ce7ad..a56e7bbcfbfa4 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterGetSettingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterGetSettingsRequest.java @@ -33,14 +33,16 @@ package org.opensearch.action.admin.cluster.settings; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeReadRequest; import org.opensearch.action.admin.cluster.state.ClusterStateRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; /** * This request is specific to the REST client. {@link ClusterStateRequest} * is used on the transport layer. + * + * @opensearch.internal */ -public class ClusterGetSettingsRequest extends MasterNodeReadRequest { +public class ClusterGetSettingsRequest extends ClusterManagerNodeReadRequest { private boolean includeDefaults = false; @Override diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterGetSettingsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterGetSettingsResponse.java index 95c148e9fe5d3..6be255696251f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterGetSettingsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterGetSettingsResponse.java @@ -32,26 +32,29 @@ package org.opensearch.action.admin.cluster.settings; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.action.admin.cluster.state.ClusterStateResponse; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * This response is specific to the REST client. {@link ClusterStateResponse} * is used on the transport layer. + * + * @opensearch.internal */ public class ClusterGetSettingsResponse extends ActionResponse implements ToXContentObject { @@ -173,7 +176,7 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsAction.java index dcc7c786cc3ae..12134e6936ddb 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for updating cluster settings + * + * @opensearch.internal + */ public class ClusterUpdateSettingsAction extends ActionType { public static final ClusterUpdateSettingsAction INSTANCE = new ClusterUpdateSettingsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsRequest.java index 12cce7cf74efa..6b62a5edd8c28 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsRequest.java @@ -34,26 +34,28 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Map; import static org.opensearch.action.ValidateActions.addValidationError; +import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; import static org.opensearch.common.settings.Settings.readSettingsFromStream; import static org.opensearch.common.settings.Settings.writeSettingsToStream; -import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; /** * Request for an update cluster settings action + * + * @opensearch.internal */ public class ClusterUpdateSettingsRequest extends AcknowledgedRequest implements ToXContentObject { @@ -118,8 +120,8 @@ public ClusterUpdateSettingsRequest transientSettings(Settings.Builder settings) /** * Sets the source containing the transient settings to be updated. They will not survive a full cluster restart */ - public ClusterUpdateSettingsRequest transientSettings(String source, XContentType xContentType) { - this.transientSettings = Settings.builder().loadFromSource(source, xContentType).build(); + public ClusterUpdateSettingsRequest transientSettings(String source, final MediaType mediaType) { + this.transientSettings = Settings.builder().loadFromSource(source, mediaType).build(); return this; } @@ -150,8 +152,8 @@ public ClusterUpdateSettingsRequest persistentSettings(Settings.Builder settings /** * Sets the source containing the persistent settings to be updated. They will get applied cross restarts */ - public ClusterUpdateSettingsRequest persistentSettings(String source, XContentType xContentType) { - this.persistentSettings = Settings.builder().loadFromSource(source, xContentType).build(); + public ClusterUpdateSettingsRequest persistentSettings(String source, final MediaType mediaType) { + this.persistentSettings = Settings.builder().loadFromSource(source, mediaType).build(); return this; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestBuilder.java index 1ec7bd6cfb47d..4d08c94f78b6a 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsRequestBuilder.java @@ -41,6 +41,8 @@ /** * Builder for a cluster update settings request + * + * @opensearch.internal */ public class ClusterUpdateSettingsRequestBuilder extends AcknowledgedRequestBuilder< ClusterUpdateSettingsRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsResponse.java index 91955126dd745..1c543260f7aee 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/ClusterUpdateSettingsResponse.java @@ -33,21 +33,23 @@ package org.opensearch.action.admin.cluster.settings; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * A response for a cluster update settings action. + * + * @opensearch.internal */ public class ClusterUpdateSettingsResponse extends AcknowledgedResponse { @@ -57,7 +59,9 @@ public class ClusterUpdateSettingsResponse extends AcknowledgedResponse { private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( "cluster_update_settings_response", true, - args -> { return new ClusterUpdateSettingsResponse((boolean) args[0], (Settings) args[1], (Settings) args[2]); } + args -> { + return new ClusterUpdateSettingsResponse((boolean) args[0], (Settings) args[1], (Settings) args[2]); + } ); static { declareAcknowledgedField(PARSER); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/SettingsUpdater.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/SettingsUpdater.java index bcc95c01b4189..c7030764a5b47 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/SettingsUpdater.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/SettingsUpdater.java @@ -50,6 +50,8 @@ /** * Updates transient and persistent cluster state settings if there are any changes * due to the update. + * + * @opensearch.internal */ final class SettingsUpdater { final Settings.Builder transientUpdates = Settings.builder(); @@ -136,6 +138,13 @@ synchronized ClusterState updateSettings( } else { blocks.removeGlobalBlock(Metadata.CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK); } + boolean createIndexBlocked = Metadata.SETTING_CREATE_INDEX_BLOCK_SETTING.get(metadata.persistentSettings()) + || Metadata.SETTING_CREATE_INDEX_BLOCK_SETTING.get(metadata.transientSettings()); + if (createIndexBlocked) { + blocks.addGlobalBlock(Metadata.CLUSTER_CREATE_INDEX_BLOCK); + } else { + blocks.removeGlobalBlock(Metadata.CLUSTER_CREATE_INDEX_BLOCK); + } clusterState = builder(currentState).metadata(metadata).blocks(blocks).build(); } else { clusterState = currentState; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java index 6d479431e1a94..2f3cc77b05550 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/TransportClusterUpdateSettingsAction.java @@ -36,9 +36,8 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.AckedClusterStateUpdateTask; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -47,18 +46,26 @@ import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportClusterUpdateSettingsAction extends TransportMasterNodeAction< +/** + * Transport action for updating cluster settings + * + * @opensearch.internal + */ +public class TransportClusterUpdateSettingsAction extends TransportClusterManagerNodeAction< ClusterUpdateSettingsRequest, ClusterUpdateSettingsResponse> { @@ -68,6 +75,8 @@ public class TransportClusterUpdateSettingsAction extends TransportMasterNodeAct private final ClusterSettings clusterSettings; + private final ClusterManagerTaskThrottler.ThrottlingKey clusterUpdateSettingTaskKey; + @Inject public TransportClusterUpdateSettingsAction( TransportService transportService, @@ -90,6 +99,10 @@ public TransportClusterUpdateSettingsAction( ); this.allocationService = allocationService; this.clusterSettings = clusterSettings; + + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + clusterUpdateSettingTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.CLUSTER_UPDATE_SETTINGS_KEY, true); + } @Override @@ -119,7 +132,7 @@ protected ClusterUpdateSettingsResponse read(StreamInput in) throws IOException } @Override - protected void masterOperation( + protected void clusterManagerOperation( final ClusterUpdateSettingsRequest request, final ClusterState state, final ActionListener listener @@ -131,6 +144,11 @@ protected void masterOperation( private volatile boolean changed = false; + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return clusterUpdateSettingTaskKey; + } + @Override protected ClusterUpdateSettingsResponse newResponse(boolean acknowledged) { return new ClusterUpdateSettingsResponse(acknowledged, updater.getTransientUpdates(), updater.getPersistentUpdate()); @@ -155,11 +173,11 @@ public void onAckTimeout() { } private void reroute(final boolean updateSettingsAcked) { - // We're about to send a second update task, so we need to check if we're still the elected master - // For example the minimum_master_node could have been breached and we're no longer elected master, + // We're about to send a second update task, so we need to check if we're still the elected cluster-manager + // For example the minimum_master_node could have been breached and we're no longer elected cluster-manager, // so we should *not* execute the reroute. - if (!clusterService.state().nodes().isLocalNodeElectedMaster()) { - logger.debug("Skipping reroute after cluster update settings, because node is no longer master"); + if (!clusterService.state().nodes().isLocalNodeElectedClusterManager()) { + logger.debug("Skipping reroute after cluster update settings, because node is no longer cluster-manager"); listener.onResponse( new ClusterUpdateSettingsResponse( updateSettingsAcked, @@ -196,9 +214,9 @@ protected ClusterUpdateSettingsResponse newResponse(boolean acknowledged) { } @Override - public void onNoLongerMaster(String source) { + public void onNoLongerClusterManager(String source) { logger.debug( - "failed to preform reroute after cluster settings were updated - current node is no longer a master" + "failed to preform reroute after cluster settings were updated - current node is no longer a cluster-manager" ); listener.onResponse( new ClusterUpdateSettingsResponse( diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/settings/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/settings/package-info.java new file mode 100644 index 0000000000000..a532b3e9f4158 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/settings/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster settings transport handlers. */ +package org.opensearch.action.admin.cluster.settings; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsAction.java index 9d6b0f9677a95..176d52d3eeaab 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for searching shards + * + * @opensearch.internal + */ public class ClusterSearchShardsAction extends ActionType { public static final ClusterSearchShardsAction INSTANCE = new ClusterSearchShardsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsGroup.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsGroup.java index 660c4629f6db2..84e9554932864 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsGroup.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsGroup.java @@ -33,15 +33,20 @@ package org.opensearch.action.admin.cluster.shards; import org.opensearch.cluster.routing.ShardRouting; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; +/** + * Transport action for searching shard groups + * + * @opensearch.internal + */ public class ClusterSearchShardsGroup implements Writeable, ToXContentObject { private final ShardId shardId; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java index 7ae4a70d6bd36..7ddb945ad911e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsRequest.java @@ -35,16 +35,23 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; -public class ClusterSearchShardsRequest extends MasterNodeReadRequest implements IndicesRequest.Replaceable { +/** + * Transport request for searching shards + * + * @opensearch.internal + */ +public class ClusterSearchShardsRequest extends ClusterManagerNodeReadRequest + implements + IndicesRequest.Replaceable { private String[] indices = Strings.EMPTY_ARRAY; @Nullable @@ -145,7 +152,8 @@ public ClusterSearchShardsRequest routing(String... routings) { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public ClusterSearchShardsRequest preference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsRequestBuilder.java index ae5c22a1c57a7..674a2c2c36221 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsRequestBuilder.java @@ -33,10 +33,15 @@ package org.opensearch.action.admin.cluster.shards; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -public class ClusterSearchShardsRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Transport request builder for searching shards + * + * @opensearch.internal + */ +public class ClusterSearchShardsRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< ClusterSearchShardsRequest, ClusterSearchShardsResponse, ClusterSearchShardsRequestBuilder> { @@ -71,7 +76,8 @@ public ClusterSearchShardsRequestBuilder setRouting(String... routing) { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public ClusterSearchShardsRequestBuilder setPreference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java index 1381a39664a49..2b24d870219bb 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/ClusterSearchShardsResponse.java @@ -32,18 +32,23 @@ package org.opensearch.action.admin.cluster.shards; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.search.internal.AliasFilter; import java.io.IOException; import java.util.Arrays; import java.util.Map; +/** + * Transport response for searching shards + * + * @opensearch.internal + */ public class ClusterSearchShardsResponse extends ActionResponse implements ToXContentObject { private final ClusterSearchShardsGroup[] groups; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java index 37a556216c2c9..a2a65b6400c97 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/TransportClusterSearchShardsAction.java @@ -32,9 +32,8 @@ package org.opensearch.action.admin.cluster.shards; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -45,8 +44,9 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.search.internal.AliasFilter; import org.opensearch.threadpool.ThreadPool; @@ -58,7 +58,12 @@ import java.util.Map; import java.util.Set; -public class TransportClusterSearchShardsAction extends TransportMasterNodeReadAction< +/** + * Transport action for searching shards + * + * @opensearch.internal + */ +public class TransportClusterSearchShardsAction extends TransportClusterManagerNodeReadAction< ClusterSearchShardsRequest, ClusterSearchShardsResponse> { @@ -103,7 +108,7 @@ protected ClusterSearchShardsResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation( + protected void clusterManagerOperation( final ClusterSearchShardsRequest request, final ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/package-info.java new file mode 100644 index 0000000000000..94866d102ac15 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Search Shards transport handlers. */ +package org.opensearch.action.admin.cluster.shards; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingAction.java new file mode 100644 index 0000000000000..aa438cd31b934 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingAction.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.delete; + +import org.opensearch.action.ActionType; + +/** + * Action to delete weights for weighted round-robin shard routing policy. + * + * @opensearch.internal + */ +public class ClusterDeleteWeightedRoutingAction extends ActionType { + public static final ClusterDeleteWeightedRoutingAction INSTANCE = new ClusterDeleteWeightedRoutingAction(); + public static final String NAME = "cluster:admin/routing/awareness/weights/delete"; + + private ClusterDeleteWeightedRoutingAction() { + super(NAME, ClusterDeleteWeightedRoutingResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingRequest.java new file mode 100644 index 0000000000000..7f69fe9fe3c72 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingRequest.java @@ -0,0 +1,149 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.delete; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.OpenSearchGenerationException; +import org.opensearch.OpenSearchParseException; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.cluster.metadata.WeightedRoutingMetadata; +import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Map; + +/** + * Request to delete weights for weighted round-robin shard routing policy. + * + * @opensearch.internal + */ +public class ClusterDeleteWeightedRoutingRequest extends ClusterManagerNodeRequest { + private static final Logger logger = LogManager.getLogger(ClusterDeleteWeightedRoutingRequest.class); + + private long version; + private String awarenessAttribute; + + public void setVersion(long version) { + this.version = version; + } + + ClusterDeleteWeightedRoutingRequest() { + this.version = WeightedRoutingMetadata.VERSION_UNSET_VALUE; + } + + public ClusterDeleteWeightedRoutingRequest(StreamInput in) throws IOException { + super(in); + version = in.readLong(); + if (in.available() != 0) { + awarenessAttribute = in.readString(); + } + } + + public long getVersion() { + return version; + } + + public String getAwarenessAttribute() { + return awarenessAttribute; + } + + public void setAwarenessAttribute(String awarenessAttribute) { + this.awarenessAttribute = awarenessAttribute; + } + + public ClusterDeleteWeightedRoutingRequest(String awarenessAttribute) { + this.awarenessAttribute = awarenessAttribute; + this.version = WeightedRoutingMetadata.VERSION_UNSET_VALUE; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + /** + * @param source weights definition from request body + * @return this request + */ + public ClusterDeleteWeightedRoutingRequest source(Map source) { + try { + if (source.isEmpty()) { + throw new OpenSearchParseException(("Empty request body")); + } + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.map(source); + setRequestBody(BytesReference.bytes(builder), builder.contentType()); + } catch (IOException e) { + throw new OpenSearchGenerationException("Failed to generate [" + source + "]", e); + } + return this; + } + + private void setRequestBody(BytesReference source, MediaType contentType) { + try ( + XContentParser parser = XContentHelper.createParser( + NamedXContentRegistry.EMPTY, + DeprecationHandler.THROW_UNSUPPORTED_OPERATION, + source, + contentType + ) + ) { + String versionAttr = null; + XContentParser.Token token; + // move to the first alias + parser.nextToken(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String fieldName = parser.currentName(); + if (fieldName != null && fieldName.equals(WeightedRoutingMetadata.VERSION)) { + versionAttr = parser.currentName(); + } else { + throw new OpenSearchParseException( + "failed to parse delete weighted routing request body [{}], unknown type", + fieldName + ); + } + } else if (token == XContentParser.Token.VALUE_STRING) { + if (versionAttr != null && versionAttr.equals(WeightedRoutingMetadata.VERSION)) { + this.version = Long.parseLong(parser.text()); + } + } else { + throw new OpenSearchParseException("failed to parse delete weighted routing request body"); + } + } + } catch (IOException e) { + logger.error("error while parsing delete request for weighted routing request object", e); + } + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeLong(version); + if (awarenessAttribute != null) { + out.writeString(awarenessAttribute); + } + } + + @Override + public String toString() { + return "ClusterDeleteWeightedRoutingRequest{" + "version= " + version + "awarenessAttribute=" + awarenessAttribute + "}"; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingRequestBuilder.java new file mode 100644 index 0000000000000..bb34fea589534 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingRequestBuilder.java @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.delete; + +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; + +/** + * Request builder to delete weights for weighted round-robin shard routing policy. + * + * @opensearch.internal + */ +public class ClusterDeleteWeightedRoutingRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< + ClusterDeleteWeightedRoutingRequest, + ClusterDeleteWeightedRoutingResponse, + ClusterDeleteWeightedRoutingRequestBuilder> { + + public ClusterDeleteWeightedRoutingRequestBuilder(OpenSearchClient client, ClusterDeleteWeightedRoutingAction action) { + super(client, action, new ClusterDeleteWeightedRoutingRequest()); + } + + public ClusterDeleteWeightedRoutingRequestBuilder setVersion(long version) { + request.setVersion(version); + return this; + } + + public ClusterDeleteWeightedRoutingRequestBuilder setAwarenessAttribute(String attribute) { + request.setAwarenessAttribute(attribute); + return this; + } + +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingResponse.java new file mode 100644 index 0000000000000..2a417e9f4287f --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/ClusterDeleteWeightedRoutingResponse.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.delete; + +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Response from deleting weights for weighted round-robin search routing policy. + * + * @opensearch.internal + */ +public class ClusterDeleteWeightedRoutingResponse extends AcknowledgedResponse { + + ClusterDeleteWeightedRoutingResponse(StreamInput in) throws IOException { + super(in); + } + + public ClusterDeleteWeightedRoutingResponse(boolean acknowledged) { + super(acknowledged); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/TransportDeleteWeightedRoutingAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/TransportDeleteWeightedRoutingAction.java new file mode 100644 index 0000000000000..cea85ebf588bd --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/TransportDeleteWeightedRoutingAction.java @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.delete; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.routing.WeightedRoutingService; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Transport action for deleting weights for weighted round-robin search routing policy + * + * @opensearch.internal + */ +public class TransportDeleteWeightedRoutingAction extends TransportClusterManagerNodeAction< + ClusterDeleteWeightedRoutingRequest, + ClusterDeleteWeightedRoutingResponse> { + + private static final Logger logger = LogManager.getLogger(TransportDeleteWeightedRoutingAction.class); + + private final WeightedRoutingService weightedRoutingService; + + @Inject + public TransportDeleteWeightedRoutingAction( + TransportService transportService, + ClusterService clusterService, + WeightedRoutingService weightedRoutingService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + ClusterDeleteWeightedRoutingAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + ClusterDeleteWeightedRoutingRequest::new, + indexNameExpressionResolver + ); + this.weightedRoutingService = weightedRoutingService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected ClusterDeleteWeightedRoutingResponse read(StreamInput in) throws IOException { + return new ClusterDeleteWeightedRoutingResponse(in); + } + + @Override + protected ClusterBlockException checkBlock(ClusterDeleteWeightedRoutingRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } + + @Override + protected void clusterManagerOperation( + ClusterDeleteWeightedRoutingRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + weightedRoutingService.deleteWeightedRoutingMetadata(request, listener); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/package-info.java new file mode 100644 index 0000000000000..d24c88ec674f3 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/delete/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** delete weighted-round robin shard routing weights. */ +package org.opensearch.action.admin.cluster.shards.routing.weighted.delete; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingAction.java new file mode 100644 index 0000000000000..7662b7cc6fcc8 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingAction.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.get; + +import org.opensearch.action.ActionType; + +/** + * Action to get weights for weighted round-robin search routing policy. + * + * @opensearch.internal + */ +public class ClusterGetWeightedRoutingAction extends ActionType { + public static final ClusterGetWeightedRoutingAction INSTANCE = new ClusterGetWeightedRoutingAction(); + public static final String NAME = "cluster:admin/routing/awareness/weights/get"; + + private ClusterGetWeightedRoutingAction() { + super(NAME, ClusterGetWeightedRoutingResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingRequest.java new file mode 100644 index 0000000000000..7dcec15c750fc --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingRequest.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.get; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Request to get weights for weighted round-robin search routing policy. + * + * @opensearch.internal + */ +public class ClusterGetWeightedRoutingRequest extends ClusterManagerNodeReadRequest { + + String awarenessAttribute; + + public String getAwarenessAttribute() { + return awarenessAttribute; + } + + public void setAwarenessAttribute(String awarenessAttribute) { + this.awarenessAttribute = awarenessAttribute; + } + + public ClusterGetWeightedRoutingRequest(String awarenessAttribute) { + this.awarenessAttribute = awarenessAttribute; + } + + public ClusterGetWeightedRoutingRequest() {} + + public ClusterGetWeightedRoutingRequest(StreamInput in) throws IOException { + super(in); + awarenessAttribute = in.readString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(awarenessAttribute); + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (awarenessAttribute == null || awarenessAttribute.isEmpty()) { + validationException = addValidationError("Awareness attribute is missing", validationException); + } + return validationException; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingRequestBuilder.java new file mode 100644 index 0000000000000..82f4c1106461d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingRequestBuilder.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.get; + +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; + +/** + * Request builder to get weights for weighted round-robin search routing policy. + * + * @opensearch.internal + */ +public class ClusterGetWeightedRoutingRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< + ClusterGetWeightedRoutingRequest, + ClusterGetWeightedRoutingResponse, + ClusterGetWeightedRoutingRequestBuilder> { + + public ClusterGetWeightedRoutingRequestBuilder(OpenSearchClient client, ClusterGetWeightedRoutingAction action) { + super(client, action, new ClusterGetWeightedRoutingRequest()); + } + + public ClusterGetWeightedRoutingRequestBuilder setRequestLocal(boolean local) { + request.local(local); + return this; + } + + public ClusterGetWeightedRoutingRequestBuilder setAwarenessAttribute(String attribute) { + request.setAwarenessAttribute(attribute); + return this; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingResponse.java new file mode 100644 index 0000000000000..5ef14d0e00c49 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/ClusterGetWeightedRoutingResponse.java @@ -0,0 +1,186 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.get; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.cluster.metadata.WeightedRoutingMetadata; +import org.opensearch.cluster.routing.WeightedRouting; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * Response from fetching weights for weighted round-robin search routing policy. + * + * @opensearch.internal + */ +public class ClusterGetWeightedRoutingResponse extends ActionResponse implements ToXContentObject { + + private static final String WEIGHTS = "weights"; + private long version; + private final Boolean discoveredClusterManager; + + private static final String DISCOVERED_CLUSTER_MANAGER = "discovered_cluster_manager"; + + public WeightedRouting getWeightedRouting() { + return weightedRouting; + } + + private final WeightedRouting weightedRouting; + + public long getVersion() { + return version; + } + + public Boolean getDiscoveredClusterManager() { + return discoveredClusterManager; + } + + ClusterGetWeightedRoutingResponse() { + this.weightedRouting = null; + this.discoveredClusterManager = null; + } + + public ClusterGetWeightedRoutingResponse(WeightedRouting weightedRouting, Boolean discoveredClusterManager, long version) { + this.discoveredClusterManager = discoveredClusterManager; + this.weightedRouting = weightedRouting; + this.version = version; + } + + ClusterGetWeightedRoutingResponse(StreamInput in) throws IOException { + if (in.available() != 0) { + this.weightedRouting = new WeightedRouting(in); + this.version = in.readLong(); + this.discoveredClusterManager = in.readOptionalBoolean(); + } else { + this.weightedRouting = null; + this.discoveredClusterManager = null; + } + } + + /** + * List of weights to return + * + * @return list or weights + */ + public WeightedRouting weights() { + return this.weightedRouting; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + if (weightedRouting != null) { + weightedRouting.writeTo(out); + out.writeLong(version); + } + if (discoveredClusterManager != null) { + out.writeOptionalBoolean(discoveredClusterManager); + } + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + if (this.weightedRouting != null) { + builder.startObject(WEIGHTS); + for (Map.Entry entry : weightedRouting.weights().entrySet()) { + builder.field(entry.getKey(), entry.getValue().toString()); + } + + builder.endObject(); + builder.field(WeightedRoutingMetadata.VERSION, version); + if (discoveredClusterManager != null) { + builder.field(DISCOVERED_CLUSTER_MANAGER, discoveredClusterManager); + } + } + builder.endObject(); + return builder; + } + + public static ClusterGetWeightedRoutingResponse fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + XContentParser.Token token; + String attrKey = null, attrValue; + Boolean discoveredClusterManager = null; + Map weights = new HashMap<>(); + long version = WeightedRoutingMetadata.VERSION_UNSET_VALUE; + String weightsAttr; + String fieldName = null; + + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + fieldName = parser.currentName(); + if (fieldName != null + && (fieldName.equals(WeightedRoutingMetadata.VERSION) || fieldName.equals(DISCOVERED_CLUSTER_MANAGER))) { + continue; + } else if (fieldName != null && fieldName.equals(WEIGHTS)) { + weightsAttr = parser.currentName(); + } else { + throw new OpenSearchParseException("failed to parse weighted routing request object", fieldName); + } + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new OpenSearchParseException( + "failed to parse weighted routing request object [{}], expected object", + weightsAttr + ); + } + + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + attrKey = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_STRING) { + attrValue = parser.text(); + if (attrKey != null) { + weights.put(attrKey, Double.parseDouble(attrValue)); + } + } else { + throw new OpenSearchParseException("failed to parse weighted routing request attribute [{}]", attrKey); + } + } + } else if (token == XContentParser.Token.VALUE_NUMBER + && fieldName != null + && fieldName.equals(WeightedRoutingMetadata.VERSION)) { + version = parser.longValue(); + + } else if (token == XContentParser.Token.VALUE_BOOLEAN + && fieldName != null + && fieldName.equals(DISCOVERED_CLUSTER_MANAGER)) { + discoveredClusterManager = Boolean.parseBoolean(parser.text()); + } else { + throw new OpenSearchParseException("failed to parse weighted routing request"); + } + } + + WeightedRouting weightedRouting = new WeightedRouting("", weights); + return new ClusterGetWeightedRoutingResponse(weightedRouting, discoveredClusterManager, version); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClusterGetWeightedRoutingResponse that = (ClusterGetWeightedRoutingResponse) o; + return weightedRouting.equals(that.weightedRouting) && discoveredClusterManager.equals(that.discoveredClusterManager); + } + + @Override + public int hashCode() { + return Objects.hash(weightedRouting, discoveredClusterManager); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/TransportGetWeightedRoutingAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/TransportGetWeightedRoutingAction.java new file mode 100644 index 0000000000000..50368d85e0011 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/TransportGetWeightedRoutingAction.java @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.get; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.metadata.WeightedRoutingMetadata; +import org.opensearch.cluster.routing.WeightedRouting; +import org.opensearch.cluster.routing.WeightedRoutingService; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Transport action for getting weights for weighted round-robin search routing policy + * + * @opensearch.internal + */ +public class TransportGetWeightedRoutingAction extends TransportClusterManagerNodeReadAction< + ClusterGetWeightedRoutingRequest, + ClusterGetWeightedRoutingResponse> { + private static final Logger logger = LogManager.getLogger(TransportGetWeightedRoutingAction.class); + private final WeightedRoutingService weightedRoutingService; + + @Inject + public TransportGetWeightedRoutingAction( + TransportService transportService, + ClusterService clusterService, + WeightedRoutingService weightedRoutingService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + ClusterGetWeightedRoutingAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + ClusterGetWeightedRoutingRequest::new, + indexNameExpressionResolver + ); + this.weightedRoutingService = weightedRoutingService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected ClusterGetWeightedRoutingResponse read(StreamInput in) throws IOException { + return new ClusterGetWeightedRoutingResponse(in); + } + + @Override + protected ClusterBlockException checkBlock(ClusterGetWeightedRoutingRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } + + @Override + protected void clusterManagerOperation( + final ClusterGetWeightedRoutingRequest request, + ClusterState state, + final ActionListener listener + ) { + try { + weightedRoutingService.verifyAwarenessAttribute(request.getAwarenessAttribute()); + WeightedRoutingMetadata weightedRoutingMetadata = state.metadata().custom(WeightedRoutingMetadata.TYPE); + ClusterGetWeightedRoutingResponse clusterGetWeightedRoutingResponse = new ClusterGetWeightedRoutingResponse(); + if (weightedRoutingMetadata != null && weightedRoutingMetadata.getWeightedRouting() != null) { + WeightedRouting weightedRouting = weightedRoutingMetadata.getWeightedRouting(); + clusterGetWeightedRoutingResponse = new ClusterGetWeightedRoutingResponse( + weightedRouting, + state.nodes().getClusterManagerNodeId() != null, + weightedRoutingMetadata.getVersion() + ); + } + listener.onResponse(clusterGetWeightedRoutingResponse); + } catch (Exception ex) { + listener.onFailure(ex); + } + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/package-info.java new file mode 100644 index 0000000000000..45e5b32b72e50 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** get weighted-round robin shard routing weights. */ +package org.opensearch.action.admin.cluster.shards.routing.weighted.get; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterAddWeightedRoutingAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterAddWeightedRoutingAction.java new file mode 100644 index 0000000000000..65c5ccca71461 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterAddWeightedRoutingAction.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.put; + +import org.opensearch.action.ActionType; + +/** + * Action to update weights for weighted round-robin shard routing policy. + * + * @opensearch.internal + */ +public final class ClusterAddWeightedRoutingAction extends ActionType { + + public static final ClusterAddWeightedRoutingAction INSTANCE = new ClusterAddWeightedRoutingAction(); + public static final String NAME = "cluster:admin/routing/awareness/weights/put"; + + private ClusterAddWeightedRoutingAction() { + super(NAME, ClusterPutWeightedRoutingResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingRequest.java new file mode 100644 index 0000000000000..8e8432a384aa5 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingRequest.java @@ -0,0 +1,232 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.put; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.OpenSearchGenerationException; +import org.opensearch.OpenSearchParseException; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.cluster.metadata.WeightedRoutingMetadata; +import org.opensearch.cluster.routing.WeightedRouting; +import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Request to update weights for weighted round-robin shard routing policy. + * + * @opensearch.internal + */ +public class ClusterPutWeightedRoutingRequest extends ClusterManagerNodeRequest { + private static final Logger logger = LogManager.getLogger(ClusterPutWeightedRoutingRequest.class); + + private WeightedRouting weightedRouting; + private String attributeName; + private long version; + + public void version(long version) { + this.version = version; + } + + public long getVersion() { + return this.version; + } + + public ClusterPutWeightedRoutingRequest() {} + + public WeightedRouting getWeightedRouting() { + return weightedRouting; + } + + public ClusterPutWeightedRoutingRequest setWeightedRouting(WeightedRouting weightedRouting) { + this.weightedRouting = weightedRouting; + return this; + } + + public void attributeName(String attributeName) { + this.attributeName = attributeName; + } + + public ClusterPutWeightedRoutingRequest(StreamInput in) throws IOException { + super(in); + weightedRouting = new WeightedRouting(in); + version = in.readLong(); + } + + public ClusterPutWeightedRoutingRequest(String attributeName) { + this.attributeName = attributeName; + } + + public void setWeightedRouting(Map source) { + try { + if (source.isEmpty()) { + throw new OpenSearchParseException(("Empty request body")); + } + XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.map(source); + setWeightedRouting(BytesReference.bytes(builder), builder.contentType()); + } catch (IOException e) { + throw new OpenSearchGenerationException("Failed to generate [" + source + "]", e); + } + } + + public void setWeightedRouting(BytesReference source, MediaType contentType) { + try ( + XContentParser parser = XContentHelper.createParser( + NamedXContentRegistry.EMPTY, + DeprecationHandler.THROW_UNSUPPORTED_OPERATION, + source, + contentType + ) + ) { + String attrValue = null; + Map weights = new HashMap<>(); + Double attrWeight = null; + XContentParser.Token token; + // move to the first alias + parser.nextToken(); + String versionAttr = null; + String weightsAttr; + long version = WeightedRoutingMetadata.VERSION_UNSET_VALUE; + + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String fieldName = parser.currentName(); + if (fieldName != null && fieldName.equals(WeightedRoutingMetadata.VERSION)) { + versionAttr = parser.currentName(); + continue; + } else if (fieldName != null && fieldName.equals("weights")) { + weightsAttr = parser.currentName(); + } else { + throw new OpenSearchParseException("failed to parse weighted routing request object [{}]", fieldName); + } + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new OpenSearchParseException( + "failed to parse weighted routing request object [{}], expected object", + weightsAttr + ); + } + + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + attrValue = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_STRING) { + attrWeight = Double.parseDouble(parser.text()); + weights.put(attrValue, attrWeight); + } else { + throw new OpenSearchParseException( + "failed to parse weighted routing request attribute [{}], " + "unknown type", + attrWeight + ); + } + } + } else if (token == XContentParser.Token.VALUE_NUMBER) { + if (versionAttr != null && versionAttr.equals(WeightedRoutingMetadata.VERSION)) { + version = parser.longValue(); + } + } else { + throw new OpenSearchParseException( + "failed to parse weighted routing request " + "[{}], unknown " + "type", + attributeName + ); + } + } + this.weightedRouting = new WeightedRouting(this.attributeName, weights); + this.version = version; + } catch (IOException e) { + logger.error("error while parsing put weighted routing request object", e); + } + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (weightedRouting == null) { + validationException = addValidationError("Weighted routing request object is null", validationException); + } + if (weightedRouting.attributeName() == null || weightedRouting.attributeName().isEmpty()) { + validationException = addValidationError("Attribute name is missing", validationException); + } + if (weightedRouting.weights() == null || weightedRouting.weights().isEmpty()) { + validationException = addValidationError("Weights are missing", validationException); + } + if (version == WeightedRoutingMetadata.VERSION_UNSET_VALUE) { + validationException = addValidationError("Version is missing", validationException); + } + int countValueWithZeroWeights = 0; + double weight; + try { + for (Object value : weightedRouting.weights().values()) { + if (value == null) { + validationException = addValidationError(("Weight is null"), validationException); + } else { + weight = Double.parseDouble(value.toString()); + countValueWithZeroWeights = (weight == 0) ? countValueWithZeroWeights + 1 : countValueWithZeroWeights; + } + } + } catch (NumberFormatException e) { + validationException = addValidationError(("Weight is not a number"), validationException); + } + // Returning validation exception here itself if it is not null, so we can have a descriptive message for the count check + if (validationException != null) { + return validationException; + } + if (countValueWithZeroWeights > weightedRouting.weights().size() / 2) { + validationException = addValidationError( + (String.format( + Locale.ROOT, + "There are too many attribute values [%s] given zero weight [%d]. Maximum expected number of routing weights having zero weight is [%d]", + weightedRouting.weights().toString(), + countValueWithZeroWeights, + weightedRouting.weights().size() / 2 + )), + null + ); + } + return validationException; + } + + /** + * @param source weights definition from request body + * @return this request + */ + public ClusterPutWeightedRoutingRequest source(Map source) { + setWeightedRouting(source); + return this; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + weightedRouting.writeTo(out); + out.writeLong(version); + } + + @Override + public String toString() { + return "ClusterPutWeightedRoutingRequest{" + "weightedRouting= " + weightedRouting.toString() + "version= " + version + "}"; + } + +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingRequestBuilder.java new file mode 100644 index 0000000000000..adfb2cf02f6d9 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingRequestBuilder.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.put; + +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; +import org.opensearch.cluster.routing.WeightedRouting; + +/** + * Request builder to update weights for weighted round-robin shard routing policy. + * + * @opensearch.internal + */ +public class ClusterPutWeightedRoutingRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< + ClusterPutWeightedRoutingRequest, + ClusterPutWeightedRoutingResponse, + ClusterPutWeightedRoutingRequestBuilder> { + public ClusterPutWeightedRoutingRequestBuilder(OpenSearchClient client, ClusterAddWeightedRoutingAction action) { + super(client, action, new ClusterPutWeightedRoutingRequest()); + } + + public ClusterPutWeightedRoutingRequestBuilder setWeightedRouting(WeightedRouting weightedRouting) { + request.setWeightedRouting(weightedRouting); + return this; + } + + public ClusterPutWeightedRoutingRequestBuilder setVersion(long version) { + request.version(version); + return this; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingResponse.java new file mode 100644 index 0000000000000..cbf10aa74f8a2 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/ClusterPutWeightedRoutingResponse.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.put; + +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.core.common.io.stream.StreamInput; + +import java.io.IOException; + +/** + * Response from updating weights for weighted round-robin search routing policy. + * + * @opensearch.internal + */ +public class ClusterPutWeightedRoutingResponse extends AcknowledgedResponse { + public ClusterPutWeightedRoutingResponse(boolean acknowledged) { + super(acknowledged); + } + + public ClusterPutWeightedRoutingResponse(StreamInput in) throws IOException { + super(in); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/TransportAddWeightedRoutingAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/TransportAddWeightedRoutingAction.java new file mode 100644 index 0000000000000..2f94842eaa39b --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/TransportAddWeightedRoutingAction.java @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.cluster.shards.routing.weighted.put; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.routing.WeightedRoutingService; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Transport action for updating weights for weighted round-robin search routing policy + * + * @opensearch.internal + */ +public class TransportAddWeightedRoutingAction extends TransportClusterManagerNodeAction< + ClusterPutWeightedRoutingRequest, + ClusterPutWeightedRoutingResponse> { + + private final WeightedRoutingService weightedRoutingService; + + @Inject + public TransportAddWeightedRoutingAction( + TransportService transportService, + ClusterService clusterService, + WeightedRoutingService weightedRoutingService, + ThreadPool threadPool, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + ClusterAddWeightedRoutingAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + ClusterPutWeightedRoutingRequest::new, + indexNameExpressionResolver + ); + this.weightedRoutingService = weightedRoutingService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected ClusterPutWeightedRoutingResponse read(StreamInput in) throws IOException { + return new ClusterPutWeightedRoutingResponse(in); + } + + @Override + protected void clusterManagerOperation( + ClusterPutWeightedRoutingRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + try { + weightedRoutingService.verifyAwarenessAttribute(request.getWeightedRouting().attributeName()); + } catch (ActionRequestValidationException ex) { + listener.onFailure(ex); + return; + } + weightedRoutingService.registerWeightedRoutingMetadata( + request, + ActionListener.delegateFailure(listener, (delegatedListener, response) -> { + delegatedListener.onResponse(new ClusterPutWeightedRoutingResponse(response.isAcknowledged())); + }) + ); + } + + @Override + protected ClusterBlockException checkBlock(ClusterPutWeightedRoutingRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/package-info.java new file mode 100644 index 0000000000000..4f18b220cd343 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/shards/routing/weighted/put/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** add/update weighted-round robin shard routing weights. */ +package org.opensearch.action.admin.cluster.shards.routing.weighted.put; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotAction.java index 0ecc088ff462a..c6fe102544a7e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action for cloning a snapshot + * + * @opensearch.internal + */ public final class CloneSnapshotAction extends ActionType { public static final CloneSnapshotAction INSTANCE = new CloneSnapshotAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequest.java index 58725c23b7fc5..694b13e37bb03 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequest.java @@ -35,18 +35,27 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import static org.opensearch.action.ValidateActions.addValidationError; -public class CloneSnapshotRequest extends MasterNodeRequest implements IndicesRequest.Replaceable, ToXContentObject { +/** + * Transport request for cloning a snapshot + * + * @opensearch.internal + */ +public class CloneSnapshotRequest extends ClusterManagerNodeRequest + implements + IndicesRequest.Replaceable, + ToXContentObject { private final String repository; @@ -178,6 +187,6 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequestBuilder.java index ba6f7a61bdc8d..0f47d77d6a9d3 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/CloneSnapshotRequestBuilder.java @@ -34,12 +34,17 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.IndicesOptions; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; -public class CloneSnapshotRequestBuilder extends MasterNodeOperationRequestBuilder< +/** + * Transport request builder for cloning a snapshot + * + * @opensearch.internal + */ +public class CloneSnapshotRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< CloneSnapshotRequest, AcknowledgedResponse, CloneSnapshotRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/TransportCloneSnapshotAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/TransportCloneSnapshotAction.java index 806cb4a82c3c5..54ef372609390 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/TransportCloneSnapshotAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/TransportCloneSnapshotAction.java @@ -32,17 +32,17 @@ package org.opensearch.action.admin.cluster.snapshots.clone; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.snapshots.SnapshotsService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -51,8 +51,10 @@ /** * Transport action for the clone snapshot operation. + * + * @opensearch.internal */ -public final class TransportCloneSnapshotAction extends TransportMasterNodeAction { +public final class TransportCloneSnapshotAction extends TransportClusterManagerNodeAction { private final SnapshotsService snapshotsService; @@ -88,7 +90,11 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(CloneSnapshotRequest request, ClusterState state, ActionListener listener) { + protected void clusterManagerOperation( + CloneSnapshotRequest request, + ClusterState state, + ActionListener listener + ) { snapshotsService.cloneSnapshot(request, ActionListener.map(listener, v -> new AcknowledgedResponse(true))); } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/package-info.java new file mode 100644 index 0000000000000..14738fe5e95b4 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/clone/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Clone Snapshot transport handler. */ +package org.opensearch.action.admin.cluster.snapshots.clone; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotAction.java index 324a28027949f..fb95e311a9783 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotAction.java @@ -36,6 +36,8 @@ /** * Create snapshot action + * + * @opensearch.internal */ public class CreateSnapshotAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java index 438d2fd006e63..7796353d81c5b 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotRequest.java @@ -37,16 +37,17 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Arrays; @@ -55,11 +56,11 @@ import java.util.Objects; import static org.opensearch.action.ValidateActions.addValidationError; -import static org.opensearch.common.Strings.EMPTY_ARRAY; import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; import static org.opensearch.common.settings.Settings.readSettingsFromStream; import static org.opensearch.common.settings.Settings.writeSettingsToStream; import static org.opensearch.common.xcontent.support.XContentMapValues.nodeBooleanValue; +import static org.opensearch.core.common.Strings.EMPTY_ARRAY; import static org.opensearch.snapshots.SnapshotInfo.METADATA_FIELD_INTRODUCED; /** @@ -71,12 +72,14 @@ *

  • must not contain whitespace (tabs or spaces)
  • *
  • must not contain comma (',')
  • *
  • must not contain hash sign ('#')
  • - *
  • must not start with underscore ('-')
  • + *
  • must not start with underscore ('_')
  • *
  • must be lowercase
  • - *
  • must not contain invalid file name characters {@link org.opensearch.common.Strings#INVALID_FILENAME_CHARS}
  • + *
  • must not contain invalid file name characters {@link Strings#INVALID_FILENAME_CHARS}
  • * + * + * @opensearch.internal */ -public class CreateSnapshotRequest extends MasterNodeRequest +public class CreateSnapshotRequest extends ClusterManagerNodeRequest implements IndicesRequest.Replaceable, ToXContentObject { @@ -371,11 +374,11 @@ public CreateSnapshotRequest settings(Settings.Builder settings) { * See repository documentation for more information. * * @param source repository-specific snapshot settings - * @param xContentType the content type of the source + * @param mediaType the content type of the source * @return this request */ - public CreateSnapshotRequest settings(String source, XContentType xContentType) { - this.settings = Settings.builder().loadFromSource(source, xContentType).build(); + public CreateSnapshotRequest settings(String source, MediaType mediaType) { + this.settings = Settings.builder().loadFromSource(source, mediaType).build(); return this; } @@ -389,9 +392,9 @@ public CreateSnapshotRequest settings(String source, XContentType xContentType) */ public CreateSnapshotRequest settings(Map source) { try { - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(MediaTypeRegistry.JSON); builder.map(source); - settings(Strings.toString(builder), builder.contentType()); + settings(builder.toString(), builder.contentType()); } catch (IOException e) { throw new OpenSearchGenerationException("Failed to generate [" + source + "]", e); } @@ -519,7 +522,7 @@ public boolean equals(Object o) { && Arrays.equals(indices, that.indices) && Objects.equals(indicesOptions, that.indicesOptions) && Objects.equals(settings, that.settings) - && Objects.equals(masterNodeTimeout, that.masterNodeTimeout) + && Objects.equals(clusterManagerNodeTimeout, that.clusterManagerNodeTimeout) && Objects.equals(userMetadata, that.userMetadata); } @@ -560,8 +563,8 @@ public String toString() { + includeGlobalState + ", waitForCompletion=" + waitForCompletion - + ", masterNodeTimeout=" - + masterNodeTimeout + + ", clusterManagerNodeTimeout=" + + clusterManagerNodeTimeout + ", metadata=" + userMetadata + '}'; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestBuilder.java index db30ce8295530..40d440419819c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotRequestBuilder.java @@ -33,7 +33,7 @@ package org.opensearch.action.admin.cluster.snapshots.create; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; @@ -42,8 +42,10 @@ /** * Create snapshot request builder + * + * @opensearch.internal */ -public class CreateSnapshotRequestBuilder extends MasterNodeOperationRequestBuilder< +public class CreateSnapshotRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< CreateSnapshotRequest, CreateSnapshotResponse, CreateSnapshotRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotResponse.java index 9a92971a2a791..1517112b9b3e1 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/CreateSnapshotResponse.java @@ -32,16 +32,16 @@ package org.opensearch.action.admin.cluster.snapshots.create; -import org.opensearch.action.ActionResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.snapshots.SnapshotInfo; import org.opensearch.snapshots.SnapshotInfo.SnapshotInfoBuilder; @@ -50,6 +50,8 @@ /** * Create snapshot response + * + * @opensearch.internal */ public class CreateSnapshotResponse extends ActionResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java index 377fe03a9b030..768a6578c75fb 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/TransportCreateSnapshotAction.java @@ -32,16 +32,16 @@ package org.opensearch.action.admin.cluster.snapshots.create; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.snapshots.SnapshotsService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -50,8 +50,10 @@ /** * Transport action for create snapshot operation + * + * @opensearch.internal */ -public class TransportCreateSnapshotAction extends TransportMasterNodeAction { +public class TransportCreateSnapshotAction extends TransportClusterManagerNodeAction { private final SnapshotsService snapshotsService; @Inject @@ -96,7 +98,7 @@ protected ClusterBlockException checkBlock(CreateSnapshotRequest request, Cluste } @Override - protected void masterOperation( + protected void clusterManagerOperation( final CreateSnapshotRequest request, ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/package-info.java new file mode 100644 index 0000000000000..58e1f4f9311ae --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/create/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Create Snapshot transport handler. */ +package org.opensearch.action.admin.cluster.snapshots.create; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotAction.java index 4c577bd0bbce2..0b98a4b31fd53 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotAction.java @@ -37,6 +37,8 @@ /** * Delete snapshot action + * + * @opensearch.internal */ public class DeleteSnapshotAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotRequest.java index fe9dc4db626f7..1dfa5de8cc852 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotRequest.java @@ -33,9 +33,9 @@ package org.opensearch.action.admin.cluster.snapshots.delete; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.snapshots.SnapshotsService; import java.io.IOException; @@ -47,8 +47,10 @@ *

    * Delete snapshot request removes snapshots from the repository and cleans up all files that are associated with the snapshots. * All files that are shared with at least one other existing snapshot are left intact. + * + * @opensearch.internal */ -public class DeleteSnapshotRequest extends MasterNodeRequest { +public class DeleteSnapshotRequest extends ClusterManagerNodeRequest { private String repository; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotRequestBuilder.java index 951f86bafb7b1..1fa855265ffd4 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/DeleteSnapshotRequestBuilder.java @@ -32,14 +32,16 @@ package org.opensearch.action.admin.cluster.snapshots.delete; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; /** * Delete snapshot request builder + * + * @opensearch.internal */ -public class DeleteSnapshotRequestBuilder extends MasterNodeOperationRequestBuilder< +public class DeleteSnapshotRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< DeleteSnapshotRequest, AcknowledgedResponse, DeleteSnapshotRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/TransportDeleteSnapshotAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/TransportDeleteSnapshotAction.java index 0781f01e9bc61..e8462e4d822f2 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/TransportDeleteSnapshotAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/TransportDeleteSnapshotAction.java @@ -32,17 +32,17 @@ package org.opensearch.action.admin.cluster.snapshots.delete; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.snapshots.SnapshotsService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -51,8 +51,10 @@ /** * Transport action for delete snapshot operation + * + * @opensearch.internal */ -public class TransportDeleteSnapshotAction extends TransportMasterNodeAction { +public class TransportDeleteSnapshotAction extends TransportClusterManagerNodeAction { private final SnapshotsService snapshotsService; @Inject @@ -93,7 +95,7 @@ protected ClusterBlockException checkBlock(DeleteSnapshotRequest request, Cluste } @Override - protected void masterOperation( + protected void clusterManagerOperation( final DeleteSnapshotRequest request, ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/package-info.java new file mode 100644 index 0000000000000..5ad5e58ebe1fd --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/delete/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Delete Snapshot transport handler. */ +package org.opensearch.action.admin.cluster.snapshots.delete; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsAction.java index 22e3e859394c7..59d7969d04f86 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsAction.java @@ -36,6 +36,8 @@ /** * Get snapshots action + * + * @opensearch.internal */ public class GetSnapshotsAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java index 549e3cea70deb..8e2e2d3390058 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsRequest.java @@ -33,10 +33,10 @@ package org.opensearch.action.admin.cluster.snapshots.get; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -44,8 +44,10 @@ /** * Get snapshot request + * + * @opensearch.internal */ -public class GetSnapshotsRequest extends MasterNodeRequest { +public class GetSnapshotsRequest extends ClusterManagerNodeRequest { public static final String ALL_SNAPSHOTS = "_all"; public static final String CURRENT_SNAPSHOT = "_current"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java index bd2ced2733169..3434f1cb47a99 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsRequestBuilder.java @@ -32,14 +32,16 @@ package org.opensearch.action.admin.cluster.snapshots.get; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.util.ArrayUtils; /** * Get snapshots request builder + * + * @opensearch.internal */ -public class GetSnapshotsRequestBuilder extends MasterNodeOperationRequestBuilder< +public class GetSnapshotsRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< GetSnapshotsRequest, GetSnapshotsResponse, GetSnapshotsRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java index 28981a6bdafe2..5d5de4c7fa85e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/GetSnapshotsResponse.java @@ -32,16 +32,17 @@ package org.opensearch.action.admin.cluster.snapshots.get; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.snapshots.SnapshotInfo; import java.io.IOException; @@ -51,6 +52,8 @@ /** * Get snapshots response + * + * @opensearch.internal */ public class GetSnapshotsResponse extends ActionResponse implements ToXContentObject { @@ -125,6 +128,6 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java index 91cec4b268f9b..c7fdc59334874 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/TransportGetSnapshotsAction.java @@ -36,10 +36,9 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.lucene.util.CollectionUtil; -import org.opensearch.action.ActionListener; +import org.opensearch.action.StepListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.PlainActionFuture; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.SnapshotsInProgress; import org.opensearch.cluster.block.ClusterBlockException; @@ -48,8 +47,9 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.regex.Regex; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.repositories.IndexId; import org.opensearch.repositories.RepositoriesService; import org.opensearch.repositories.Repository; @@ -76,8 +76,10 @@ /** * Transport Action for get snapshots operation + * + * @opensearch.internal */ -public class TransportGetSnapshotsAction extends TransportMasterNodeAction { +public class TransportGetSnapshotsAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportGetSnapshotsAction.class); @@ -120,7 +122,7 @@ protected ClusterBlockException checkBlock(GetSnapshotsRequest request, ClusterS } @Override - protected void masterOperation( + protected void clusterManagerOperation( final GetSnapshotsRequest request, final ClusterState state, final ActionListener listener @@ -136,57 +138,64 @@ protected void masterOperation( currentSnapshots.add(snapshotInfo); } - final RepositoryData repositoryData; + final StepListener repositoryDataListener = new StepListener<>(); if (isCurrentSnapshotsOnly(request.snapshots()) == false) { - repositoryData = PlainActionFuture.get(fut -> repositoriesService.getRepositoryData(repository, fut)); - for (SnapshotId snapshotId : repositoryData.getSnapshotIds()) { - allSnapshotIds.put(snapshotId.getName(), snapshotId); - } + repositoriesService.getRepositoryData(repository, repositoryDataListener); } else { - repositoryData = null; + // Setting repositoryDataListener response to be null if the request has only current snapshot + repositoryDataListener.onResponse(null); } + repositoryDataListener.whenComplete(repositoryData -> { + if (repositoryData != null) { + for (SnapshotId snapshotId : repositoryData.getSnapshotIds()) { + allSnapshotIds.put(snapshotId.getName(), snapshotId); + } + } - final Set toResolve = new HashSet<>(); - if (isAllSnapshots(request.snapshots())) { - toResolve.addAll(allSnapshotIds.values()); - } else { - for (String snapshotOrPattern : request.snapshots()) { - if (GetSnapshotsRequest.CURRENT_SNAPSHOT.equalsIgnoreCase(snapshotOrPattern)) { - toResolve.addAll(currentSnapshots.stream().map(SnapshotInfo::snapshotId).collect(Collectors.toList())); - } else if (Regex.isSimpleMatchPattern(snapshotOrPattern) == false) { - if (allSnapshotIds.containsKey(snapshotOrPattern)) { - toResolve.add(allSnapshotIds.get(snapshotOrPattern)); - } else if (request.ignoreUnavailable() == false) { - throw new SnapshotMissingException(repository, snapshotOrPattern); - } - } else { - for (Map.Entry entry : allSnapshotIds.entrySet()) { - if (Regex.simpleMatch(snapshotOrPattern, entry.getKey())) { - toResolve.add(entry.getValue()); + final Set toResolve = new HashSet<>(); + if (isAllSnapshots(request.snapshots())) { + toResolve.addAll(allSnapshotIds.values()); + } else { + for (String snapshotOrPattern : request.snapshots()) { + if (GetSnapshotsRequest.CURRENT_SNAPSHOT.equalsIgnoreCase(snapshotOrPattern)) { + toResolve.addAll(currentSnapshots.stream().map(SnapshotInfo::snapshotId).collect(Collectors.toList())); + } else if (Regex.isSimpleMatchPattern(snapshotOrPattern) == false) { + if (allSnapshotIds.containsKey(snapshotOrPattern)) { + toResolve.add(allSnapshotIds.get(snapshotOrPattern)); + } else if (request.ignoreUnavailable() == false) { + throw new SnapshotMissingException(repository, snapshotOrPattern); + } + } else { + for (Map.Entry entry : allSnapshotIds.entrySet()) { + if (Regex.simpleMatch(snapshotOrPattern, entry.getKey())) { + toResolve.add(entry.getValue()); + } } } } - } - if (toResolve.isEmpty() && request.ignoreUnavailable() == false && isCurrentSnapshotsOnly(request.snapshots()) == false) { - throw new SnapshotMissingException(repository, request.snapshots()[0]); + if (toResolve.isEmpty() + && request.ignoreUnavailable() == false + && isCurrentSnapshotsOnly(request.snapshots()) == false) { + throw new SnapshotMissingException(repository, request.snapshots()[0]); + } } - } - final List snapshotInfos; - if (request.verbose()) { - snapshotInfos = snapshots(snapshotsInProgress, repository, new ArrayList<>(toResolve), request.ignoreUnavailable()); - } else { - if (repositoryData != null) { - // want non-current snapshots as well, which are found in the repository data - snapshotInfos = buildSimpleSnapshotInfos(toResolve, repositoryData, currentSnapshots); + final List snapshotInfos; + if (request.verbose()) { + snapshotInfos = snapshots(snapshotsInProgress, repository, new ArrayList<>(toResolve), request.ignoreUnavailable()); } else { - // only want current snapshots - snapshotInfos = currentSnapshots.stream().map(SnapshotInfo::basic).collect(Collectors.toList()); - CollectionUtil.timSort(snapshotInfos); + if (repositoryData != null) { + // want non-current snapshots as well, which are found in the repository data + snapshotInfos = buildSimpleSnapshotInfos(toResolve, repositoryData, currentSnapshots); + } else { + // only want current snapshots + snapshotInfos = currentSnapshots.stream().map(SnapshotInfo::basic).collect(Collectors.toList()); + CollectionUtil.timSort(snapshotInfos); + } } - } - listener.onResponse(new GetSnapshotsResponse(snapshotInfos)); + listener.onResponse(new GetSnapshotsResponse(snapshotInfos)); + }, listener::onFailure); } catch (Exception e) { listener.onFailure(e); } @@ -237,9 +246,11 @@ private List snapshots( repositoryName, snapshotIdsToIterate.stream().map(SnapshotId::getName).collect(Collectors.toList()) ); + // filter and incorporate the snapshots in progress for (SnapshotsInProgress.Entry entry : entries) { - snapshotSet.add(new SnapshotInfo(entry)); - snapshotIdsToIterate.remove(entry.snapshot().getSnapshotId()); + if (snapshotIdsToIterate.remove(entry.snapshot().getSnapshotId())) { + snapshotSet.add(new SnapshotInfo(entry)); + } } // then, look in the repository final Repository repository = repositoriesService.repository(repositoryName); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/package-info.java new file mode 100644 index 0000000000000..a00088759051c --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Get Snapshot transport handler. */ +package org.opensearch.action.admin.cluster.snapshots.get; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/package-info.java new file mode 100644 index 0000000000000..9bfd68dd93178 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Snapshot transport handlers. */ +package org.opensearch.action.admin.cluster.snapshots; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreClusterStateListener.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreClusterStateListener.java index e596348127faf..1f6e865b78ffd 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreClusterStateListener.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreClusterStateListener.java @@ -34,34 +34,47 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterStateListener; import org.opensearch.cluster.RestoreInProgress; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.snapshots.RestoreInfo; import org.opensearch.snapshots.RestoreService; +import java.util.Map; +import java.util.function.Function; + import static org.opensearch.snapshots.RestoreService.restoreInProgress; -public class RestoreClusterStateListener implements ClusterStateListener { +/** + * Transport listener for cluster state updates + * + * @opensearch.internal + */ +public class RestoreClusterStateListener implements ClusterStateListener { private static final Logger logger = LogManager.getLogger(RestoreClusterStateListener.class); private final ClusterService clusterService; private final String uuid; - private final ActionListener listener; + private final String restoreIdentifier; + private final ActionListener listener; + private final Function actionResponseFactory; private RestoreClusterStateListener( ClusterService clusterService, RestoreService.RestoreCompletionResponse response, - ActionListener listener + ActionListener listener, + Function actionResponseFactory ) { this.clusterService = clusterService; this.uuid = response.getUuid(); + this.restoreIdentifier = response.getSnapshot() != null ? response.getSnapshot().getSnapshotId().getName() : "remote_store"; this.listener = listener; + this.actionResponseFactory = actionResponseFactory; } @Override @@ -69,27 +82,27 @@ public void clusterChanged(ClusterChangedEvent changedEvent) { final RestoreInProgress.Entry prevEntry = restoreInProgress(changedEvent.previousState(), uuid); final RestoreInProgress.Entry newEntry = restoreInProgress(changedEvent.state(), uuid); if (prevEntry == null) { - // When there is a master failure after a restore has been started, this listener might not be registered - // on the current master and as such it might miss some intermediary cluster states due to batching. + // When there is a cluster-manager failure after a restore has been started, this listener might not be registered + // on the current cluster-manager and as such it might miss some intermediary cluster states due to batching. // Clean up listener in that case and acknowledge completion of restore operation to client. clusterService.removeListener(this); - listener.onResponse(new RestoreSnapshotResponse((RestoreInfo) null)); + listener.onResponse(actionResponseFactory.apply(null)); } else if (newEntry == null) { clusterService.removeListener(this); - ImmutableOpenMap shards = prevEntry.shards(); - assert prevEntry.state().completed() : "expected completed snapshot state but was " + prevEntry.state(); + final Map shards = prevEntry.shards(); + assert prevEntry.state().completed() : "expected completed snapshot/remote store restore state but was " + prevEntry.state(); assert RestoreService.completed(shards) : "expected all restore entries to be completed"; RestoreInfo ri = new RestoreInfo( - prevEntry.snapshot().getSnapshotId().getName(), + restoreIdentifier, prevEntry.indices(), shards.size(), shards.size() - RestoreService.failedShards(shards) ); - RestoreSnapshotResponse response = new RestoreSnapshotResponse(ri); - logger.debug("restore of [{}] completed", prevEntry.snapshot().getSnapshotId()); + T response = actionResponseFactory.apply(ri); + logger.debug("restore of [{}] completed", restoreIdentifier); listener.onResponse(response); } else { - // restore not completed yet, wait for next cluster state update + logger.debug("restore not completed yet, wait for next cluster state update"); } } @@ -97,11 +110,12 @@ public void clusterChanged(ClusterChangedEvent changedEvent) { * Creates a cluster state listener and registers it with the cluster service. The listener passed as a * parameter will be called when the restore is complete. */ - public static void createAndRegisterListener( + public static void createAndRegisterListener( ClusterService clusterService, RestoreService.RestoreCompletionResponse response, - ActionListener listener + ActionListener listener, + Function actionResponseFactory ) { - clusterService.addListener(new RestoreClusterStateListener(clusterService, response, listener)); + clusterService.addListener(new RestoreClusterStateListener(clusterService, response, listener, actionResponseFactory)); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotAction.java index e86ad12b8a231..1813e8214aeb0 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotAction.java @@ -36,6 +36,8 @@ /** * Restore snapshot action + * + * @opensearch.internal */ public class RestoreSnapshotAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java index ed45ba4f65941..ff32a68aa9522 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java @@ -33,18 +33,20 @@ package org.opensearch.action.admin.cluster.snapshots.restore; import org.opensearch.LegacyESVersion; +import org.opensearch.Version; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; @@ -54,18 +56,52 @@ import java.util.Objects; import static org.opensearch.action.ValidateActions.addValidationError; +import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; import static org.opensearch.common.settings.Settings.readSettingsFromStream; import static org.opensearch.common.settings.Settings.writeSettingsToStream; -import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; import static org.opensearch.common.xcontent.support.XContentMapValues.nodeBooleanValue; /** * Restore snapshot request + * + * @opensearch.internal */ -public class RestoreSnapshotRequest extends MasterNodeRequest implements ToXContentObject { +public class RestoreSnapshotRequest extends ClusterManagerNodeRequest implements ToXContentObject { private static final DeprecationLogger DEPRECATION_LOGGER = DeprecationLogger.getLogger(RestoreSnapshotRequest.class); + /** + * Enumeration of possible storage types + */ + public enum StorageType { + LOCAL("local"), + REMOTE_SNAPSHOT("remote_snapshot"); + + private final String text; + + StorageType(String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } + + private void toXContent(XContentBuilder builder) throws IOException { + builder.field("storage_type", text); + } + + private static StorageType fromString(String string) { + for (StorageType type : values()) { + if (type.text.equals(string)) { + return type; + } + } + throw new IllegalArgumentException("Invalid storage_type: " + string); + } + } + private String snapshot; private String repository; private String[] indices = Strings.EMPTY_ARRAY; @@ -78,6 +114,9 @@ public class RestoreSnapshotRequest extends MasterNodeRequest source) { } else { throw new IllegalArgumentException("malformed ignore_index_settings section, should be an array of strings"); } + } else if (name.equals("storage_type")) { + + if (entry.getValue() instanceof String) { + storageType(StorageType.fromString((String) entry.getValue())); + } else { + throw new IllegalArgumentException("malformed storage_type"); + } + + } else if (name.equals("source_remote_store_repository")) { + if (entry.getValue() instanceof String) { + setSourceRemoteStoreRepository((String) entry.getValue()); + } else { + throw new IllegalArgumentException("malformed source_remote_store_repository"); + } } else { if (IndicesOptions.isIndicesOptions(name) == false) { throw new IllegalArgumentException("Unknown parameter " + name); @@ -577,6 +677,12 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.value(ignoreIndexSetting); } builder.endArray(); + if (storageType != null) { + storageType.toXContent(builder); + } + if (sourceRemoteStoreRepository != null) { + builder.field("source_remote_store_repository", sourceRemoteStoreRepository); + } builder.endObject(); return builder; } @@ -591,7 +697,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RestoreSnapshotRequest that = (RestoreSnapshotRequest) o; - return waitForCompletion == that.waitForCompletion + boolean equals = waitForCompletion == that.waitForCompletion && includeGlobalState == that.includeGlobalState && partial == that.partial && includeAliases == that.includeAliases @@ -603,12 +709,16 @@ public boolean equals(Object o) { && Objects.equals(renameReplacement, that.renameReplacement) && Objects.equals(indexSettings, that.indexSettings) && Arrays.equals(ignoreIndexSettings, that.ignoreIndexSettings) - && Objects.equals(snapshotUuid, that.snapshotUuid); + && Objects.equals(snapshotUuid, that.snapshotUuid) + && Objects.equals(storageType, that.storageType) + && Objects.equals(sourceRemoteStoreRepository, that.sourceRemoteStoreRepository); + return equals; } @Override public int hashCode() { - int result = Objects.hash( + int result; + result = Objects.hash( snapshot, repository, indicesOptions, @@ -619,7 +729,9 @@ public int hashCode() { partial, includeAliases, indexSettings, - snapshotUuid + snapshotUuid, + storageType, + sourceRemoteStoreRepository ); result = 31 * result + Arrays.hashCode(indices); result = 31 * result + Arrays.hashCode(ignoreIndexSettings); @@ -628,6 +740,6 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java index 19fc86c9c4ace..d9cca536d1c41 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequestBuilder.java @@ -33,7 +33,7 @@ package org.opensearch.action.admin.cluster.snapshots.restore; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.XContentType; @@ -43,8 +43,10 @@ /** * Restore snapshot request builder + * + * @opensearch.internal */ -public class RestoreSnapshotRequestBuilder extends MasterNodeOperationRequestBuilder< +public class RestoreSnapshotRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< RestoreSnapshotRequest, RestoreSnapshotResponse, RestoreSnapshotRequestBuilder> { @@ -246,4 +248,20 @@ public RestoreSnapshotRequestBuilder setIgnoreIndexSettings(List ignoreI request.ignoreIndexSettings(ignoreIndexSettings); return this; } + + /** + * Sets the storage type + */ + public RestoreSnapshotRequestBuilder setStorageType(RestoreSnapshotRequest.StorageType storageType) { + request.storageType(storageType); + return this; + } + + /** + * Sets the source remote store repository name + */ + public RestoreSnapshotRequestBuilder setSourceRemoteStoreRepository(String repositoryName) { + request.setSourceRemoteStoreRepository(repositoryName); + return this; + } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotResponse.java index 324e3054fb1a3..f6e50c69da3dc 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotResponse.java @@ -32,26 +32,28 @@ package org.opensearch.action.admin.cluster.snapshots.restore; -import org.opensearch.action.ActionResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.snapshots.RestoreInfo; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Contains information about restores snapshot + * + * @opensearch.internal */ public class RestoreSnapshotResponse extends ActionResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java index c07f771081902..73e3070f7e44b 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/TransportRestoreSnapshotAction.java @@ -32,16 +32,16 @@ package org.opensearch.action.admin.cluster.snapshots.restore; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.snapshots.RestoreService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -50,8 +50,10 @@ /** * Transport action for restore snapshot operation + * + * @opensearch.internal */ -public class TransportRestoreSnapshotAction extends TransportMasterNodeAction { +public class TransportRestoreSnapshotAction extends TransportClusterManagerNodeAction { private final RestoreService restoreService; @Inject @@ -100,14 +102,19 @@ protected ClusterBlockException checkBlock(RestoreSnapshotRequest request, Clust } @Override - protected void masterOperation( + protected void clusterManagerOperation( final RestoreSnapshotRequest request, final ClusterState state, final ActionListener listener ) { restoreService.restoreSnapshot(request, ActionListener.delegateFailure(listener, (delegatedListener, restoreCompletionResponse) -> { if (restoreCompletionResponse.getRestoreInfo() == null && request.waitForCompletion()) { - RestoreClusterStateListener.createAndRegisterListener(clusterService, restoreCompletionResponse, delegatedListener); + RestoreClusterStateListener.createAndRegisterListener( + clusterService, + restoreCompletionResponse, + delegatedListener, + RestoreSnapshotResponse::new + ); } else { delegatedListener.onResponse(new RestoreSnapshotResponse(restoreCompletionResponse.getRestoreInfo())); } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/package-info.java new file mode 100644 index 0000000000000..69db3f77010ff --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Restore Snapshot transport handler. */ +package org.opensearch.action.admin.cluster.snapshots.restore; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStage.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStage.java index 59ead89c4a4ee..6e250962d1210 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStage.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStage.java @@ -32,6 +32,11 @@ package org.opensearch.action.admin.cluster.snapshots.status; +/** + * Stage for snapshotting an Index Shard + * + * @opensearch.internal + */ public enum SnapshotIndexShardStage { /** diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatus.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatus.java index 74ac12b951dc8..fc3ffd4977da5 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatus.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexShardStatus.java @@ -35,24 +35,29 @@ import org.opensearch.OpenSearchParseException; import org.opensearch.action.support.broadcast.BroadcastShardResponse; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; -import org.opensearch.index.Index; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import org.opensearch.index.snapshots.IndexShardSnapshotStatus; import java.io.IOException; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +/** + * Status for snapshotting an Index Shard + * + * @opensearch.internal + */ public class SnapshotIndexShardStatus extends BroadcastShardResponse implements ToXContentFragment { private SnapshotIndexShardStage stage = SnapshotIndexShardStage.INIT; @@ -161,6 +166,11 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(failure); } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String STAGE = "stage"; static final String REASON = "reason"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java index 63cbc0a18ffff..9c2db62c33bd0 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotIndexStatus.java @@ -32,13 +32,13 @@ package org.opensearch.action.admin.cluster.snapshots.status; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import java.io.IOException; import java.util.Collection; @@ -49,10 +49,12 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * Represents snapshot status of all shards in the index + * + * @opensearch.internal */ public class SnapshotIndexStatus implements Iterable, ToXContentFragment { @@ -122,6 +124,11 @@ public Iterator iterator() { return indexShards.values().iterator(); } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String SHARDS = "shards"; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotShardsStats.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotShardsStats.java index b945ba20afe80..ad514a13312ba 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotShardsStats.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotShardsStats.java @@ -32,20 +32,22 @@ package org.opensearch.action.admin.cluster.snapshots.status; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collection; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * Status of a snapshot shards + * + * @opensearch.internal */ public class SnapshotShardsStats implements ToXContentObject { @@ -139,6 +141,11 @@ public int getTotalShards() { return totalShards; } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String SHARDS_STATS = "shards_stats"; static final String INITIALIZING = "initializing"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotStats.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotStats.java index 3f348e47c4f4a..babdb7540a314 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotStats.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotStats.java @@ -32,20 +32,26 @@ package org.opensearch.action.admin.cluster.snapshots.status; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import java.io.IOException; +/** + * Stats for snapshots + * + * @opensearch.internal + */ public class SnapshotStats implements Writeable, ToXContentObject { private long startTime; @@ -165,6 +171,11 @@ public void writeTo(StreamOutput out) throws IOException { out.writeVLong(totalSize); } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String STATS = "stats"; @@ -346,7 +357,7 @@ void add(SnapshotStats stats, boolean updateTimestamps) { time = endTime - startTime; } assert time >= 0 : "Update with [" - + Strings.toString(stats) + + Strings.toString(MediaTypeRegistry.JSON, stats) + "][" + updateTimestamps + "] resulted in negative total time [" diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotStatus.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotStatus.java index 76764a39ff7e2..40d51f17fc914 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotStatus.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotStatus.java @@ -36,16 +36,17 @@ import org.opensearch.cluster.SnapshotsInProgress; import org.opensearch.cluster.SnapshotsInProgress.State; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.snapshots.Snapshot; import org.opensearch.snapshots.SnapshotId; @@ -62,11 +63,13 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Status of a snapshot + * + * @opensearch.internal */ public class SnapshotStatus implements ToXContentObject, Writeable { @@ -213,7 +216,7 @@ public void writeTo(StreamOutput out) throws IOException { @Override public String toString() { - return Strings.toString(this, true, false); + return Strings.toString(MediaTypeRegistry.JSON, this, true, false); } /** diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusAction.java index d4f398d543b81..c4fe06e985f6f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusAction.java @@ -36,6 +36,8 @@ /** * Snapshots status action + * + * @opensearch.internal */ public class SnapshotsStatusAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequest.java index f7e29cfef0bb4..22fca3c54c604 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequest.java @@ -33,10 +33,10 @@ package org.opensearch.action.admin.cluster.snapshots.status; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -44,8 +44,10 @@ /** * Get snapshot status request + * + * @opensearch.internal */ -public class SnapshotsStatusRequest extends MasterNodeRequest { +public class SnapshotsStatusRequest extends ClusterManagerNodeRequest { private String repository = "_all"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequestBuilder.java index 67c82182e38c9..55f156d4a470e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusRequestBuilder.java @@ -32,14 +32,16 @@ package org.opensearch.action.admin.cluster.snapshots.status; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.util.ArrayUtils; /** * Snapshots status request builder + * + * @opensearch.internal */ -public class SnapshotsStatusRequestBuilder extends MasterNodeOperationRequestBuilder< +public class SnapshotsStatusRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< SnapshotsStatusRequest, SnapshotsStatusResponse, SnapshotsStatusRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponse.java index 8519b1f6a3379..2f0e5e4b4686f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/SnapshotsStatusResponse.java @@ -32,24 +32,26 @@ package org.opensearch.action.admin.cluster.snapshots.status; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * Snapshot status response + * + * @opensearch.internal */ public class SnapshotsStatusResponse extends ActionResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/TransportNodesSnapshotsStatus.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/TransportNodesSnapshotsStatus.java index b5247141739b7..26b8be0f5bb82 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/TransportNodesSnapshotsStatus.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/TransportNodesSnapshotsStatus.java @@ -44,9 +44,9 @@ import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.snapshots.IndexShardSnapshotStatus; import org.opensearch.snapshots.Snapshot; import org.opensearch.snapshots.SnapshotShardsService; @@ -63,6 +63,8 @@ /** * Transport action that collects snapshot shard statuses from data nodes + * + * @opensearch.internal */ public class TransportNodesSnapshotsStatus extends TransportNodesAction< TransportNodesSnapshotsStatus.Request, @@ -143,6 +145,11 @@ protected NodeSnapshotStatus nodeOperation(NodeRequest request) { } } + /** + * Inner Request + * + * @opensearch.internal + */ public static class Request extends BaseNodesRequest { private Snapshot[] snapshots; @@ -169,6 +176,11 @@ public void writeTo(StreamOutput out) throws IOException { } } + /** + * Inner Node Snapshot Status + * + * @opensearch.internal + */ public static class NodesSnapshotStatus extends BaseNodesResponse { public NodesSnapshotStatus(StreamInput in) throws IOException { @@ -190,6 +202,11 @@ protected void writeNodesTo(StreamOutput out, List nodes) th } } + /** + * Inner Node Request + * + * @opensearch.internal + */ public static class NodeRequest extends BaseNodeRequest { private final List snapshots; @@ -210,6 +227,11 @@ public void writeTo(StreamOutput out) throws IOException { } } + /** + * Inner Node Shapshot Status + * + * @opensearch.internal + */ public static class NodeSnapshotStatus extends BaseNodeResponse { private final Map> status; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java index 33d4ac5d50347..7f6c039cf2ecc 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/TransportSnapshotsStatusAction.java @@ -32,15 +32,12 @@ package org.opensearch.action.admin.cluster.snapshots.status; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.StepListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.SnapshotsInProgress; import org.opensearch.cluster.block.ClusterBlockException; @@ -48,12 +45,13 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.util.CollectionUtils; import org.opensearch.common.util.set.Sets; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.snapshots.IndexShardSnapshotStatus; import org.opensearch.repositories.IndexId; import org.opensearch.repositories.RepositoriesService; @@ -84,7 +82,12 @@ import static java.util.Collections.unmodifiableMap; -public class TransportSnapshotsStatusAction extends TransportMasterNodeAction { +/** + * Transport action for accessing snapshot status + * + * @opensearch.internal + */ +public class TransportSnapshotsStatusAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportSnapshotsStatusAction.class); @@ -131,7 +134,7 @@ protected SnapshotsStatusResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation( + protected void clusterManagerOperation( final SnapshotsStatusRequest request, final ClusterState state, final ActionListener listener @@ -149,9 +152,9 @@ protected void masterOperation( Set nodesIds = new HashSet<>(); for (SnapshotsInProgress.Entry entry : currentSnapshots) { - for (ObjectCursor status : entry.shards().values()) { - if (status.value.nodeId() != null) { - nodesIds.add(status.value.nodeId()); + for (final SnapshotsInProgress.ShardSnapshotStatus status : entry.shards().values()) { + if (status.nodeId() != null) { + nodesIds.add(status.nodeId()); } } } @@ -164,7 +167,7 @@ protected void masterOperation( } transportNodesSnapshotsStatus.execute( new TransportNodesSnapshotsStatus.Request(nodesIds.toArray(Strings.EMPTY_ARRAY)).snapshots(snapshots) - .timeout(request.masterNodeTimeout()), + .timeout(request.clusterManagerNodeTimeout()), ActionListener.wrap( nodeSnapshotStatuses -> threadPool.generic() .execute( @@ -205,26 +208,26 @@ private void buildResponse( currentSnapshotNames.add(entry.snapshot().getSnapshotId().getName()); List shardStatusBuilder = new ArrayList<>(); Map indexIdLookup = null; - for (ObjectObjectCursor shardEntry : entry.shards()) { - SnapshotsInProgress.ShardSnapshotStatus status = shardEntry.value; + for (final Map.Entry shardEntry : entry.shards().entrySet()) { + SnapshotsInProgress.ShardSnapshotStatus status = shardEntry.getValue(); if (status.nodeId() != null) { // We should have information about this shard from the shard: TransportNodesSnapshotsStatus.NodeSnapshotStatus nodeStatus = nodeSnapshotStatusMap.get(status.nodeId()); if (nodeStatus != null) { Map shardStatues = nodeStatus.status().get(entry.snapshot()); if (shardStatues != null) { - SnapshotIndexShardStatus shardStatus = shardStatues.get(shardEntry.key); + SnapshotIndexShardStatus shardStatus = shardStatues.get(shardEntry.getKey()); if (shardStatus != null) { // We have full information about this shard if (shardStatus.getStage() == SnapshotIndexShardStage.DONE - && shardEntry.value.state() != SnapshotsInProgress.ShardState.SUCCESS) { + && shardEntry.getValue().state() != SnapshotsInProgress.ShardState.SUCCESS) { // Unlikely edge case: // Data node has finished snapshotting the shard but the cluster state has not yet been updated // to reflect this. We adjust the status to show up as snapshot metadata being written because - // technically if the data node failed before successfully reporting DONE state to master, then - // this shards state would jump to a failed state. + // technically if the data node failed before successfully reporting DONE state to cluster-manager, + // then this shards state would jump to a failed state. shardStatus = new SnapshotIndexShardStatus( - shardEntry.key, + shardEntry.getKey(), SnapshotIndexShardStage.FINALIZE, shardStatus.getStats(), shardStatus.getNodeId(), @@ -242,7 +245,7 @@ private void buildResponse( // We rebuild the information they would have provided from their in memory state from the cluster // state and the repository contents in the below logic final SnapshotIndexShardStage stage; - switch (shardEntry.value.state()) { + switch (shardEntry.getValue().state()) { case FAILED: case ABORTED: case MISSING: @@ -257,7 +260,7 @@ private void buildResponse( stage = SnapshotIndexShardStage.DONE; break; default: - throw new IllegalArgumentException("Unknown snapshot state " + shardEntry.value.state()); + throw new IllegalArgumentException("Unknown snapshot state " + shardEntry.getValue().state()); } final SnapshotIndexShardStatus shardStatus; if (stage == SnapshotIndexShardStage.DONE) { @@ -266,7 +269,7 @@ private void buildResponse( if (indexIdLookup == null) { indexIdLookup = entry.indices().stream().collect(Collectors.toMap(IndexId::getName, Function.identity())); } - final ShardId shardId = shardEntry.key; + final ShardId shardId = shardEntry.getKey(); shardStatus = new SnapshotIndexShardStatus( shardId, repositoriesService.repository(entry.repository()) @@ -278,7 +281,7 @@ private void buildResponse( .asCopy() ); } else { - shardStatus = new SnapshotIndexShardStatus(shardEntry.key, stage); + shardStatus = new SnapshotIndexShardStatus(shardEntry.getKey(), stage); } shardStatusBuilder.add(shardStatus); } @@ -406,7 +409,7 @@ private SnapshotInfo snapshot(SnapshotsInProgress snapshotsInProgress, String re /** * Returns status of shards currently finished snapshots *

    - * This method is executed on master node and it's complimentary to the + * This method is executed on cluster-manager node and it's complimentary to the * {@link SnapshotShardsService#currentSnapshotShards(Snapshot)} because it * returns similar information but for already finished snapshots. *

    diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/package-info.java new file mode 100644 index 0000000000000..3244beaf97961 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/snapshots/status/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Snapshot Status transport handlers. */ +package org.opensearch.action.admin.cluster.snapshots.status; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateAction.java index dff45b708fb13..ba3658d68296e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for obtaining cluster state + * + * @opensearch.internal + */ public class ClusterStateAction extends ActionType { public static final ClusterStateAction INSTANCE = new ClusterStateAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateRequest.java index 91e01aa74f8a5..7c937a1700db0 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateRequest.java @@ -35,15 +35,20 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; -public class ClusterStateRequest extends MasterNodeReadRequest implements IndicesRequest.Replaceable { +/** + * Transport request for obtaining cluster state + * + * @opensearch.internal + */ +public class ClusterStateRequest extends ClusterManagerNodeReadRequest implements IndicesRequest.Replaceable { public static final TimeValue DEFAULT_WAIT_FOR_NODE_TIMEOUT = TimeValue.timeValueMinutes(1); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateRequestBuilder.java index cf3eabfc4167d..b9bfeca9f7386 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateRequestBuilder.java @@ -33,11 +33,16 @@ package org.opensearch.action.admin.cluster.state; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.unit.TimeValue; -public class ClusterStateRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Transport request builder for obtaining cluster state + * + * @opensearch.internal + */ +public class ClusterStateRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< ClusterStateRequest, ClusterStateResponse, ClusterStateRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateResponse.java index d2f053137e446..a6bff09361b33 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/state/ClusterStateResponse.java @@ -33,19 +33,21 @@ package org.opensearch.action.admin.cluster.state; import org.opensearch.LegacyESVersion; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.unit.ByteSizeValue; import java.io.IOException; import java.util.Objects; /** * The response for getting the cluster state. + * + * @opensearch.internal */ public class ClusterStateResponse extends ActionResponse { @@ -102,32 +104,37 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(waitForTimedOut); } + @Override + public String toString() { + return "ClusterStateResponse{" + "clusterState=" + clusterState + '}'; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ClusterStateResponse response = (ClusterStateResponse) o; return waitForTimedOut == response.waitForTimedOut && Objects.equals(clusterName, response.clusterName) && - // Best effort. Only compare cluster state version and master node id, + // Best effort. Only compare cluster state version and cluster-manager node id, // because cluster state doesn't implement equals() Objects.equals(getVersion(clusterState), getVersion(response.clusterState)) - && Objects.equals(getMasterNodeId(clusterState), getMasterNodeId(response.clusterState)); + && Objects.equals(getClusterManagerNodeId(clusterState), getClusterManagerNodeId(response.clusterState)); } @Override public int hashCode() { - // Best effort. Only use cluster state version and master node id, + // Best effort. Only use cluster state version and cluster-manager node id, // because cluster state doesn't implement hashcode() - return Objects.hash(clusterName, getVersion(clusterState), getMasterNodeId(clusterState), waitForTimedOut); + return Objects.hash(clusterName, getVersion(clusterState), getClusterManagerNodeId(clusterState), waitForTimedOut); } - private static String getMasterNodeId(ClusterState clusterState) { + private static String getClusterManagerNodeId(ClusterState clusterState) { if (clusterState == null) { return null; } DiscoveryNodes nodes = clusterState.getNodes(); if (nodes != null) { - return nodes.getMasterNodeId(); + return nodes.getClusterManagerNodeId(); } else { return null; } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/state/TransportClusterStateAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/state/TransportClusterStateAction.java index 42497c5244167..4aaa7f1950823 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/state/TransportClusterStateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/state/TransportClusterStateAction.java @@ -32,15 +32,13 @@ package org.opensearch.action.admin.cluster.state; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; -import org.opensearch.cluster.NotMasterException; +import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; @@ -49,16 +47,23 @@ import org.opensearch.cluster.routing.RoutingTable; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.node.NodeClosedException; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.Map; import java.util.function.Predicate; -public class TransportClusterStateAction extends TransportMasterNodeReadAction { +/** + * Transport action for obtaining cluster state + * + * @opensearch.internal + */ +public class TransportClusterStateAction extends TransportClusterManagerNodeReadAction { private final Logger logger = LogManager.getLogger(getClass()); @@ -110,7 +115,7 @@ protected ClusterBlockException checkBlock(ClusterStateRequest request, ClusterS } @Override - protected void masterOperation( + protected void clusterManagerOperation( final ClusterStateRequest request, final ClusterState state, final ActionListener listener @@ -122,7 +127,7 @@ protected void masterOperation( final Predicate acceptableClusterStateOrNotMasterPredicate = request.local() ? acceptableClusterStatePredicate - : acceptableClusterStatePredicate.or(clusterState -> clusterState.nodes().isLocalNodeElectedMaster() == false); + : acceptableClusterStatePredicate.or(clusterState -> clusterState.nodes().isLocalNodeElectedClusterManager() == false); if (acceptableClusterStatePredicate.test(state)) { ActionListener.completeWith(listener, () -> buildResponse(request, state)); @@ -137,8 +142,8 @@ public void onNewClusterState(ClusterState newState) { ActionListener.completeWith(listener, () -> buildResponse(request, newState)); } else { listener.onFailure( - new NotMasterException( - "master stepped down waiting for metadata version " + request.waitForMetadataVersion() + new NotClusterManagerException( + "cluster-manager stepped down waiting for metadata version " + request.waitForMetadataVersion() ) ); } @@ -207,18 +212,18 @@ private ClusterStateResponse buildResponse(final ClusterStateRequest request, fi } // filter out metadata that shouldn't be returned by the API - for (ObjectObjectCursor custom : currentState.metadata().customs()) { - if (custom.value.context().contains(Metadata.XContentContext.API) == false) { - mdBuilder.removeCustom(custom.key); + for (final Map.Entry custom : currentState.metadata().customs().entrySet()) { + if (custom.getValue().context().contains(Metadata.XContentContext.API) == false) { + mdBuilder.removeCustom(custom.getKey()); } } } builder.metadata(mdBuilder); if (request.customs()) { - for (ObjectObjectCursor custom : currentState.customs()) { - if (custom.value.isPrivate() == false) { - builder.putCustom(custom.key, custom.value); + for (final Map.Entry custom : currentState.customs().entrySet()) { + if (custom.getValue().isPrivate() == false) { + builder.putCustom(custom.getKey(), custom.getValue()); } } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/state/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/state/package-info.java new file mode 100644 index 0000000000000..ab935d939e459 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/state/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster State transport handler. */ +package org.opensearch.action.admin.cluster.state; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/AnalysisStats.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/AnalysisStats.java index 3ae8d684ee870..3372448bdacae 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/stats/AnalysisStats.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/AnalysisStats.java @@ -35,14 +35,15 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; @@ -59,6 +60,8 @@ /** * Statistics about analysis usage. + * + * @opensearch.internal */ public final class AnalysisStats implements ToXContentFragment, Writeable { @@ -344,6 +347,6 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this, true, true); + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsAction.java index baeeec91bd8ce..ef20087a667df 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for obtaining cluster stats + * + * @opensearch.internal + */ public class ClusterStatsAction extends ActionType { public static final ClusterStatsAction INSTANCE = new ClusterStatsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsIndices.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsIndices.java index dd74c2ad66a9b..63ac76ae65783 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsIndices.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsIndices.java @@ -32,11 +32,9 @@ package org.opensearch.action.admin.cluster.stats; -import com.carrotsearch.hppc.ObjectObjectHashMap; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.action.admin.indices.stats.CommonStats; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.cache.query.QueryCacheStats; import org.opensearch.index.engine.SegmentsStats; import org.opensearch.index.fielddata.FieldDataStats; @@ -45,8 +43,15 @@ import org.opensearch.search.suggest.completion.CompletionStats; import java.io.IOException; +import java.util.HashMap; import java.util.List; +import java.util.Map; +/** + * Cluster Stats per index + * + * @opensearch.internal + */ public class ClusterStatsIndices implements ToXContentFragment { private int indexCount; @@ -61,7 +66,7 @@ public class ClusterStatsIndices implements ToXContentFragment { private MappingStats mappings; public ClusterStatsIndices(List nodeResponses, MappingStats mappingStats, AnalysisStats analysisStats) { - ObjectObjectHashMap countsPerIndex = new ObjectObjectHashMap<>(); + Map countsPerIndex = new HashMap<>(); this.docs = new DocsStats(); this.store = new StoreStats(); @@ -96,8 +101,8 @@ public ClusterStatsIndices(List nodeResponses, Mapping shards = new ShardStats(); indexCount = countsPerIndex.size(); - for (ObjectObjectCursor indexCountsCursor : countsPerIndex) { - shards.addIndexShardCount(indexCountsCursor.value); + for (final ShardStats indexCountsCursor : countsPerIndex.values()) { + shards.addIndexShardCount(indexCountsCursor); } this.mappings = mappingStats; @@ -144,6 +149,11 @@ public AnalysisStats getAnalysis() { return analysis; } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String COUNT = "count"; } @@ -167,6 +177,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Inner Shard Stats + * + * @opensearch.internal + */ public static class ShardStats implements ToXContentFragment { int indices; @@ -311,6 +326,11 @@ public void addIndexShardCount(ShardStats indexShardCount) { } } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String SHARDS = "shards"; static final String TOTAL = "total"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsNodeResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsNodeResponse.java index 7607a2ef70980..1b25bf84356d6 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsNodeResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsNodeResponse.java @@ -39,11 +39,16 @@ import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport action for obtaining cluster stats from node level + * + * @opensearch.internal + */ public class ClusterStatsNodeResponse extends BaseNodeResponse { private final NodeInfo nodeInfo; @@ -85,7 +90,7 @@ public NodeStats nodeStats() { } /** - * Cluster Health Status, only populated on master nodes. + * Cluster Health Status, only populated on cluster-manager nodes. */ @Nullable public ClusterHealthStatus clusterStatus() { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsNodes.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsNodes.java index fbca94780f827..5689596763cef 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsNodes.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsNodes.java @@ -32,22 +32,21 @@ package org.opensearch.action.admin.cluster.stats; -import com.carrotsearch.hppc.ObjectIntHashMap; -import com.carrotsearch.hppc.cursors.ObjectIntCursor; import org.opensearch.Version; import org.opensearch.action.admin.cluster.node.info.NodeInfo; import org.opensearch.action.admin.cluster.node.info.PluginsAndModules; import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodeRole; -import org.opensearch.common.Strings; +import org.opensearch.common.metrics.OperationStats; import org.opensearch.common.network.NetworkModule; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.discovery.DiscoveryModule; import org.opensearch.monitor.fs.FsInfo; import org.opensearch.monitor.jvm.JvmInfo; @@ -69,6 +68,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +/** + * Per Node Cluster Stats + * + * @opensearch.internal + */ public class ClusterStatsNodes implements ToXContentFragment { private final Counts counts; @@ -145,6 +149,11 @@ public Set getPlugins() { return plugins; } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String COUNT = "count"; static final String VERSIONS = "versions"; @@ -202,6 +211,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Inner Counts + * + * @opensearch.internal + */ public static class Counts implements ToXContentFragment { static final String COORDINATING_ONLY = "coordinating_only"; @@ -246,6 +260,11 @@ public Map getRoles() { return roles; } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String TOTAL = "total"; } @@ -260,19 +279,24 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } } + /** + * Inner Operating System Stats + * + * @opensearch.internal + */ public static class OsStats implements ToXContentFragment { final int availableProcessors; final int allocatedProcessors; - final ObjectIntHashMap names; - final ObjectIntHashMap prettyNames; + final Map names; + final Map prettyNames; final org.opensearch.monitor.os.OsStats.Mem mem; /** * Build the stats from information about each node. */ private OsStats(List nodeInfos, List nodeStatsList) { - this.names = new ObjectIntHashMap<>(); - this.prettyNames = new ObjectIntHashMap<>(); + final Map names = new HashMap<>(nodeInfos.size()); + final Map prettyNames = new HashMap<>(nodeInfos.size()); int availableProcessors = 0; int allocatedProcessors = 0; for (NodeInfo nodeInfo : nodeInfos) { @@ -280,12 +304,14 @@ private OsStats(List nodeInfos, List nodeStatsList) { allocatedProcessors += nodeInfo.getInfo(OsInfo.class).getAllocatedProcessors(); if (nodeInfo.getInfo(OsInfo.class).getName() != null) { - names.addTo(nodeInfo.getInfo(OsInfo.class).getName(), 1); + names.merge(nodeInfo.getInfo(OsInfo.class).getName(), 1, Integer::sum); } if (nodeInfo.getInfo(OsInfo.class).getPrettyName() != null) { - prettyNames.addTo(nodeInfo.getInfo(OsInfo.class).getPrettyName(), 1); + prettyNames.merge(nodeInfo.getInfo(OsInfo.class).getPrettyName(), 1, Integer::sum); } } + this.names = Collections.unmodifiableMap(names); + this.prettyNames = Collections.unmodifiableMap(prettyNames); this.availableProcessors = availableProcessors; this.allocatedProcessors = allocatedProcessors; @@ -318,6 +344,11 @@ public org.opensearch.monitor.os.OsStats.Mem getMem() { return mem; } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String AVAILABLE_PROCESSORS = "available_processors"; static final String ALLOCATED_PROCESSORS = "allocated_processors"; @@ -334,11 +365,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(Fields.ALLOCATED_PROCESSORS, allocatedProcessors); builder.startArray(Fields.NAMES); { - for (ObjectIntCursor name : names) { + for (final Map.Entry name : names.entrySet()) { builder.startObject(); { - builder.field(Fields.NAME, name.key); - builder.field(Fields.COUNT, name.value); + builder.field(Fields.NAME, name.getKey()); + builder.field(Fields.COUNT, name.getValue()); } builder.endObject(); } @@ -346,11 +377,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endArray(); builder.startArray(Fields.PRETTY_NAMES); { - for (final ObjectIntCursor prettyName : prettyNames) { + for (final Map.Entry prettyName : prettyNames.entrySet()) { builder.startObject(); { - builder.field(Fields.PRETTY_NAME, prettyName.key); - builder.field(Fields.COUNT, prettyName.value); + builder.field(Fields.PRETTY_NAME, prettyName.getKey()); + builder.field(Fields.COUNT, prettyName.getValue()); } builder.endObject(); } @@ -361,6 +392,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } } + /** + * Inner Process Stats + * + * @opensearch.internal + */ public static class ProcessStats implements ToXContentFragment { final int count; @@ -431,6 +467,11 @@ public long getMinOpenFileDescriptors() { return minOpenFileDescriptors; } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String CPU = "cpu"; static final String PERCENT = "percent"; @@ -454,9 +495,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } } + /** + * Inner JVM Stats + * + * @opensearch.internal + */ public static class JvmStats implements ToXContentFragment { - private final ObjectIntHashMap versions; + private final Map versions; private final long threads; private final long maxUptime; private final long heapUsed; @@ -466,15 +512,15 @@ public static class JvmStats implements ToXContentFragment { * Build from lists of information about each node. */ private JvmStats(List nodeInfos, List nodeStatsList) { - this.versions = new ObjectIntHashMap<>(); + final Map versions = new HashMap<>(nodeInfos.size()); long threads = 0; long maxUptime = 0; long heapMax = 0; long heapUsed = 0; for (NodeInfo nodeInfo : nodeInfos) { - versions.addTo(new JvmVersion(nodeInfo.getInfo(JvmInfo.class)), 1); + versions.merge(new JvmVersion(nodeInfo.getInfo(JvmInfo.class)), 1, Integer::sum); } - + this.versions = Collections.unmodifiableMap(versions); for (NodeStats nodeStats : nodeStatsList) { org.opensearch.monitor.jvm.JvmStats js = nodeStats.getJvm(); if (js == null) { @@ -495,7 +541,7 @@ private JvmStats(List nodeInfos, List nodeStatsList) { this.heapMax = heapMax; } - public ObjectIntHashMap getVersions() { + public Map getVersions() { return versions; } @@ -527,6 +573,11 @@ public ByteSizeValue getHeapMax() { return new ByteSizeValue(heapMax); } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String VERSIONS = "versions"; static final String VERSION = "version"; @@ -550,15 +601,15 @@ static final class Fields { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.humanReadableField(Fields.MAX_UPTIME_IN_MILLIS, Fields.MAX_UPTIME, new TimeValue(maxUptime)); builder.startArray(Fields.VERSIONS); - for (ObjectIntCursor v : versions) { + for (final Map.Entry v : versions.entrySet()) { builder.startObject(); - builder.field(Fields.VERSION, v.key.version); - builder.field(Fields.VM_NAME, v.key.vmName); - builder.field(Fields.VM_VERSION, v.key.vmVersion); - builder.field(Fields.VM_VENDOR, v.key.vmVendor); - builder.field(Fields.BUNDLED_JDK, v.key.bundledJdk); - builder.field(Fields.USING_BUNDLED_JDK, v.key.usingBundledJdk); - builder.field(Fields.COUNT, v.value); + builder.field(Fields.VERSION, v.getKey().version); + builder.field(Fields.VM_NAME, v.getKey().vmName); + builder.field(Fields.VM_VERSION, v.getKey().vmVersion); + builder.field(Fields.VM_VENDOR, v.getKey().vmVendor); + builder.field(Fields.BUNDLED_JDK, v.getKey().bundledJdk); + builder.field(Fields.USING_BUNDLED_JDK, v.getKey().usingBundledJdk); + builder.field(Fields.COUNT, v.getValue()); builder.endObject(); } builder.endArray(); @@ -572,6 +623,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } } + /** + * Inner JVM Version + * + * @opensearch.internal + */ public static class JvmVersion { String version; String vmName; @@ -609,6 +665,11 @@ public int hashCode() { } } + /** + * Inner Network Types + * + * @opensearch.internal + */ static class NetworkTypes implements ToXContentFragment { private final Map transportTypes; @@ -652,6 +713,11 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa } + /** + * Inner Discovery Types + * + * @opensearch.internal + */ static class DiscoveryTypes implements ToXContentFragment { private final Map discoveryTypes; @@ -677,6 +743,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } } + /** + * Inner Packaging Types + * + * @opensearch.internal + */ static class PackagingTypes implements ToXContentFragment { private final Map packagingTypes; @@ -709,6 +780,11 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa } + /** + * Inner Ingest Stats + * + * @opensearch.internal + */ static class IngestStats implements ToXContentFragment { final int pipelineCount; @@ -725,18 +801,18 @@ static class IngestStats implements ToXContentFragment { pipelineIds.add(processorStats.getKey()); for (org.opensearch.ingest.IngestStats.ProcessorStat stat : processorStats.getValue()) { stats.compute(stat.getType(), (k, v) -> { - org.opensearch.ingest.IngestStats.Stats nodeIngestStats = stat.getStats(); + OperationStats nodeIngestStats = stat.getStats(); if (v == null) { return new long[] { - nodeIngestStats.getIngestCount(), - nodeIngestStats.getIngestFailedCount(), - nodeIngestStats.getIngestCurrent(), - nodeIngestStats.getIngestTimeInMillis() }; + nodeIngestStats.getCount(), + nodeIngestStats.getFailedCount(), + nodeIngestStats.getCurrent(), + nodeIngestStats.getTotalTimeInMillis() }; } else { - v[0] += nodeIngestStats.getIngestCount(); - v[1] += nodeIngestStats.getIngestFailedCount(); - v[2] += nodeIngestStats.getIngestCurrent(); - v[3] += nodeIngestStats.getIngestTimeInMillis(); + v[0] += nodeIngestStats.getCount(); + v[1] += nodeIngestStats.getFailedCount(); + v[2] += nodeIngestStats.getCurrent(); + v[3] += nodeIngestStats.getTotalTimeInMillis(); return v; } }); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsRequest.java index ed658ae23999a..dc472c10f550b 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsRequest.java @@ -33,13 +33,15 @@ package org.opensearch.action.admin.cluster.stats; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * A request to get cluster level stats. + * + * @opensearch.internal */ public class ClusterStatsRequest extends BaseNodesRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsRequestBuilder.java index 33c346a493986..aaf5e3aeffeb8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.support.nodes.NodesOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder for obtaining cluster stats + * + * @opensearch.internal + */ public class ClusterStatsRequestBuilder extends NodesOperationRequestBuilder< ClusterStatsRequest, ClusterStatsResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsResponse.java index 1470f252756a5..d9751c819d407 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/ClusterStatsResponse.java @@ -38,17 +38,21 @@ import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.health.ClusterHealthStatus; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.List; import java.util.Locale; +/** + * Transport response for obtaining cluster stats + * + * @opensearch.internal + */ public class ClusterStatsResponse extends BaseNodesResponse implements ToXContentFragment { final ClusterStatsNodes nodesStats; @@ -60,7 +64,7 @@ public class ClusterStatsResponse extends BaseNodesResponse shardsStats = new ArrayList<>(); @@ -192,7 +203,7 @@ protected ClusterStatsNodeResponse nodeOperation(ClusterStatsNodeRequest nodeReq } ClusterHealthStatus clusterStatus = null; - if (clusterService.state().nodes().isLocalNodeElectedMaster()) { + if (clusterService.state().nodes().isLocalNodeElectedClusterManager()) { clusterStatus = new ClusterStateHealth(clusterService.state()).getStatus(); } @@ -206,6 +217,11 @@ protected ClusterStatsNodeResponse nodeOperation(ClusterStatsNodeRequest nodeReq } + /** + * Inner Cluster Stats Node Request + * + * @opensearch.internal + */ public static class ClusterStatsNodeRequest extends BaseNodeRequest { ClusterStatsRequest request; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/stats/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/stats/package-info.java new file mode 100644 index 0000000000000..b10c976bbddf6 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/stats/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Stats transport handlers. */ +package org.opensearch.action.admin.cluster.stats; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptAction.java index ab20c4052938d..3645ef21d2e12 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action for deleting stored scripts + * + * @opensearch.internal + */ public class DeleteStoredScriptAction extends ActionType { public static final DeleteStoredScriptAction INSTANCE = new DeleteStoredScriptAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequest.java index eda9aa053854f..7d92162015950 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequest.java @@ -34,13 +34,18 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport request for deleting stored scripts + * + * @opensearch.internal + */ public class DeleteStoredScriptRequest extends AcknowledgedRequest { private String id; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequestBuilder.java index d45b0b02d9d83..34e0d429f2098 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/DeleteStoredScriptRequestBuilder.java @@ -36,6 +36,11 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder for deleting stored scripts + * + * @opensearch.internal + */ public class DeleteStoredScriptRequestBuilder extends AcknowledgedRequestBuilder< DeleteStoredScriptRequest, AcknowledgedResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextAction.java index 9aebd60c8997b..df33aa8081849 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for getting stored scripts + * + * @opensearch.internal + */ public class GetScriptContextAction extends ActionType { public static final GetScriptContextAction INSTANCE = new GetScriptContextAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextRequest.java index 90ec611eeae9d..07d8530eb7ef6 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextRequest.java @@ -33,10 +33,15 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Transport context for getting stored scripts + * + * @opensearch.internal + */ public class GetScriptContextRequest extends ActionRequest { public GetScriptContextRequest() { super(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java index 7bfcface8b75c..b91d636b5ec76 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptContextResponse.java @@ -32,15 +32,15 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.script.ScriptContextInfo; import java.io.IOException; @@ -54,6 +54,11 @@ import java.util.function.Function; import java.util.stream.Collectors; +/** + * Transport context response for getting stored scripts + * + * @opensearch.internal + */ public class GetScriptContextResponse extends ActionResponse implements StatusToXContentObject { private static final ParseField CONTEXTS = new ParseField("contexts"); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageAction.java index 90cc05ba983de..b2da146a7ccbe 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for getting script language + * + * @opensearch.internal + */ public class GetScriptLanguageAction extends ActionType { public static final GetScriptLanguageAction INSTANCE = new GetScriptLanguageAction(); public static final String NAME = "cluster:admin/script_language/get"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageRequest.java index 0a68af3acd5d7..a81ca39595dfb 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageRequest.java @@ -34,10 +34,15 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Transport request for getting script language + * + * @opensearch.internal + */ public class GetScriptLanguageRequest extends ActionRequest { public GetScriptLanguageRequest() { super(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageResponse.java index d9d6fa9e650de..eabac2eb94a02 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetScriptLanguageResponse.java @@ -32,19 +32,24 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.script.ScriptLanguagesInfo; import java.io.IOException; import java.util.Objects; +/** + * Transport response for getting script language + * + * @opensearch.internal + */ public class GetScriptLanguageResponse extends ActionResponse implements StatusToXContentObject, Writeable { public final ScriptLanguagesInfo info; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptAction.java index 40d887987ae40..f4cb82d68456a 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for getting stored script + * + * @opensearch.internal + */ public class GetStoredScriptAction extends ActionType { public static final GetStoredScriptAction INSTANCE = new GetStoredScriptAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java index afecdc09d991d..70384b5fb648e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequest.java @@ -33,15 +33,20 @@ package org.opensearch.action.admin.cluster.storedscripts; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import static org.opensearch.action.ValidateActions.addValidationError; -public class GetStoredScriptRequest extends MasterNodeReadRequest { +/** + * Transport request for getting stored script + * + * @opensearch.internal + */ +public class GetStoredScriptRequest extends ClusterManagerNodeReadRequest { protected String id; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequestBuilder.java index cbae829e76c04..ae969963be62f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptRequestBuilder.java @@ -32,10 +32,15 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -public class GetStoredScriptRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Transport request builder for getting stored script + * + * @opensearch.internal + */ +public class GetStoredScriptRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< GetStoredScriptRequest, GetStoredScriptResponse, GetStoredScriptRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java index 7739f87db74f9..cf5a3ec44e560 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/GetStoredScriptResponse.java @@ -32,24 +32,29 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.script.StoredScriptSource; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +/** + * Transport response for getting stored script + * + * @opensearch.internal + */ public class GetStoredScriptResponse extends ActionResponse implements StatusToXContentObject { public static final ParseField _ID_PARSE_FIELD = new ParseField("_id"); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptAction.java index 75a2dc12d81f6..2845d895a69e8 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action for putting stored script + * + * @opensearch.internal + */ public class PutStoredScriptAction extends ActionType { public static final PutStoredScriptAction INSTANCE = new PutStoredScriptAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java index 4665354918c8f..f45bee955da02 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java @@ -32,15 +32,18 @@ package org.opensearch.action.admin.cluster.storedscripts; +import org.opensearch.Version; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.script.StoredScriptSource; import java.io.IOException; @@ -48,19 +51,28 @@ import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport request for putting stored script + * + * @opensearch.internal + */ public class PutStoredScriptRequest extends AcknowledgedRequest implements ToXContentFragment { private String id; private String context; private BytesReference content; - private XContentType xContentType; + private MediaType mediaType; private StoredScriptSource source; public PutStoredScriptRequest(StreamInput in) throws IOException { super(in); id = in.readOptionalString(); content = in.readBytesReference(); - xContentType = in.readEnum(XContentType.class); + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType = in.readMediaType(); + } else { + mediaType = in.readEnum(XContentType.class); + } context = in.readOptionalString(); source = new StoredScriptSource(in); } @@ -69,12 +81,12 @@ public PutStoredScriptRequest() { super(); } - public PutStoredScriptRequest(String id, String context, BytesReference content, XContentType xContentType, StoredScriptSource source) { + public PutStoredScriptRequest(String id, String context, BytesReference content, MediaType mediaType, StoredScriptSource source) { super(); this.id = id; this.context = context; this.content = content; - this.xContentType = Objects.requireNonNull(xContentType); + this.mediaType = Objects.requireNonNull(mediaType); this.source = source; } @@ -117,8 +129,8 @@ public BytesReference content() { return content; } - public XContentType xContentType() { - return xContentType; + public MediaType mediaType() { + return mediaType; } public StoredScriptSource source() { @@ -128,10 +140,10 @@ public StoredScriptSource source() { /** * Set the script source and the content type of the bytes. */ - public PutStoredScriptRequest content(BytesReference content, XContentType xContentType) { + public PutStoredScriptRequest content(BytesReference content, MediaType mediaType) { this.content = content; - this.xContentType = Objects.requireNonNull(xContentType); - this.source = StoredScriptSource.parse(content, xContentType); + this.mediaType = Objects.requireNonNull(mediaType); + this.source = StoredScriptSource.parse(content, mediaType); return this; } @@ -140,17 +152,21 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeOptionalString(id); out.writeBytesReference(content); - out.writeEnum(xContentType); + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType.writeTo(out); + } else { + out.writeEnum((XContentType) mediaType); + } out.writeOptionalString(context); source.writeTo(out); } @Override public String toString() { - String source = "_na_"; + String source = Strings.UNKNOWN_UUID_VALUE; try { - source = XContentHelper.convertToJson(content, false, xContentType); + source = XContentHelper.convertToJson(content, false, mediaType); } catch (Exception e) { // ignore } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequestBuilder.java index 414ed3d273b75..2a06cd23c10b6 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/PutStoredScriptRequestBuilder.java @@ -35,9 +35,14 @@ import org.opensearch.action.support.master.AcknowledgedRequestBuilder; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +/** + * Transport request builder for putting stored script + * + * @opensearch.internal + */ public class PutStoredScriptRequestBuilder extends AcknowledgedRequestBuilder< PutStoredScriptRequest, AcknowledgedResponse, @@ -55,8 +60,8 @@ public PutStoredScriptRequestBuilder setId(String id) { /** * Set the source of the script along with the content type of the source */ - public PutStoredScriptRequestBuilder setContent(BytesReference source, XContentType xContentType) { - request.content(source, xContentType); + public PutStoredScriptRequestBuilder setContent(BytesReference source, MediaType mediaType) { + request.content(source, mediaType); return this; } } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java index 0b5f9d3040add..b0863939fd04c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportDeleteStoredScriptAction.java @@ -32,26 +32,34 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.script.ScriptService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportDeleteStoredScriptAction extends TransportMasterNodeAction { +/** + * Transport action for deleting stored script + * + * @opensearch.internal + */ +public class TransportDeleteStoredScriptAction extends TransportClusterManagerNodeAction { private final ScriptService scriptService; + private final ClusterManagerTaskThrottler.ThrottlingKey deleteScriptTaskKey; @Inject public TransportDeleteStoredScriptAction( @@ -72,6 +80,8 @@ public TransportDeleteStoredScriptAction( indexNameExpressionResolver ); this.scriptService = scriptService; + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + deleteScriptTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.DELETE_SCRIPT_KEY, true); } @Override @@ -85,9 +95,12 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(DeleteStoredScriptRequest request, ClusterState state, ActionListener listener) - throws Exception { - scriptService.deleteStoredScript(clusterService, request, listener); + protected void clusterManagerOperation( + DeleteStoredScriptRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + scriptService.deleteStoredScript(clusterService, request, deleteScriptTaskKey, listener); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java index 0bcd9a71109ed..0dd997a47bc7e 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptContextAction.java @@ -31,10 +31,10 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; import org.opensearch.script.ScriptContextInfo; import org.opensearch.script.ScriptService; import org.opensearch.tasks.Task; @@ -42,6 +42,11 @@ import java.util.Set; +/** + * Transport action for getting script context + * + * @opensearch.internal + */ public class TransportGetScriptContextAction extends HandledTransportAction { private final ScriptService scriptService; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptLanguageAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptLanguageAction.java index 255889b63dbd8..f41a22bcd0a4c 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptLanguageAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetScriptLanguageAction.java @@ -32,14 +32,19 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; import org.opensearch.script.ScriptService; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; +/** + * Transport action for getting script language + * + * @opensearch.internal + */ public class TransportGetScriptLanguageAction extends HandledTransportAction { private final ScriptService scriptService; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java index 4a87f6795da50..db1f1edde2812 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportGetStoredScriptAction.java @@ -32,23 +32,28 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.script.ScriptService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportGetStoredScriptAction extends TransportMasterNodeReadAction { +/** + * Transport action for getting stored script + * + * @opensearch.internal + */ +public class TransportGetStoredScriptAction extends TransportClusterManagerNodeReadAction { private final ScriptService scriptService; @@ -84,8 +89,11 @@ protected GetStoredScriptResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(GetStoredScriptRequest request, ClusterState state, ActionListener listener) - throws Exception { + protected void clusterManagerOperation( + GetStoredScriptRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { listener.onResponse(new GetStoredScriptResponse(request.id(), scriptService.getStoredScript(state, request))); } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java index a8288fc0147c2..61ee641b4764d 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/TransportPutStoredScriptAction.java @@ -32,26 +32,34 @@ package org.opensearch.action.admin.cluster.storedscripts; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.script.ScriptService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportPutStoredScriptAction extends TransportMasterNodeAction { +/** + * Transport action for putting stored script + * + * @opensearch.internal + */ +public class TransportPutStoredScriptAction extends TransportClusterManagerNodeAction { private final ScriptService scriptService; + private final ClusterManagerTaskThrottler.ThrottlingKey putScriptTaskKey; @Inject public TransportPutStoredScriptAction( @@ -72,6 +80,8 @@ public TransportPutStoredScriptAction( indexNameExpressionResolver ); this.scriptService = scriptService; + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + putScriptTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.PUT_SCRIPT_KEY, true); } @Override @@ -85,9 +95,12 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(PutStoredScriptRequest request, ClusterState state, ActionListener listener) - throws Exception { - scriptService.putStoredScript(clusterService, request, listener); + protected void clusterManagerOperation( + PutStoredScriptRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + scriptService.putStoredScript(clusterService, request, putScriptTaskKey, listener); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/package-info.java new file mode 100644 index 0000000000000..8f2196ded7c23 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/storedscripts/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Stored Scripts transport handlers. */ +package org.opensearch.action.admin.cluster.storedscripts; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksAction.java index 851b88f1e639a..9f3e8720d1f56 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for getting pending cluster tasks + * + * @opensearch.internal + */ public class PendingClusterTasksAction extends ActionType { public static final PendingClusterTasksAction INSTANCE = new PendingClusterTasksAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksRequest.java index 463481fe5d0e5..83e8b93b32e0f 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksRequest.java @@ -33,12 +33,17 @@ package org.opensearch.action.admin.cluster.tasks; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; -public class PendingClusterTasksRequest extends MasterNodeReadRequest { +/** + * Transport request for getting pending cluster tasks + * + * @opensearch.internal + */ +public class PendingClusterTasksRequest extends ClusterManagerNodeReadRequest { public PendingClusterTasksRequest() {} diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksRequestBuilder.java index 8f3bccdf5e55f..b5e77f291a701 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksRequestBuilder.java @@ -32,10 +32,15 @@ package org.opensearch.action.admin.cluster.tasks; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -public class PendingClusterTasksRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Transport request builder for getting pending cluster tasks + * + * @opensearch.internal + */ +public class PendingClusterTasksRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< PendingClusterTasksRequest, PendingClusterTasksResponse, PendingClusterTasksRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksResponse.java b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksResponse.java index 5b355f9f00166..118d2cf3065e6 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/PendingClusterTasksResponse.java @@ -32,17 +32,22 @@ package org.opensearch.action.admin.cluster.tasks; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.service.PendingClusterTask; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Iterator; import java.util.List; +/** + * Transport response for getting pending cluster tasks + * + * @opensearch.internal + */ public class PendingClusterTasksResponse extends ActionResponse implements Iterable, ToXContentObject { private final List pendingTasks; @@ -108,6 +113,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String TASKS = "tasks"; diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java index 932722eae8a80..5d5053cc80738 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/TransportPendingClusterTasksAction.java @@ -34,23 +34,28 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.cluster.service.PendingClusterTask; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; import java.util.List; -public class TransportPendingClusterTasksAction extends TransportMasterNodeReadAction< +/** + * Transport action for getting pending cluster tasks + * + * @opensearch.internal + */ +public class TransportPendingClusterTasksAction extends TransportClusterManagerNodeReadAction< PendingClusterTasksRequest, PendingClusterTasksResponse> { @@ -95,13 +100,13 @@ protected ClusterBlockException checkBlock(PendingClusterTasksRequest request, C } @Override - protected void masterOperation( + protected void clusterManagerOperation( PendingClusterTasksRequest request, ClusterState state, ActionListener listener ) { logger.trace("fetching pending tasks from cluster service"); - final List pendingTasks = clusterService.getMasterService().pendingTasks(); + final List pendingTasks = clusterService.getClusterManagerService().pendingTasks(); logger.trace("done fetching pending tasks from cluster service"); listener.onResponse(new PendingClusterTasksResponse(pendingTasks)); } diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/tasks/package-info.java b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/package-info.java new file mode 100644 index 0000000000000..4169a343aaabf --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/cluster/tasks/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Pending Cluster Tasks transport handlers. */ +package org.opensearch.action.admin.cluster.tasks; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/Alias.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/Alias.java index d8853bcede3ef..fe7b57f779b26 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/Alias.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/Alias.java @@ -35,18 +35,17 @@ import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchGenerationException; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.QueryBuilder; import java.io.IOException; @@ -55,6 +54,8 @@ /** * Represents an alias, to be associated with an index + * + * @opensearch.internal */ public class Alias implements Writeable, ToXContentFragment { @@ -130,9 +131,9 @@ public Alias filter(Map filter) { return this; } try { - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(MediaTypeRegistry.JSON); builder.map(filter); - this.filter = Strings.toString(builder); + this.filter = builder.toString(); return this; } catch (IOException e) { throw new OpenSearchGenerationException("Failed to generate [" + filter + "]", e); @@ -148,10 +149,10 @@ public Alias filter(QueryBuilder filterBuilder) { return this; } try { - XContentBuilder builder = XContentFactory.jsonBuilder(); + XContentBuilder builder = MediaTypeRegistry.JSON.contentBuilder(); filterBuilder.toXContent(builder, ToXContent.EMPTY_PARAMS); builder.close(); - this.filter = Strings.toString(builder); + this.filter = builder.toString(); return this; } catch (IOException e) { throw new OpenSearchGenerationException("Failed to build json for alias request", e); @@ -283,7 +284,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (filter != null) { try (InputStream stream = new BytesArray(filter).streamInput()) { - builder.rawField(FILTER.getPreferredName(), stream, XContentType.JSON); + builder.rawField(FILTER.getPreferredName(), stream, MediaTypeRegistry.JSON); } } @@ -310,7 +311,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesAction.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesAction.java index 33566820d9762..4d735e984c34e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action for listing index aliases + * + * @opensearch.internal + */ public class IndicesAliasesAction extends ActionType { public static final IndicesAliasesAction INSTANCE = new IndicesAliasesAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesClusterStateUpdateRequest.java index f89f8a06727c1..4a445ca92d2dd 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesClusterStateUpdateRequest.java @@ -38,6 +38,8 @@ /** * Cluster state update request that allows to add or remove aliases + * + * @opensearch.internal */ public class IndicesAliasesClusterStateUpdateRequest extends ClusterStateUpdateRequest { private final List actions; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequest.java index 9481e6287c878..4a58b7bec7250 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequest.java @@ -39,22 +39,22 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; import org.opensearch.cluster.metadata.AliasAction; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ObjectParser.ValueType; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ObjectParser.ValueType; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.QueryBuilder; import java.io.IOException; @@ -67,11 +67,13 @@ import java.util.function.Supplier; import static org.opensearch.action.ValidateActions.addValidationError; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; -import static org.opensearch.common.xcontent.ObjectParser.fromList; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ObjectParser.fromList; /** * A request to add/remove aliases for one or more indices. + * + * @opensearch.internal */ public class IndicesAliasesRequest extends AcknowledgedRequest implements ToXContentObject { @@ -97,6 +99,8 @@ public IndicesAliasesRequest() {} /** * Request to take one or more actions on one or more indexes and alias combinations. + * + * @opensearch.internal */ public static class AliasActions implements AliasesRequest, Writeable, ToXContentObject { @@ -116,6 +120,11 @@ public static class AliasActions implements AliasesRequest, Writeable, ToXConten private static final ParseField REMOVE = new ParseField("remove"); private static final ParseField REMOVE_INDEX = new ParseField("remove_index"); + /** + * The type of request. + * + * @opensearch.internal + */ public enum Type { ADD((byte) 0, AliasActions.ADD), REMOVE((byte) 1, AliasActions.REMOVE), @@ -440,9 +449,9 @@ public AliasActions filter(Map filter) { return this; } try { - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + XContentBuilder builder = MediaTypeRegistry.JSON.contentBuilder(); builder.map(filter); - this.filter = Strings.toString(builder); + this.filter = builder.toString(); return this; } catch (IOException e) { throw new OpenSearchGenerationException("Failed to generate [" + filter + "]", e); @@ -458,7 +467,7 @@ public AliasActions filter(QueryBuilder filter) { XContentBuilder builder = XContentFactory.jsonBuilder(); filter.toXContent(builder, ToXContent.EMPTY_PARAMS); builder.close(); - this.filter = Strings.toString(builder); + this.filter = builder.toString(); return this; } catch (IOException e) { throw new OpenSearchGenerationException("Failed to build json for alias request", e); @@ -544,7 +553,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } if (false == Strings.isEmpty(filter)) { try (InputStream stream = new BytesArray(filter).streamInput()) { - builder.rawField(FILTER.getPreferredName(), stream, XContentType.JSON); + builder.rawField(FILTER.getPreferredName(), stream, MediaTypeRegistry.JSON); } } if (false == Strings.isEmpty(routing)) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequestBuilder.java index f74363db3dcfd..13c57cc781925 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/IndicesAliasesRequestBuilder.java @@ -41,6 +41,8 @@ /** * Builder for request to modify many aliases at once. + * + * @opensearch.internal */ public class IndicesAliasesRequestBuilder extends AcknowledgedRequestBuilder< IndicesAliasesRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/TransportIndicesAliasesAction.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/TransportIndicesAliasesAction.java index 82eb3aed7da16..77cc0662a1c96 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/TransportIndicesAliasesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/TransportIndicesAliasesAction.java @@ -32,14 +32,12 @@ package org.opensearch.action.admin.indices.alias; -import com.carrotsearch.hppc.cursors.ObjectCursor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.RequestValidators; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.block.ClusterBlockException; @@ -51,10 +49,10 @@ import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.MetadataIndexAliasesService; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; import org.opensearch.rest.action.admin.indices.AliasesNotFoundException; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -64,6 +62,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -72,8 +71,10 @@ /** * Add/remove aliases action + * + * @opensearch.internal */ -public class TransportIndicesAliasesAction extends TransportMasterNodeAction { +public class TransportIndicesAliasesAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportIndicesAliasesAction.class); @@ -124,7 +125,7 @@ protected ClusterBlockException checkBlock(IndicesAliasesRequest request, Cluste } @Override - protected void masterOperation( + protected void clusterManagerOperation( final IndicesAliasesRequest request, final ClusterState state, final ActionListener listener @@ -198,7 +199,7 @@ protected void masterOperation( request.aliasActions().clear(); IndicesAliasesClusterStateUpdateRequest updateRequest = new IndicesAliasesClusterStateUpdateRequest(unmodifiableList(finalActions)) .ackTimeout(request.timeout()) - .masterNodeTimeout(request.masterNodeTimeout()); + .masterNodeTimeout(request.clusterManagerNodeTimeout()); indexAliasesService.indicesAliases(updateRequest, new ActionListener() { @Override @@ -218,10 +219,10 @@ private static String[] concreteAliases(IndicesAliasesRequest.AliasActions actio if (action.expandAliasesWildcards()) { // for DELETE we expand the aliases String[] indexAsArray = { concreteIndex }; - ImmutableOpenMap> aliasMetadata = metadata.findAliases(action, indexAsArray); + final Map> aliasMetadata = metadata.findAliases(action, indexAsArray); List finalAliases = new ArrayList<>(); - for (ObjectCursor> curAliases : aliasMetadata.values()) { - for (AliasMetadata aliasMeta : curAliases.value) { + for (final List curAliases : aliasMetadata.values()) { + for (AliasMetadata aliasMeta : curAliases) { finalAliases.add(aliasMeta.alias()); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistAction.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistAction.java deleted file mode 100644 index 964648ab04705..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistAction.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.alias.exists; - -import org.opensearch.action.ActionType; - -public class AliasesExistAction extends ActionType { - - public static final AliasesExistAction INSTANCE = new AliasesExistAction(); - public static final String NAME = "indices:admin/aliases/exists"; - - private AliasesExistAction() { - super(NAME, AliasesExistResponse::new); - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistRequestBuilder.java deleted file mode 100644 index 20949360ddab8..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistRequestBuilder.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.alias.exists; - -import org.opensearch.action.admin.indices.alias.get.BaseAliasesRequestBuilder; -import org.opensearch.client.OpenSearchClient; - -public class AliasesExistRequestBuilder extends BaseAliasesRequestBuilder { - - public AliasesExistRequestBuilder(OpenSearchClient client, AliasesExistAction action, String... aliases) { - super(client, action, aliases); - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistResponse.java deleted file mode 100644 index 447f10e5ab34a..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/AliasesExistResponse.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.alias.exists; - -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; - -import java.io.IOException; - -public class AliasesExistResponse extends ActionResponse { - - private boolean exists; - - public AliasesExistResponse(boolean exists) { - this.exists = exists; - } - - AliasesExistResponse(StreamInput in) throws IOException { - super(in); - exists = in.readBoolean(); - } - - public boolean exists() { - return exists; - } - - public boolean isExists() { - return exists(); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeBoolean(exists); - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/TransportAliasesExistAction.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/TransportAliasesExistAction.java deleted file mode 100644 index 8a86a27561527..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/exists/TransportAliasesExistAction.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.alias.exists; - -import org.opensearch.action.ActionListener; -import org.opensearch.action.admin.indices.alias.get.GetAliasesRequest; -import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.block.ClusterBlockException; -import org.opensearch.cluster.block.ClusterBlockLevel; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.TransportService; - -import java.io.IOException; - -public class TransportAliasesExistAction extends TransportMasterNodeReadAction { - - @Inject - public TransportAliasesExistAction( - TransportService transportService, - ClusterService clusterService, - ThreadPool threadPool, - ActionFilters actionFilters, - IndexNameExpressionResolver indexNameExpressionResolver - ) { - super( - AliasesExistAction.NAME, - transportService, - clusterService, - threadPool, - actionFilters, - GetAliasesRequest::new, - indexNameExpressionResolver - ); - } - - @Override - protected String executor() { - // very lightweight operation, no need to fork - return ThreadPool.Names.SAME; - } - - @Override - protected AliasesExistResponse read(StreamInput in) throws IOException { - return new AliasesExistResponse(in); - } - - @Override - protected ClusterBlockException checkBlock(GetAliasesRequest request, ClusterState state) { - return state.blocks() - .indicesBlockedException(ClusterBlockLevel.METADATA_READ, indexNameExpressionResolver.concreteIndexNames(state, request)); - } - - @Override - protected void masterOperation(GetAliasesRequest request, ClusterState state, ActionListener listener) { - String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request); - boolean result = state.metadata().hasAliases(request.aliases(), concreteIndices); - listener.onResponse(new AliasesExistResponse(result)); - } - -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/BaseAliasesRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/BaseAliasesRequestBuilder.java index 3d8fa05fb7658..7cda041b7d9cf 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/BaseAliasesRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/BaseAliasesRequestBuilder.java @@ -33,15 +33,20 @@ package org.opensearch.action.admin.indices.alias.get; import org.opensearch.action.ActionType; -import org.opensearch.action.ActionResponse; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.util.ArrayUtils; +import org.opensearch.core.action.ActionResponse; +/** + * Base request builder for listing index aliases + * + * @opensearch.internal + */ public abstract class BaseAliasesRequestBuilder< Response extends ActionResponse, - Builder extends BaseAliasesRequestBuilder> extends MasterNodeReadOperationRequestBuilder< + Builder extends BaseAliasesRequestBuilder> extends ClusterManagerNodeReadOperationRequestBuilder< GetAliasesRequest, Response, Builder> { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesAction.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesAction.java index 198fa328111b8..d45f988330010 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for listing index aliases + * + * @opensearch.internal + */ public class GetAliasesAction extends ActionType { public static final GetAliasesAction INSTANCE = new GetAliasesAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesRequest.java index 661af82d7020d..28894baa629b3 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesRequest.java @@ -34,14 +34,19 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.AliasesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; -public class GetAliasesRequest extends MasterNodeReadRequest implements AliasesRequest { +/** + * Transport request for listing index aliases + * + * @opensearch.internal + */ +public class GetAliasesRequest extends ClusterManagerNodeReadRequest implements AliasesRequest { private String[] indices = Strings.EMPTY_ARRAY; private String[] aliases = Strings.EMPTY_ARRAY; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesRequestBuilder.java index f7902ef73fbd5..aecbd689a647c 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesRequestBuilder.java @@ -34,6 +34,11 @@ import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder for listing index aliases + * + * @opensearch.internal + */ public class GetAliasesRequestBuilder extends BaseAliasesRequestBuilder { public GetAliasesRequestBuilder(OpenSearchClient client, GetAliasesAction action, String... aliases) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesResponse.java index 8901a067bb020..e50540e6b9aea 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/GetAliasesResponse.java @@ -32,30 +32,36 @@ package org.opensearch.action.admin.indices.alias.get; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.AliasMetadata; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; +/** + * Transport response for listing index aliases + * + * @opensearch.internal + */ public class GetAliasesResponse extends ActionResponse { - private final ImmutableOpenMap> aliases; + private final Map> aliases; - public GetAliasesResponse(ImmutableOpenMap> aliases) { - this.aliases = aliases; + public GetAliasesResponse(final Map> aliases) { + this.aliases = Collections.unmodifiableMap(aliases); } public GetAliasesResponse(StreamInput in) throws IOException { super(in); - aliases = in.readImmutableMap(StreamInput::readString, i -> i.readList(AliasMetadata::new)); + aliases = in.readMap(StreamInput::readString, i -> i.readList(AliasMetadata::new)); } - public ImmutableOpenMap> getAliases() { + public Map> getAliases() { return aliases; } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/TransportGetAliasesAction.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/TransportGetAliasesAction.java index fa26560e4fedf..3aca9c1976f16 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/TransportGetAliasesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/TransportGetAliasesAction.java @@ -31,9 +31,8 @@ package org.opensearch.action.admin.indices.alias.get; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -41,11 +40,11 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.indices.SystemIndices; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -53,13 +52,20 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -public class TransportGetAliasesAction extends TransportMasterNodeReadAction { +/** + * Transport action for listing index aliases + * + * @opensearch.internal + */ +public class TransportGetAliasesAction extends TransportClusterManagerNodeReadAction { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(TransportGetAliasesAction.class); private final SystemIndices systemIndices; @@ -107,7 +113,7 @@ protected GetAliasesResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(GetAliasesRequest request, ClusterState state, ActionListener listener) { + protected void clusterManagerOperation(GetAliasesRequest request, ClusterState state, ActionListener listener) { String[] concreteIndices; // Switch to a context which will drop any deprecation warnings, because there may be indices resolved here which are not // returned in the final response. We'll add warnings back later if necessary in checkSystemIndexAccess. @@ -115,7 +121,7 @@ protected void masterOperation(GetAliasesRequest request, ClusterState state, Ac concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request); } final boolean systemIndexAccessAllowed = indexNameExpressionResolver.isSystemIndexAccessAllowed(); - ImmutableOpenMap> aliases = state.metadata().findAliases(request, concreteIndices); + final Map> aliases = state.metadata().findAliases(request, concreteIndices); listener.onResponse( new GetAliasesResponse(postProcess(request, concreteIndices, aliases, state, systemIndexAccessAllowed, systemIndices)) ); @@ -124,23 +130,23 @@ protected void masterOperation(GetAliasesRequest request, ClusterState state, Ac /** * Fills alias result with empty entries for requested indices when no specific aliases were requested. */ - static ImmutableOpenMap> postProcess( + static Map> postProcess( GetAliasesRequest request, String[] concreteIndices, - ImmutableOpenMap> aliases, + final Map> aliases, ClusterState state, boolean systemIndexAccessAllowed, SystemIndices systemIndices ) { boolean noAliasesSpecified = request.getOriginalAliases() == null || request.getOriginalAliases().length == 0; - ImmutableOpenMap.Builder> mapBuilder = ImmutableOpenMap.builder(aliases); + final Map> mapBuilder = new HashMap<>(aliases); for (String index : concreteIndices) { if (aliases.get(index) == null && noAliasesSpecified) { List previous = mapBuilder.put(index, Collections.emptyList()); assert previous == null; } } - final ImmutableOpenMap> finalResponse = mapBuilder.build(); + final Map> finalResponse = Collections.unmodifiableMap(mapBuilder); if (systemIndexAccessAllowed == false) { checkSystemIndexAccess(request, systemIndices, state, finalResponse); } @@ -151,10 +157,10 @@ private static void checkSystemIndexAccess( GetAliasesRequest request, SystemIndices systemIndices, ClusterState state, - ImmutableOpenMap> aliasesMap + final Map> aliasesMap ) { Set systemIndicesNames = new HashSet<>(); - for (Iterator it = aliasesMap.keysIt(); it.hasNext();) { + for (Iterator it = aliasesMap.keySet().iterator(); it.hasNext();) { String indexName = it.next(); IndexMetadata index = state.metadata().index(indexName); if (index != null && index.isSystem()) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/package-info.java new file mode 100644 index 0000000000000..e4ea33da95743 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Get Alias transport handler. */ +package org.opensearch.action.admin.indices.alias.get; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/alias/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/alias/package-info.java new file mode 100644 index 0000000000000..3630775073c68 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/alias/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Alias transport handlers. */ +package org.opensearch.action.admin.indices.alias; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/analyze/AnalyzeAction.java b/server/src/main/java/org/opensearch/action/admin/indices/analyze/AnalyzeAction.java index 592575d9b6019..6514e84b4c2bd 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/analyze/AnalyzeAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/analyze/AnalyzeAction.java @@ -34,19 +34,20 @@ import org.opensearch.LegacyESVersion; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.support.single.shard.SingleShardRequest; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.analysis.NameOrDefinition; import java.io.IOException; @@ -59,6 +60,11 @@ import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport action for analyzing text + * + * @opensearch.internal + */ public class AnalyzeAction extends ActionType { public static final AnalyzeAction INSTANCE = new AnalyzeAction(); @@ -71,6 +77,8 @@ private AnalyzeAction() { /** * A request to analyze a text associated with a specific index. Allow to provide * the actual analyzer name to perform the analysis with. + * + * @opensearch.internal */ public static class Request extends SingleShardRequest { @@ -298,6 +306,11 @@ public static Request fromXContent(XContentParser parser, String index) throws I } + /** + * Inner Response + * + * @opensearch.internal + */ public static class Response extends ActionResponse implements ToXContentObject { private final DetailAnalyzeResponse detail; @@ -397,9 +410,14 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this, true, true); + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); } + /** + * Inner Fields used for creating XContent and parsing + * + * @opensearch.internal + */ static final class Fields { static final String TOKENS = "tokens"; @@ -407,6 +425,11 @@ static final class Fields { } } + /** + * Inner Analyze Token + * + * @opensearch.internal + */ public static class AnalyzeToken implements Writeable, ToXContentObject { private final String term; private final int startOffset; @@ -540,6 +563,11 @@ public void writeTo(StreamOutput out) throws IOException { } } + /** + * Inner Detail Analyze Response + * + * @opensearch.internal + */ public static class DetailAnalyzeResponse implements Writeable, ToXContentFragment { private final boolean customAnalyzer; @@ -702,6 +730,11 @@ public void writeTo(StreamOutput out) throws IOException { } } + /** + * Inner Analyze Token List + * + * @opensearch.internal + */ public static class AnalyzeTokenList implements Writeable, ToXContentObject { private final String name; private final AnalyzeToken[] tokens; @@ -794,6 +827,11 @@ public void writeTo(StreamOutput out) throws IOException { } } + /** + * Inner character filtered text + * + * @opensearch.internal + */ public static class CharFilteredText implements Writeable, ToXContentObject { private final String name; private final String[] texts; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/analyze/AnalyzeRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/analyze/AnalyzeRequestBuilder.java index 167feb7935be2..a7f21b2af16fc 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/analyze/AnalyzeRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/analyze/AnalyzeRequestBuilder.java @@ -36,6 +36,11 @@ import java.util.Map; +/** + * Transport request builder for analyzing text + * + * @opensearch.internal + */ public class AnalyzeRequestBuilder extends SingleShardOperationRequestBuilder< AnalyzeAction.Request, AnalyzeAction.Response, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/analyze/TransportAnalyzeAction.java b/server/src/main/java/org/opensearch/action/admin/indices/analyze/TransportAnalyzeAction.java index cf578af8dbacb..8dc55e580b9ec 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/analyze/TransportAnalyzeAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/analyze/TransportAnalyzeAction.java @@ -49,9 +49,10 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Settings; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AnalysisRegistry; @@ -64,7 +65,6 @@ import org.opensearch.index.analysis.TokenizerFactory; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.mapper.StringFieldType; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -82,6 +82,8 @@ /** * Transport action used to execute analyze requests + * + * @opensearch.internal */ public class TransportAnalyzeAction extends TransportSingleShardAction { @@ -465,6 +467,11 @@ private static String writeCharStream(Reader input) { return sb.toString(); } + /** + * Inner Token Counter + * + * @opensearch.internal + */ private static class TokenCounter { private int tokenCount = 0; private int maxTokenCount; @@ -486,6 +493,11 @@ private void increment() { } } + /** + * Inner Token List Creator + * + * @opensearch.internal + */ private static class TokenListCreator { int lastPosition = -1; int lastOffset = 0; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/analyze/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/analyze/package-info.java new file mode 100644 index 0000000000000..2de987da0f204 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/analyze/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Indices analyze transport handler. */ +package org.opensearch.action.admin.indices.analyze; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheAction.java b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheAction.java index b21b52f063691..f605f042a05ac 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for clearing cache + * + * @opensearch.internal + */ public class ClearIndicesCacheAction extends ActionType { public static final ClearIndicesCacheAction INSTANCE = new ClearIndicesCacheAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheRequest.java index a9eafc7defaba..3b69a0980e97f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheRequest.java @@ -32,18 +32,25 @@ package org.opensearch.action.admin.indices.cache.clear; +import org.opensearch.Version; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport request for clearing cache + * + * @opensearch.internal + */ public class ClearIndicesCacheRequest extends BroadcastRequest { private boolean queryCache = false; private boolean fieldDataCache = false; private boolean requestCache = false; + private boolean fileCache = false; private String[] fields = Strings.EMPTY_ARRAY; public ClearIndicesCacheRequest(StreamInput in) throws IOException { @@ -52,6 +59,9 @@ public ClearIndicesCacheRequest(StreamInput in) throws IOException { fieldDataCache = in.readBoolean(); fields = in.readStringArray(); requestCache = in.readBoolean(); + if (in.getVersion().onOrAfter(Version.V_2_8_0)) { + fileCache = in.readBoolean(); + } } public ClearIndicesCacheRequest(String... indices) { @@ -85,6 +95,15 @@ public ClearIndicesCacheRequest fieldDataCache(boolean fieldDataCache) { return this; } + public boolean fileCache() { + return this.fileCache; + } + + public ClearIndicesCacheRequest fileCache(boolean fileCache) { + this.fileCache = fileCache; + return this; + } + public ClearIndicesCacheRequest fields(String... fields) { this.fields = fields == null ? Strings.EMPTY_ARRAY : fields; return this; @@ -101,5 +120,8 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(fieldDataCache); out.writeStringArrayNullable(fields); out.writeBoolean(requestCache); + if (out.getVersion().onOrAfter(Version.V_2_8_0)) { + out.writeBoolean(fileCache); + } } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheRequestBuilder.java index c7365a0e22e83..e0513e77a5aa5 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.support.broadcast.BroadcastOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder for clearing cache + * + * @opensearch.internal + */ public class ClearIndicesCacheRequestBuilder extends BroadcastOperationRequestBuilder< ClearIndicesCacheRequest, ClearIndicesCacheResponse, @@ -54,6 +59,11 @@ public ClearIndicesCacheRequestBuilder setRequestCache(boolean requestCache) { return this; } + public ClearIndicesCacheRequestBuilder setFileCache(boolean fileCache) { + request.fileCache(fileCache); + return this; + } + public ClearIndicesCacheRequestBuilder setFieldDataCache(boolean fieldDataCache) { request.fieldDataCache(fieldDataCache); return this; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheResponse.java index 58d8767697528..0c6c5ca27b24d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/ClearIndicesCacheResponse.java @@ -32,11 +32,11 @@ package org.opensearch.action.admin.indices.cache.clear; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; @@ -44,6 +44,8 @@ /** * The response of a clear cache action. + * + * @opensearch.internal */ public class ClearIndicesCacheResponse extends BroadcastResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/TransportClearIndicesCacheAction.java b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/TransportClearIndicesCacheAction.java index baa51e023e75a..acc6a6c14c5fd 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/TransportClearIndicesCacheAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/TransportClearIndicesCacheAction.java @@ -33,7 +33,6 @@ package org.opensearch.action.admin.indices.cache.clear; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -43,16 +42,23 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.index.shard.ShardPath; import org.opensearch.indices.IndicesService; +import org.opensearch.node.Node; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; +import java.nio.file.Path; import java.util.List; +import java.util.function.Predicate; /** * Indices clear cache action. + * + * @opensearch.internal */ public class TransportClearIndicesCacheAction extends TransportBroadcastByNodeAction< ClearIndicesCacheRequest, @@ -61,11 +67,14 @@ public class TransportClearIndicesCacheAction extends TransportBroadcastByNodeAc private final IndicesService indicesService; + private final Node node; + @Inject public TransportClearIndicesCacheAction( ClusterService clusterService, TransportService transportService, IndicesService indicesService, + Node node, ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver ) { @@ -80,6 +89,7 @@ public TransportClearIndicesCacheAction( false ); this.indicesService = indicesService; + this.node = node; } @Override @@ -107,6 +117,14 @@ protected ClearIndicesCacheRequest readRequestFrom(StreamInput in) throws IOExce @Override protected EmptyResult shardOperation(ClearIndicesCacheRequest request, ShardRouting shardRouting) { + if (request.fileCache()) { + if (node.fileCache() != null) { + ShardPath shardPath = ShardPath.loadFileCachePath(node.getNodeEnvironment(), shardRouting.shardId()); + Predicate pathStartsWithShardPathPredicate = path -> path.startsWith(shardPath.getDataPath()); + node.fileCache().prune(pathStartsWithShardPathPredicate); + } + } + indicesService.clearIndexShardCache( shardRouting.shardId(), request.queryCache(), @@ -127,11 +145,11 @@ protected ShardsIterator shards(ClusterState clusterState, ClearIndicesCacheRequ @Override protected ClusterBlockException checkGlobalBlock(ClusterState state, ClearIndicesCacheRequest request) { - return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); } @Override protected ClusterBlockException checkRequestBlock(ClusterState state, ClearIndicesCacheRequest request, String[] concreteIndices) { - return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_WRITE, concreteIndices); + return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_READ, concreteIndices); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/package-info.java new file mode 100644 index 0000000000000..41e8437bc15c1 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/cache/clear/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Clear indices cache transport handler. */ +package org.opensearch.action.admin.indices.cache.clear; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/cache/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/cache/package-info.java new file mode 100644 index 0000000000000..367c663bdc637 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/cache/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Indices Cache transport handlers. */ +package org.opensearch.action.admin.indices.cache; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexAction.java index ae8347a5ce812..2dea3e415ae0d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for closing an index + * + * @opensearch.internal + */ public class CloseIndexAction extends ActionType { public static final CloseIndexAction INSTANCE = new CloseIndexAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexClusterStateUpdateRequest.java index b94d080a331be..4b446f9d1ad2a 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexClusterStateUpdateRequest.java @@ -36,6 +36,8 @@ /** * Cluster state update request that allows to close one or more indices + * + * @opensearch.internal */ public class CloseIndexClusterStateUpdateRequest extends IndicesClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexRequest.java index 3fa1c8f066135..2c3d350824904 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexRequest.java @@ -38,9 +38,9 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.util.CollectionUtils; import java.io.IOException; @@ -48,6 +48,8 @@ /** * A request to close an index. + * + * @opensearch.internal */ public class CloseIndexRequest extends AcknowledgedRequest implements IndicesRequest.Replaceable { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexRequestBuilder.java index 66dc07e35fbaa..b3b53a0043c70 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexRequestBuilder.java @@ -39,6 +39,8 @@ /** * Builder for close index request + * + * @opensearch.internal */ public class CloseIndexRequestBuilder extends AcknowledgedRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexResponse.java index 998ffc4af7028..ab05399cf7c35 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/close/CloseIndexResponse.java @@ -33,17 +33,18 @@ import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.util.CollectionUtils; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.Index; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.List; @@ -52,6 +53,11 @@ import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; +/** + * Transport response for closing an index + * + * @opensearch.internal + */ public class CloseIndexResponse extends ShardsAcknowledgedResponse { private final List indices; @@ -97,9 +103,14 @@ protected void addCustomFields(final XContentBuilder builder, final Params param @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * Inner index result + * + * @opensearch.internal + */ public static class IndexResult implements Writeable, ToXContentFragment { private final Index index; @@ -191,10 +202,15 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } + /** + * Shard Result from Close Index Response + * + * @opensearch.internal + */ public static class ShardResult implements Writeable, ToXContentFragment { private final int id; @@ -245,9 +261,14 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * Inner Failure if something goes wrong + * + * @opensearch.internal + */ public static class Failure extends DefaultShardOperationFailedException { private @Nullable String nodeId; @@ -286,7 +307,7 @@ public XContentBuilder innerToXContent(final XContentBuilder builder, final Para @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } static Failure readFailure(final StreamInput in) throws IOException { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/close/TransportCloseIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/close/TransportCloseIndexAction.java index 13525a2a3b23e..cde2ef63df02d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/close/TransportCloseIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/close/TransportCloseIndexAction.java @@ -35,10 +35,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.DestructiveOperations; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -46,12 +45,13 @@ import org.opensearch.cluster.metadata.MetadataIndexStateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -61,8 +61,10 @@ /** * Close index action + * + * @opensearch.internal */ -public class TransportCloseIndexAction extends TransportMasterNodeAction { +public class TransportCloseIndexAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportCloseIndexAction.class); @@ -138,7 +140,7 @@ protected ClusterBlockException checkBlock(CloseIndexRequest request, ClusterSta } @Override - protected void masterOperation( + protected void clusterManagerOperation( final CloseIndexRequest request, final ClusterState state, final ActionListener listener @@ -147,7 +149,7 @@ protected void masterOperation( } @Override - protected void masterOperation( + protected void clusterManagerOperation( final Task task, final CloseIndexRequest request, final ClusterState state, @@ -161,7 +163,10 @@ protected void masterOperation( final CloseIndexClusterStateUpdateRequest closeRequest = new CloseIndexClusterStateUpdateRequest(task.getId()).ackTimeout( request.timeout() - ).masterNodeTimeout(request.masterNodeTimeout()).waitForActiveShards(request.waitForActiveShards()).indices(concreteIndices); + ) + .masterNodeTimeout(request.clusterManagerNodeTimeout()) + .waitForActiveShards(request.waitForActiveShards()) + .indices(concreteIndices); indexStateService.closeIndices(closeRequest, ActionListener.delegateResponse(listener, (delegatedListener, t) -> { logger.debug(() -> new ParameterizedMessage("failed to close indices [{}]", (Object) concreteIndices), t); delegatedListener.onFailure(t); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/close/TransportVerifyShardBeforeCloseAction.java b/server/src/main/java/org/opensearch/action/admin/indices/close/TransportVerifyShardBeforeCloseAction.java index 8baf1f5d851ad..c869c4c055598 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/close/TransportVerifyShardBeforeCloseAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/close/TransportVerifyShardBeforeCloseAction.java @@ -34,7 +34,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.LegacyESVersion; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.flush.FlushRequest; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.replication.ReplicationOperation; @@ -46,20 +45,26 @@ import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; -import org.opensearch.tasks.TaskId; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; import java.util.Objects; +/** + * Transport action for verifying a shard before closing an index + * + * @opensearch.internal + */ public class TransportVerifyShardBeforeCloseAction extends TransportReplicationAction< TransportVerifyShardBeforeCloseAction.ShardRequest, TransportVerifyShardBeforeCloseAction.ShardRequest, @@ -186,6 +191,11 @@ public void markShardCopyAsStaleIfNeeded( } } + /** + * Shard Request for verifying shards before closing + * + * @opensearch.internal + */ public static class ShardRequest extends ReplicationRequest { private final ClusterBlock clusterBlock; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/close/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/close/package-info.java new file mode 100644 index 0000000000000..fb2d419124461 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/close/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Close indices transport handlers. */ +package org.opensearch.action.admin.indices.close; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/create/AutoCreateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/create/AutoCreateAction.java index 23cb728540ab3..02dd2de803f58 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/create/AutoCreateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/create/AutoCreateAction.java @@ -31,29 +31,31 @@ package org.opensearch.action.admin.indices.create; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionType; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.ActiveShardsObserver; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.AckedClusterStateUpdateTask; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.ComposableIndexTemplate; import org.opensearch.cluster.metadata.ComposableIndexTemplate.DataStreamTemplate; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.MetadataCreateDataStreamService; import org.opensearch.cluster.metadata.MetadataCreateDataStreamService.CreateDataStreamClusterStateUpdateRequest; import org.opensearch.cluster.metadata.MetadataCreateIndexService; import org.opensearch.cluster.metadata.MetadataIndexTemplateService; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -62,6 +64,8 @@ /** * Api that auto creates an index or data stream that originate from requests that write into an index that doesn't yet exist. + * + * @opensearch.internal */ public final class AutoCreateAction extends ActionType { @@ -72,11 +76,17 @@ private AutoCreateAction() { super(NAME, CreateIndexResponse::new); } - public static final class TransportAction extends TransportMasterNodeAction { + /** + * Transport Action for Auto Create + * + * @opensearch.internal + */ + public static final class TransportAction extends TransportClusterManagerNodeAction { private final ActiveShardsObserver activeShardsObserver; private final MetadataCreateIndexService createIndexService; private final MetadataCreateDataStreamService metadataCreateDataStreamService; + private final ClusterManagerTaskThrottler.ThrottlingKey autoCreateTaskKey; @Inject public TransportAction( @@ -92,6 +102,9 @@ public TransportAction( this.activeShardsObserver = new ActiveShardsObserver(clusterService, threadPool); this.createIndexService = createIndexService; this.metadataCreateDataStreamService = metadataCreateDataStreamService; + + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + autoCreateTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.AUTO_CREATE_KEY, true); } @Override @@ -105,7 +118,11 @@ protected CreateIndexResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(CreateIndexRequest request, ClusterState state, ActionListener finalListener) { + protected void clusterManagerOperation( + CreateIndexRequest request, + ClusterState state, + ActionListener finalListener + ) { AtomicReference indexNameRef = new AtomicReference<>(); ActionListener listener = ActionListener.wrap(response -> { String indexName = indexNameRef.get(); @@ -115,7 +132,9 @@ protected void masterOperation(CreateIndexRequest request, ClusterState state, A new String[] { indexName }, ActiveShardCount.DEFAULT, request.timeout(), - shardsAcked -> { finalListener.onResponse(new CreateIndexResponse(true, shardsAcked, indexName)); }, + shardsAcked -> { + finalListener.onResponse(new CreateIndexResponse(true, shardsAcked, indexName)); + }, finalListener::onFailure ); } else { @@ -131,13 +150,18 @@ protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { return new ClusterStateUpdateResponse(acknowledged); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return autoCreateTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) throws Exception { DataStreamTemplate dataStreamTemplate = resolveAutoCreateDataStream(request, currentState.metadata()); if (dataStreamTemplate != null) { CreateDataStreamClusterStateUpdateRequest createRequest = new CreateDataStreamClusterStateUpdateRequest( request.index(), - request.masterNodeTimeout(), + request.clusterManagerNodeTimeout(), request.timeout() ); ClusterState clusterState = metadataCreateDataStreamService.createDataStream(createRequest, currentState); @@ -150,7 +174,7 @@ public ClusterState execute(ClusterState currentState) throws Exception { request.cause(), indexName, request.index() - ).ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout()); + ).ackTimeout(request.timeout()).masterNodeTimeout(request.clusterManagerNodeTimeout()); return createIndexService.applyCreateIndexRequest(currentState, updateRequest, false); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexAction.java index 87bbc093fce43..220620790fa4d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for creating an index + * + * @opensearch.internal + */ public class CreateIndexAction extends ActionType { public static final CreateIndexAction INSTANCE = new CreateIndexAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java index 5ca6fb4226b64..ad45e5346f9fa 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexClusterStateUpdateRequest.java @@ -39,13 +39,15 @@ import org.opensearch.cluster.block.ClusterBlock; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; import java.util.HashSet; import java.util.Set; /** * Cluster state update request that allows to create an index + * + * @opensearch.internal */ public class CreateIndexClusterStateUpdateRequest extends ClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexRequest.java index 7f1f516d13a04..72b65138f59c9 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexRequest.java @@ -43,21 +43,21 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.DeprecationHandler; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import java.io.IOException; @@ -80,6 +80,8 @@ * @see org.opensearch.client.IndicesAdminClient#create(CreateIndexRequest) * @see org.opensearch.client.Requests#createIndexRequest(String) * @see CreateIndexResponse + * + * @opensearch.internal */ public class CreateIndexRequest extends AcknowledgedRequest implements IndicesRequest { @@ -210,17 +212,28 @@ public CreateIndexRequest settings(Settings settings) { /** * The settings to create the index with (either json or yaml format) + * + * @deprecated use {@link #settings(String source, MediaType mediaType)} instead */ + @Deprecated public CreateIndexRequest settings(String source, XContentType xContentType) { this.settings = Settings.builder().loadFromSource(source, xContentType).build(); return this; } + /** + * The settings to create the index with (using a generic MediaType) + */ + public CreateIndexRequest settings(String source, MediaType mediaType) { + this.settings = Settings.builder().loadFromSource(source, mediaType).build(); + return this; + } + /** * Allows to set the settings using a json builder. */ public CreateIndexRequest settings(XContentBuilder builder) { - settings(Strings.toString(builder), builder.contentType()); + settings(builder.toString(), builder.contentType()); return this; } @@ -245,12 +258,38 @@ public CreateIndexRequest mapping(String mapping) { return this; } + /** + * Adds mapping that will be added when the index gets created. + * + * @param source The mapping source + * @param xContentType The content type of the source + * + * @deprecated use {@link #mapping(String source, MediaType mediaType)} instead + */ + @Deprecated + public CreateIndexRequest mapping(String source, XContentType xContentType) { + return mapping(new BytesArray(source), xContentType); + } + + /** + * Adds mapping that will be added when the index gets created. + * + * Note that the definition should *not* be nested under a type name. + * + * @param source The mapping source + * @param mediaType The media type of the source + */ + public CreateIndexRequest mapping(String source, MediaType mediaType) { + return mapping(new BytesArray(source), mediaType); + } + /** * Adds mapping that will be added when the index gets created. * * @param source The mapping source * @param xContentType the content type of the mapping source - * @deprecated types are being removed + * + * @deprecated use {@link #mapping(BytesReference source, MediaType mediaType)} instead */ @Deprecated private CreateIndexRequest mapping(BytesReference source, XContentType xContentType) { @@ -259,6 +298,20 @@ private CreateIndexRequest mapping(BytesReference source, XContentType xContentT return mapping(MapperService.SINGLE_MAPPING_NAME, mappingAsMap); } + /** + * Adds mapping that will be added when the index gets created. + * + * Note that the definition should *not* be nested under a type name. + * + * @param source The mapping source + * @param mediaType The media type of the source + */ + public CreateIndexRequest mapping(BytesReference source, MediaType mediaType) { + Objects.requireNonNull(mediaType); + Map mappingAsMap = XContentHelper.convertToMap(source, false, mediaType).v2(); + return mapping(MapperService.SINGLE_MAPPING_NAME, mappingAsMap); + } + /** * Adds mapping that will be added when the index gets created. * @@ -296,7 +349,7 @@ private CreateIndexRequest mapping(String type, Map source) { try { XContentBuilder builder = XContentFactory.jsonBuilder(); builder.map(source); - return mapping(Strings.toString(builder)); + return mapping(builder.toString()); } catch (IOException e) { throw new OpenSearchGenerationException("Failed to generate [" + source + "]", e); } @@ -373,11 +426,23 @@ public CreateIndexRequest alias(Alias alias) { /** * Sets the settings and mappings as a single source. + * + * @deprecated use {@link #source(String, MediaType)} instead */ + @Deprecated public CreateIndexRequest source(String source, XContentType xContentType) { return source(new BytesArray(source), xContentType); } + /** + * Sets the settings and mappings as a single source. + * + * Note that the mapping definition should *not* be nested under a type name. + */ + public CreateIndexRequest source(String source, MediaType mediaType) { + return source(new BytesArray(source), mediaType); + } + /** * Sets the settings and mappings as a single source. */ @@ -387,14 +452,29 @@ public CreateIndexRequest source(XContentBuilder source) { /** * Sets the settings and mappings as a single source. + * + * @deprecated use {@link #source(byte[], MediaType mediaType)} instead */ + @Deprecated public CreateIndexRequest source(byte[] source, XContentType xContentType) { return source(source, 0, source.length, xContentType); } /** * Sets the settings and mappings as a single source. + * + * Note that the mapping definition should *not* be nested under a type name. */ + public CreateIndexRequest source(byte[] source, MediaType mediaType) { + return source(source, 0, source.length, mediaType); + } + + /** + * Sets the settings and mappings as a single source. + * + * @deprecated use {@link #source(byte[], int, int, MediaType)} instead + */ + @Deprecated public CreateIndexRequest source(byte[] source, int offset, int length, XContentType xContentType) { return source(new BytesArray(source, offset, length), xContentType); } @@ -402,12 +482,31 @@ public CreateIndexRequest source(byte[] source, int offset, int length, XContent /** * Sets the settings and mappings as a single source. */ + public CreateIndexRequest source(byte[] source, int offset, int length, MediaType mediaType) { + return source(new BytesArray(source, offset, length), mediaType); + } + + /** + * Sets the settings and mappings as a single source. + * + * @deprecated use {@link #source(BytesReference, MediaType)} instead + */ + @Deprecated public CreateIndexRequest source(BytesReference source, XContentType xContentType) { Objects.requireNonNull(xContentType); source(XContentHelper.convertToMap(source, false, xContentType).v2(), LoggingDeprecationHandler.INSTANCE); return this; } + /** + * Sets the settings and mappings as a single source. + */ + public CreateIndexRequest source(BytesReference source, MediaType mediaType) { + Objects.requireNonNull(mediaType); + source(XContentHelper.convertToMap(source, false, mediaType).v2(), LoggingDeprecationHandler.INSTANCE); + return this; + } + /** * Sets the settings and mappings as a single source. */ diff --git a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexRequestBuilder.java index cc99f63c6a844..3493ee06827c1 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexRequestBuilder.java @@ -36,16 +36,19 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.master.AcknowledgedRequestBuilder; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.XContentBuilder; import java.util.Map; /** * Builder for a create index request + * + * @opensearch.internal */ public class CreateIndexRequestBuilder extends AcknowledgedRequestBuilder< CreateIndexRequest, @@ -95,8 +98,8 @@ public CreateIndexRequestBuilder setSettings(XContentBuilder builder) { /** * The settings to create the index with (either json or yaml format) */ - public CreateIndexRequestBuilder setSettings(String source, XContentType xContentType) { - request.settings(source, xContentType); + public CreateIndexRequestBuilder setSettings(String source, MediaType mediaType) { + request.settings(source, mediaType); return this; } @@ -198,24 +201,24 @@ public CreateIndexRequestBuilder addAlias(Alias alias) { /** * Sets the settings and mappings as a single source. */ - public CreateIndexRequestBuilder setSource(String source, XContentType xContentType) { - request.source(source, xContentType); + public CreateIndexRequestBuilder setSource(String source, MediaType mediaType) { + request.source(source, mediaType); return this; } /** * Sets the settings and mappings as a single source. */ - public CreateIndexRequestBuilder setSource(BytesReference source, XContentType xContentType) { - request.source(source, xContentType); + public CreateIndexRequestBuilder setSource(BytesReference source, MediaType mediaType) { + request.source(source, mediaType); return this; } /** * Sets the settings and mappings as a single source. */ - public CreateIndexRequestBuilder setSource(byte[] source, XContentType xContentType) { - request.source(source, xContentType); + public CreateIndexRequestBuilder setSource(byte[] source, MediaType mediaType) { + request.source(source, mediaType); return this; } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexResponse.java index dedc022180cda..1b3ad48402eed 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/create/CreateIndexResponse.java @@ -33,21 +33,23 @@ package org.opensearch.action.admin.indices.create; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * A response for a create index action. + * + * @opensearch.internal */ public class CreateIndexResponse extends ShardsAcknowledgedResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/create/TransportCreateIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/create/TransportCreateIndexAction.java index 2269931deafc8..b5f822bd45b7e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/create/TransportCreateIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/create/TransportCreateIndexAction.java @@ -32,9 +32,8 @@ package org.opensearch.action.admin.indices.create; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -42,7 +41,8 @@ import org.opensearch.cluster.metadata.MetadataCreateIndexService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -50,8 +50,10 @@ /** * Create index action. + * + * @opensearch.internal */ -public class TransportCreateIndexAction extends TransportMasterNodeAction { +public class TransportCreateIndexAction extends TransportClusterManagerNodeAction { private final MetadataCreateIndexService createIndexService; @@ -89,11 +91,17 @@ protected CreateIndexResponse read(StreamInput in) throws IOException { @Override protected ClusterBlockException checkBlock(CreateIndexRequest request, ClusterState state) { - return state.blocks().indexBlockedException(ClusterBlockLevel.METADATA_WRITE, request.index()); + ClusterBlockException clusterBlockException = state.blocks() + .indexBlockedException(ClusterBlockLevel.METADATA_WRITE, request.index()); + + if (clusterBlockException == null) { + return state.blocks().createIndexBlockedException(ClusterBlockLevel.CREATE_INDEX); + } + return clusterBlockException; } @Override - protected void masterOperation( + protected void clusterManagerOperation( final CreateIndexRequest request, final ClusterState state, final ActionListener listener @@ -109,7 +117,7 @@ protected void masterOperation( indexName, request.index() ).ackTimeout(request.timeout()) - .masterNodeTimeout(request.masterNodeTimeout()) + .masterNodeTimeout(request.clusterManagerNodeTimeout()) .settings(request.settings()) .mappings(request.mappings()) .aliases(request.aliases()) diff --git a/server/src/main/java/org/opensearch/action/admin/indices/create/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/create/package-info.java index e386817090285..07bdc0eaa5c64 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/create/package-info.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/create/package-info.java @@ -25,12 +25,12 @@ * under the License. */ -/** - * Create index action. - */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ +/** + * Create index action. + */ package org.opensearch.action.admin.indices.create; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/DanglingIndexInfo.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/DanglingIndexInfo.java index decca8ab4151c..47ba1cca9fc4a 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/DanglingIndexInfo.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/DanglingIndexInfo.java @@ -32,15 +32,17 @@ package org.opensearch.action.admin.indices.dangling; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; /** * Contains information about a dangling index, i.e. an index that OpenSearch has found * on-disk but is not present in the cluster state. + * + * @opensearch.internal */ public class DanglingIndexInfo implements Writeable { private final String nodeId; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/DeleteDanglingIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/DeleteDanglingIndexAction.java index f151a3ea46465..6559ef4cd89bd 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/DeleteDanglingIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/DeleteDanglingIndexAction.java @@ -37,6 +37,8 @@ /** * This action causes a dangling index to be considered as deleted by the cluster. + * + * @opensearch.internal */ public class DeleteDanglingIndexAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/DeleteDanglingIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/DeleteDanglingIndexRequest.java index 733fb0a24ebec..0443325f82778 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/DeleteDanglingIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/DeleteDanglingIndexRequest.java @@ -34,8 +34,8 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; @@ -43,6 +43,8 @@ /** * Represents a request to delete a particular dangling index, specified by its UUID. The {@link #acceptDataLoss} * flag must also be explicitly set to true, or later validation will fail. + * + * @opensearch.internal */ public class DeleteDanglingIndexRequest extends AcknowledgedRequest { private final String indexUUID; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/TransportDeleteDanglingIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/TransportDeleteDanglingIndexAction.java index baa173a0cdf88..751a872ee7dcd 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/TransportDeleteDanglingIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/TransportDeleteDanglingIndexAction.java @@ -32,11 +32,9 @@ package org.opensearch.action.admin.indices.dangling.delete; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.admin.indices.dangling.DanglingIndexInfo; import org.opensearch.action.admin.indices.dangling.list.ListDanglingIndicesAction; @@ -44,8 +42,8 @@ import org.opensearch.action.admin.indices.dangling.list.ListDanglingIndicesResponse; import org.opensearch.action.admin.indices.dangling.list.NodeListDanglingIndicesResponse; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.AckedClusterStateUpdateTask; import org.opensearch.cluster.ClusterState; @@ -54,11 +52,14 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -70,12 +71,17 @@ * Implements the deletion of a dangling index. When handling a {@link DeleteDanglingIndexAction}, * this class first checks that such a dangling index exists. It then submits a cluster state update * to add the index to the index graveyard. + * + * @opensearch.internal */ -public class TransportDeleteDanglingIndexAction extends TransportMasterNodeAction { +public class TransportDeleteDanglingIndexAction extends TransportClusterManagerNodeAction< + DeleteDanglingIndexRequest, + AcknowledgedResponse> { private static final Logger logger = LogManager.getLogger(TransportDeleteDanglingIndexAction.class); private final Settings settings; private final NodeClient nodeClient; + private final ClusterManagerTaskThrottler.ThrottlingKey deleteDanglingIndexTaskKey; @Inject public TransportDeleteDanglingIndexAction( @@ -98,6 +104,8 @@ public TransportDeleteDanglingIndexAction( ); this.settings = settings; this.nodeClient = nodeClient; + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + deleteDanglingIndexTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.DELETE_DANGLING_INDEX_KEY, true); } @Override @@ -111,7 +119,7 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation( + protected void clusterManagerOperation( DeleteDanglingIndexRequest deleteRequest, ClusterState state, ActionListener deleteListener @@ -153,6 +161,11 @@ protected AcknowledgedResponse newResponse(boolean acknowledged) { return new AcknowledgedResponse(acknowledged); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return deleteDanglingIndexTaskKey; + } + @Override public ClusterState execute(final ClusterState currentState) { return deleteDanglingIndex(currentState, indexToDelete); @@ -172,8 +185,8 @@ public void onFailure(Exception e) { private ClusterState deleteDanglingIndex(ClusterState currentState, Index indexToDelete) { final Metadata metaData = currentState.getMetadata(); - for (ObjectObjectCursor each : metaData.indices()) { - if (indexToDelete.getUUID().equals(each.value.getIndexUUID())) { + for (final IndexMetadata each : metaData.indices().values()) { + if (indexToDelete.getUUID().equals(each.getIndexUUID())) { throw new IllegalArgumentException( "Refusing to delete dangling index " + indexToDelete diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/package-info.java new file mode 100644 index 0000000000000..15a966b35ab55 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/delete/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Delete Dangling Indices transport handlers. */ +package org.opensearch.action.admin.indices.dangling.delete; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexAction.java index fa55661788d3e..f308728c7a85e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexAction.java @@ -36,6 +36,8 @@ /** * Represents a request to find a particular dangling index by UUID. + * + * @opensearch.internal */ public class FindDanglingIndexAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexRequest.java index c5a3621bd8340..f853a47b3c2bf 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexRequest.java @@ -33,12 +33,17 @@ package org.opensearch.action.admin.indices.dangling.find; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport request for finding a dangling index + * + * @opensearch.internal + */ public class FindDanglingIndexRequest extends BaseNodesRequest { private final String indexUUID; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexResponse.java index c1b1a2901e5e2..b834775be1767 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/FindDanglingIndexResponse.java @@ -35,8 +35,8 @@ import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.List; @@ -44,6 +44,8 @@ /** * Models a response to a {@link FindDanglingIndexRequest}. A find request queries every node in the * cluster looking for a dangling index with a specific UUID. + * + * @opensearch.internal */ public class FindDanglingIndexResponse extends BaseNodesResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/NodeFindDanglingIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/NodeFindDanglingIndexRequest.java index ddb16ec51a080..276d92c50c69c 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/NodeFindDanglingIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/NodeFindDanglingIndexRequest.java @@ -32,14 +32,16 @@ package org.opensearch.action.admin.indices.dangling.find; -import java.io.IOException; - import org.opensearch.action.support.nodes.BaseNodeRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; /** * Used when querying every node in the cluster for a specific dangling index. + * + * @opensearch.internal */ public class NodeFindDanglingIndexRequest extends BaseNodeRequest { private final String indexUUID; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/NodeFindDanglingIndexResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/NodeFindDanglingIndexResponse.java index 0f21db3eb7a91..41504a436750a 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/NodeFindDanglingIndexResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/NodeFindDanglingIndexResponse.java @@ -35,14 +35,16 @@ import org.opensearch.action.support.nodes.BaseNodeResponse; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.List; /** * Used when querying every node in the cluster for a specific dangling index. + * + * @opensearch.internal */ public class NodeFindDanglingIndexResponse extends BaseNodeResponse { /** diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/TransportFindDanglingIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/TransportFindDanglingIndexAction.java index 0da65f79db2af..13da03a04acbd 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/TransportFindDanglingIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/TransportFindDanglingIndexAction.java @@ -32,10 +32,6 @@ package org.opensearch.action.admin.indices.dangling.find; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.nodes.TransportNodesAction; @@ -43,13 +39,19 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.gateway.DanglingIndicesState; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + /** * Finds a specified dangling index by its UUID, searching across all nodes. + * + * @opensearch.internal */ public class TransportFindDanglingIndexAction extends TransportNodesAction< FindDanglingIndexRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/package-info.java new file mode 100644 index 0000000000000..d5536d5794f3c --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/find/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Find Dangling Indices transport handlers. */ +package org.opensearch.action.admin.indices.dangling.find; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/ImportDanglingIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/ImportDanglingIndexAction.java index 1f6bbdecc7c12..5f7a096b1d749 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/ImportDanglingIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/ImportDanglingIndexAction.java @@ -37,6 +37,8 @@ /** * Represents a request to import a particular dangling index. + * + * @opensearch.internal */ public class ImportDanglingIndexAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/ImportDanglingIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/ImportDanglingIndexRequest.java index b154d048a10d6..590f08a82c1d2 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/ImportDanglingIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/ImportDanglingIndexRequest.java @@ -34,8 +34,8 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Locale; @@ -45,6 +45,8 @@ * Represents a request to import a particular dangling index, specified * by its UUID. The {@link #acceptDataLoss} flag must also be * explicitly set to true, or later validation will fail. + * + * @opensearch.internal */ public class ImportDanglingIndexRequest extends AcknowledgedRequest { private final String indexUUID; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/TransportImportDanglingIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/TransportImportDanglingIndexAction.java index c648f9eea837a..3f47d1bf083f4 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/TransportImportDanglingIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/TransportImportDanglingIndexAction.java @@ -32,17 +32,9 @@ package org.opensearch.action.admin.indices.dangling.import_index; -import static java.util.Collections.singletonList; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.admin.indices.dangling.find.FindDanglingIndexAction; import org.opensearch.action.admin.indices.dangling.find.FindDanglingIndexRequest; @@ -54,14 +46,24 @@ import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; import org.opensearch.gateway.LocalAllocateDangledIndices; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import static java.util.Collections.singletonList; + /** * Implements the import of a dangling index. When handling a {@link ImportDanglingIndexAction}, * this class first checks that such a dangling index exists. It then calls {@link LocalAllocateDangledIndices} * to perform the actual allocation. + * + * @opensearch.internal */ public class TransportImportDanglingIndexAction extends HandledTransportAction { private static final Logger logger = LogManager.getLogger(TransportImportDanglingIndexAction.class); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/package-info.java new file mode 100644 index 0000000000000..fce0f5a130c2f --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/import_index/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Import Dangling Index transport handler. */ +package org.opensearch.action.admin.indices.dangling.import_index; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesAction.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesAction.java index 0e797ca930400..d8ace959c61d3 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesAction.java @@ -36,6 +36,8 @@ /** * Represents a request to list all dangling indices known to the cluster. + * + * @opensearch.internal */ public class ListDanglingIndicesAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesRequest.java index 6c2bd386119a2..adbfe9b760ed8 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesRequest.java @@ -33,12 +33,17 @@ package org.opensearch.action.admin.indices.dangling.list; import org.opensearch.action.support.nodes.BaseNodesRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport request for listing a dangling indices + * + * @opensearch.internal + */ public class ListDanglingIndicesRequest extends BaseNodesRequest { /** * Filter the response by index UUID. Leave as null to find all indices. diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesResponse.java index d412d3e323d68..0cff01af536dc 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/ListDanglingIndicesResponse.java @@ -36,12 +36,12 @@ import org.opensearch.action.admin.indices.dangling.DanglingIndexInfo; import org.opensearch.action.support.nodes.BaseNodesResponse; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; @@ -57,6 +57,8 @@ * cluster and aggregates their responses. When the aggregated response is converted to {@link XContent}, * information for each dangling index is presented under the "dangling_indices" key. If any nodes * in the cluster failed to answer, the details are presented under the "_nodes.failures" key. + * + * @opensearch.internal */ public class ListDanglingIndicesResponse extends BaseNodesResponse implements StatusToXContentObject { @@ -126,7 +128,13 @@ protected void writeNodesTo(StreamOutput out, List indexMetaData; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/TransportListDanglingIndicesAction.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/TransportListDanglingIndicesAction.java index 1274010bfb8a2..59076cc6ef8fb 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/TransportListDanglingIndicesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/TransportListDanglingIndicesAction.java @@ -40,7 +40,7 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.gateway.DanglingIndicesState; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -52,6 +52,8 @@ /** * Implements the listing of all dangling indices. All nodes in the cluster are queried, and * their answers aggregated. Finding dangling indices is performed in {@link DanglingIndicesState}. + * + * @opensearch.internal */ public class TransportListDanglingIndicesAction extends TransportNodesAction< ListDanglingIndicesRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/package-info.java new file mode 100644 index 0000000000000..f6c37345fb73c --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/list/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** List Dangling Indices transport handler. */ +package org.opensearch.action.admin.indices.dangling.list; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/dangling/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/dangling/package-info.java index e97ff628d8d79..e69b6acf995e3 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/dangling/package-info.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/dangling/package-info.java @@ -25,6 +25,11 @@ * under the License. */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + /** * Dangling indices are indices that exist on disk on one or more nodes but * which do not currently exist in the cluster state. They arise in a @@ -33,17 +38,13 @@ *
      *
    • A user overflows the index graveyard by deleting more than 500 indices while a node is offline and then the node rejoins the * cluster
    • - *
    • A node (unsafely) moves from one cluster to another, perhaps because the original cluster lost all its master nodes
    • + *
    • A node (unsafely) moves from one cluster to another, perhaps because the original cluster lost all its cluster-manager nodes
    • *
    • A user (unsafely) meddles with the contents of the data path, maybe restoring an old index folder from a backup
    • *
    • A disk partially fails and the user has no replicas and no snapshots and wants to (unsafely) recover whatever they can
    • - *
    • A cluster loses all master nodes and those are (unsafely) restored from backup, but the backup does not contain the index
    • + *
    • A cluster loses all cluster-manager nodes and those are (unsafely) restored from backup, but the backup does not contain the index
    • *
    * *

    The classes in this package form an API for managing dangling indices, allowing them to be listed, imported or deleted. */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ package org.opensearch.action.admin.indices.dangling; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/datastream/CreateDataStreamAction.java b/server/src/main/java/org/opensearch/action/admin/indices/datastream/CreateDataStreamAction.java index 6c5c417825abf..14b29c4b2bbba 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/datastream/CreateDataStreamAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/datastream/CreateDataStreamAction.java @@ -31,16 +31,15 @@ package org.opensearch.action.admin.indices.datastream; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.ActionType; import org.opensearch.action.IndicesRequest; import org.opensearch.action.ValidateActions; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.IndicesOptions; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedRequest; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -48,16 +47,22 @@ import org.opensearch.cluster.metadata.MetadataCreateDataStreamService; import org.opensearch.cluster.metadata.MetadataCreateDataStreamService.CreateDataStreamClusterStateUpdateRequest; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; import java.util.Objects; +/** + * Transport action for creating a datastream + * + * @opensearch.internal + */ public class CreateDataStreamAction extends ActionType { public static final CreateDataStreamAction INSTANCE = new CreateDataStreamAction(); @@ -67,6 +72,11 @@ private CreateDataStreamAction() { super(NAME, AcknowledgedResponse::new); } + /** + * Request for Creating Data Stream + * + * @opensearch.internal + */ public static class Request extends AcknowledgedRequest implements IndicesRequest { private final String name; @@ -119,7 +129,12 @@ public IndicesOptions indicesOptions() { } } - public static class TransportAction extends TransportMasterNodeAction { + /** + * Transport Action for Creating Data Stream + * + * @opensearch.internal + */ + public static class TransportAction extends TransportClusterManagerNodeAction { private final MetadataCreateDataStreamService metadataCreateDataStreamService; @@ -147,11 +162,11 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(Request request, ClusterState state, ActionListener listener) + protected void clusterManagerOperation(Request request, ClusterState state, ActionListener listener) throws Exception { CreateDataStreamClusterStateUpdateRequest updateRequest = new CreateDataStreamClusterStateUpdateRequest( request.name, - request.masterNodeTimeout(), + request.clusterManagerNodeTimeout(), request.timeout() ); metadataCreateDataStreamService.createDataStream(updateRequest, listener); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/datastream/DataStreamsStatsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/datastream/DataStreamsStatsAction.java index cca6b83015ecc..fcb13f4091638 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/datastream/DataStreamsStatsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/datastream/DataStreamsStatsAction.java @@ -37,7 +37,6 @@ import org.apache.lucene.index.PointValues; import org.opensearch.action.ActionType; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastRequest; import org.opensearch.action.support.broadcast.BroadcastResponse; import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; @@ -52,12 +51,13 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexService; import org.opensearch.index.engine.Engine; import org.opensearch.index.shard.IndexShard; @@ -78,6 +78,11 @@ import java.util.SortedMap; import java.util.stream.Stream; +/** + * Transport action for retrieving datastream stats + * + * @opensearch.internal + */ public class DataStreamsStatsAction extends ActionType { public static final DataStreamsStatsAction INSTANCE = new DataStreamsStatsAction(); @@ -87,6 +92,11 @@ public DataStreamsStatsAction() { super(NAME, DataStreamsStatsAction.Response::new); } + /** + * Request for Data Streams Stats + * + * @opensearch.internal + */ public static class Request extends BroadcastRequest { public Request() { super((String[]) null); @@ -97,6 +107,11 @@ public Request(StreamInput in) throws IOException { } } + /** + * Response for Data Streams Stats + * + * @opensearch.internal + */ public static class Response extends BroadcastResponse { private final int dataStreamCount; private final int backingIndices; @@ -198,6 +213,11 @@ public String toString() { } } + /** + * The Data Streams Stats container + * + * @opensearch.internal + */ public static class DataStreamStats implements ToXContentObject, Writeable { private final String dataStream; private final int backingIndices; @@ -289,6 +309,11 @@ public String toString() { } } + /** + * Per Shard Data Stream stats + * + * @opensearch.internal + */ public static class DataStreamShardStats implements Writeable { private final ShardRouting shardRouting; private final StoreStats storeStats; @@ -326,12 +351,22 @@ public long getMaxTimestamp() { } } + /** + * Aggregated data Stream stats + * + * @opensearch.internal + */ private static class AggregatedStats { Set backingIndices = new HashSet<>(); long storageBytes = 0L; long maxTimestamp = 0L; } + /** + * Transport Action for Data Stream Stats + * + * @opensearch.internal + */ public static class TransportAction extends TransportBroadcastByNodeAction { private final ClusterService clusterService; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/datastream/DeleteDataStreamAction.java b/server/src/main/java/org/opensearch/action/admin/indices/datastream/DeleteDataStreamAction.java index 5d79f51cbab65..190194e071bc4 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/datastream/DeleteDataStreamAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/datastream/DeleteDataStreamAction.java @@ -33,15 +33,14 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.ActionType; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.IndicesOptions; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.block.ClusterBlockException; @@ -50,16 +49,19 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.metadata.MetadataDeleteIndexService; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.regex.Regex; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.CollectionUtils; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.Index; import org.opensearch.snapshots.SnapshotInProgressException; import org.opensearch.snapshots.SnapshotsService; import org.opensearch.threadpool.ThreadPool; @@ -73,6 +75,11 @@ import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport action for deleting a datastream + * + * @opensearch.internal + */ public class DeleteDataStreamAction extends ActionType { private static final Logger logger = LogManager.getLogger(DeleteDataStreamAction.class); @@ -84,7 +91,12 @@ private DeleteDataStreamAction() { super(NAME, AcknowledgedResponse::new); } - public static class Request extends MasterNodeRequest implements IndicesRequest.Replaceable { + /** + * Request for deleting data streams + * + * @opensearch.internal + */ + public static class Request extends ClusterManagerNodeRequest implements IndicesRequest.Replaceable { private String[] names; @@ -149,9 +161,15 @@ public IndicesRequest indices(String... indices) { } } - public static class TransportAction extends TransportMasterNodeAction { + /** + * Transport action for deleting data streams + * + * @opensearch.internal + */ + public static class TransportAction extends TransportClusterManagerNodeAction { private final MetadataDeleteIndexService deleteIndexService; + private final ClusterManagerTaskThrottler.ThrottlingKey removeDataStreamTaskKey; @Inject public TransportAction( @@ -164,6 +182,8 @@ public TransportAction( ) { super(NAME, transportService, clusterService, threadPool, actionFilters, Request::new, indexNameExpressionResolver); this.deleteIndexService = deleteIndexService; + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + removeDataStreamTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.REMOVE_DATA_STREAM_KEY, true); } @Override @@ -177,7 +197,7 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(Request request, ClusterState state, ActionListener listener) + protected void clusterManagerOperation(Request request, ClusterState state, ActionListener listener) throws Exception { clusterService.submitStateUpdateTask( "remove-data-stream [" + Strings.arrayToCommaDelimitedString(request.names) + "]", @@ -185,7 +205,7 @@ protected void masterOperation(Request request, ClusterState state, ActionListen @Override public TimeValue timeout() { - return request.masterNodeTimeout(); + return request.clusterManagerNodeTimeout(); } @Override @@ -193,6 +213,11 @@ public void onFailure(String source, Exception e) { listener.onFailure(e); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return removeDataStreamTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) { return removeDataStream(deleteIndexService, currentState, request); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/datastream/GetDataStreamAction.java b/server/src/main/java/org/opensearch/action/admin/indices/datastream/GetDataStreamAction.java index d519c80f6d6c2..6a7967d31653c 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/datastream/GetDataStreamAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/datastream/GetDataStreamAction.java @@ -33,15 +33,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.AbstractDiffable; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -53,14 +51,16 @@ import org.opensearch.cluster.metadata.MetadataIndexTemplateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.Index; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -73,6 +73,11 @@ import java.util.Objects; import java.util.stream.Collectors; +/** + * Transport action for getting a datastream + * + * @opensearch.internal + */ public class GetDataStreamAction extends ActionType { public static final GetDataStreamAction INSTANCE = new GetDataStreamAction(); @@ -82,7 +87,12 @@ private GetDataStreamAction() { super(NAME, Response::new); } - public static class Request extends MasterNodeReadRequest implements IndicesRequest.Replaceable { + /** + * Request for getting data streams + * + * @opensearch.internal + */ + public static class Request extends ClusterManagerNodeReadRequest implements IndicesRequest.Replaceable { private String[] names; @@ -143,9 +153,19 @@ public IndicesRequest indices(String... indices) { } } + /** + * Response for getting data streams + * + * @opensearch.internal + */ public static class Response extends ActionResponse implements ToXContentObject { public static final ParseField DATASTREAMS_FIELD = new ParseField("data_streams"); + /** + * Data streams information + * + * @opensearch.internal + */ public static class DataStreamInfo extends AbstractDiffable implements ToXContentObject { public static final ParseField STATUS_FIELD = new ParseField("status"); @@ -262,7 +282,12 @@ public int hashCode() { } } - public static class TransportAction extends TransportMasterNodeReadAction { + /** + * Transport Action for getting data streams + * + * @opensearch.internal + */ + public static class TransportAction extends TransportClusterManagerNodeReadAction { private static final Logger logger = LogManager.getLogger(TransportAction.class); @@ -288,7 +313,7 @@ protected Response read(StreamInput in) throws IOException { } @Override - protected void masterOperation(Request request, ClusterState state, ActionListener listener) throws Exception { + protected void clusterManagerOperation(Request request, ClusterState state, ActionListener listener) throws Exception { List dataStreams = getDataStreams(state, indexNameExpressionResolver, request); List dataStreamInfos = new ArrayList<>(dataStreams.size()); for (DataStream dataStream : dataStreams) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/datastream/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/datastream/package-info.java new file mode 100644 index 0000000000000..a37c2bed1adff --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/datastream/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Data Stream transport handlers. */ +package org.opensearch.action.admin.indices.datastream; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexAction.java index 6f0dd781b4cec..696c1244c7504 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action for deleting an index + * + * @opensearch.internal + */ public class DeleteIndexAction extends ActionType { public static final DeleteIndexAction INSTANCE = new DeleteIndexAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexClusterStateUpdateRequest.java index a6d06833ebae9..5088d021ca9b8 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexClusterStateUpdateRequest.java @@ -35,6 +35,8 @@ /** * Cluster state update request that allows to close one or more indices + * + * @opensearch.internal */ public class DeleteIndexClusterStateUpdateRequest extends IndicesClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexRequest.java index c29072b135b85..006da6b3cbb09 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexRequest.java @@ -36,9 +36,9 @@ import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.util.CollectionUtils; import java.io.IOException; @@ -46,6 +46,8 @@ /** * A request to delete an index. Best created with {@link org.opensearch.client.Requests#deleteIndexRequest(String)}. + * + * @opensearch.internal */ public class DeleteIndexRequest extends AcknowledgedRequest implements IndicesRequest.Replaceable { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexRequestBuilder.java index 741f46d44d8b7..33f6342e94139 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/delete/DeleteIndexRequestBuilder.java @@ -37,6 +37,11 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder for deleting an index + * + * @opensearch.internal + */ public class DeleteIndexRequestBuilder extends AcknowledgedRequestBuilder< DeleteIndexRequest, AcknowledgedResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/delete/TransportDeleteIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/delete/TransportDeleteIndexAction.java index ec8c0fe8ed011..51c90a982e71b 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/delete/TransportDeleteIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/delete/TransportDeleteIndexAction.java @@ -35,11 +35,10 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.DestructiveOperations; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.block.ClusterBlockException; @@ -47,8 +46,9 @@ import org.opensearch.cluster.metadata.MetadataDeleteIndexService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -60,8 +60,10 @@ /** * Delete index action. + * + * @opensearch.internal */ -public class TransportDeleteIndexAction extends TransportMasterNodeAction { +public class TransportDeleteIndexAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportDeleteIndexAction.class); @@ -113,7 +115,7 @@ protected ClusterBlockException checkBlock(DeleteIndexRequest request, ClusterSt } @Override - protected void masterOperation( + protected void clusterManagerOperation( final DeleteIndexRequest request, final ClusterState state, final ActionListener listener @@ -125,7 +127,7 @@ protected void masterOperation( } DeleteIndexClusterStateUpdateRequest deleteRequest = new DeleteIndexClusterStateUpdateRequest().ackTimeout(request.timeout()) - .masterNodeTimeout(request.masterNodeTimeout()) + .masterNodeTimeout(request.clusterManagerNodeTimeout()) .indices(concreteIndices.toArray(new Index[concreteIndices.size()])); deleteIndexService.deleteIndices(deleteRequest, new ActionListener() { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsAction.java index 1eae6b99bc1ee..6bd0cddc00d07 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for checking if an index exists + * + * @opensearch.internal + */ public class IndicesExistsAction extends ActionType { public static final IndicesExistsAction INSTANCE = new IndicesExistsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsRequest.java index d511c18f5505e..f9bd849e549b7 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsRequest.java @@ -35,16 +35,21 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import static org.opensearch.action.ValidateActions.addValidationError; -public class IndicesExistsRequest extends MasterNodeReadRequest implements IndicesRequest.Replaceable { +/** + * Transport request for checking if an index exists + * + * @opensearch.internal + */ +public class IndicesExistsRequest extends ClusterManagerNodeReadRequest implements IndicesRequest.Replaceable { private String[] indices = Strings.EMPTY_ARRAY; private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, false, true, true); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsRequestBuilder.java index bb38d14530588..8459bbd8b874e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsRequestBuilder.java @@ -32,10 +32,15 @@ package org.opensearch.action.admin.indices.exists.indices; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -public class IndicesExistsRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Transport request builder for checking if an index exists + * + * @opensearch.internal + */ +public class IndicesExistsRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< IndicesExistsRequest, IndicesExistsResponse, IndicesExistsRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsResponse.java index 336bb6147d07d..4ac85492229f0 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/IndicesExistsResponse.java @@ -32,12 +32,17 @@ package org.opensearch.action.admin.indices.exists.indices; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport response for checking if an index exists + * + * @opensearch.internal + */ public class IndicesExistsResponse extends ActionResponse { private boolean exists; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/TransportIndicesExistsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/TransportIndicesExistsAction.java index 076e2ecc2c119..428a0eb35513d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/TransportIndicesExistsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/TransportIndicesExistsAction.java @@ -32,17 +32,17 @@ package org.opensearch.action.admin.indices.exists.indices; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.IndexNotFoundException; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -51,8 +51,10 @@ /** * Indices exists action. + * + * @opensearch.internal */ -public class TransportIndicesExistsAction extends TransportMasterNodeReadAction { +public class TransportIndicesExistsAction extends TransportClusterManagerNodeReadAction { @Inject public TransportIndicesExistsAction( @@ -101,7 +103,7 @@ protected ClusterBlockException checkBlock(IndicesExistsRequest request, Cluster } @Override - protected void masterOperation( + protected void clusterManagerOperation( final IndicesExistsRequest request, final ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/package-info.java new file mode 100644 index 0000000000000..e5b320dc1bdcd --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/exists/indices/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Exists transport handler. */ +package org.opensearch.action.admin.indices.exists.indices; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/package-info.java new file mode 100644 index 0000000000000..0a91ac2fd9a20 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/exists/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Exists transport handlers. */ +package org.opensearch.action.admin.indices.exists; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TransportTypesExistsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TransportTypesExistsAction.java deleted file mode 100644 index bf4e0375941e7..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TransportTypesExistsAction.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.exists.types; - -import org.opensearch.action.ActionListener; -import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.block.ClusterBlockException; -import org.opensearch.cluster.block.ClusterBlockLevel; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.TransportService; - -import java.io.IOException; - -/** - * Types exists transport action. - */ -public class TransportTypesExistsAction extends TransportMasterNodeReadAction { - - @Inject - public TransportTypesExistsAction( - TransportService transportService, - ClusterService clusterService, - ThreadPool threadPool, - ActionFilters actionFilters, - IndexNameExpressionResolver indexNameExpressionResolver - ) { - super( - TypesExistsAction.NAME, - transportService, - clusterService, - threadPool, - actionFilters, - TypesExistsRequest::new, - indexNameExpressionResolver - ); - } - - @Override - protected String executor() { - // lightweight check - return ThreadPool.Names.SAME; - } - - @Override - protected TypesExistsResponse read(StreamInput in) throws IOException { - return new TypesExistsResponse(in); - } - - @Override - protected ClusterBlockException checkBlock(TypesExistsRequest request, ClusterState state) { - return state.blocks() - .indicesBlockedException(ClusterBlockLevel.METADATA_READ, indexNameExpressionResolver.concreteIndexNames(state, request)); - } - - @Override - protected void masterOperation( - final TypesExistsRequest request, - final ClusterState state, - final ActionListener listener - ) { - String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request.indicesOptions(), request.indices()); - if (concreteIndices.length == 0) { - listener.onResponse(new TypesExistsResponse(false)); - return; - } - - for (String concreteIndex : concreteIndices) { - if (!state.metadata().hasConcreteIndex(concreteIndex)) { - listener.onResponse(new TypesExistsResponse(false)); - return; - } - - MappingMetadata mapping = state.metadata().getIndices().get(concreteIndex).mapping(); - if (mapping == null) { - listener.onResponse(new TypesExistsResponse(false)); - return; - } - - for (String type : request.types()) { - if (mapping.type().equals(type) == false) { - listener.onResponse(new TypesExistsResponse(false)); - return; - } - } - } - - listener.onResponse(new TypesExistsResponse(true)); - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsAction.java deleted file mode 100644 index df461559dda7c..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsAction.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.exists.types; - -import org.opensearch.action.ActionType; - -public class TypesExistsAction extends ActionType { - - public static final TypesExistsAction INSTANCE = new TypesExistsAction(); - public static final String NAME = "indices:admin/types/exists"; - - private TypesExistsAction() { - super(NAME, TypesExistsResponse::new); - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsRequest.java deleted file mode 100644 index eee8076a9b176..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsRequest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.exists.types; - -import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.IndicesRequest; -import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; - -import java.io.IOException; - -import static org.opensearch.action.ValidateActions.addValidationError; - -public class TypesExistsRequest extends MasterNodeReadRequest implements IndicesRequest.Replaceable { - - private String[] indices; - private String[] types; - - private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen(); - - public TypesExistsRequest() {} - - public TypesExistsRequest(String[] indices, String... types) { - this.indices = indices; - this.types = types; - } - - public TypesExistsRequest(StreamInput in) throws IOException { - super(in); - indices = in.readStringArray(); - types = in.readStringArray(); - indicesOptions = IndicesOptions.readIndicesOptions(in); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - super.writeTo(out); - out.writeStringArray(indices); - out.writeStringArray(types); - indicesOptions.writeIndicesOptions(out); - } - - @Override - public String[] indices() { - return indices; - } - - @Override - public TypesExistsRequest indices(String... indices) { - this.indices = indices; - return this; - } - - public String[] types() { - return types; - } - - public void types(String[] types) { - this.types = types; - } - - @Override - public IndicesOptions indicesOptions() { - return indicesOptions; - } - - public TypesExistsRequest indicesOptions(IndicesOptions indicesOptions) { - this.indicesOptions = indicesOptions; - return this; - } - - @Override - public ActionRequestValidationException validate() { - ActionRequestValidationException validationException = null; - if (indices == null) { // Specifying '*' via rest api results in an empty array - validationException = addValidationError("index/indices is missing", validationException); - } - if (types == null || types.length == 0) { - validationException = addValidationError("type/types is missing", validationException); - } - - return validationException; - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsRequestBuilder.java deleted file mode 100644 index 12ed1b5766775..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsRequestBuilder.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.exists.types; - -import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; -import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.Strings; - -/** - * A builder for {@link TypesExistsRequest}. - */ -@Deprecated -public class TypesExistsRequestBuilder extends MasterNodeReadOperationRequestBuilder< - TypesExistsRequest, - TypesExistsResponse, - TypesExistsRequestBuilder> { - - /** - * @param indices What indices to check for types - */ - public TypesExistsRequestBuilder(OpenSearchClient client, TypesExistsAction action, String... indices) { - super(client, action, new TypesExistsRequest(indices, Strings.EMPTY_ARRAY)); - } - - TypesExistsRequestBuilder(OpenSearchClient client, TypesExistsAction action) { - super(client, action, new TypesExistsRequest()); - } - - /** - * @param indices What indices to check for types - */ - public TypesExistsRequestBuilder setIndices(String[] indices) { - request.indices(indices); - return this; - } - - /** - * @param types The types to check if they exist - */ - public TypesExistsRequestBuilder setTypes(String... types) { - request.types(types); - return this; - } - - /** - * @param indicesOptions Specifies how to resolve indices that aren't active / ready and indices wildcard expressions - */ - public TypesExistsRequestBuilder setIndicesOptions(IndicesOptions indicesOptions) { - request.indicesOptions(indicesOptions); - return this; - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsResponse.java deleted file mode 100644 index d7e08b5c9cdc9..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/exists/types/TypesExistsResponse.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.exists.types; - -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; - -import java.io.IOException; - -/** - * Whether all of the existed types exist. - */ -public class TypesExistsResponse extends ActionResponse { - - private boolean exists; - - TypesExistsResponse(StreamInput in) throws IOException { - super(in); - exists = in.readBoolean(); - } - - public TypesExistsResponse(boolean exists) { - this.exists = exists; - } - - public boolean isExists() { - return this.exists; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - out.writeBoolean(exists); - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushAction.java b/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushAction.java index ace63f2346e30..288a46977521e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for flushing one or more indices + * + * @opensearch.internal + */ public class FlushAction extends ActionType { public static final FlushAction INSTANCE = new FlushAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushRequest.java index 1020a0cfb33a9..c8b28efc5f294 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushRequest.java @@ -34,8 +34,8 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -51,6 +51,8 @@ * @see org.opensearch.client.Requests#flushRequest(String...) * @see org.opensearch.client.IndicesAdminClient#flush(FlushRequest) * @see FlushResponse + * + * @opensearch.internal */ public class FlushRequest extends BroadcastRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushRequestBuilder.java index c72a55b1f8519..d0cbd1d27fba6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.support.broadcast.BroadcastOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder for flushing one or more indices + * + * @opensearch.internal + */ public class FlushRequestBuilder extends BroadcastOperationRequestBuilder { public FlushRequestBuilder(OpenSearchClient client, FlushAction action) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushResponse.java index 9ab04dfe8c081..4135654e66271 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/flush/FlushResponse.java @@ -32,11 +32,11 @@ package org.opensearch.action.admin.indices.flush; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; @@ -44,6 +44,8 @@ /** * A response to flush action. + * + * @opensearch.internal */ public class FlushResponse extends BroadcastResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/flush/ShardFlushRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/flush/ShardFlushRequest.java index d49bf04af623b..ab6e99064b43c 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/flush/ShardFlushRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/flush/ShardFlushRequest.java @@ -34,12 +34,17 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.replication.ReplicationRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; +/** + * Transport request for flushing one or more indices + * + * @opensearch.internal + */ public class ShardFlushRequest extends ReplicationRequest { private final FlushRequest request; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/flush/TransportFlushAction.java b/server/src/main/java/org/opensearch/action/admin/indices/flush/TransportFlushAction.java index 552f88400a87d..07434c65862b6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/flush/TransportFlushAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/flush/TransportFlushAction.java @@ -33,19 +33,21 @@ package org.opensearch.action.admin.indices.flush; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.replication.ReplicationResponse; import org.opensearch.action.support.replication.TransportBroadcastReplicationAction; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.transport.TransportService; import java.util.List; /** * Flush ActionType. + * + * @opensearch.internal */ public class TransportFlushAction extends TransportBroadcastReplicationAction< FlushRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/flush/TransportShardFlushAction.java b/server/src/main/java/org/opensearch/action/admin/indices/flush/TransportShardFlushAction.java index 53e774306e746..90db53af72257 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/flush/TransportShardFlushAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/flush/TransportShardFlushAction.java @@ -33,18 +33,18 @@ package org.opensearch.action.admin.indices.flush; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.replication.ReplicationResponse; import org.opensearch.action.support.replication.TransportReplicationAction; import org.opensearch.cluster.action.shard.ShardStateAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; @@ -55,6 +55,11 @@ import java.io.IOException; +/** + * Transport action for flushing one or more indices + * + * @opensearch.internal + */ public class TransportShardFlushAction extends TransportReplicationAction { public static final String NAME = FlushAction.NAME + "[s]"; @@ -120,6 +125,11 @@ protected void shardOperationOnReplica(ShardFlushRequest request, IndexShard rep // TODO: Remove this transition in OpenSearch 3.0 private static final String PRE_SYNCED_FLUSH_ACTION_NAME = "internal:indices/flush/synced/pre"; + /** + * A Pre Shard Synced Flush Request + * + * @opensearch.internal + */ private static class PreShardSyncedFlushRequest extends TransportRequest { private final ShardId shardId; @@ -141,6 +151,11 @@ public void writeTo(StreamOutput out) throws IOException { } } + /** + * Pre synced flush handler for the transport layer + * + * @opensearch.internal + */ private static final class PreSyncedFlushTransportHandler implements TransportRequestHandler { private final IndicesService indicesService; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeAction.java b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeAction.java index 07b337e61323e..06b639fadc1c7 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to request force merging the segments of one or more indices. + * + * @opensearch.internal + */ public class ForceMergeAction extends ActionType { public static final ForceMergeAction INSTANCE = new ForceMergeAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequest.java index 605f39ffd1312..7151d5dd64283 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequest.java @@ -37,8 +37,8 @@ import org.opensearch.action.support.broadcast.BroadcastRequest; import org.opensearch.common.Nullable; import org.opensearch.common.UUIDs; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Arrays; @@ -54,9 +54,16 @@ * @see org.opensearch.client.Requests#forceMergeRequest(String...) * @see org.opensearch.client.IndicesAdminClient#forceMerge(ForceMergeRequest) * @see ForceMergeResponse + * + * @opensearch.internal */ public class ForceMergeRequest extends BroadcastRequest { + /** + * Defaults for the Force Merge Request + * + * @opensearch.internal + */ public static final class Defaults { public static final int MAX_NUM_SEGMENTS = -1; public static final boolean ONLY_EXPUNGE_DELETES = false; @@ -76,6 +83,8 @@ public static final class Defaults { @Nullable private final String forceMergeUUID; + private boolean shouldStoreResult; + /** * Constructs a merge request over one or more indices. * @@ -156,6 +165,18 @@ public ForceMergeRequest flush(boolean flush) { return this; } + /** + * Should this task store its result after it has finished? + */ + public void setShouldStoreResult(boolean shouldStoreResult) { + this.shouldStoreResult = shouldStoreResult; + } + + @Override + public boolean getShouldStoreResult() { + return shouldStoreResult; + } + @Override public String getDescription() { return "Force-merge indices " diff --git a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequestBuilder.java index 2d0e7d4401a01..cff05f194cac4 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeRequestBuilder.java @@ -41,6 +41,8 @@ * {@link #setMaxNumSegments(int)} allows to control the number of segments to force * merge down to. By default, will cause the force merge process to merge down * to half the configured number of segments. + * + * @opensearch.internal */ public class ForceMergeRequestBuilder extends BroadcastOperationRequestBuilder< ForceMergeRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeResponse.java index c898dad8bcdc9..c5316f3375ad5 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/ForceMergeResponse.java @@ -32,18 +32,22 @@ package org.opensearch.action.admin.indices.forcemerge; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; import java.util.List; +import static java.lang.Math.min; + /** * A response for force merge action. + * + * @opensearch.internal */ public class ForceMergeResponse extends BroadcastResponse { @@ -76,4 +80,15 @@ public class ForceMergeResponse extends BroadcastResponse { public static ForceMergeResponse fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()).append("["); + builder.append("total_shards=").append(getTotalShards()).append(','); + builder.append("successful_shards=").append(getSuccessfulShards()).append(','); + builder.append("failed_shards=").append(getFailedShards()).append(','); + builder.append("failures=").append(Arrays.asList(getShardFailures()).subList(0, min(3, getShardFailures().length))); + return builder.append(']').toString(); + } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/TransportForceMergeAction.java b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/TransportForceMergeAction.java index 22f4b912cbe0b..fb8eb86c12269 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/TransportForceMergeAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/forcemerge/TransportForceMergeAction.java @@ -33,7 +33,6 @@ package org.opensearch.action.admin.indices.forcemerge; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -43,7 +42,8 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; @@ -54,6 +54,8 @@ /** * ForceMerge index/indices action. + * + * @opensearch.internal */ public class TransportForceMergeAction extends TransportBroadcastByNodeAction< ForceMergeRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexAction.java index 6d5875a012b91..92200c71ef685 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to get information about an index. + * + * @opensearch.internal + */ public class GetIndexAction extends ActionType { public static final GetIndexAction INSTANCE = new GetIndexAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequest.java index e1e0b2c54c904..0b2084865e23e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequest.java @@ -34,16 +34,23 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.info.ClusterInfoRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.util.ArrayUtils; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * A request to retrieve information about an index. + * + * @opensearch.internal */ public class GetIndexRequest extends ClusterInfoRequest { + /** + * The features to get. + * + * @opensearch.internal + */ public enum Feature { ALIASES((byte) 0), MAPPINGS((byte) 1), diff --git a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequestBuilder.java index 6f93614fe3487..3019191e5570e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.support.master.info.ClusterInfoRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder to get information about an index. + * + * @opensearch.internal + */ public class GetIndexRequestBuilder extends ClusterInfoRequestBuilder { public GetIndexRequestBuilder(OpenSearchClient client, GetIndexAction action, String... indices) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexResponse.java index 4465dc88fe87d..4c06a69c74104 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/get/GetIndexResponse.java @@ -32,47 +32,50 @@ package org.opensearch.action.admin.indices.get; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; import java.io.IOException; 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.Objects; /** * A response for a get index action. + * + * @opensearch.internal */ public class GetIndexResponse extends ActionResponse implements ToXContentObject { - private ImmutableOpenMap mappings = ImmutableOpenMap.of(); - private ImmutableOpenMap> aliases = ImmutableOpenMap.of(); - private ImmutableOpenMap settings = ImmutableOpenMap.of(); - private ImmutableOpenMap defaultSettings = ImmutableOpenMap.of(); - private ImmutableOpenMap dataStreams = ImmutableOpenMap.of(); + private Map mappings = Map.of(); + private Map> aliases = Map.of(); + private Map settings = Map.of(); + private Map defaultSettings = Map.of(); + private Map dataStreams = Map.of(); private final String[] indices; public GetIndexResponse( String[] indices, - ImmutableOpenMap mappings, - ImmutableOpenMap> aliases, - ImmutableOpenMap settings, - ImmutableOpenMap defaultSettings, - ImmutableOpenMap dataStreams + Map mappings, + final Map> aliases, + final Map settings, + final Map defaultSettings, + final Map dataStreams ) { this.indices = indices; // to have deterministic order @@ -81,16 +84,16 @@ public GetIndexResponse( this.mappings = mappings; } if (aliases != null) { - this.aliases = aliases; + this.aliases = Collections.unmodifiableMap(aliases); } if (settings != null) { - this.settings = settings; + this.settings = Collections.unmodifiableMap(settings); } if (defaultSettings != null) { - this.defaultSettings = defaultSettings; + this.defaultSettings = Collections.unmodifiableMap(defaultSettings); } if (dataStreams != null) { - this.dataStreams = dataStreams; + this.dataStreams = Collections.unmodifiableMap(dataStreams); } } @@ -99,7 +102,7 @@ public GetIndexResponse( this.indices = in.readStringArray(); int mappingsSize = in.readVInt(); - ImmutableOpenMap.Builder mappingsMapBuilder = ImmutableOpenMap.builder(); + Map mappingsMapBuilder = new HashMap<>(); for (int i = 0; i < mappingsSize; i++) { String index = in.readString(); if (in.getVersion().before(Version.V_2_0_0)) { @@ -120,10 +123,10 @@ public GetIndexResponse( mappingsMapBuilder.put(index, metadata != null ? metadata : MappingMetadata.EMPTY_MAPPINGS); } } - mappings = mappingsMapBuilder.build(); + mappings = Collections.unmodifiableMap(mappingsMapBuilder); int aliasesSize = in.readVInt(); - ImmutableOpenMap.Builder> aliasesMapBuilder = ImmutableOpenMap.builder(); + final Map> aliasesMapBuilder = new HashMap<>(); for (int i = 0; i < aliasesSize; i++) { String key = in.readString(); int valueSize = in.readVInt(); @@ -133,30 +136,30 @@ public GetIndexResponse( } aliasesMapBuilder.put(key, Collections.unmodifiableList(aliasEntryBuilder)); } - aliases = aliasesMapBuilder.build(); + aliases = Collections.unmodifiableMap(aliasesMapBuilder); int settingsSize = in.readVInt(); - ImmutableOpenMap.Builder settingsMapBuilder = ImmutableOpenMap.builder(); + final Map settingsMapBuilder = new HashMap<>(); for (int i = 0; i < settingsSize; i++) { String key = in.readString(); settingsMapBuilder.put(key, Settings.readSettingsFromStream(in)); } - settings = settingsMapBuilder.build(); + settings = Collections.unmodifiableMap(settingsMapBuilder); - ImmutableOpenMap.Builder defaultSettingsMapBuilder = ImmutableOpenMap.builder(); + final Map defaultSettingsMapBuilder = new HashMap<>(); int defaultSettingsSize = in.readVInt(); for (int i = 0; i < defaultSettingsSize; i++) { defaultSettingsMapBuilder.put(in.readString(), Settings.readSettingsFromStream(in)); } - defaultSettings = defaultSettingsMapBuilder.build(); + defaultSettings = Collections.unmodifiableMap(defaultSettingsMapBuilder); if (in.getVersion().onOrAfter(LegacyESVersion.V_7_8_0)) { - ImmutableOpenMap.Builder dataStreamsMapBuilder = ImmutableOpenMap.builder(); + final Map dataStreamsMapBuilder = new HashMap<>(); int dataStreamsSize = in.readVInt(); for (int i = 0; i < dataStreamsSize; i++) { dataStreamsMapBuilder.put(in.readString(), in.readOptionalString()); } - dataStreams = dataStreamsMapBuilder.build(); + dataStreams = Collections.unmodifiableMap(dataStreamsMapBuilder); } } @@ -168,31 +171,31 @@ public String[] getIndices() { return indices(); } - public ImmutableOpenMap mappings() { + public Map mappings() { return mappings; } - public ImmutableOpenMap getMappings() { + public Map getMappings() { return mappings(); } - public ImmutableOpenMap> aliases() { + public Map> aliases() { return aliases; } - public ImmutableOpenMap> getAliases() { + public Map> getAliases() { return aliases(); } - public ImmutableOpenMap settings() { + public Map settings() { return settings; } - public ImmutableOpenMap dataStreams() { + public Map dataStreams() { return dataStreams; } - public ImmutableOpenMap getDataStreams() { + public Map getDataStreams() { return dataStreams(); } @@ -204,11 +207,11 @@ public ImmutableOpenMap getDataStreams() { * via {@link #settings()}. * See also {@link GetIndexRequest#includeDefaults(boolean)} */ - public ImmutableOpenMap defaultSettings() { + public Map defaultSettings() { return defaultSettings; } - public ImmutableOpenMap getSettings() { + public Map getSettings() { return settings(); } @@ -240,41 +243,41 @@ public String getSetting(String index, String setting) { public void writeTo(StreamOutput out) throws IOException { out.writeStringArray(indices); out.writeVInt(mappings.size()); - for (ObjectObjectCursor indexEntry : mappings) { - out.writeString(indexEntry.key); + for (final Map.Entry indexEntry : mappings.entrySet()) { + out.writeString(indexEntry.getKey()); if (out.getVersion().before(Version.V_2_0_0)) { - out.writeVInt(indexEntry.value == MappingMetadata.EMPTY_MAPPINGS ? 0 : 1); - if (indexEntry.value != MappingMetadata.EMPTY_MAPPINGS) { + out.writeVInt(indexEntry.getValue() == MappingMetadata.EMPTY_MAPPINGS ? 0 : 1); + if (indexEntry.getValue() != MappingMetadata.EMPTY_MAPPINGS) { out.writeString(MapperService.SINGLE_MAPPING_NAME); - indexEntry.value.writeTo(out); + indexEntry.getValue().writeTo(out); } } else { - out.writeOptionalWriteable(indexEntry.value); + out.writeOptionalWriteable(indexEntry.getValue()); } } out.writeVInt(aliases.size()); - for (ObjectObjectCursor> indexEntry : aliases) { - out.writeString(indexEntry.key); - out.writeVInt(indexEntry.value.size()); - for (AliasMetadata aliasEntry : indexEntry.value) { + for (final Map.Entry> indexEntry : aliases.entrySet()) { + out.writeString(indexEntry.getKey()); + out.writeVInt(indexEntry.getValue().size()); + for (AliasMetadata aliasEntry : indexEntry.getValue()) { aliasEntry.writeTo(out); } } out.writeVInt(settings.size()); - for (ObjectObjectCursor indexEntry : settings) { - out.writeString(indexEntry.key); - Settings.writeSettingsToStream(indexEntry.value, out); + for (final Map.Entry indexEntry : settings.entrySet()) { + out.writeString(indexEntry.getKey()); + Settings.writeSettingsToStream(indexEntry.getValue(), out); } out.writeVInt(defaultSettings.size()); - for (ObjectObjectCursor indexEntry : defaultSettings) { - out.writeString(indexEntry.key); - Settings.writeSettingsToStream(indexEntry.value, out); + for (final Map.Entry indexEntry : defaultSettings.entrySet()) { + out.writeString(indexEntry.getKey()); + Settings.writeSettingsToStream(indexEntry.getValue(), out); } if (out.getVersion().onOrAfter(LegacyESVersion.V_7_8_0)) { out.writeVInt(dataStreams.size()); - for (ObjectObjectCursor indexEntry : dataStreams) { - out.writeString(indexEntry.key); - out.writeOptionalString(indexEntry.value); + for (final Map.Entry indexEntry : dataStreams.entrySet()) { + out.writeString(indexEntry.getKey()); + out.writeOptionalString(indexEntry.getValue()); } } } @@ -330,7 +333,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/indices/get/TransportGetIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/get/TransportGetIndexAction.java index 0cd3214307359..755119401c6b5 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/get/TransportGetIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/get/TransportGetIndexAction.java @@ -32,32 +32,36 @@ package org.opensearch.action.admin.indices.get; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.info.TransportClusterInfoAction; +import org.opensearch.action.support.clustermanager.info.TransportClusterInfoAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.AliasMetadata; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Spliterators; import java.util.stream.Collectors; import java.util.stream.StreamSupport; /** * Get index action. + * + * @opensearch.internal */ public class TransportGetIndexAction extends TransportClusterInfoAction { @@ -96,22 +100,20 @@ protected GetIndexResponse read(StreamInput in) throws IOException { } @Override - protected void doMasterOperation( + protected void doClusterManagerOperation( final GetIndexRequest request, String[] concreteIndices, final ClusterState state, final ActionListener listener ) { - ImmutableOpenMap mappingsResult = ImmutableOpenMap.of(); - ImmutableOpenMap> aliasesResult = ImmutableOpenMap.of(); - ImmutableOpenMap settings = ImmutableOpenMap.of(); - ImmutableOpenMap defaultSettings = ImmutableOpenMap.of(); - ImmutableOpenMap dataStreams = ImmutableOpenMap.builder() - .putAll( - StreamSupport.stream(state.metadata().findDataStreams(concreteIndices).spliterator(), false) - .collect(Collectors.toMap(k -> k.key, v -> v.value.getName())) - ) - .build(); + Map mappingsResult = Map.of(); + Map> aliasesResult = Map.of(); + Map settings = Map.of(); + Map defaultSettings = Map.of(); + final Map dataStreams = new HashMap<>( + StreamSupport.stream(Spliterators.spliterator(state.metadata().findDataStreams(concreteIndices).entrySet(), 0), false) + .collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue().getName())) + ); GetIndexRequest.Feature[] features = request.features(); boolean doneAliases = false; boolean doneMappings = false; @@ -130,15 +132,15 @@ protected void doMasterOperation( } break; case ALIASES: - if (!doneAliases) { + if (doneAliases == false) { aliasesResult = state.metadata().findAllAliases(concreteIndices); doneAliases = true; } break; case SETTINGS: if (!doneSettings) { - ImmutableOpenMap.Builder settingsMapBuilder = ImmutableOpenMap.builder(); - ImmutableOpenMap.Builder defaultSettingsMapBuilder = ImmutableOpenMap.builder(); + final Map settingsMapBuilder = new HashMap<>(); + final Map defaultSettingsMapBuilder = new HashMap<>(); for (String index : concreteIndices) { Settings indexSettings = state.metadata().index(index).getSettings(); if (request.humanReadable()) { @@ -152,8 +154,8 @@ protected void doMasterOperation( defaultSettingsMapBuilder.put(index, defaultIndexSettings); } } - settings = settingsMapBuilder.build(); - defaultSettings = defaultSettingsMapBuilder.build(); + settings = settingsMapBuilder; + defaultSettings = defaultSettingsMapBuilder; doneSettings = true; } break; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/get/package-info.java new file mode 100644 index 0000000000000..b438da7ef31dc --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Get Index transport handler. */ +package org.opensearch.action.admin.indices.get; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsAction.java index 320cfa622f11a..2ddd763be20fd 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to get field mappings. + * + * @opensearch.internal + */ public class GetFieldMappingsAction extends ActionType { public static final GetFieldMappingsAction INSTANCE = new GetFieldMappingsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsIndexRequest.java index 961662ecdcf7e..c82db7eca3fd9 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsIndexRequest.java @@ -37,12 +37,17 @@ import org.opensearch.action.OriginalIndices; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.single.shard.SingleShardRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport action to get field mappings. + * + * @opensearch.internal + */ public class GetFieldMappingsIndexRequest extends SingleShardRequest { private final boolean includeDefaults; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsRequest.java index e6a2ad3187250..e3c812aedcfe7 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsRequest.java @@ -37,9 +37,9 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Arrays; @@ -49,6 +49,8 @@ * * Note: there is a new class with the same name for the Java HLRC that uses a typeless format. * Any changes done to this class should go to that client class as well. + * + * @opensearch.internal */ public class GetFieldMappingsRequest extends ActionRequest implements IndicesRequest.Replaceable { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsRequestBuilder.java index 4a8c624e7e06e..ebc0c015c5140 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsRequestBuilder.java @@ -37,7 +37,11 @@ import org.opensearch.client.OpenSearchClient; import org.opensearch.common.util.ArrayUtils; -/** A helper class to build {@link GetFieldMappingsRequest} objects */ +/** + * A helper class to build {@link GetFieldMappingsRequest} objects + * + * @opensearch.internal + **/ public class GetFieldMappingsRequestBuilder extends ActionRequestBuilder { public GetFieldMappingsRequestBuilder(OpenSearchClient client, GetFieldMappingsAction action, String... indices) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java index 12024ef455a32..d874b5bb6b1ac 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java @@ -33,19 +33,19 @@ package org.opensearch.action.admin.indices.mapping.get; import org.opensearch.Version; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.Mapper; import org.opensearch.index.mapper.MapperService; @@ -56,14 +56,16 @@ import java.util.Objects; import static java.util.Collections.unmodifiableMap; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Response object for {@link GetFieldMappingsRequest} API * * Note: there is a new class with the same name for the Java HLRC that uses a typeless format. * Any changes done to this class should go to that client class as well. + * + * @opensearch.internal */ public class GetFieldMappingsResponse extends ActionResponse implements ToXContentObject { @@ -173,6 +175,11 @@ private void addFieldMappingsToBuilder(XContentBuilder builder, Params params, M } } + /** + * Metadata for field mappings for toXContent + * + * @opensearch.internal + */ public static class FieldMappingMetadata implements ToXContentFragment { private static final ParseField FULL_NAME = new ParseField("full_name"); @@ -207,7 +214,7 @@ public String fullName() { /** Returns the mappings as a map. Note that the returned map has a single key which is always the field's {@link Mapper#name}. */ public Map sourceAsMap() { - return XContentHelper.convertToMap(source, true, XContentType.JSON).v2(); + return XContentHelper.convertToMap(source, true, MediaTypeRegistry.JSON).v2(); } // pkg-private for testing @@ -226,7 +233,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field("mapping", sourceAsMap()); } else { try (InputStream stream = source.streamInput()) { - builder.rawField(MAPPING.getPreferredName(), stream, XContentType.JSON); + builder.rawField(MAPPING.getPreferredName(), stream, MediaTypeRegistry.JSON); } } return builder; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsAction.java index 6d9ed5ba0411a..a78625a1595ad 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to get field mappings. + * + * @opensearch.internal + */ public class GetMappingsAction extends ActionType { public static final GetMappingsAction INSTANCE = new GetMappingsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequest.java index ae78f5f3a0b30..3988b0dd5a508 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequest.java @@ -34,10 +34,15 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.info.ClusterInfoRequest; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Transport request to get field mappings. + * + * @opensearch.internal + */ public class GetMappingsRequest extends ClusterInfoRequest { public GetMappingsRequest() {} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequestBuilder.java index 8401272353eaf..85bf8c2ffd9c6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.support.master.info.ClusterInfoRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder to get field mappings. + * + * @opensearch.internal + */ public class GetMappingsRequestBuilder extends ClusterInfoRequestBuilder< GetMappingsRequest, GetMappingsResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsResponse.java index 3be8e75be7290..e59f08c4fa162 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/GetMappingsResponse.java @@ -32,35 +32,42 @@ package org.opensearch.action.admin.indices.mapping.get; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.Version; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.MappingMetadata; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +/** + * Transport response to get field mappings. + * + * @opensearch.internal + */ public class GetMappingsResponse extends ActionResponse implements ToXContentFragment { private static final ParseField MAPPINGS = new ParseField("mappings"); - private final ImmutableOpenMap mappings; + private final Map mappings; - public GetMappingsResponse(ImmutableOpenMap mappings) { - this.mappings = mappings; + public GetMappingsResponse(final Map mappings) { + this.mappings = Collections.unmodifiableMap(mappings); } GetMappingsResponse(StreamInput in) throws IOException { super(in); int size = in.readVInt(); - ImmutableOpenMap.Builder indexMapBuilder = ImmutableOpenMap.builder(); + final Map indexMapBuilder = new HashMap<>(); for (int i = 0; i < size; i++) { String index = in.readString(); if (in.getVersion().before(Version.V_2_0_0)) { @@ -81,40 +88,40 @@ public GetMappingsResponse(ImmutableOpenMap mappings) { indexMapBuilder.put(index, hasMapping ? new MappingMetadata(in) : MappingMetadata.EMPTY_MAPPINGS); } } - mappings = indexMapBuilder.build(); + mappings = Collections.unmodifiableMap(indexMapBuilder); } - public ImmutableOpenMap mappings() { + public Map mappings() { return mappings; } - public ImmutableOpenMap getMappings() { + public Map getMappings() { return mappings(); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVInt(mappings.size()); - for (ObjectObjectCursor indexEntry : mappings) { - out.writeString(indexEntry.key); + for (Map.Entry indexEntry : mappings.entrySet()) { + out.writeString(indexEntry.getKey()); if (out.getVersion().before(Version.V_2_0_0)) { - out.writeVInt(indexEntry.value == MappingMetadata.EMPTY_MAPPINGS ? 0 : 1); - if (indexEntry.value != MappingMetadata.EMPTY_MAPPINGS) { + out.writeVInt(indexEntry.getValue() == MappingMetadata.EMPTY_MAPPINGS ? 0 : 1); + if (indexEntry.getValue() != MappingMetadata.EMPTY_MAPPINGS) { out.writeString(MapperService.SINGLE_MAPPING_NAME); - indexEntry.value.writeTo(out); + indexEntry.getValue().writeTo(out); } } else { - out.writeOptionalWriteable(indexEntry.value); + out.writeOptionalWriteable(indexEntry.getValue()); } } } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - for (final ObjectObjectCursor indexEntry : getMappings()) { - builder.startObject(indexEntry.key); - if (indexEntry.value != null) { - builder.field(MAPPINGS.getPreferredName(), indexEntry.value.sourceAsMap()); + for (final Map.Entry indexEntry : getMappings().entrySet()) { + builder.startObject(indexEntry.getKey()); + if (indexEntry.getValue() != null) { + builder.field(MAPPINGS.getPreferredName(), indexEntry.getValue().sourceAsMap()); } else { builder.startObject(MAPPINGS.getPreferredName()).endObject(); } @@ -125,7 +132,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java index bdb5222a6dcba..53dbb86233803 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java @@ -32,13 +32,13 @@ package org.opensearch.action.admin.indices.mapping.get; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -50,6 +50,11 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; +/** + * Transport action to get field mappings. + * + * @opensearch.internal + */ public class TransportGetFieldMappingsAction extends HandledTransportAction { private final ClusterService clusterService; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetFieldMappingsIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetFieldMappingsIndexAction.java index ca07475f0deab..368fef1446ba5 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetFieldMappingsIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetFieldMappingsIndexAction.java @@ -43,18 +43,18 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.regex.Regex; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentHelper; import org.opensearch.index.IndexService; -import org.opensearch.index.mapper.MappingLookup; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.Mapper; -import org.opensearch.index.shard.ShardId; +import org.opensearch.index.mapper.MappingLookup; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -69,6 +69,8 @@ /** * Transport action used to retrieve the mappings related to fields that belong to a specific index + * + * @opensearch.internal */ public class TransportGetFieldMappingsIndexAction extends TransportSingleShardAction< GetFieldMappingsIndexRequest, @@ -220,7 +222,7 @@ private static void addFieldMapper( try { BytesReference bytes = XContentHelper.toXContent( fieldMapper, - XContentType.JSON, + MediaTypeRegistry.JSON, includeDefaults ? includeDefaultsParams : ToXContent.EMPTY_PARAMS, false ); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetMappingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetMappingsAction.java index 3f6cb8ed35af9..6f07aa4dbf48a 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetMappingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/TransportGetMappingsAction.java @@ -34,22 +34,27 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.info.TransportClusterInfoAction; +import org.opensearch.action.support.clustermanager.info.TransportClusterInfoAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.MappingMetadata; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.Map; +/** + * Transport action to get field mappings. + * + * @opensearch.internal + */ public class TransportGetMappingsAction extends TransportClusterInfoAction { private static final Logger logger = LogManager.getLogger(TransportGetMappingsAction.class); @@ -83,7 +88,7 @@ protected GetMappingsResponse read(StreamInput in) throws IOException { } @Override - protected void doMasterOperation( + protected void doClusterManagerOperation( final GetMappingsRequest request, String[] concreteIndices, final ClusterState state, @@ -91,8 +96,7 @@ protected void doMasterOperation( ) { logger.trace("serving getMapping request based on version {}", state.version()); try { - ImmutableOpenMap result = state.metadata() - .findMappings(concreteIndices, indicesService.getFieldFilter()); + final Map result = state.metadata().findMappings(concreteIndices, indicesService.getFieldFilter()); listener.onResponse(new GetMappingsResponse(result)); } catch (IOException e) { listener.onFailure(e); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/package-info.java new file mode 100644 index 0000000000000..fcdd9846640bc --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Get Mapping transport handlers. */ +package org.opensearch.action.admin.indices.mapping.get; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/AutoPutMappingAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/AutoPutMappingAction.java index 72cfcdaffca31..f2430eb54db9b 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/AutoPutMappingAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/AutoPutMappingAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action to automatically put field mappings. + * + * @opensearch.internal + */ public class AutoPutMappingAction extends ActionType { public static final AutoPutMappingAction INSTANCE = new AutoPutMappingAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingAction.java index 4f90e38ac4416..8bca1b59ee2e2 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action to put field mappings. + * + * @opensearch.internal + */ public class PutMappingAction extends ActionType { public static final PutMappingAction INSTANCE = new PutMappingAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingClusterStateUpdateRequest.java index 27081048fcdae..8d51182d838cc 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingClusterStateUpdateRequest.java @@ -36,6 +36,8 @@ /** * Cluster state update request that allows to put a mapping + * + * @opensearch.internal */ public class PutMappingClusterStateUpdateRequest extends IndicesClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingRequest.java index be3e676a4a1a2..1e780373e51df 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingRequest.java @@ -32,7 +32,6 @@ package org.opensearch.action.admin.indices.mapping.put; -import com.carrotsearch.hppc.ObjectHashSet; import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchGenerationException; import org.opensearch.Version; @@ -41,18 +40,19 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.util.CollectionUtils; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; import java.io.IOException; @@ -61,6 +61,7 @@ import java.util.Arrays; import java.util.Map; import java.util.Objects; +import java.util.Set; import static org.opensearch.action.ValidateActions.addValidationError; @@ -74,10 +75,12 @@ * @see org.opensearch.client.Requests#putMappingRequest(String...) * @see org.opensearch.client.IndicesAdminClient#putMapping(PutMappingRequest) * @see AcknowledgedResponse + * + * @opensearch.internal */ public class PutMappingRequest extends AcknowledgedRequest implements IndicesRequest.Replaceable, ToXContentObject { - private static ObjectHashSet RESERVED_FIELDS = ObjectHashSet.from( + private static final Set RESERVED_FIELDS = Set.of( "_uid", "_id", "_type", @@ -301,7 +304,7 @@ public PutMappingRequest source(XContentBuilder mappingBuilder) { */ public PutMappingRequest source(Map mappingSource) { try { - XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(MediaTypeRegistry.JSON); builder.map(mappingSource); return source(BytesReference.bytes(builder), builder.contentType()); } catch (IOException e) { @@ -312,17 +315,17 @@ public PutMappingRequest source(Map mappingSource) { /** * The mapping source definition. */ - public PutMappingRequest source(String mappingSource, XContentType xContentType) { - return source(new BytesArray(mappingSource), xContentType); + public PutMappingRequest source(String mappingSource, MediaType mediaType) { + return source(new BytesArray(mappingSource), mediaType); } /** * The mapping source definition. */ - public PutMappingRequest source(BytesReference mappingSource, XContentType xContentType) { - Objects.requireNonNull(xContentType); + public PutMappingRequest source(BytesReference mappingSource, MediaType mediaType) { + Objects.requireNonNull(mediaType); try { - this.source = XContentHelper.convertToJson(mappingSource, false, false, xContentType); + this.source = XContentHelper.convertToJson(mappingSource, false, false, mediaType); return this; } catch (IOException e) { throw new UncheckedIOException("failed to convert source to json", e); @@ -361,7 +364,7 @@ public void writeTo(StreamOutput out) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { if (source != null) { try (InputStream stream = new BytesArray(source).streamInput()) { - builder.rawValue(stream, XContentType.JSON); + builder.rawValue(stream, MediaTypeRegistry.JSON); } } else { builder.startObject().endObject(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingRequestBuilder.java index 3ef96254b3f9b..9d703bf428c8e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/PutMappingRequestBuilder.java @@ -36,14 +36,16 @@ import org.opensearch.action.support.master.AcknowledgedRequestBuilder; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.XContentBuilder; import java.util.Map; /** * Builder for a put mapping request + * + * @opensearch.internal */ public class PutMappingRequestBuilder extends AcknowledgedRequestBuilder< PutMappingRequest, @@ -93,8 +95,8 @@ public PutMappingRequestBuilder setSource(Map mappingSource) { /** * The mapping source definition. */ - public PutMappingRequestBuilder setSource(String mappingSource, XContentType xContentType) { - request.source(mappingSource, xContentType); + public PutMappingRequestBuilder setSource(String mappingSource, MediaType mediaType) { + request.source(mappingSource, mediaType); return this; } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/TransportAutoPutMappingAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/TransportAutoPutMappingAction.java index a172fce831c8f..4722c1048014f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/TransportAutoPutMappingAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/TransportAutoPutMappingAction.java @@ -31,10 +31,9 @@ package org.opensearch.action.admin.indices.mapping.put; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -42,15 +41,21 @@ import org.opensearch.cluster.metadata.MetadataMappingService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportAutoPutMappingAction extends TransportMasterNodeAction { +/** + * Transport action to automatically put field mappings. + * + * @opensearch.internal + */ +public class TransportAutoPutMappingAction extends TransportClusterManagerNodeAction { private final MetadataMappingService metadataMappingService; @@ -102,7 +107,7 @@ protected ClusterBlockException checkBlock(PutMappingRequest request, ClusterSta } @Override - protected void masterOperation( + protected void clusterManagerOperation( final PutMappingRequest request, final ClusterState state, final ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/TransportPutMappingAction.java b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/TransportPutMappingAction.java index f1093a15a3d26..ac797914aafd8 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/TransportPutMappingAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/mapping/put/TransportPutMappingAction.java @@ -35,11 +35,10 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.RequestValidators; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.block.ClusterBlockException; @@ -48,8 +47,9 @@ import org.opensearch.cluster.metadata.MetadataMappingService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexNotFoundException; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -63,8 +63,10 @@ /** * Put mapping action. + * + * @opensearch.internal */ -public class TransportPutMappingAction extends TransportMasterNodeAction { +public class TransportPutMappingAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportPutMappingAction.class); @@ -117,7 +119,7 @@ protected ClusterBlockException checkBlock(PutMappingRequest request, ClusterSta } @Override - protected void masterOperation( + protected void clusterManagerOperation( final PutMappingRequest request, final ClusterState state, final ActionListener listener @@ -169,7 +171,7 @@ static void performMappingUpdate( ) { PutMappingClusterStateUpdateRequest updateRequest = new PutMappingClusterStateUpdateRequest(request.source()).indices( concreteIndices - ).ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout()); + ).ackTimeout(request.timeout()).masterNodeTimeout(request.clusterManagerNodeTimeout()); metadataMappingService.putMapping(updateRequest, new ActionListener() { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexAction.java index 1b80e5cb8a347..5c4302d94e4a6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to open an index. + * + * @opensearch.internal + */ public class OpenIndexAction extends ActionType { public static final OpenIndexAction INSTANCE = new OpenIndexAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexClusterStateUpdateRequest.java index bf299d103ba78..a36c2744f7669 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexClusterStateUpdateRequest.java @@ -36,6 +36,8 @@ /** * Cluster state update request that allows to open one or more indices + * + * @opensearch.internal */ public class OpenIndexClusterStateUpdateRequest extends IndicesClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexRequest.java index be0e0254edff6..16451e311e7d3 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexRequest.java @@ -37,22 +37,26 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.util.CollectionUtils; import java.io.IOException; +import java.util.Arrays; import static org.opensearch.action.ValidateActions.addValidationError; /** * A request to open an index. + * + * @opensearch.internal */ public class OpenIndexRequest extends AcknowledgedRequest implements IndicesRequest.Replaceable { private String[] indices; private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, true, false, true); private ActiveShardCount waitForActiveShards = ActiveShardCount.DEFAULT; + private boolean shouldStoreResult; public OpenIndexRequest(StreamInput in) throws IOException { super(in); @@ -159,6 +163,18 @@ public OpenIndexRequest waitForActiveShards(final int waitForActiveShards) { return waitForActiveShards(ActiveShardCount.from(waitForActiveShards)); } + /** + * Should this task store its result after it has finished? + */ + public void setShouldStoreResult(boolean shouldStoreResult) { + this.shouldStoreResult = shouldStoreResult; + } + + @Override + public boolean getShouldStoreResult() { + return shouldStoreResult; + } + @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); @@ -166,4 +182,14 @@ public void writeTo(StreamOutput out) throws IOException { indicesOptions.writeIndicesOptions(out); waitForActiveShards.writeTo(out); } + + @Override + public String toString() { + return "open indices " + Arrays.toString(indices()); + } + + @Override + public String getDescription() { + return this.toString(); + } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexRequestBuilder.java index 5aadd81cc8838..bf09c3f173491 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexRequestBuilder.java @@ -39,6 +39,8 @@ /** * Builder for for open index request + * + * @opensearch.internal */ public class OpenIndexRequestBuilder extends AcknowledgedRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexResponse.java index 4b811b215d717..bd96a1071c129 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/open/OpenIndexResponse.java @@ -33,15 +33,17 @@ package org.opensearch.action.admin.indices.open; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; /** * A response for a open index action. + * + * @opensearch.internal */ public class OpenIndexResponse extends ShardsAcknowledgedResponse { @@ -72,4 +74,13 @@ public void writeTo(StreamOutput out) throws IOException { public static OpenIndexResponse fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()).append("["); + builder.append("acknowledged=").append(isAcknowledged()).append(','); + builder.append("shards_acknowledged=").append(isShardsAcknowledged()); + return builder.append(']').toString(); + } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/open/TransportOpenIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/open/TransportOpenIndexAction.java index 05b3bfd7a885e..0243990dce2ff 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/open/TransportOpenIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/open/TransportOpenIndexAction.java @@ -35,10 +35,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.DestructiveOperations; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.OpenIndexClusterStateUpdateResponse; import org.opensearch.cluster.block.ClusterBlockException; @@ -47,8 +46,9 @@ import org.opensearch.cluster.metadata.MetadataIndexStateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -57,8 +57,10 @@ /** * Open index action + * + * @opensearch.internal */ -public class TransportOpenIndexAction extends TransportMasterNodeAction { +public class TransportOpenIndexAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportOpenIndexAction.class); @@ -112,7 +114,7 @@ protected ClusterBlockException checkBlock(OpenIndexRequest request, ClusterStat } @Override - protected void masterOperation( + protected void clusterManagerOperation( final OpenIndexRequest request, final ClusterState state, final ActionListener listener @@ -123,7 +125,7 @@ protected void masterOperation( return; } OpenIndexClusterStateUpdateRequest updateRequest = new OpenIndexClusterStateUpdateRequest().ackTimeout(request.timeout()) - .masterNodeTimeout(request.masterNodeTimeout()) + .masterNodeTimeout(request.clusterManagerNodeTimeout()) .indices(concreteIndices) .waitForActiveShards(request.waitForActiveShards()); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/open/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/open/package-info.java new file mode 100644 index 0000000000000..4fdea8fd18769 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/open/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Open Index transport handlers. */ +package org.opensearch.action.admin.indices.open; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockAction.java b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockAction.java index 482f543ae6b04..3bca633554908 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to add an index block. + * + * @opensearch.internal + */ public class AddIndexBlockAction extends ActionType { public static final AddIndexBlockAction INSTANCE = new AddIndexBlockAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockClusterStateUpdateRequest.java index c934cad44b097..4b44624ece303 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockClusterStateUpdateRequest.java @@ -36,6 +36,8 @@ /** * Cluster state update request that allows to add a block to one or more indices + * + * @opensearch.internal */ public class AddIndexBlockClusterStateUpdateRequest extends IndicesClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockRequest.java index c2f24d3e927b5..b5097f96fe52b 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockRequest.java @@ -37,9 +37,9 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; import org.opensearch.cluster.metadata.IndexMetadata.APIBlock; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.util.CollectionUtils; import java.io.IOException; import java.util.Objects; @@ -48,6 +48,8 @@ /** * A request to add a block to an index. + * + * @opensearch.internal */ public class AddIndexBlockRequest extends AcknowledgedRequest implements IndicesRequest.Replaceable { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockRequestBuilder.java index 074e7fc5f6664..8322ba19f433e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockRequestBuilder.java @@ -39,6 +39,8 @@ /** * Builder for add index block request + * + * @opensearch.internal */ public class AddIndexBlockRequestBuilder extends AcknowledgedRequestBuilder< AddIndexBlockRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockResponse.java index 036a8ec635efc..13cee3f8e0159 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/readonly/AddIndexBlockResponse.java @@ -32,17 +32,18 @@ package org.opensearch.action.admin.indices.readonly; import org.opensearch.OpenSearchException; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.util.CollectionUtils; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.Index; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.List; @@ -50,6 +51,11 @@ import static java.util.Collections.unmodifiableList; +/** + * Transport response to open an index. + * + * @opensearch.internal + */ public class AddIndexBlockResponse extends ShardsAcknowledgedResponse { private final List indices; @@ -87,9 +93,14 @@ protected void addCustomFields(final XContentBuilder builder, final Params param @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * Result for adding a block + * + * @opensearch.internal + */ public static class AddBlockResult implements Writeable, ToXContentFragment { private final Index index; @@ -181,10 +192,15 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } + /** + * Per shard result for adding a block + * + * @opensearch.internal + */ public static class AddBlockShardResult implements Writeable, ToXContentFragment { private final int id; @@ -236,9 +252,14 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * Contains failure information + * + * @opensearch.internal + */ public static class Failure extends DefaultShardOperationFailedException { private @Nullable String nodeId; @@ -277,7 +298,7 @@ public XContentBuilder innerToXContent(final XContentBuilder builder, final Para @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } static Failure readFailure(final StreamInput in) throws IOException { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/readonly/TransportAddIndexBlockAction.java b/server/src/main/java/org/opensearch/action/admin/indices/readonly/TransportAddIndexBlockAction.java index a58d199287ff7..eb018d16119e0 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/readonly/TransportAddIndexBlockAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/readonly/TransportAddIndexBlockAction.java @@ -35,10 +35,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.DestructiveOperations; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -46,8 +45,9 @@ import org.opensearch.cluster.metadata.MetadataIndexStateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -62,8 +62,10 @@ * in-flight writes to an index have been completed prior to the response being returned. These actions * are done in multiple cluster state updates (at least two). See also {@link TransportVerifyShardIndexBlockAction} * for the eventual delegation for shard-level verification. + * + * @opensearch.internal */ -public class TransportAddIndexBlockAction extends TransportMasterNodeAction { +public class TransportAddIndexBlockAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportAddIndexBlockAction.class); @@ -121,13 +123,13 @@ protected ClusterBlockException checkBlock(AddIndexBlockRequest request, Cluster } @Override - protected void masterOperation(AddIndexBlockRequest request, ClusterState state, ActionListener listener) + protected void clusterManagerOperation(AddIndexBlockRequest request, ClusterState state, ActionListener listener) throws Exception { throw new UnsupportedOperationException("The task parameter is required"); } @Override - protected void masterOperation( + protected void clusterManagerOperation( final Task task, final AddIndexBlockRequest request, final ClusterState state, @@ -142,7 +144,7 @@ protected void masterOperation( final AddIndexBlockClusterStateUpdateRequest addBlockRequest = new AddIndexBlockClusterStateUpdateRequest( request.getBlock(), task.getId() - ).ackTimeout(request.timeout()).masterNodeTimeout(request.masterNodeTimeout()).indices(concreteIndices); + ).ackTimeout(request.timeout()).masterNodeTimeout(request.clusterManagerNodeTimeout()).indices(concreteIndices); indexStateService.addIndexBlock(addBlockRequest, ActionListener.delegateResponse(listener, (delegatedListener, t) -> { logger.debug(() -> new ParameterizedMessage("failed to mark indices as readonly [{}]", (Object) concreteIndices), t); delegatedListener.onFailure(t); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/readonly/TransportVerifyShardIndexBlockAction.java b/server/src/main/java/org/opensearch/action/admin/indices/readonly/TransportVerifyShardIndexBlockAction.java index c96a94476c1fb..a86475a16a779 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/readonly/TransportVerifyShardIndexBlockAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/readonly/TransportVerifyShardIndexBlockAction.java @@ -33,7 +33,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionType; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.replication.ReplicationOperation; @@ -45,14 +44,15 @@ import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; -import org.opensearch.tasks.TaskId; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -64,6 +64,8 @@ * and are no longer executing any operations in violation of that block. This action * requests all operation permits of the shard in order to wait for all write operations * to complete. + * + * @opensearch.internal */ public class TransportVerifyShardIndexBlockAction extends TransportReplicationAction< TransportVerifyShardIndexBlockAction.ShardRequest, @@ -168,6 +170,8 @@ protected ReplicationOperation.Replicas newReplicasProxy() { * A {@link ReplicasProxy} that marks as stale the shards that are unavailable during the verification * and the flush of the shard. This is done to ensure that such shards won't be later promoted as primary * or reopened in an unverified state with potential non flushed translog operations. + * + * @opensearch.internal */ class VerifyShardReadOnlyActionReplicasProxy extends ReplicasProxy { @Override @@ -181,6 +185,11 @@ public void markShardCopyAsStaleIfNeeded( } } + /** + * Per shard request + * + * @opensearch.internal + */ public static class ShardRequest extends ReplicationRequest { private final ClusterBlock clusterBlock; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/readonly/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/readonly/package-info.java new file mode 100644 index 0000000000000..d34d3c64eb1ad --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/readonly/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** ReadOnly Index transport handlers. */ +package org.opensearch.action.admin.indices.readonly; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryAction.java b/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryAction.java index ce764f8890f86..ac00e7f404b37 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryAction.java @@ -36,6 +36,8 @@ /** * Recovery information action + * + * @opensearch.internal */ public class RecoveryAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryRequest.java index 2457ca3fc5b90..69b20b697dd9a 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryRequest.java @@ -34,14 +34,16 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * Request for recovery information + * + * @opensearch.internal */ public class RecoveryRequest extends BroadcastRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryRequestBuilder.java index 12c58c22fe8c8..99a1fb430fb28 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryRequestBuilder.java @@ -37,6 +37,8 @@ /** * Recovery information request builder. + * + * @opensearch.internal */ public class RecoveryRequestBuilder extends BroadcastOperationRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryResponse.java index 9c2b380392b03..e6440fd95aa39 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/recovery/RecoveryResponse.java @@ -32,12 +32,13 @@ package org.opensearch.action.admin.indices.recovery; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.indices.recovery.RecoveryState; import java.io.IOException; @@ -46,6 +47,8 @@ /** * Information regarding the recovery state of indices and their associated shards. + * + * @opensearch.internal */ public class RecoveryResponse extends BroadcastResponse { @@ -117,6 +120,6 @@ public void writeTo(StreamOutput out) throws IOException { @Override public String toString() { - return Strings.toString(this, true, true); + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/recovery/TransportRecoveryAction.java b/server/src/main/java/org/opensearch/action/admin/indices/recovery/TransportRecoveryAction.java index dd5ae31c01e56..de76ec2d4f5a7 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/recovery/TransportRecoveryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/recovery/TransportRecoveryAction.java @@ -33,7 +33,6 @@ package org.opensearch.action.admin.indices.recovery; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -43,7 +42,8 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.IndexService; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; @@ -60,6 +60,8 @@ /** * Transport action for shard recovery operation. This transport action does not actually * perform shard recovery, it only reports on recoveries (both active and complete). + * + * @opensearch.internal */ public class TransportRecoveryAction extends TransportBroadcastByNodeAction { @@ -87,7 +89,7 @@ public TransportRecoveryAction( @Override protected RecoveryState readShardResult(StreamInput in) throws IOException { - return RecoveryState.readRecoveryState(in); + return new RecoveryState(in); } @Override diff --git a/server/src/main/java/org/opensearch/action/admin/indices/recovery/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/recovery/package-info.java new file mode 100644 index 0000000000000..51b7dd7997a15 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/recovery/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Recovery transport handlers. */ +package org.opensearch.action.admin.indices.recovery; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshAction.java b/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshAction.java index 6b7006c3b70ce..c321700ae65bd 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Refresh information action + * + * @opensearch.internal + */ public class RefreshAction extends ActionType { public static final RefreshAction INSTANCE = new RefreshAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshRequest.java index c113527d1cc2f..c6e230cc66373 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshRequest.java @@ -33,7 +33,7 @@ package org.opensearch.action.admin.indices.refresh; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; @@ -45,6 +45,8 @@ * @see org.opensearch.client.Requests#refreshRequest(String...) * @see org.opensearch.client.IndicesAdminClient#refresh(RefreshRequest) * @see RefreshResponse + * + * @opensearch.internal */ public class RefreshRequest extends BroadcastRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshRequestBuilder.java index 47a16f2fc968d..5b27ae13f24be 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshRequestBuilder.java @@ -39,6 +39,8 @@ * A refresh request making all operations performed since the last refresh available for search. The (near) real-time * capabilities depends on the index engine used. For example, the internal one requires refresh to be called, but by * default a refresh is scheduled periodically. + * + * @opensearch.internal */ public class RefreshRequestBuilder extends BroadcastOperationRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshResponse.java index deae44b1c4676..4c89962dfec5e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/refresh/RefreshResponse.java @@ -32,11 +32,11 @@ package org.opensearch.action.admin.indices.refresh; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; @@ -44,6 +44,8 @@ /** * The response of a refresh action. + * + * @opensearch.internal */ public class RefreshResponse extends BroadcastResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/refresh/TransportRefreshAction.java b/server/src/main/java/org/opensearch/action/admin/indices/refresh/TransportRefreshAction.java index 97e8344d32664..e276cbf900ff0 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/refresh/TransportRefreshAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/refresh/TransportRefreshAction.java @@ -34,20 +34,22 @@ import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.ActiveShardCount; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.replication.BasicReplicationRequest; import org.opensearch.action.support.replication.ReplicationResponse; import org.opensearch.action.support.replication.TransportBroadcastReplicationAction; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.transport.TransportService; import java.util.List; /** * Refresh action. + * + * @opensearch.internal */ public class TransportRefreshAction extends TransportBroadcastReplicationAction< RefreshRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/refresh/TransportShardRefreshAction.java b/server/src/main/java/org/opensearch/action/admin/indices/refresh/TransportShardRefreshAction.java index c58d8c520ad0b..e17fa35395770 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/refresh/TransportShardRefreshAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/refresh/TransportShardRefreshAction.java @@ -32,7 +32,6 @@ package org.opensearch.action.admin.indices.refresh; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.replication.BasicReplicationRequest; import org.opensearch.action.support.replication.ReplicationResponse; @@ -40,8 +39,9 @@ import org.opensearch.cluster.action.shard.ShardStateAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; @@ -49,6 +49,11 @@ import java.io.IOException; +/** + * Refresh information action + * + * @opensearch.internal + */ public class TransportShardRefreshAction extends TransportReplicationAction< BasicReplicationRequest, BasicReplicationRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationShardStatsResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationShardStatsResponse.java new file mode 100644 index 0000000000000..ce17176a220ae --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationShardStatsResponse.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.replication; + +import org.opensearch.common.Nullable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.index.SegmentReplicationPerGroupStats; +import org.opensearch.indices.replication.SegmentReplicationState; + +import java.io.IOException; + +/** + * Segment Replication specific response object for fetching stats from either a primary + * or replica shard. The stats returned are different depending on primary or replica. + * + * @opensearch.internal + */ +public class SegmentReplicationShardStatsResponse implements Writeable { + + @Nullable + private final SegmentReplicationPerGroupStats primaryStats; + + @Nullable + private final SegmentReplicationState replicaStats; + + public SegmentReplicationShardStatsResponse(StreamInput in) throws IOException { + this.primaryStats = in.readOptionalWriteable(SegmentReplicationPerGroupStats::new); + this.replicaStats = in.readOptionalWriteable(SegmentReplicationState::new); + } + + public SegmentReplicationShardStatsResponse(SegmentReplicationPerGroupStats primaryStats) { + this.primaryStats = primaryStats; + this.replicaStats = null; + } + + public SegmentReplicationShardStatsResponse(SegmentReplicationState replicaStats) { + this.replicaStats = replicaStats; + this.primaryStats = null; + } + + public SegmentReplicationPerGroupStats getPrimaryStats() { + return primaryStats; + } + + public SegmentReplicationState getReplicaStats() { + return replicaStats; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeOptionalWriteable(primaryStats); + out.writeOptionalWriteable(replicaStats); + } + + @Override + public String toString() { + return "SegmentReplicationShardStatsResponse{" + "primaryStats=" + primaryStats + ", replicaStats=" + replicaStats + '}'; + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsAction.java new file mode 100644 index 0000000000000..9d1de20a8ff37 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsAction.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.replication; + +import org.opensearch.action.ActionType; + +/** + * Segment Replication stats information action + * + * @opensearch.internal + */ +public class SegmentReplicationStatsAction extends ActionType { + public static final SegmentReplicationStatsAction INSTANCE = new SegmentReplicationStatsAction(); + public static final String NAME = "indices:monitor/segment_replication"; + + private SegmentReplicationStatsAction() { + super(NAME, SegmentReplicationStatsResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsRequest.java new file mode 100644 index 0000000000000..c46940fbfecf9 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsRequest.java @@ -0,0 +1,114 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.replication; + +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.action.support.broadcast.BroadcastRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Request for Segment Replication stats information + * + * @opensearch.internal + */ +public class SegmentReplicationStatsRequest extends BroadcastRequest { + private boolean detailed = false; // Provides extra details in the response + private boolean activeOnly = false; // Only reports on active segment replication events + private String[] shards = new String[0]; + + /** + * Constructs a request for segment replication stats information for all shards + */ + public SegmentReplicationStatsRequest() { + this(Strings.EMPTY_ARRAY); + } + + public SegmentReplicationStatsRequest(StreamInput in) throws IOException { + super(in); + detailed = in.readBoolean(); + activeOnly = in.readBoolean(); + } + + /** + * Constructs a request for segment replication stats information for all shards for the given indices + * + * @param indices Comma-separated list of indices about which to gather segment replication information + */ + public SegmentReplicationStatsRequest(String... indices) { + super(indices, IndicesOptions.STRICT_EXPAND_OPEN_CLOSED); + } + + /** + * True if detailed flag is set, false otherwise. This value if false by default. + * + * @return True if detailed flag is set, false otherwise + */ + public boolean detailed() { + return detailed; + } + + /** + * Set value of the detailed flag. Detailed requests will contain extra + * information like timing metric of each stage of segment replication event. + * + * @param detailed Whether or not to set the detailed flag + */ + public void detailed(boolean detailed) { + this.detailed = detailed; + } + + /** + * True if activeOnly flag is set, false otherwise. This value is false by default. + * + * @return True if activeOnly flag is set, false otherwise + */ + public boolean activeOnly() { + return activeOnly; + } + + /** + * Set value of the activeOnly flag. If true, this request will only respond with + * on-going segment replication event information. + * + * @param activeOnly Whether or not to set the activeOnly flag. + */ + public void activeOnly(boolean activeOnly) { + this.activeOnly = activeOnly; + } + + /** + * Contains list of shard id's if shards are passed, empty otherwise. Array is empty by default. + * + * @return list of shard id's if shards are passed, empty otherwise + */ + public String[] shards() { + return shards; + } + + /** + * Set value of the shards. If shard id's are passed, this request will only respond with + * given specific shard's segment replication event information, instead of all shards. + * + * @param shards contains list of shard id's. + */ + public void shards(String[] shards) { + this.shards = shards; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeBoolean(detailed); + out.writeBoolean(activeOnly); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsRequestBuilder.java new file mode 100644 index 0000000000000..7e68d2ac59f07 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsRequestBuilder.java @@ -0,0 +1,43 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.replication; + +import org.opensearch.action.support.broadcast.BroadcastOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; + +/** + * Segment Replication stats information request builder. + * + * @opensearch.internal + */ +public class SegmentReplicationStatsRequestBuilder extends BroadcastOperationRequestBuilder< + SegmentReplicationStatsRequest, + SegmentReplicationStatsResponse, + SegmentReplicationStatsRequestBuilder> { + + public SegmentReplicationStatsRequestBuilder(OpenSearchClient client, SegmentReplicationStatsAction action) { + super(client, action, new SegmentReplicationStatsRequest()); + } + + public SegmentReplicationStatsRequestBuilder setDetailed(boolean detailed) { + request.detailed(detailed); + return this; + } + + public SegmentReplicationStatsRequestBuilder setActiveOnly(boolean activeOnly) { + request.activeOnly(activeOnly); + return this; + } + + public SegmentReplicationStatsRequestBuilder shards(String... indices) { + request.shards(indices); + return this; + } + +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsResponse.java new file mode 100644 index 0000000000000..f1b6b90e0cae1 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/replication/SegmentReplicationStatsResponse.java @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.replication; + +import org.opensearch.action.support.broadcast.BroadcastResponse; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.index.SegmentReplicationPerGroupStats; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Stats Information regarding the Segment Replication state of indices and their associated shards. + * + * @opensearch.internal + */ +public class SegmentReplicationStatsResponse extends BroadcastResponse { + private final Map> replicationStats; + + public SegmentReplicationStatsResponse(StreamInput in) throws IOException { + super(in); + replicationStats = in.readMapOfLists(StreamInput::readString, SegmentReplicationPerGroupStats::new); + } + + /** + * Constructs segment replication stats information for a collection of indices and associated shards. Keeps track of how many total shards + * were seen, and out of those how many were successfully processed and how many failed. + * + * @param totalShards Total count of shards seen + * @param successfulShards Count of shards successfully processed + * @param failedShards Count of shards which failed to process + * @param replicationStats Map of indices to a list of {@link SegmentReplicationPerGroupStats} + * @param shardFailures List of failures processing shards + */ + public SegmentReplicationStatsResponse( + int totalShards, + int successfulShards, + int failedShards, + Map> replicationStats, + List shardFailures + ) { + super(totalShards, successfulShards, failedShards, shardFailures); + this.replicationStats = replicationStats; + } + + public Map> getReplicationStats() { + return replicationStats; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + if (replicationStats.size() > 0) { + for (String index : replicationStats.keySet()) { + List segmentReplicationStates = replicationStats.get(index); + if (segmentReplicationStates == null || segmentReplicationStates.size() == 0) { + continue; + } + builder.startObject(index); + builder.startArray("primary_stats"); + for (SegmentReplicationPerGroupStats segmentReplicationState : segmentReplicationStates) { + builder.startObject(); + segmentReplicationState.toXContent(builder, params); + builder.endObject(); + } + builder.endArray(); + builder.endObject(); + } + } + builder.endObject(); + return builder; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeMapOfLists(replicationStats, StreamOutput::writeString, (o, v) -> v.writeTo(o)); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/replication/TransportSegmentReplicationStatsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/replication/TransportSegmentReplicationStatsAction.java new file mode 100644 index 0000000000000..1b912518d7e04 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/replication/TransportSegmentReplicationStatsAction.java @@ -0,0 +1,184 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.replication; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.ShardsIterator; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.index.IndexService; +import org.opensearch.index.SegmentReplicationPerGroupStats; +import org.opensearch.index.SegmentReplicationPressureService; +import org.opensearch.index.SegmentReplicationShardStats; +import org.opensearch.index.shard.IndexShard; +import org.opensearch.indices.IndicesService; +import org.opensearch.indices.replication.SegmentReplicationState; +import org.opensearch.indices.replication.SegmentReplicationTargetService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Transport action for shard segment replication operation. This transport action does not actually + * perform segment replication, it only reports on metrics/stats of segment replication event (both active and complete). + * + * @opensearch.internal + */ +public class TransportSegmentReplicationStatsAction extends TransportBroadcastByNodeAction< + SegmentReplicationStatsRequest, + SegmentReplicationStatsResponse, + SegmentReplicationShardStatsResponse> { + + private final SegmentReplicationTargetService targetService; + private final IndicesService indicesService; + private final SegmentReplicationPressureService pressureService; + + @Inject + public TransportSegmentReplicationStatsAction( + ClusterService clusterService, + TransportService transportService, + IndicesService indicesService, + SegmentReplicationTargetService targetService, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver, + SegmentReplicationPressureService pressureService + ) { + super( + SegmentReplicationStatsAction.NAME, + clusterService, + transportService, + actionFilters, + indexNameExpressionResolver, + SegmentReplicationStatsRequest::new, + ThreadPool.Names.MANAGEMENT + ); + this.indicesService = indicesService; + this.targetService = targetService; + this.pressureService = pressureService; + } + + @Override + protected SegmentReplicationShardStatsResponse readShardResult(StreamInput in) throws IOException { + return new SegmentReplicationShardStatsResponse(in); + } + + @Override + protected SegmentReplicationStatsResponse newResponse( + SegmentReplicationStatsRequest request, + int totalShards, + int successfulShards, + int failedShards, + List responses, + List shardFailures, + ClusterState clusterState + ) { + String[] shards = request.shards(); + final List shardsToFetch = Arrays.stream(shards).map(Integer::valueOf).collect(Collectors.toList()); + + // organize replica responses by allocationId. + final Map replicaStats = new HashMap<>(); + // map of index name to list of replication group stats. + final Map> primaryStats = new HashMap<>(); + for (SegmentReplicationShardStatsResponse response : responses) { + if (response != null) { + if (response.getReplicaStats() != null) { + final ShardRouting shardRouting = response.getReplicaStats().getShardRouting(); + if (shardsToFetch.isEmpty() || shardsToFetch.contains(shardRouting.shardId().getId())) { + replicaStats.putIfAbsent(shardRouting.allocationId().getId(), response.getReplicaStats()); + } + } + if (response.getPrimaryStats() != null) { + final ShardId shardId = response.getPrimaryStats().getShardId(); + if (shardsToFetch.isEmpty() || shardsToFetch.contains(shardId.getId())) { + primaryStats.compute(shardId.getIndexName(), (k, v) -> { + if (v == null) { + final ArrayList list = new ArrayList<>(); + list.add(response.getPrimaryStats()); + return list; + } else { + v.add(response.getPrimaryStats()); + return v; + } + }); + } + } + } + } + // combine the replica stats to the shard stat entry in each group. + for (Map.Entry> entry : primaryStats.entrySet()) { + for (SegmentReplicationPerGroupStats group : entry.getValue()) { + for (SegmentReplicationShardStats replicaStat : group.getReplicaStats()) { + replicaStat.setCurrentReplicationState(replicaStats.getOrDefault(replicaStat.getAllocationId(), null)); + } + } + } + return new SegmentReplicationStatsResponse(totalShards, successfulShards, failedShards, primaryStats, shardFailures); + } + + @Override + protected SegmentReplicationStatsRequest readRequestFrom(StreamInput in) throws IOException { + return new SegmentReplicationStatsRequest(in); + } + + @Override + protected SegmentReplicationShardStatsResponse shardOperation(SegmentReplicationStatsRequest request, ShardRouting shardRouting) { + IndexService indexService = indicesService.indexServiceSafe(shardRouting.shardId().getIndex()); + IndexShard indexShard = indexService.getShard(shardRouting.shardId().id()); + ShardId shardId = shardRouting.shardId(); + + if (indexShard.indexSettings().isSegRepEnabled() == false) { + return null; + } + + if (shardRouting.primary()) { + return new SegmentReplicationShardStatsResponse(pressureService.getStatsForShard(indexShard)); + } + + // return information about only on-going segment replication events. + if (request.activeOnly()) { + return new SegmentReplicationShardStatsResponse(targetService.getOngoingEventSegmentReplicationState(shardId)); + } + return new SegmentReplicationShardStatsResponse(targetService.getSegmentReplicationState(shardId)); + } + + @Override + protected ShardsIterator shards(ClusterState state, SegmentReplicationStatsRequest request, String[] concreteIndices) { + return state.routingTable().allShardsIncludingRelocationTargets(concreteIndices); + } + + @Override + protected ClusterBlockException checkGlobalBlock(ClusterState state, SegmentReplicationStatsRequest request) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } + + @Override + protected ClusterBlockException checkRequestBlock( + ClusterState state, + SegmentReplicationStatsRequest request, + String[] concreteIndices + ) { + return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_READ, concreteIndices); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/replication/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/replication/package-info.java new file mode 100644 index 0000000000000..db69c19825199 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/replication/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Segment Replication Stats transport handlers. */ +package org.opensearch.action.admin.indices.replication; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/resolve/ResolveIndexAction.java b/server/src/main/java/org/opensearch/action/admin/indices/resolve/ResolveIndexAction.java index 8a9df3f59bc4c..98c2446e17998 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/resolve/ResolveIndexAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/resolve/ResolveIndexAction.java @@ -32,10 +32,8 @@ package org.opensearch.action.admin.indices.resolve; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.IndicesRequest; import org.opensearch.action.OriginalIndices; @@ -51,15 +49,17 @@ import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.util.concurrent.CountDown; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.RemoteClusterAware; @@ -79,6 +79,11 @@ import java.util.TreeMap; import java.util.stream.StreamSupport; +/** + * Transport action to resolve an index. + * + * @opensearch.internal + */ public class ResolveIndexAction extends ActionType { public static final ResolveIndexAction INSTANCE = new ResolveIndexAction(); @@ -88,6 +93,11 @@ private ResolveIndexAction() { super(NAME, Response::new); } + /** + * Request for resolving an index + * + * @opensearch.internal + */ public static class Request extends ActionRequest implements IndicesRequest.Replaceable { public static final IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.strictExpandOpen(); @@ -157,6 +167,11 @@ public boolean includeDataStreams() { } } + /** + * Abstraction class for resolving an index + * + * @opensearch.internal + */ public static class ResolvedIndexAbstraction { static final ParseField NAME_FIELD = new ParseField("name"); @@ -178,6 +193,11 @@ public String getName() { } } + /** + * The resolved index + * + * @opensearch.internal + */ public static class ResolvedIndex extends ResolvedIndexAbstraction implements Writeable, ToXContentObject { static final ParseField ALIASES_FIELD = new ParseField("aliases"); @@ -261,6 +281,11 @@ public int hashCode() { } } + /** + * The resolved index alias + * + * @opensearch.internal + */ public static class ResolvedAlias extends ResolvedIndexAbstraction implements Writeable, ToXContentObject { static final ParseField INDICES_FIELD = new ParseField("indices"); @@ -318,6 +343,11 @@ public int hashCode() { } } + /** + * The resolved data stream + * + * @opensearch.internal + */ public static class ResolvedDataStream extends ResolvedIndexAbstraction implements Writeable, ToXContentObject { static final ParseField BACKING_INDICES_FIELD = new ParseField("backing_indices"); @@ -385,6 +415,11 @@ public int hashCode() { } } + /** + * Response for resolving an index + * + * @opensearch.internal + */ public static class Response extends ActionResponse implements ToXContentObject { static final ParseField INDICES_FIELD = new ParseField("indices"); @@ -450,6 +485,11 @@ public int hashCode() { } } + /** + * Transport action for resolving an index + * + * @opensearch.internal + */ public static class TransportAction extends HandledTransportAction { private final ThreadPool threadPool; @@ -595,7 +635,7 @@ private static void enrichIndexAbstraction( IndexAbstraction.Index index = (IndexAbstraction.Index) ia; String[] aliasNames = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(index.getWriteIndex().getAliases().keysIt(), 0), + Spliterators.spliteratorUnknownSize(index.getWriteIndex().getAliases().keySet().iterator(), 0), false ).toArray(String[]::new); Arrays.sort(aliasNames); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/resolve/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/resolve/package-info.java new file mode 100644 index 0000000000000..a7732563cb041 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/resolve/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Resolve transport handler. */ +package org.opensearch.action.admin.indices.resolve; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/Condition.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/Condition.java index 7b2707ed16ca2..e014d6d703500 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/Condition.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/Condition.java @@ -33,14 +33,16 @@ package org.opensearch.action.admin.indices.rollover; import org.opensearch.Version; -import org.opensearch.common.io.stream.NamedWriteable; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.ToXContentFragment; +import org.opensearch.core.common.io.stream.NamedWriteable; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContentFragment; import java.util.Objects; /** * Base class for rollover request conditions + * + * @opensearch.internal */ public abstract class Condition implements NamedWriteable, ToXContentFragment { @@ -93,6 +95,8 @@ public String name() { /** * Holder for index stats used to evaluate conditions + * + * @opensearch.internal */ public static class Stats { public final long numDocs; @@ -108,6 +112,8 @@ public Stats(long numDocs, long indexCreated, ByteSizeValue indexSize) { /** * Holder for evaluated condition result + * + * @opensearch.internal */ public static class Result { public final Condition condition; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxAgeCondition.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxAgeCondition.java index 20c45d88c8d32..da750594e7264 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxAgeCondition.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxAgeCondition.java @@ -32,17 +32,19 @@ package org.opensearch.action.admin.indices.rollover; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; /** * Condition for index maximum age. Evaluates to true * when the index is at least {@link #value} old + * + * @opensearch.internal */ public class MaxAgeCondition extends Condition { public static final String NAME = "max_age"; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxDocsCondition.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxDocsCondition.java index 8491b381fd9d3..9c7684e25e543 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxDocsCondition.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxDocsCondition.java @@ -32,16 +32,18 @@ package org.opensearch.action.admin.indices.rollover; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; /** * Condition for maximum index docs. Evaluates to true * when the index has at least {@link #value} docs + * + * @opensearch.internal */ public class MaxDocsCondition extends Condition { public static final String NAME = "max_docs"; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxSizeCondition.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxSizeCondition.java index 147d81a52961d..faa3558420a5c 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxSizeCondition.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/MaxSizeCondition.java @@ -32,18 +32,20 @@ package org.opensearch.action.admin.indices.rollover; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; /** * A size-based condition for an index size. * Evaluates to true if the index size is at least {@link #value}. + * + * @opensearch.internal */ public class MaxSizeCondition extends Condition { public static final String NAME = "max_size"; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/MetadataRolloverService.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/MetadataRolloverService.java index 19a7b8c95199b..c6193c076ee50 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/MetadataRolloverService.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/MetadataRolloverService.java @@ -48,9 +48,9 @@ import org.opensearch.cluster.metadata.MetadataIndexAliasesService; import org.opensearch.cluster.metadata.MetadataIndexTemplateService; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.threadpool.ThreadPool; import java.util.Arrays; @@ -69,10 +69,12 @@ /** * Service responsible for handling rollover requests for write aliases and data streams + * + * @opensearch.internal */ public class MetadataRolloverService { private static final Pattern INDEX_NAME_PATTERN = Pattern.compile("^.*-\\d+$"); - private static final List VALID_ROLLOVER_TARGETS = org.opensearch.common.collect.List.of(ALIAS, DATA_STREAM); + private static final List VALID_ROLLOVER_TARGETS = List.of(ALIAS, DATA_STREAM); private final ThreadPool threadPool; private final MetadataCreateIndexService createIndexService; @@ -92,6 +94,11 @@ public MetadataRolloverService( this.indexNameExpressionResolver = indexNameExpressionResolver; } + /** + * Result for rollover request + * + * @opensearch.internal + */ public static class RolloverResult { public final String rolloverIndexName; public final String sourceIndexName; @@ -181,7 +188,7 @@ private RolloverResult rolloverAlias( ClusterState newState = createIndexService.applyCreateIndexRequest(currentState, createIndexClusterStateRequest, silent); newState = indexAliasesService.applyAliasActions( newState, - rolloverAliasToNewIndex(sourceIndexName, rolloverIndexName, explicitWriteIndex, aliasMetadata.isHidden(), aliasName) + rolloverAliasToNewIndex(sourceIndexName, rolloverIndexName, explicitWriteIndex, aliasMetadata, aliasName) ); RolloverInfo rolloverInfo = new RolloverInfo(aliasName, metConditions, threadPool.absoluteTimeInMillis()); @@ -286,7 +293,7 @@ static CreateIndexClusterStateUpdateRequest prepareCreateIndexRequest( b.put(settings); } return new CreateIndexClusterStateUpdateRequest(cause, targetIndexName, providedIndexName).ackTimeout(createIndexRequest.timeout()) - .masterNodeTimeout(createIndexRequest.masterNodeTimeout()) + .masterNodeTimeout(createIndexRequest.clusterManagerNodeTimeout()) .settings(b.build()) .aliases(createIndexRequest.aliases()) .waitForActiveShards(ActiveShardCount.NONE) // not waiting for shards here, will wait on the alias switch operation @@ -302,20 +309,46 @@ static List rolloverAliasToNewIndex( String oldIndex, String newIndex, boolean explicitWriteIndex, - @Nullable Boolean isHidden, + AliasMetadata aliasMetadata, String alias ) { + String filterAsString = aliasMetadata.getFilter() != null ? aliasMetadata.getFilter().string() : null; + if (explicitWriteIndex) { return Collections.unmodifiableList( Arrays.asList( - new AliasAction.Add(newIndex, alias, null, null, null, true, isHidden), - new AliasAction.Add(oldIndex, alias, null, null, null, false, isHidden) + new AliasAction.Add( + newIndex, + alias, + filterAsString, + aliasMetadata.getIndexRouting(), + aliasMetadata.getSearchRouting(), + true, + aliasMetadata.isHidden() + ), + new AliasAction.Add( + oldIndex, + alias, + filterAsString, + aliasMetadata.getIndexRouting(), + aliasMetadata.getSearchRouting(), + false, + aliasMetadata.isHidden() + ) ) ); } else { return Collections.unmodifiableList( Arrays.asList( - new AliasAction.Add(newIndex, alias, null, null, null, null, isHidden), + new AliasAction.Add( + newIndex, + alias, + filterAsString, + aliasMetadata.getIndexRouting(), + aliasMetadata.getSearchRouting(), + null, + aliasMetadata.isHidden() + ), new AliasAction.Remove(oldIndex, alias, null) ) ); @@ -341,7 +374,7 @@ static void checkNoDuplicatedAliasInIndexTemplate( Locale.ROOT, "Rollover alias [%s] can point to multiple indices, found duplicated alias [%s] in index template [%s]", rolloverRequestAlias, - template.aliases().keys(), + template.aliases().keySet(), template.name() ) ); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverAction.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverAction.java index f95cf1b83250a..e6c22cc48c1ad 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to rollover an index. + * + * @opensearch.internal + */ public class RolloverAction extends ActionType { public static final RolloverAction INSTANCE = new RolloverAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverInfo.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverInfo.java index 3fd145a7c7655..475e44e1820d5 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverInfo.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverInfo.java @@ -34,15 +34,16 @@ import org.opensearch.cluster.AbstractDiffable; import org.opensearch.cluster.Diff; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.List; @@ -50,6 +51,8 @@ /** * Class for holding Rollover related information within an index + * + * @opensearch.internal */ public class RolloverInfo extends AbstractDiffable implements Writeable, ToXContentFragment { @@ -146,6 +149,6 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequest.java index 402b3741205a2..b25bc94a5c8e2 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequest.java @@ -37,13 +37,13 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import java.io.IOException; @@ -57,6 +57,8 @@ * * Note: there is a new class with the same name for the Java HLRC that uses a typeless format. * Any changes done to this class should also go to that client class. + * + * @opensearch.internal */ public class RolloverRequest extends AcknowledgedRequest implements IndicesRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequestBuilder.java index c74f71a70e09d..9e6b8518e92d3 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverRequestBuilder.java @@ -33,13 +33,21 @@ import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.support.ActiveShardCount; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.unit.ByteSizeValue; -public class RolloverRequestBuilder extends MasterNodeOperationRequestBuilder { +/** + * Transport request to rollover an index. + * + * @opensearch.internal + */ +public class RolloverRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< + RolloverRequest, + RolloverResponse, + RolloverRequestBuilder> { public RolloverRequestBuilder(OpenSearchClient client, RolloverAction action) { super(client, action, new RolloverRequest()); } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverResponse.java index 5600d05120abb..55ee65d0a4973 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/RolloverResponse.java @@ -33,27 +33,29 @@ package org.opensearch.action.admin.indices.rollover; import org.opensearch.action.support.master.ShardsAcknowledgedResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * Response object for {@link RolloverRequest} API * * Note: there is a new class with the same name for the Java HLRC that uses a typeless format. * Any changes done to this class should also go to that client class. + * + * @opensearch.internal */ public final class RolloverResponse extends ShardsAcknowledgedResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/TransportRolloverAction.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/TransportRolloverAction.java index 38f25422a5956..3b11a3d82d707 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/rollover/TransportRolloverAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/TransportRolloverAction.java @@ -33,14 +33,13 @@ package org.opensearch.action.admin.indices.rollover; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.stats.IndicesStatsAction; import org.opensearch.action.admin.indices.stats.IndicesStatsRequest; import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.ActiveShardsObserver; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.client.Client; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateUpdateTask; @@ -49,11 +48,14 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.unit.ByteSizeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.index.shard.DocsStats; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; @@ -69,12 +71,15 @@ /** * Main class to swap the index pointed to by an alias, given some conditions + * + * @opensearch.internal */ -public class TransportRolloverAction extends TransportMasterNodeAction { +public class TransportRolloverAction extends TransportClusterManagerNodeAction { private final MetadataRolloverService rolloverService; private final ActiveShardsObserver activeShardsObserver; private final Client client; + private final ClusterManagerTaskThrottler.ThrottlingKey rolloverIndexTaskKey; @Inject public TransportRolloverAction( @@ -98,6 +103,8 @@ public TransportRolloverAction( this.rolloverService = rolloverService; this.client = client; this.activeShardsObserver = new ActiveShardsObserver(clusterService, threadPool); + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + rolloverIndexTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.ROLLOVER_INDEX_KEY, true); } @Override @@ -128,13 +135,13 @@ protected ClusterBlockException checkBlock(RolloverRequest request, ClusterState } @Override - protected void masterOperation(RolloverRequest request, ClusterState state, ActionListener listener) + protected void clusterManagerOperation(RolloverRequest request, ClusterState state, ActionListener listener) throws Exception { throw new UnsupportedOperationException("The task parameter is required"); } @Override - protected void masterOperation( + protected void clusterManagerOperation( Task task, final RolloverRequest rolloverRequest, final ClusterState state, @@ -202,6 +209,11 @@ public ClusterState execute(ClusterState currentState) throws Exception { return rolloverResult.clusterState; } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return rolloverIndexTaskKey; + } + @Override public void onFailure(String source, Exception e) { listener.onFailure(e); @@ -213,7 +225,7 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS activeShardsObserver.waitForActiveShards( new String[] { rolloverIndexName }, rolloverRequest.getCreateIndexRequest().waitForActiveShards(), - rolloverRequest.masterNodeTimeout(), + rolloverRequest.clusterManagerNodeTimeout(), isShardsAcknowledged -> listener.onResponse( new RolloverResponse( sourceIndexName, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/rollover/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/rollover/package-info.java new file mode 100644 index 0000000000000..23b5b18e3ae87 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/rollover/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Rollover transport handlers. */ +package org.opensearch.action.admin.indices.rollover; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndexSegments.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndexSegments.java index 907f352a5ed89..88973ce094d8b 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndexSegments.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndexSegments.java @@ -38,6 +38,11 @@ import java.util.List; import java.util.Map; +/** + * List of Index Segments + * + * @opensearch.internal + */ public class IndexSegments implements Iterable { private final String index; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndexShardSegments.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndexShardSegments.java index 470480c2ac064..a6caf0649fde1 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndexShardSegments.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndexShardSegments.java @@ -32,11 +32,16 @@ package org.opensearch.action.admin.indices.segments; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import java.util.Arrays; import java.util.Iterator; +/** + * List of Index Shard Segments + * + * @opensearch.internal + */ public class IndexShardSegments implements Iterable { private final ShardId shardId; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentResponse.java index 82fe438236d0f..64c98fa999d08 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentResponse.java @@ -36,18 +36,15 @@ import org.apache.lucene.search.SortField; import org.apache.lucene.search.SortedNumericSortField; import org.apache.lucene.search.SortedSetSortField; -import org.apache.lucene.util.Accountable; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.engine.Segment; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -55,6 +52,11 @@ import java.util.Map; import java.util.Set; +/** + * Transport response for retrieving indices segment information + * + * @opensearch.internal + */ public class IndicesSegmentResponse extends BroadcastResponse { private final ShardSegments[] shards; @@ -192,21 +194,11 @@ private static void toXContent(XContentBuilder builder, Sort sort) throws IOExce builder.endArray(); } - private static void toXContent(XContentBuilder builder, Accountable tree) throws IOException { - builder.startObject(); - builder.field(Fields.DESCRIPTION, tree.toString()); - builder.humanReadableField(Fields.SIZE_IN_BYTES, Fields.SIZE, new ByteSizeValue(tree.ramBytesUsed())); - Collection children = tree.getChildResources(); - if (children.isEmpty() == false) { - builder.startArray(Fields.CHILDREN); - for (Accountable child : children) { - toXContent(builder, child); - } - builder.endArray(); - } - builder.endObject(); - } - + /** + * Fields for parsing and toXContent + * + * @opensearch.internal + */ static final class Fields { static final String INDICES = "indices"; static final String SHARDS = "shards"; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsAction.java index 467de4590e746..54a10ce259190 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for retrieving indices segment information + * + * @opensearch.internal + */ public class IndicesSegmentsAction extends ActionType { public static final IndicesSegmentsAction INSTANCE = new IndicesSegmentsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsRequest.java index 14f6999692731..03a41cd21572f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsRequest.java @@ -33,12 +33,17 @@ package org.opensearch.action.admin.indices.segments; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport request for retrieving indices segment information + * + * @opensearch.internal + */ public class IndicesSegmentsRequest extends BroadcastRequest { protected boolean verbose = false; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsRequestBuilder.java index 4352344114f85..4b758e1f4bfb1 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/IndicesSegmentsRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.support.broadcast.BroadcastOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder for retrieving indices segment information + * + * @opensearch.internal + */ public class IndicesSegmentsRequestBuilder extends BroadcastOperationRequestBuilder< IndicesSegmentsRequest, IndicesSegmentResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsAction.java new file mode 100644 index 0000000000000..b52ef32a91b16 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsAction.java @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.segments; + +import org.opensearch.action.ActionType; + +/** + * Action for retrieving segment information for PITs + */ +public class PitSegmentsAction extends ActionType { + + public static final PitSegmentsAction INSTANCE = new PitSegmentsAction(); + public static final String NAME = "indices:monitor/point_in_time/segments"; + + private PitSegmentsAction() { + super(NAME, IndicesSegmentResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsRequest.java new file mode 100644 index 0000000000000..aec624ed09b83 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/PitSegmentsRequest.java @@ -0,0 +1,121 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.admin.indices.segments; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.broadcast.BroadcastRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Transport request for retrieving PITs segment information + */ +public class PitSegmentsRequest extends BroadcastRequest { + private boolean verbose = false; + private final List pitIds = new ArrayList<>(); + + public PitSegmentsRequest() { + this(Strings.EMPTY_ARRAY); + } + + public PitSegmentsRequest(StreamInput in) throws IOException { + super(in); + pitIds.addAll(Arrays.asList(in.readStringArray())); + verbose = in.readBoolean(); + } + + public PitSegmentsRequest(String... pitIds) { + super(pitIds); + this.pitIds.addAll(Arrays.asList(pitIds)); + } + + /** + * true if detailed information about each segment should be returned, + * false otherwise. + */ + public boolean isVerbose() { + return verbose; + } + + /** + * Sets the verbose option. + * @see #isVerbose() + */ + public void setVerbose(boolean v) { + verbose = v; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeStringArrayNullable((pitIds == null) ? null : pitIds.toArray(new String[pitIds.size()])); + out.writeBoolean(verbose); + } + + public List getPitIds() { + return Collections.unmodifiableList(pitIds); + } + + public void clearAndSetPitIds(List pitIds) { + this.pitIds.clear(); + this.pitIds.addAll(pitIds); + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (pitIds == null || pitIds.isEmpty()) { + validationException = addValidationError("no pit ids specified", validationException); + } + return validationException; + } + + public void fromXContent(XContentParser parser) throws IOException { + pitIds.clear(); + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new IllegalArgumentException("Malformed content, must start with an object"); + } else { + XContentParser.Token token; + String currentFieldName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if ("pit_id".equals(currentFieldName)) { + if (token == XContentParser.Token.START_ARRAY) { + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (token.isValue() == false) { + throw new IllegalArgumentException("pit_id array element should only contain PIT identifier"); + } + pitIds.add(parser.text()); + } + } else { + if (token.isValue() == false) { + throw new IllegalArgumentException("pit_id element should only contain PIT identifier"); + } + pitIds.add(parser.text()); + } + } else { + throw new IllegalArgumentException( + "Unknown parameter [" + currentFieldName + "] in request body or parameter is of the wrong type[" + token + "] " + ); + } + } + } + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/ShardSegments.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/ShardSegments.java index a6e34d1d55823..90317542244ff 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/segments/ShardSegments.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/ShardSegments.java @@ -33,15 +33,20 @@ package org.opensearch.action.admin.indices.segments; import org.opensearch.cluster.routing.ShardRouting; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.index.engine.Segment; import java.io.IOException; import java.util.Iterator; import java.util.List; +/** + * Collection of shard segments + * + * @opensearch.internal + */ public class ShardSegments implements Writeable, Iterable { private final ShardRouting shardRouting; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportIndicesSegmentsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportIndicesSegmentsAction.java index 7ff7bb3591f1d..9ec305b30e2b0 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportIndicesSegmentsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportIndicesSegmentsAction.java @@ -33,7 +33,6 @@ package org.opensearch.action.admin.indices.segments; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -43,7 +42,8 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.IndexService; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; @@ -53,6 +53,11 @@ import java.io.IOException; import java.util.List; +/** + * Transport response for retrieving indices segment information + * + * @opensearch.internal + */ public class TransportIndicesSegmentsAction extends TransportBroadcastByNodeAction< IndicesSegmentsRequest, IndicesSegmentResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportPitSegmentsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportPitSegmentsAction.java new file mode 100644 index 0000000000000..805073f79645a --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/TransportPitSegmentsAction.java @@ -0,0 +1,264 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.action.admin.indices.segments; + +import org.opensearch.action.search.ListPitInfo; +import org.opensearch.action.search.PitService; +import org.opensearch.action.search.SearchContextId; +import org.opensearch.action.search.SearchContextIdForNode; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.routing.AllocationId; +import org.opensearch.cluster.routing.PlainShardsIterator; +import org.opensearch.cluster.routing.RecoverySource; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.cluster.routing.ShardRoutingState; +import org.opensearch.cluster.routing.ShardsIterator; +import org.opensearch.cluster.routing.UnassignedInfo; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.indices.IndicesService; +import org.opensearch.search.SearchService; +import org.opensearch.search.internal.PitReaderContext; +import org.opensearch.tasks.Task; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.opensearch.action.search.SearchContextId.decode; + +/** + * Transport action for retrieving segment information of PITs + */ +public class TransportPitSegmentsAction extends TransportBroadcastByNodeAction { + private final ClusterService clusterService; + private final IndicesService indicesService; + private final SearchService searchService; + private final NamedWriteableRegistry namedWriteableRegistry; + private final TransportService transportService; + private final PitService pitService; + + @Inject + public TransportPitSegmentsAction( + ClusterService clusterService, + TransportService transportService, + IndicesService indicesService, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver, + SearchService searchService, + NamedWriteableRegistry namedWriteableRegistry, + PitService pitService + ) { + super( + PitSegmentsAction.NAME, + clusterService, + transportService, + actionFilters, + indexNameExpressionResolver, + PitSegmentsRequest::new, + ThreadPool.Names.MANAGEMENT + ); + this.clusterService = clusterService; + this.indicesService = indicesService; + this.searchService = searchService; + this.namedWriteableRegistry = namedWriteableRegistry; + this.transportService = transportService; + this.pitService = pitService; + } + + /** + * Execute PIT segments flow for all PITs or request PIT IDs + */ + @Override + protected void doExecute(Task task, PitSegmentsRequest request, ActionListener listener) { + if (request.getPitIds().size() == 1 && "_all".equals(request.getPitIds().get(0))) { + pitService.getAllPits(ActionListener.wrap(response -> { + request.clearAndSetPitIds(response.getPitInfos().stream().map(ListPitInfo::getPitId).collect(Collectors.toList())); + super.doExecute(task, request, listener); + }, listener::onFailure)); + } else { + super.doExecute(task, request, listener); + } + } + + /** + * This adds list of shards on which we need to retrieve pit segments details + * @param clusterState the cluster state + * @param request the underlying request + * @param concreteIndices the concrete indices on which to execute the operation + */ + @Override + protected ShardsIterator shards(ClusterState clusterState, PitSegmentsRequest request, String[] concreteIndices) { + final ArrayList iterators = new ArrayList<>(); + // remove duplicates from the request + Set uniquePitIds = new LinkedHashSet<>(request.getPitIds()); + for (String pitId : uniquePitIds) { + SearchContextId searchContext = decode(namedWriteableRegistry, pitId); + for (Map.Entry entry : searchContext.shards().entrySet()) { + final SearchContextIdForNode perNode = entry.getValue(); + // check if node is part of local cluster + if (Strings.isEmpty(perNode.getClusterAlias())) { + final ShardId shardId = entry.getKey(); + iterators.add( + new PitAwareShardRouting( + pitId, + shardId, + perNode.getNode(), + null, + true, + ShardRoutingState.STARTED, + null, + null, + null, + -1L + ) + ); + } + } + } + return new PlainShardsIterator(iterators); + } + + @Override + protected ClusterBlockException checkGlobalBlock(ClusterState state, PitSegmentsRequest request) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } + + @Override + protected ClusterBlockException checkRequestBlock(ClusterState state, PitSegmentsRequest countRequest, String[] concreteIndices) { + return state.blocks().indicesBlockedException(ClusterBlockLevel.METADATA_READ, concreteIndices); + } + + @Override + protected ShardSegments readShardResult(StreamInput in) throws IOException { + return new ShardSegments(in); + } + + @Override + protected IndicesSegmentResponse newResponse( + PitSegmentsRequest request, + int totalShards, + int successfulShards, + int failedShards, + List results, + List shardFailures, + ClusterState clusterState + ) { + return new IndicesSegmentResponse( + results.toArray(new ShardSegments[results.size()]), + totalShards, + successfulShards, + failedShards, + shardFailures + ); + } + + @Override + protected PitSegmentsRequest readRequestFrom(StreamInput in) throws IOException { + return new PitSegmentsRequest(in); + } + + @Override + public List getShardRoutingsFromInputStream(StreamInput in) throws IOException { + return in.readList(PitAwareShardRouting::new); + } + + /** + * This retrieves segment details of PIT context + * @param request the node-level request + * @param shardRouting the shard on which to execute the operation + */ + @Override + protected ShardSegments shardOperation(PitSegmentsRequest request, ShardRouting shardRouting) { + assert shardRouting instanceof PitAwareShardRouting; + PitAwareShardRouting pitAwareShardRouting = (PitAwareShardRouting) shardRouting; + SearchContextIdForNode searchContextIdForNode = decode(namedWriteableRegistry, pitAwareShardRouting.getPitId()).shards() + .get(shardRouting.shardId()); + PitReaderContext pitReaderContext = searchService.getPitReaderContext(searchContextIdForNode.getSearchContextId()); + if (pitReaderContext == null) { + return new ShardSegments(shardRouting, Collections.emptyList()); + } + return new ShardSegments(pitReaderContext.getShardRouting(), pitReaderContext.getSegments()); + } + + /** + * This holds PIT id which is used to perform broadcast operation in PIT shards to retrieve segments information + */ + public class PitAwareShardRouting extends ShardRouting { + + private final String pitId; + + public PitAwareShardRouting(StreamInput in) throws IOException { + super(in); + this.pitId = in.readString(); + } + + public PitAwareShardRouting( + String pitId, + ShardId shardId, + String currentNodeId, + String relocatingNodeId, + boolean primary, + ShardRoutingState state, + RecoverySource recoverySource, + UnassignedInfo unassignedInfo, + AllocationId allocationId, + long expectedShardSize + ) { + super( + shardId, + currentNodeId, + relocatingNodeId, + primary, + state, + recoverySource, + unassignedInfo, + allocationId, + expectedShardSize + ); + this.pitId = pitId; + } + + public String getPitId() { + return pitId; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(pitId); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + super.toXContent(builder, params); + builder.field("pit_id", pitId); + return builder.endObject(); + } + } +} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/segments/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/segments/package-info.java new file mode 100644 index 0000000000000..0bbd0164b8205 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/segments/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Segment level transport handlers. */ +package org.opensearch.action.admin.indices.segments; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsAction.java index cc64009682d43..e035b4d9ef622 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for getting index segments + * + * @opensearch.internal + */ public class GetSettingsAction extends ActionType { public static final GetSettingsAction INSTANCE = new GetSettingsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsRequest.java index 739dfcae6287c..01383b6b6545d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsRequest.java @@ -36,16 +36,21 @@ import org.opensearch.action.IndicesRequest; import org.opensearch.action.ValidateActions; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Arrays; import java.util.Objects; -public class GetSettingsRequest extends MasterNodeReadRequest implements IndicesRequest.Replaceable { +/** + * Transport request for getting index segments + * + * @opensearch.internal + */ +public class GetSettingsRequest extends ClusterManagerNodeReadRequest implements IndicesRequest.Replaceable { private String[] indices = Strings.EMPTY_ARRAY; private IndicesOptions indicesOptions = IndicesOptions.fromOptions(false, true, true, true); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsRequestBuilder.java index 034d5d64f1ca7..84cd4e8682e93 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsRequestBuilder.java @@ -33,11 +33,16 @@ package org.opensearch.action.admin.indices.settings.get; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.util.ArrayUtils; -public class GetSettingsRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Transport request builder for getting index segments + * + * @opensearch.internal + */ +public class GetSettingsRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< GetSettingsRequest, GetSettingsResponse, GetSettingsRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsResponse.java index 0694f93f585ee..61c9b68629194 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/GetSettingsResponse.java @@ -32,43 +32,43 @@ package org.opensearch.action.admin.indices.settings.get; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; +/** + * Transport response for getting index segments + * + * @opensearch.internal + */ public class GetSettingsResponse extends ActionResponse implements ToXContentObject { - private final ImmutableOpenMap indexToSettings; - private final ImmutableOpenMap indexToDefaultSettings; + private final Map indexToSettings; + private final Map indexToDefaultSettings; - public GetSettingsResponse( - ImmutableOpenMap indexToSettings, - ImmutableOpenMap indexToDefaultSettings - ) { - this.indexToSettings = indexToSettings; - this.indexToDefaultSettings = indexToDefaultSettings; + public GetSettingsResponse(Map indexToSettings, Map indexToDefaultSettings) { + this.indexToSettings = Collections.unmodifiableMap(indexToSettings); + this.indexToDefaultSettings = Collections.unmodifiableMap(indexToDefaultSettings); } public GetSettingsResponse(StreamInput in) throws IOException { super(in); - indexToSettings = in.readImmutableMap(StreamInput::readString, Settings::readSettingsFromStream); - indexToDefaultSettings = in.readImmutableMap(StreamInput::readString, Settings::readSettingsFromStream); + indexToSettings = in.readMap(StreamInput::readString, Settings::readSettingsFromStream); + indexToDefaultSettings = in.readMap(StreamInput::readString, Settings::readSettingsFromStream); } /** @@ -76,7 +76,7 @@ public GetSettingsResponse(StreamInput in) throws IOException { * objects contain only those settings explicitly set on a given index. Any settings * taking effect as defaults must be accessed via {@link #getIndexToDefaultSettings()}. */ - public ImmutableOpenMap getIndexToSettings() { + public Map getIndexToSettings() { return indexToSettings; } @@ -88,7 +88,7 @@ public ImmutableOpenMap getIndexToSettings() { * via {@link #getIndexToSettings()}. * See also {@link GetSettingsRequest#includeDefaults(boolean)} */ - public ImmutableOpenMap getIndexToDefaultSettings() { + public Map getIndexToDefaultSettings() { return indexToDefaultSettings; } @@ -180,10 +180,8 @@ public static GetSettingsResponse fromXContent(XContentParser parser) throws IOE } } - ImmutableOpenMap settingsMap = ImmutableOpenMap.builder().putAll(indexToSettings).build(); - ImmutableOpenMap defaultSettingsMap = ImmutableOpenMap.builder() - .putAll(indexToDefaultSettings) - .build(); + final Map settingsMap = Collections.unmodifiableMap(indexToSettings); + final Map defaultSettingsMap = Collections.unmodifiableMap(indexToDefaultSettings); return new GetSettingsResponse(settingsMap, defaultSettingsMap); } @@ -194,7 +192,7 @@ public String toString() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); XContentBuilder builder = new XContentBuilder(JsonXContent.jsonXContent, baos); toXContent(builder, ToXContent.EMPTY_PARAMS, false); - return Strings.toString(builder); + return builder.toString(); } catch (IOException e) { throw new IllegalStateException(e); // should not be possible here } @@ -207,18 +205,18 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws private XContentBuilder toXContent(XContentBuilder builder, Params params, boolean omitEmptySettings) throws IOException { builder.startObject(); - for (ObjectObjectCursor cursor : getIndexToSettings()) { + for (final Map.Entry cursor : getIndexToSettings().entrySet()) { // no settings, jump over it to shorten the response data - if (omitEmptySettings && cursor.value.isEmpty()) { + if (omitEmptySettings && cursor.getValue().isEmpty()) { continue; } - builder.startObject(cursor.key); + builder.startObject(cursor.getKey()); builder.startObject("settings"); - cursor.value.toXContent(builder, params); + cursor.getValue().toXContent(builder, params); builder.endObject(); if (indexToDefaultSettings.isEmpty() == false) { builder.startObject("defaults"); - indexToDefaultSettings.get(cursor.key).toXContent(builder, params); + indexToDefaultSettings.get(cursor.getKey()).toXContent(builder, params); builder.endObject(); } builder.endObject(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/TransportGetSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/TransportGetSettingsAction.java index 5e2c13b5037e2..d8f2180208b18 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/TransportGetSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/TransportGetSettingsAction.java @@ -32,30 +32,36 @@ package org.opensearch.action.admin.indices.settings.get; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.settings.SettingsFilter; -import org.opensearch.common.util.CollectionUtils; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.Index; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; -public class TransportGetSettingsAction extends TransportMasterNodeReadAction { +/** + * Transport action for getting index settings + * + * @opensearch.internal + */ +public class TransportGetSettingsAction extends TransportClusterManagerNodeReadAction { private final SettingsFilter settingsFilter; private final IndexScopedSettings indexScopedSettings; @@ -105,10 +111,10 @@ private static boolean isFilteredRequest(GetSettingsRequest request) { } @Override - protected void masterOperation(GetSettingsRequest request, ClusterState state, ActionListener listener) { + protected void clusterManagerOperation(GetSettingsRequest request, ClusterState state, ActionListener listener) { Index[] concreteIndices = indexNameExpressionResolver.concreteIndices(state, request); - ImmutableOpenMap.Builder indexToSettingsBuilder = ImmutableOpenMap.builder(); - ImmutableOpenMap.Builder indexToDefaultSettingsBuilder = ImmutableOpenMap.builder(); + final Map indexToSettingsBuilder = new HashMap<>(); + final Map indexToDefaultSettingsBuilder = new HashMap<>(); for (Index concreteIndex : concreteIndices) { IndexMetadata indexMetadata = state.getMetadata().index(concreteIndex); if (indexMetadata == null) { @@ -133,6 +139,6 @@ protected void masterOperation(GetSettingsRequest request, ClusterState state, A indexToDefaultSettingsBuilder.put(concreteIndex.getName(), defaultSettings); } } - listener.onResponse(new GetSettingsResponse(indexToSettingsBuilder.build(), indexToDefaultSettingsBuilder.build())); + listener.onResponse(new GetSettingsResponse(indexToSettingsBuilder, indexToDefaultSettingsBuilder)); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/package-info.java new file mode 100644 index 0000000000000..e767b1a8870e0 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Get Index Settings transport handler. */ +package org.opensearch.action.admin.indices.settings.get; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/package-info.java new file mode 100644 index 0000000000000..862dfad419bd4 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Settings transport handlers. */ +package org.opensearch.action.admin.indices.settings; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/TransportUpdateSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/TransportUpdateSettingsAction.java index 8b81bb8d8a821..1c57dc27df8c6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/TransportUpdateSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/TransportUpdateSettingsAction.java @@ -35,10 +35,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.block.ClusterBlockException; @@ -48,17 +47,41 @@ import org.opensearch.cluster.metadata.MetadataUpdateSettingsService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.Index; +import org.opensearch.index.IndexModule; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.Set; +import java.util.stream.Stream; -public class TransportUpdateSettingsAction extends TransportMasterNodeAction { +import static org.opensearch.index.IndexModule.INDEX_STORE_TYPE_SETTING; + +/** + * Transport action for updating index settings + * + * @opensearch.internal + */ +public class TransportUpdateSettingsAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportUpdateSettingsAction.class); + private final static Set ALLOWLIST_REMOTE_SNAPSHOT_SETTINGS = Set.of( + "index.max_result_window", + "index.max_inner_result_window", + "index.max_rescore_window", + "index.max_docvalue_fields_search", + "index.max_script_fields", + "index.max_terms_count", + "index.max_regex_length", + "index.highlight.max_analyzed_offset" + ); + + private final static String[] ALLOWLIST_REMOTE_SNAPSHOT_SETTINGS_PREFIXES = { "index.search.slowlog" }; + private final MetadataUpdateSettingsService updateSettingsService; @Inject @@ -101,8 +124,31 @@ protected ClusterBlockException checkBlock(UpdateSettingsRequest request, Cluste || IndexMetadata.INDEX_BLOCKS_READ_ONLY_ALLOW_DELETE_SETTING.exists(request.settings())) { return null; } - return state.blocks() - .indicesBlockedException(ClusterBlockLevel.METADATA_WRITE, indexNameExpressionResolver.concreteIndexNames(state, request)); + + final Index[] requestIndices = indexNameExpressionResolver.concreteIndices(state, request); + boolean allowSearchableSnapshotSettingsUpdate = true; + // check if all indices in the request are remote snapshot + for (Index index : requestIndices) { + if (state.blocks().indexBlocked(ClusterBlockLevel.METADATA_WRITE, index.getName())) { + allowSearchableSnapshotSettingsUpdate = allowSearchableSnapshotSettingsUpdate + && IndexModule.Type.REMOTE_SNAPSHOT.match( + state.getMetadata().getIndexSafe(index).getSettings().get(INDEX_STORE_TYPE_SETTING.getKey()) + ); + } + } + // check if all settings in the request are in the allow list + if (allowSearchableSnapshotSettingsUpdate) { + for (String setting : request.settings().keySet()) { + allowSearchableSnapshotSettingsUpdate = allowSearchableSnapshotSettingsUpdate + && (ALLOWLIST_REMOTE_SNAPSHOT_SETTINGS.contains(setting) + || Stream.of(ALLOWLIST_REMOTE_SNAPSHOT_SETTINGS_PREFIXES).anyMatch(setting::startsWith)); + } + } + + return allowSearchableSnapshotSettingsUpdate + ? null + : state.blocks() + .indicesBlockedException(ClusterBlockLevel.METADATA_WRITE, indexNameExpressionResolver.concreteIndexNames(state, request)); } @Override @@ -111,7 +157,7 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation( + protected void clusterManagerOperation( final UpdateSettingsRequest request, final ClusterState state, final ActionListener listener @@ -123,7 +169,7 @@ protected void masterOperation( .settings(request.settings()) .setPreserveExisting(request.isPreserveExisting()) .ackTimeout(request.timeout()) - .masterNodeTimeout(request.masterNodeTimeout()); + .masterNodeTimeout(request.clusterManagerNodeTimeout()); updateSettingsService.updateSettings(clusterStateUpdateRequest, new ActionListener() { @Override diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsAction.java index 7c0182b0704de..2333a2aad6bc6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Action for updating index settings + * + * @opensearch.internal + */ public class UpdateSettingsAction extends ActionType { public static final UpdateSettingsAction INSTANCE = new UpdateSettingsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsClusterStateUpdateRequest.java index ce36e01ac465e..4b0dd05575309 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsClusterStateUpdateRequest.java @@ -37,6 +37,8 @@ /** * Cluster state update request that allows to update settings for some indices + * + * @opensearch.internal */ public class UpdateSettingsClusterStateUpdateRequest extends IndicesClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsRequest.java index 70f3dc683d599..43571dc8220f9 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsRequest.java @@ -36,14 +36,15 @@ import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; @@ -52,12 +53,14 @@ import java.util.Objects; import static org.opensearch.action.ValidateActions.addValidationError; +import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; import static org.opensearch.common.settings.Settings.readSettingsFromStream; import static org.opensearch.common.settings.Settings.writeSettingsToStream; -import static org.opensearch.common.settings.Settings.Builder.EMPTY_SETTINGS; /** * Request for an update index settings action + * + * @opensearch.internal */ public class UpdateSettingsRequest extends AcknowledgedRequest implements @@ -155,8 +158,8 @@ public UpdateSettingsRequest settings(Settings.Builder settings) { /** * Sets the settings to be updated (either json or yaml format) */ - public UpdateSettingsRequest settings(String source, XContentType xContentType) { - this.settings = Settings.builder().loadFromSource(source, xContentType).build(); + public UpdateSettingsRequest settings(String source, MediaType mediaType) { + this.settings = Settings.builder().loadFromSource(source, mediaType).build(); return this; } @@ -219,7 +222,7 @@ public UpdateSettingsRequest fromXContent(XContentParser parser) throws IOExcept @Override public String toString() { - return "indices : " + Arrays.toString(indices) + "," + Strings.toString(this); + return "indices : " + Arrays.toString(indices) + "," + Strings.toString(MediaTypeRegistry.JSON, this); } @Override @@ -231,7 +234,7 @@ public boolean equals(Object o) { return false; } UpdateSettingsRequest that = (UpdateSettingsRequest) o; - return masterNodeTimeout.equals(that.masterNodeTimeout) + return clusterManagerNodeTimeout.equals(that.clusterManagerNodeTimeout) && timeout.equals(that.timeout) && Objects.equals(settings, that.settings) && Objects.equals(indicesOptions, that.indicesOptions) @@ -241,7 +244,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(masterNodeTimeout, timeout, settings, indicesOptions, preserveExisting, Arrays.hashCode(indices)); + return Objects.hash(clusterManagerNodeTimeout, timeout, settings, indicesOptions, preserveExisting, Arrays.hashCode(indices)); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsRequestBuilder.java index dd3b78ce901f4..7501f0c7798de 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/UpdateSettingsRequestBuilder.java @@ -43,6 +43,8 @@ /** * Builder for an update index settings request + * + * @opensearch.internal */ public class UpdateSettingsRequestBuilder extends AcknowledgedRequestBuilder< UpdateSettingsRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/settings/put/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/package-info.java new file mode 100644 index 0000000000000..dc38136b72d57 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/settings/put/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Put Settings transport handler. */ +package org.opensearch.action.admin.indices.settings.put; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoreRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoreRequestBuilder.java index 8a5b8bea75119..bc9633f2bd2db 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoreRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoreRequestBuilder.java @@ -34,14 +34,16 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.cluster.health.ClusterHealthStatus; /** * Request builder for {@link IndicesShardStoresRequest} + * + * @opensearch.internal */ -public class IndicesShardStoreRequestBuilder extends MasterNodeReadOperationRequestBuilder< +public class IndicesShardStoreRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< IndicesShardStoresRequest, IndicesShardStoresResponse, IndicesShardStoreRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresAction.java b/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresAction.java index ec67ae051a5b1..79a6f88b1dbc4 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresAction.java @@ -40,6 +40,8 @@ * Exposes shard store information for requested indices. * Shard store information reports which nodes hold shard copies, how recent they are * and any exceptions on opening the shard index or from previous engine failures + * + * @opensearch.internal */ public class IndicesShardStoresAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresRequest.java index ff5b5c4cf0c2a..d3261bea68f38 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresRequest.java @@ -34,19 +34,23 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; import org.opensearch.cluster.health.ClusterHealthStatus; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.EnumSet; /** * Request for {@link IndicesShardStoresAction} + * + * @opensearch.internal */ -public class IndicesShardStoresRequest extends MasterNodeReadRequest implements IndicesRequest.Replaceable { +public class IndicesShardStoresRequest extends ClusterManagerNodeReadRequest + implements + IndicesRequest.Replaceable { private String[] indices = Strings.EMPTY_ARRAY; private IndicesOptions indicesOptions = IndicesOptions.strictExpand(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresResponse.java index d09bf99ca7258..fae5a62b6ebbc 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shards/IndicesShardStoresResponse.java @@ -32,35 +32,36 @@ package org.opensearch.action.admin.indices.shards; -import com.carrotsearch.hppc.cursors.IntObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionResponse; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.collect.ImmutableOpenIntMap; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.Map; /** * Response for {@link IndicesShardStoresAction} * * Consists of {@link StoreStatus}s for requested indices grouped by * indices and shard ids and a list of encountered node {@link Failure}s + * + * @opensearch.internal */ public class IndicesShardStoresResponse extends ActionResponse implements ToXContentFragment { /** * Shard store information from a node + * + * @opensearch.internal */ public static class StoreStatus implements Writeable, ToXContentFragment, Comparable { private final DiscoveryNode node; @@ -231,6 +232,8 @@ public int compareTo(StoreStatus other) { /** * Single node failure while retrieving shard store information + * + * @opensearch.internal */ public static class Failure extends DefaultShardOperationFailedException { private String nodeId; @@ -276,31 +279,25 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t } } - private final ImmutableOpenMap>> storeStatuses; + private final Map>> storeStatuses; private final List failures; - public IndicesShardStoresResponse( - ImmutableOpenMap>> storeStatuses, - List failures - ) { - this.storeStatuses = storeStatuses; + public IndicesShardStoresResponse(final Map>> storeStatuses, List failures) { + this.storeStatuses = Collections.unmodifiableMap(storeStatuses); this.failures = failures; } IndicesShardStoresResponse() { - this(ImmutableOpenMap.of(), Collections.emptyList()); + this(Map.of(), Collections.emptyList()); } public IndicesShardStoresResponse(StreamInput in) throws IOException { super(in); - storeStatuses = in.readImmutableMap(StreamInput::readString, i -> { - int indexEntries = i.readVInt(); - ImmutableOpenIntMap.Builder> shardEntries = ImmutableOpenIntMap.builder(); - for (int shardCount = 0; shardCount < indexEntries; shardCount++) { - shardEntries.put(i.readInt(), i.readList(StoreStatus::new)); - } - return shardEntries.build(); - }); + final Map>> storeStatuses = in.readMap( + StreamInput::readString, + i -> i.readMap(StreamInput::readInt, j -> j.readList(StoreStatus::new)) + ); + this.storeStatuses = Collections.unmodifiableMap(storeStatuses); failures = Collections.unmodifiableList(in.readList(Failure::readFailure)); } @@ -308,7 +305,7 @@ public IndicesShardStoresResponse(StreamInput in) throws IOException { * Returns {@link StoreStatus}s * grouped by their index names and shard ids. */ - public ImmutableOpenMap>> getStoreStatuses() { + public Map>> getStoreStatuses() { return storeStatuses; } @@ -322,13 +319,11 @@ public List getFailures() { @Override public void writeTo(StreamOutput out) throws IOException { - out.writeMap(storeStatuses, StreamOutput::writeString, (o, v) -> { - o.writeVInt(v.size()); - for (IntObjectCursor> shardStatusesEntry : v) { - o.writeInt(shardStatusesEntry.key); - o.writeCollection(shardStatusesEntry.value); - } - }); + out.writeMap( + storeStatuses, + StreamOutput::writeString, + (o, v) -> o.writeMap(v, StreamOutput::writeInt, StreamOutput::writeCollection) + ); out.writeList(failures); } @@ -343,14 +338,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } builder.startObject(Fields.INDICES); - for (ObjectObjectCursor>> indexShards : storeStatuses) { - builder.startObject(indexShards.key); + for (final Map.Entry>> indexShards : storeStatuses.entrySet()) { + builder.startObject(indexShards.getKey()); builder.startObject(Fields.SHARDS); - for (IntObjectCursor> shardStatusesEntry : indexShards.value) { - builder.startObject(String.valueOf(shardStatusesEntry.key)); + for (final Map.Entry> shardStatusesEntry : indexShards.getValue().entrySet()) { + builder.startObject(String.valueOf(shardStatusesEntry.getKey())); builder.startArray(Fields.STORES); - for (StoreStatus storeStatus : shardStatusesEntry.value) { + for (StoreStatus storeStatus : shardStatusesEntry.getValue()) { builder.startObject(); storeStatus.toXContent(builder, params); builder.endObject(); @@ -367,6 +362,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Fields for parsing and toXContent + * + * @opensearch.internal + */ static final class Fields { static final String INDICES = "indices"; static final String SHARDS = "shards"; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java b/server/src/main/java/org/opensearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java index 32acd9e305130..41225bc362235 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shards/TransportIndicesShardStoresAction.java @@ -34,10 +34,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.CollectionUtil; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -53,24 +52,25 @@ import org.opensearch.cluster.routing.RoutingTable; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenIntMap; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.collect.Tuple; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.util.concurrent.CountDown; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.gateway.AsyncShardFetch; import org.opensearch.gateway.TransportNodesListGatewayStartedShards; import org.opensearch.gateway.TransportNodesListGatewayStartedShards.NodeGatewayStartedShards; -import org.opensearch.index.shard.ShardId; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; @@ -78,8 +78,10 @@ /** * Transport action that reads the cluster state for shards with the requested criteria (see {@link ClusterHealthStatus}) of specific * indices and fetches store information from all the nodes using {@link TransportNodesListGatewayStartedShards} + * + * @opensearch.internal */ -public class TransportIndicesShardStoresAction extends TransportMasterNodeReadAction< +public class TransportIndicesShardStoresAction extends TransportClusterManagerNodeReadAction< IndicesShardStoresRequest, IndicesShardStoresResponse> { @@ -119,7 +121,7 @@ protected IndicesShardStoresResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation( + protected void clusterManagerOperation( IndicesShardStoresRequest request, ClusterState state, ActionListener listener @@ -160,6 +162,11 @@ protected ClusterBlockException checkBlock(IndicesShardStoresRequest request, Cl .indicesBlockedException(ClusterBlockLevel.METADATA_READ, indexNameExpressionResolver.concreteIndexNames(state, request)); } + /** + * Information for async shard stores + * + * @opensearch.internal + */ private class AsyncShardStoresInfoFetches { private final DiscoveryNodes nodes; private final RoutingNodes routingNodes; @@ -193,6 +200,11 @@ void start() { } } + /** + * Internal async fetch + * + * @opensearch.internal + */ private class InternalAsyncFetch extends AsyncShardFetch { InternalAsyncFetch( @@ -218,20 +230,17 @@ protected synchronized void processAsyncFetch( } void finish() { - ImmutableOpenMap.Builder< - String, - ImmutableOpenIntMap>> indicesStoreStatusesBuilder = - ImmutableOpenMap.builder(); + final Map>> indicesStoreStatusesBuilder = new HashMap<>(); java.util.List failureBuilder = new ArrayList<>(); for (Response fetchResponse : fetchResponses) { - ImmutableOpenIntMap> indexStoreStatuses = + final Map> indexStoreStatuses = indicesStoreStatusesBuilder.get(fetchResponse.shardId.getIndexName()); - final ImmutableOpenIntMap.Builder> indexShardsBuilder; + final Map> indexShardsBuilder; if (indexStoreStatuses == null) { - indexShardsBuilder = ImmutableOpenIntMap.builder(); + indexShardsBuilder = new HashMap<>(); } else { - indexShardsBuilder = ImmutableOpenIntMap.builder(indexStoreStatuses); + indexShardsBuilder = new HashMap<>(indexStoreStatuses); } java.util.List storeStatuses = indexShardsBuilder.get( fetchResponse.shardId.id() @@ -258,7 +267,7 @@ void finish() { } CollectionUtil.timSort(storeStatuses); indexShardsBuilder.put(fetchResponse.shardId.id(), storeStatuses); - indicesStoreStatusesBuilder.put(fetchResponse.shardId.getIndexName(), indexShardsBuilder.build()); + indicesStoreStatusesBuilder.put(fetchResponse.shardId.getIndexName(), Collections.unmodifiableMap(indexShardsBuilder)); for (FailedNodeException failure : fetchResponse.failures) { failureBuilder.add( new IndicesShardStoresResponse.Failure( @@ -271,7 +280,7 @@ void finish() { } } listener.onResponse( - new IndicesShardStoresResponse(indicesStoreStatusesBuilder.build(), Collections.unmodifiableList(failureBuilder)) + new IndicesShardStoresResponse(indicesStoreStatusesBuilder, Collections.unmodifiableList(failureBuilder)) ); } @@ -307,6 +316,11 @@ protected void reroute(ShardId shardId, String reason) { // no-op } + /** + * Response for shard stores action + * + * @opensearch.internal + */ public class Response { private final ShardId shardId; private final List responses; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shards/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/shards/package-info.java new file mode 100644 index 0000000000000..187ffc0714e7e --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/shards/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Shards transport handlers. */ +package org.opensearch.action.admin.indices.shards; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeAction.java b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeAction.java index 5ecdd62206177..7ea6391f1c429 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for resizing an index + * + * @opensearch.internal + */ public class ResizeAction extends ActionType { public static final ResizeAction INSTANCE = new ResizeAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeRequest.java index b74c6c8f8d195..ea6892126dc65 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeRequest.java @@ -32,6 +32,7 @@ package org.opensearch.action.admin.indices.shrink; import org.opensearch.LegacyESVersion; +import org.opensearch.Version; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.admin.indices.alias.Alias; @@ -40,13 +41,14 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.master.AcknowledgedRequest; import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; @@ -55,10 +57,14 @@ /** * Request class to shrink an index into a single shard + * + * @opensearch.internal */ public class ResizeRequest extends AcknowledgedRequest implements IndicesRequest, ToXContentObject { public static final ObjectParser PARSER = new ObjectParser<>("resize_request"); + private static final ParseField MAX_SHARD_SIZE = new ParseField("max_shard_size"); + static { PARSER.declareField( (parser, request, context) -> request.getTargetIndexRequest().settings(parser.map()), @@ -70,12 +76,20 @@ public class ResizeRequest extends AcknowledgedRequest implements new ParseField("aliases"), ObjectParser.ValueType.OBJECT ); + PARSER.declareField( + ResizeRequest::setMaxShardSize, + (p, c) -> ByteSizeValue.parseBytesSizeValue(p.text(), MAX_SHARD_SIZE.getPreferredName()), + MAX_SHARD_SIZE, + ObjectParser.ValueType.STRING + ); } private CreateIndexRequest targetIndexRequest; private String sourceIndex; private ResizeType type = ResizeType.SHRINK; private Boolean copySettings = true; + private ByteSizeValue maxShardSize; + private boolean shouldStoreResult; public ResizeRequest(StreamInput in) throws IOException { super(in); @@ -83,6 +97,9 @@ public ResizeRequest(StreamInput in) throws IOException { sourceIndex = in.readString(); type = in.readEnum(ResizeType.class); copySettings = in.readOptionalBoolean(); + if (in.getVersion().onOrAfter(Version.V_2_5_0)) { + maxShardSize = in.readOptionalWriteable(ByteSizeValue::new); + } } ResizeRequest() {} @@ -104,9 +121,53 @@ public ActionRequestValidationException validate() { if (targetIndexRequest.settings().getByPrefix("index.sort.").isEmpty() == false) { validationException = addValidationError("can't override index sort when resizing an index", validationException); } + if (IndexMetadata.INDEX_ROUTING_PARTITION_SIZE_SETTING.exists(targetIndexRequest.settings())) { + validationException = addValidationError( + "cannot provide a routing partition size value when resizing an index", + validationException + ); + } if (type == ResizeType.SPLIT && IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.exists(targetIndexRequest.settings()) == false) { validationException = addValidationError("index.number_of_shards is required for split operations", validationException); } + + // max_shard_size is only supported for shrink + if (type != ResizeType.SHRINK && maxShardSize != null) { + validationException = addValidationError("Unsupported parameter [max_shard_size]", validationException); + } + // max_shard_size conflicts with the index.number_of_shards setting + if (type == ResizeType.SHRINK + && IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.exists(targetIndexRequest.settings()) + && maxShardSize != null) { + validationException = addValidationError( + "Cannot set max_shard_size and index.number_of_shards at the same time!", + validationException + ); + } + if (maxShardSize != null && maxShardSize.getBytes() <= 0) { + validationException = addValidationError("max_shard_size must be greater than 0", validationException); + } + // Check target index's settings, if `index.blocks.read_only` is `true`, the target index's metadata writes will be disabled + // and then cause the new shards to be unassigned. + if (IndexMetadata.INDEX_READ_ONLY_SETTING.get(targetIndexRequest.settings()) == true) { + validationException = addValidationError( + "target index [" + + targetIndexRequest.index() + + "] will be blocked by [index.blocks.read_only=true], this will disable metadata writes and cause the shards to be unassigned", + validationException + ); + } + + // Check target index's settings, if `index.blocks.metadata` is `true`, the target index's metadata writes will be disabled + // and then cause the new shards to be unassigned. + if (IndexMetadata.INDEX_BLOCKS_METADATA_SETTING.get(targetIndexRequest.settings()) == true) { + validationException = addValidationError( + "target index [" + + targetIndexRequest.index() + + "] will be blocked by [index.blocks.metadata=true], this will disable metadata writes and cause the shards to be unassigned", + validationException + ); + } assert copySettings == null || copySettings; return validationException; } @@ -125,6 +186,9 @@ public void writeTo(StreamOutput out) throws IOException { } out.writeEnum(type); out.writeOptionalBoolean(copySettings); + if (out.getVersion().onOrAfter(Version.V_2_5_0)) { + out.writeOptionalWriteable(maxShardSize); + } } @Override @@ -207,6 +271,36 @@ public Boolean getCopySettings() { return copySettings; } + /** + * Sets the maximum size of a primary shard in the new shrunken index. + * This parameter can be used to calculate the lowest factor of the source index's shards number + * which satisfies the maximum shard size requirement. + * + * @param maxShardSize the maximum size of a primary shard in the new shrunken index + */ + public void setMaxShardSize(ByteSizeValue maxShardSize) { + this.maxShardSize = maxShardSize; + } + + /** + * Returns the maximum size of a primary shard in the new shrunken index. + */ + public ByteSizeValue getMaxShardSize() { + return maxShardSize; + } + + /** + * Should this task store its result after it has finished? + */ + public void setShouldStoreResult(boolean shouldStoreResult) { + this.shouldStoreResult = shouldStoreResult; + } + + @Override + public boolean getShouldStoreResult() { + return shouldStoreResult; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -223,6 +317,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } } builder.endObject(); + if (maxShardSize != null) { + builder.field(MAX_SHARD_SIZE.getPreferredName(), maxShardSize); + } } builder.endObject(); return builder; @@ -231,4 +328,27 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws public void fromXContent(XContentParser parser) throws IOException { PARSER.parse(parser, this, null); } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + switch (getResizeType()) { + case SPLIT: + b.append("split from"); + break; + case CLONE: + b.append("clone from"); + break; + default: + b.append("shrink from"); + } + b.append(" [").append(sourceIndex).append("]"); + b.append(" to [").append(getTargetIndexRequest().index()).append(']'); + return b.toString(); + } + + @Override + public String getDescription() { + return this.toString(); + } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeRequestBuilder.java index 766ed78e63497..855e678c77b9b 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeRequestBuilder.java @@ -37,7 +37,13 @@ import org.opensearch.action.support.master.AcknowledgedRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.unit.ByteSizeValue; +/** + * Transport request builder for resizing an index + * + * @opensearch.internal + */ public class ResizeRequestBuilder extends AcknowledgedRequestBuilder { public ResizeRequestBuilder(OpenSearchClient client, ActionType action) { super(client, action, new ResizeRequest()); @@ -90,4 +96,12 @@ public ResizeRequestBuilder setResizeType(ResizeType type) { this.request.setResizeType(type); return this; } + + /** + * Sets the maximum size of a primary shard in the new shrunken index. + */ + public ResizeRequestBuilder setMaxShardSize(ByteSizeValue maxShardSize) { + this.request.setMaxShardSize(maxShardSize); + return this; + } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeResponse.java index 3846591d26f3a..1aa09023e3583 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeResponse.java @@ -33,14 +33,16 @@ package org.opensearch.action.admin.indices.shrink; import org.opensearch.action.admin.indices.create.CreateIndexResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; /** * A response for a resize index action, either shrink or split index. + * + * @opensearch.internal */ public final class ResizeResponse extends CreateIndexResponse { @@ -65,4 +67,14 @@ public ResizeResponse(boolean acknowledged, boolean shardsAcknowledged, String i public static ResizeResponse fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()).append("["); + builder.append("acknowledged=").append(isAcknowledged()).append(','); + builder.append("shards_acknowledged=").append(isShardsAcknowledged()).append(','); + builder.append("index=").append(index()); + return builder.append(']').toString(); + } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeType.java b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeType.java index 62b20ed9d3dcf..6403ed735ae49 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeType.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ResizeType.java @@ -34,6 +34,8 @@ /** * The type of the resize operation + * + * @opensearch.internal */ public enum ResizeType { SHRINK, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ShrinkAction.java b/server/src/main/java/org/opensearch/action/admin/indices/shrink/ShrinkAction.java deleted file mode 100644 index 1fa894b265573..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/shrink/ShrinkAction.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.shrink; - -import org.opensearch.action.ActionType; - -public class ShrinkAction extends ActionType { - - public static final ShrinkAction INSTANCE = new ShrinkAction(); - public static final String NAME = "indices:admin/shrink"; - - private ShrinkAction() { - super(NAME, ResizeResponse::new); - } - -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shrink/TransportResizeAction.java b/server/src/main/java/org/opensearch/action/admin/indices/shrink/TransportResizeAction.java index b35febe60af31..23cd8efdcaf59 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/shrink/TransportResizeAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/shrink/TransportResizeAction.java @@ -33,12 +33,11 @@ package org.opensearch.action.admin.indices.shrink; import org.apache.lucene.index.IndexWriter; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.stats.IndexShardStats; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.client.Client; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -49,12 +48,15 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.IndexSettings; import org.opensearch.index.shard.DocsStats; -import org.opensearch.index.shard.ShardId; +import org.opensearch.index.store.StoreStats; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -64,10 +66,14 @@ import java.util.Set; import java.util.function.IntFunction; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; + /** * Main class to initiate resizing (shrink / split) an index into a new index + * + * @opensearch.internal */ -public class TransportResizeAction extends TransportMasterNodeAction { +public class TransportResizeAction extends TransportClusterManagerNodeAction { private final MetadataCreateIndexService createIndexService; private final Client client; @@ -125,7 +131,7 @@ protected ClusterBlockException checkBlock(ResizeRequest request, ClusterState s } @Override - protected void masterOperation( + protected void clusterManagerOperation( final ResizeRequest resizeRequest, final ClusterState state, final ActionListener listener @@ -134,24 +140,78 @@ protected void masterOperation( // there is no need to fetch docs stats for split but we keep it simple and do it anyway for simplicity of the code final String sourceIndex = indexNameExpressionResolver.resolveDateMathExpression(resizeRequest.getSourceIndex()); final String targetIndex = indexNameExpressionResolver.resolveDateMathExpression(resizeRequest.getTargetIndexRequest().index()); - client.admin() - .indices() - .prepareStats(sourceIndex) - .clear() - .setDocs(true) - .execute(ActionListener.delegateFailure(listener, (delegatedListener, indicesStatsResponse) -> { - CreateIndexClusterStateUpdateRequest updateRequest = prepareCreateIndexRequest(resizeRequest, state, i -> { - IndexShardStats shard = indicesStatsResponse.getIndex(sourceIndex).getIndexShards().get(i); - return shard == null ? null : shard.getPrimary().getDocs(); - }, sourceIndex, targetIndex); - createIndexService.createIndex( - updateRequest, - ActionListener.map( - delegatedListener, - response -> new ResizeResponse(response.isAcknowledged(), response.isShardsAcknowledged(), updateRequest.index()) - ) - ); - })); + + IndexMetadata indexMetadata = state.metadata().index(sourceIndex); + if (resizeRequest.getResizeType().equals(ResizeType.SHRINK) + && state.metadata().isSegmentReplicationEnabled(sourceIndex) + && indexMetadata != null + && Integer.valueOf(indexMetadata.getSettings().get(SETTING_NUMBER_OF_REPLICAS)) > 0) { + client.admin() + .indices() + .prepareRefresh(sourceIndex) + .execute(ActionListener.delegateFailure(listener, (delegatedRefreshListener, refreshResponse) -> { + client.admin() + .indices() + .prepareStats(sourceIndex) + .clear() + .setDocs(true) + .setStore(true) + .setSegments(true) + .execute(ActionListener.delegateFailure(listener, (delegatedIndicesStatsListener, indicesStatsResponse) -> { + CreateIndexClusterStateUpdateRequest updateRequest = prepareCreateIndexRequest(resizeRequest, state, i -> { + IndexShardStats shard = indicesStatsResponse.getIndex(sourceIndex).getIndexShards().get(i); + return shard == null ? null : shard.getPrimary().getDocs(); + }, indicesStatsResponse.getPrimaries().store, sourceIndex, targetIndex); + + if (indicesStatsResponse.getIndex(sourceIndex) + .getTotal() + .getSegments() + .getReplicationStats().maxBytesBehind != 0) { + throw new IllegalStateException( + " For index [" + + sourceIndex + + "] replica shards haven't caught up with primary, please retry after sometime." + ); + } + + createIndexService.createIndex( + updateRequest, + ActionListener.map( + delegatedIndicesStatsListener, + response -> new ResizeResponse( + response.isAcknowledged(), + response.isShardsAcknowledged(), + updateRequest.index() + ) + ) + ); + })); + })); + } else { + client.admin() + .indices() + .prepareStats(sourceIndex) + .clear() + .setDocs(true) + .setStore(true) + .execute(ActionListener.delegateFailure(listener, (delegatedListener, indicesStatsResponse) -> { + CreateIndexClusterStateUpdateRequest updateRequest = prepareCreateIndexRequest(resizeRequest, state, i -> { + IndexShardStats shard = indicesStatsResponse.getIndex(sourceIndex).getIndexShards().get(i); + return shard == null ? null : shard.getPrimary().getDocs(); + }, indicesStatsResponse.getPrimaries().store, sourceIndex, targetIndex); + createIndexService.createIndex( + updateRequest, + ActionListener.map( + delegatedListener, + response -> new ResizeResponse( + response.isAcknowledged(), + response.isShardsAcknowledged(), + updateRequest.index() + ) + ) + ); + })); + } } @@ -160,6 +220,7 @@ static CreateIndexClusterStateUpdateRequest prepareCreateIndexRequest( final ResizeRequest resizeRequest, final ClusterState state, final IntFunction perShardDocStats, + final StoreStats primaryShardsStoreStats, String sourceIndexName, String targetIndexName ) { @@ -174,12 +235,28 @@ static CreateIndexClusterStateUpdateRequest prepareCreateIndexRequest( targetIndexSettingsBuilder.remove(IndexMetadata.SETTING_HISTORY_UUID); final Settings targetIndexSettings = targetIndexSettingsBuilder.build(); final int numShards; + + // We should check the source index's setting `index.blocks.read_only`, because the setting will be copied to target index, + // it will block target index's metadata writes and then cause the new shards to be unassigned, + // but if user overwrites the setting to `false` or `null`, everything is fine. + // We don't need to check the setting `index.blocks.metadata`, because it was checked when fetching index stats + if (IndexMetadata.INDEX_READ_ONLY_SETTING.get(metadata.getSettings()) == true + && IndexMetadata.INDEX_READ_ONLY_SETTING.exists(targetIndexSettings) == false) { + throw new IllegalArgumentException( + "target index [" + + targetIndexName + + "] will be blocked by [index.blocks.read_only=true] which is copied from the source index [" + + sourceIndexName + + "], this will disable metadata writes and cause the shards to be unassigned" + ); + } + if (IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.exists(targetIndexSettings)) { numShards = IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.get(targetIndexSettings); } else { assert resizeRequest.getResizeType() != ResizeType.SPLIT : "split must specify the number of shards explicitly"; if (resizeRequest.getResizeType() == ResizeType.SHRINK) { - numShards = 1; + numShards = calculateTargetIndexShardsNum(resizeRequest.getMaxShardSize(), primaryShardsStoreStats, metadata); } else { assert resizeRequest.getResizeType() == ResizeType.CLONE; numShards = metadata.getNumberOfShards(); @@ -213,9 +290,6 @@ static CreateIndexClusterStateUpdateRequest prepareCreateIndexRequest( } } - if (IndexMetadata.INDEX_ROUTING_PARTITION_SIZE_SETTING.exists(targetIndexSettings)) { - throw new IllegalArgumentException("cannot provide a routing partition size value when resizing an index"); - } if (IndexMetadata.INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING.exists(targetIndexSettings)) { // if we have a source index with 1 shards it's legal to set this final boolean splitFromSingleShards = resizeRequest.getResizeType() == ResizeType.SPLIT && metadata.getNumberOfShards() == 1; @@ -239,7 +313,7 @@ static CreateIndexClusterStateUpdateRequest prepareCreateIndexRequest( // applied once we took the snapshot and if somebody messes things up and switches the index read/write and adds docs we // miss the mappings for everything is corrupted and hard to debug .ackTimeout(targetIndex.timeout()) - .masterNodeTimeout(targetIndex.masterNodeTimeout()) + .masterNodeTimeout(targetIndex.clusterManagerNodeTimeout()) .settings(targetIndex.settings()) .aliases(targetIndex.aliases()) .waitForActiveShards(targetIndex.waitForActiveShards()) @@ -248,8 +322,48 @@ static CreateIndexClusterStateUpdateRequest prepareCreateIndexRequest( .copySettings(resizeRequest.getCopySettings() == null ? false : resizeRequest.getCopySettings()); } + /** + * Calculate target index's shards count according to max_shard_ize and the source index's storage(only primary shards included) + * for shrink. Target index's shards count is the lowest factor of the source index's primary shards count which satisfies the + * maximum shard size requirement. If max_shard_size is less than the source index's single shard size, then target index's shards count + * will be equal to the source index's shards count. + * @param maxShardSize the maximum size of a primary shard in the target index + * @param sourceIndexShardStoreStats primary shards' store stats of the source index + * @param sourceIndexMetaData source index's metadata + * @return target index's shards number + */ + protected static int calculateTargetIndexShardsNum( + ByteSizeValue maxShardSize, + StoreStats sourceIndexShardStoreStats, + IndexMetadata sourceIndexMetaData + ) { + if (maxShardSize == null + || sourceIndexShardStoreStats == null + || maxShardSize.getBytes() == 0 + || sourceIndexShardStoreStats.getSizeInBytes() == 0) { + return 1; + } + + int sourceIndexShardsNum = sourceIndexMetaData.getNumberOfShards(); + // calculate the minimum shards count according to source index's storage, ceiling ensures that the minimum shards count is never + // less than 1 + int minValue = (int) Math.ceil((double) sourceIndexShardStoreStats.getSizeInBytes() / maxShardSize.getBytes()); + // if minimum shards count is greater than the source index's shards count, then the source index's shards count will be returned + if (minValue >= sourceIndexShardsNum) { + return sourceIndexShardsNum; + } + + // find the lowest factor of the source index's shards count here, because minimum shards count may not be a factor + for (int i = minValue; i < sourceIndexShardsNum; i++) { + if (sourceIndexShardsNum % i == 0) { + return i; + } + } + return sourceIndexShardsNum; + } + @Override - protected String getMasterActionName(DiscoveryNode node) { - return super.getMasterActionName(node); + protected String getClusterManagerActionName(DiscoveryNode node) { + return super.getClusterManagerActionName(node); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/shrink/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/shrink/package-info.java new file mode 100644 index 0000000000000..414095575f0d9 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/shrink/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Shrink Index transport handlers. */ +package org.opensearch.action.admin.indices.shrink; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/CommonStats.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/CommonStats.java index 2949af00a30d0..e4abaef4ddfa8 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/CommonStats.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/CommonStats.java @@ -34,13 +34,13 @@ import org.apache.lucene.store.AlreadyClosedException; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.cache.query.QueryCacheStats; import org.opensearch.index.cache.request.RequestCacheStats; import org.opensearch.index.engine.SegmentsStats; @@ -65,6 +65,11 @@ import java.util.Objects; import java.util.stream.Stream; +/** + * Common Stats for OpenSearch + * + * @opensearch.internal + */ public class CommonStats implements Writeable, ToXContentFragment { @Nullable diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/CommonStatsFlags.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/CommonStatsFlags.java index e17b497ce312a..501abe269556b 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/CommonStatsFlags.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/CommonStatsFlags.java @@ -34,15 +34,20 @@ import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; import java.util.Collections; import java.util.EnumSet; +/** + * Common Stats Flags for OpenSearch + * + * @opensearch.internal + */ public class CommonStatsFlags implements Writeable, Cloneable { public static final CommonStatsFlags ALL = new CommonStatsFlags().all(); @@ -256,6 +261,11 @@ public CommonStatsFlags clone() { } } + /** + * The flags. + * + * @opensearch.internal + */ public enum Flag { Store("store", 0), Indexing("indexing", 1), diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndexShardStats.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndexShardStats.java index b8ecd6d4bfc3d..1635ce0bf83fc 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndexShardStats.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndexShardStats.java @@ -32,15 +32,20 @@ package org.opensearch.action.admin.indices.stats; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; +/** + * IndexShardStats for OpenSearch + * + * @opensearch.internal + */ public class IndexShardStats implements Iterable, Writeable { private final ShardId shardId; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndexStats.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndexStats.java index f0eec4ebbd27a..c98d46a0caed6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndexStats.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndexStats.java @@ -38,6 +38,11 @@ import java.util.List; import java.util.Map; +/** + * Index Stats for OpenSearch + * + * @opensearch.internal + */ public class IndexStats implements Iterable { private final String index; @@ -127,6 +132,11 @@ public CommonStats getPrimaries() { return stats; } + /** + * Builder for Index Stats + * + * @opensearch.internal + */ public static class IndexStatsBuilder { private final String indexName; private final String uuid; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsAction.java index c0a56c3f00536..bee33671ee291 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for retrieving index stats + * + * @opensearch.internal + */ public class IndicesStatsAction extends ActionType { public static final IndicesStatsAction INSTANCE = new IndicesStatsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsRequest.java index bbe69b700b876..54f3e9b7d1a24 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsRequest.java @@ -33,8 +33,8 @@ package org.opensearch.action.admin.indices.stats; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -45,6 +45,8 @@ *

    * All the stats to be returned can be cleared using {@link #clear()}, at which point, specific * stats can be enabled. + * + * @opensearch.internal */ public class IndicesStatsRequest extends BroadcastRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsRequestBuilder.java index 23c33401966b4..c211812b32c48 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsRequestBuilder.java @@ -44,6 +44,8 @@ *

    * All the stats to be returned can be cleared using {@link #clear()}, at which point, specific * stats can be enabled. + * + * @opensearch.internal */ public class IndicesStatsRequestBuilder extends BroadcastOperationRequestBuilder< IndicesStatsRequest, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsResponse.java index 3614d8de6c884..6f051fa19c99f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/IndicesStatsResponse.java @@ -33,14 +33,15 @@ package org.opensearch.action.admin.indices.stats; import org.opensearch.action.admin.indices.stats.IndexStats.IndexStatsBuilder; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; import org.opensearch.cluster.routing.ShardRouting; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.Index; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.HashMap; @@ -50,6 +51,11 @@ import static java.util.Collections.unmodifiableMap; +/** + * Transport response for retrieving indices stats + * + * @opensearch.internal + */ public class IndicesStatsResponse extends BroadcastResponse { private ShardStats[] shards; @@ -208,6 +214,11 @@ protected void addCustomXContentFields(XContentBuilder builder, Params params) t } } + /** + * Fields for parsing and toXContent + * + * @opensearch.internal + */ static final class Fields { static final String INDICES = "indices"; static final String SHARDS = "shards"; @@ -215,6 +226,6 @@ static final class Fields { @Override public String toString() { - return Strings.toString(this, true, false); + return Strings.toString(MediaTypeRegistry.JSON, this, true, false); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/ShardStats.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/ShardStats.java index c5d3fba2a5805..77562fa19b319 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/ShardStats.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/ShardStats.java @@ -34,11 +34,11 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.engine.CommitStats; import org.opensearch.index.seqno.RetentionLeaseStats; import org.opensearch.index.seqno.SeqNoStats; @@ -46,6 +46,11 @@ import java.io.IOException; +/** + * Shard Stats for OpenSearch + * + * @opensearch.internal + */ public class ShardStats implements Writeable, ToXContentFragment { private ShardRouting shardRouting; @@ -172,6 +177,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Fields for parsing and toXContent + * + * @opensearch.internal + */ static final class Fields { static final String ROUTING = "routing"; static final String STATE = "state"; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/TransportIndicesStatsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/TransportIndicesStatsAction.java index 11ca1462787e8..747213a23f577 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/stats/TransportIndicesStatsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/TransportIndicesStatsAction.java @@ -34,7 +34,6 @@ import org.apache.lucene.store.AlreadyClosedException; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -44,7 +43,8 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.IndexService; import org.opensearch.index.engine.CommitStats; import org.opensearch.index.seqno.RetentionLeaseStats; @@ -58,6 +58,11 @@ import java.io.IOException; import java.util.List; +/** + * Transport action for retrieving indices stats + * + * @opensearch.internal + */ public class TransportIndicesStatsAction extends TransportBroadcastByNodeAction { private final IndicesService indicesService; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/stats/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/stats/package-info.java new file mode 100644 index 0000000000000..38241a0d3bcbb --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/stats/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Stats transport handlers. */ +package org.opensearch.action.admin.indices.stats; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteComponentTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteComponentTemplateAction.java index 1a4f0a1892ec7..1f427a349c2ea 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteComponentTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteComponentTemplateAction.java @@ -34,15 +34,20 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.ActionType; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport action for deleting an index template component + * + * @opensearch.internal + */ public class DeleteComponentTemplateAction extends ActionType { public static final DeleteComponentTemplateAction INSTANCE = new DeleteComponentTemplateAction(); @@ -52,7 +57,12 @@ private DeleteComponentTemplateAction() { super(NAME, AcknowledgedResponse::new); } - public static class Request extends MasterNodeRequest { + /** + * Inner Request class for deleting component template + * + * @opensearch.internal + */ + public static class Request extends ClusterManagerNodeRequest { private String name; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteComposableIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteComposableIndexTemplateAction.java index bc8b96f6259e8..496358cdfd2b1 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteComposableIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteComposableIndexTemplateAction.java @@ -34,16 +34,21 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.ActionType; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport action for deleting a composable index template + * + * @opensearch.internal + */ public class DeleteComposableIndexTemplateAction extends ActionType { public static final DeleteComposableIndexTemplateAction INSTANCE = new DeleteComposableIndexTemplateAction(); @@ -53,7 +58,12 @@ private DeleteComposableIndexTemplateAction() { super(NAME, AcknowledgedResponse::new); } - public static class Request extends MasterNodeRequest { + /** + * Inner Request class for deleting composable index template + * + * @opensearch.internal + */ + public static class Request extends ClusterManagerNodeRequest { private String name; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateAction.java index a91ec2850a107..789d03f8e8d8c 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action for deleting an index template + * + * @opensearch.internal + */ public class DeleteIndexTemplateAction extends ActionType { public static final DeleteIndexTemplateAction INSTANCE = new DeleteIndexTemplateAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateRequest.java index a3762bb62fb94..85524bddc56d8 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateRequest.java @@ -32,9 +32,9 @@ package org.opensearch.action.admin.indices.template.delete; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -42,8 +42,10 @@ /** * A request to delete an index template. + * + * @opensearch.internal */ -public class DeleteIndexTemplateRequest extends MasterNodeRequest { +public class DeleteIndexTemplateRequest extends ClusterManagerNodeRequest { private String name; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateRequestBuilder.java index b58cdd06da5e9..4a990b7837120 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/DeleteIndexTemplateRequestBuilder.java @@ -31,11 +31,16 @@ package org.opensearch.action.admin.indices.template.delete; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -public class DeleteIndexTemplateRequestBuilder extends MasterNodeOperationRequestBuilder< +/** + * Transport request builder for deleting an index template + * + * @opensearch.internal + */ +public class DeleteIndexTemplateRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< DeleteIndexTemplateRequest, AcknowledgedResponse, DeleteIndexTemplateRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteComponentTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteComponentTemplateAction.java index 15ed0443525fb..30cb0cb3e5d00 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteComponentTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteComponentTemplateAction.java @@ -34,10 +34,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -45,13 +44,19 @@ import org.opensearch.cluster.metadata.MetadataIndexTemplateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportDeleteComponentTemplateAction extends TransportMasterNodeAction< +/** + * Transport action for deleting a component template + * + * @opensearch.internal + */ +public class TransportDeleteComponentTemplateAction extends TransportClusterManagerNodeAction< DeleteComponentTemplateAction.Request, AcknowledgedResponse> { @@ -97,11 +102,11 @@ protected ClusterBlockException checkBlock(DeleteComponentTemplateAction.Request } @Override - protected void masterOperation( + protected void clusterManagerOperation( final DeleteComponentTemplateAction.Request request, final ClusterState state, final ActionListener listener ) { - indexTemplateService.removeComponentTemplate(request.name(), request.masterNodeTimeout(), listener); + indexTemplateService.removeComponentTemplate(request.name(), request.clusterManagerNodeTimeout(), listener); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteComposableIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteComposableIndexTemplateAction.java index 87b3a883a3c10..27ea64809e3a7 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteComposableIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteComposableIndexTemplateAction.java @@ -34,10 +34,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -45,13 +44,19 @@ import org.opensearch.cluster.metadata.MetadataIndexTemplateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportDeleteComposableIndexTemplateAction extends TransportMasterNodeAction< +/** + * Transport action for deleting a composable index template + * + * @opensearch.internal + */ +public class TransportDeleteComposableIndexTemplateAction extends TransportClusterManagerNodeAction< DeleteComposableIndexTemplateAction.Request, AcknowledgedResponse> { @@ -97,11 +102,11 @@ protected ClusterBlockException checkBlock(DeleteComposableIndexTemplateAction.R } @Override - protected void masterOperation( + protected void clusterManagerOperation( final DeleteComposableIndexTemplateAction.Request request, final ClusterState state, final ActionListener listener ) { - indexTemplateService.removeIndexTemplateV2(request.name(), request.masterNodeTimeout(), listener); + indexTemplateService.removeIndexTemplateV2(request.name(), request.clusterManagerNodeTimeout(), listener); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteIndexTemplateAction.java index e75d3aafe6a85..c9542c7a58810 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/TransportDeleteIndexTemplateAction.java @@ -34,10 +34,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -45,7 +44,8 @@ import org.opensearch.cluster.metadata.MetadataIndexTemplateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -53,8 +53,12 @@ /** * Delete index action. + * + * @opensearch.internal */ -public class TransportDeleteIndexTemplateAction extends TransportMasterNodeAction { +public class TransportDeleteIndexTemplateAction extends TransportClusterManagerNodeAction< + DeleteIndexTemplateRequest, + AcknowledgedResponse> { private static final Logger logger = LogManager.getLogger(TransportDeleteIndexTemplateAction.class); @@ -98,13 +102,13 @@ protected ClusterBlockException checkBlock(DeleteIndexTemplateRequest request, C } @Override - protected void masterOperation( + protected void clusterManagerOperation( final DeleteIndexTemplateRequest request, final ClusterState state, final ActionListener listener ) { indexTemplateService.removeTemplates( - new MetadataIndexTemplateService.RemoveRequest(request.name()).masterTimeout(request.masterNodeTimeout()), + new MetadataIndexTemplateService.RemoveRequest(request.name()).clusterManagerTimeout(request.clusterManagerNodeTimeout()), new MetadataIndexTemplateService.RemoveListener() { @Override public void onResponse(MetadataIndexTemplateService.RemoveResponse response) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/delete/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/package-info.java new file mode 100644 index 0000000000000..f331c9dae4a38 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/delete/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Delete Index Templates transport handlers. */ +package org.opensearch.action.admin.indices.template.delete; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetComponentTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetComponentTemplateAction.java index 7f01ec0717332..51bd63c8473e6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetComponentTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetComponentTemplateAction.java @@ -33,16 +33,16 @@ package org.opensearch.action.admin.indices.template.get; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; -import org.opensearch.action.support.master.MasterNodeReadRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; import org.opensearch.cluster.metadata.ComponentTemplate; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Map; @@ -50,6 +50,8 @@ /** * Action to retrieve one or more component templates + * + * @opensearch.internal */ public class GetComponentTemplateAction extends ActionType { @@ -62,8 +64,10 @@ private GetComponentTemplateAction() { /** * Request that to retrieve one or more component templates + * + * @opensearch.internal */ - public static class Request extends MasterNodeReadRequest { + public static class Request extends ClusterManagerNodeReadRequest { @Nullable private String name; @@ -106,6 +110,11 @@ public String name() { } } + /** + * Inner response for getting component template + * + * @opensearch.internal + */ public static class Response extends ActionResponse implements ToXContentObject { public static final ParseField NAME = new ParseField("name"); public static final ParseField COMPONENT_TEMPLATES = new ParseField("component_templates"); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetComposableIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetComposableIndexTemplateAction.java index 5f7ccb0b4f90f..0fdb08285e8b5 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetComposableIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetComposableIndexTemplateAction.java @@ -33,21 +33,26 @@ package org.opensearch.action.admin.indices.template.get; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; -import org.opensearch.action.support.master.MasterNodeReadRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; import org.opensearch.cluster.metadata.ComposableIndexTemplate; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Map; import java.util.Objects; +/** + * Action to retrieve one or more Composable Index templates + * + * @opensearch.internal + */ public class GetComposableIndexTemplateAction extends ActionType { public static final GetComposableIndexTemplateAction INSTANCE = new GetComposableIndexTemplateAction(); @@ -59,8 +64,10 @@ private GetComposableIndexTemplateAction() { /** * Request that to retrieve one or more index templates + * + * @opensearch.internal */ - public static class Request extends MasterNodeReadRequest { + public static class Request extends ClusterManagerNodeReadRequest { @Nullable private String name; @@ -120,6 +127,11 @@ public boolean equals(Object obj) { } } + /** + * Inner response for getting composable index template + * + * @opensearch.internal + */ public static class Response extends ActionResponse implements ToXContentObject { public static final ParseField NAME = new ParseField("name"); public static final ParseField INDEX_TEMPLATES = new ParseField("index_templates"); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesAction.java index 1194abbab1cbb..f380ad6fddb55 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesAction.java @@ -33,6 +33,11 @@ import org.opensearch.action.ActionType; +/** + * Action to retrieve one or more Index templates + * + * @opensearch.internal + */ public class GetIndexTemplatesAction extends ActionType { public static final GetIndexTemplatesAction INSTANCE = new GetIndexTemplatesAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesRequest.java index da48bc4d8bc29..18142eb7b787d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesRequest.java @@ -32,10 +32,10 @@ package org.opensearch.action.admin.indices.template.get; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -43,8 +43,10 @@ /** * Request that allows to retrieve index templates + * + * @opensearch.internal */ -public class GetIndexTemplatesRequest extends MasterNodeReadRequest { +public class GetIndexTemplatesRequest extends ClusterManagerNodeReadRequest { private String[] names; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesRequestBuilder.java index 348075d051616..09de1733239fc 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesRequestBuilder.java @@ -31,10 +31,15 @@ package org.opensearch.action.admin.indices.template.get; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -public class GetIndexTemplatesRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Request builder to retrieve one or more Index templates + * + * @opensearch.internal + */ +public class GetIndexTemplatesRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< GetIndexTemplatesRequest, GetIndexTemplatesResponse, GetIndexTemplatesRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesResponse.java index e6d487e0a40b3..761a345b49538 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/GetIndexTemplatesResponse.java @@ -31,13 +31,13 @@ package org.opensearch.action.admin.indices.template.get; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.IndexTemplateMetadata; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; @@ -46,6 +46,11 @@ import static java.util.Collections.singletonMap; +/** + * Response for retrieving one or more Index templates + * + * @opensearch.internal + */ public class GetIndexTemplatesResponse extends ActionResponse implements ToXContentObject { private final List indexTemplates; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetComponentTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetComponentTemplateAction.java index 62615465fbb4a..e2594cd792cd3 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetComponentTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetComponentTemplateAction.java @@ -33,9 +33,8 @@ package org.opensearch.action.admin.indices.template.get; import org.opensearch.ResourceNotFoundException; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -43,8 +42,9 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.regex.Regex; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -52,7 +52,12 @@ import java.util.HashMap; import java.util.Map; -public class TransportGetComponentTemplateAction extends TransportMasterNodeReadAction< +/** + * Action to retrieve one or more Component templates + * + * @opensearch.internal + */ +public class TransportGetComponentTemplateAction extends TransportClusterManagerNodeReadAction< GetComponentTemplateAction.Request, GetComponentTemplateAction.Response> { @@ -91,7 +96,7 @@ protected ClusterBlockException checkBlock(GetComponentTemplateAction.Request re } @Override - protected void masterOperation( + protected void clusterManagerOperation( GetComponentTemplateAction.Request request, ClusterState state, ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java index efa6d4723ce0d..b1ef32db7274f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetComposableIndexTemplateAction.java @@ -33,9 +33,8 @@ package org.opensearch.action.admin.indices.template.get; import org.opensearch.ResourceNotFoundException; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -43,8 +42,9 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.regex.Regex; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -52,7 +52,12 @@ import java.util.HashMap; import java.util.Map; -public class TransportGetComposableIndexTemplateAction extends TransportMasterNodeReadAction< +/** + * Transport Action to retrieve one or more Composable Index templates + * + * @opensearch.internal + */ +public class TransportGetComposableIndexTemplateAction extends TransportClusterManagerNodeReadAction< GetComposableIndexTemplateAction.Request, GetComposableIndexTemplateAction.Response> { @@ -91,7 +96,7 @@ protected ClusterBlockException checkBlock(GetComposableIndexTemplateAction.Requ } @Override - protected void masterOperation( + protected void clusterManagerOperation( GetComposableIndexTemplateAction.Request request, ClusterState state, ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetIndexTemplatesAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetIndexTemplatesAction.java index df6531d0a3862..10b4975f7b9d0 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetIndexTemplatesAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/TransportGetIndexTemplatesAction.java @@ -31,10 +31,8 @@ package org.opensearch.action.admin.indices.template.get; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -42,8 +40,9 @@ import org.opensearch.cluster.metadata.IndexTemplateMetadata; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.regex.Regex; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -51,8 +50,16 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; -public class TransportGetIndexTemplatesAction extends TransportMasterNodeReadAction { +/** + * Transport action to retrieve one or more Index templates + * + * @opensearch.internal + */ +public class TransportGetIndexTemplatesAction extends TransportClusterManagerNodeReadAction< + GetIndexTemplatesRequest, + GetIndexTemplatesResponse> { @Inject public TransportGetIndexTemplatesAction( @@ -89,7 +96,7 @@ protected ClusterBlockException checkBlock(GetIndexTemplatesRequest request, Clu } @Override - protected void masterOperation( + protected void clusterManagerOperation( GetIndexTemplatesRequest request, ClusterState state, ActionListener listener @@ -98,16 +105,16 @@ protected void masterOperation( // If we did not ask for a specific name, then we return all templates if (request.names().length == 0) { - results = Arrays.asList(state.metadata().templates().values().toArray(IndexTemplateMetadata.class)); + results = Arrays.asList(state.metadata().templates().values().toArray(new IndexTemplateMetadata[0])); } else { results = new ArrayList<>(); } for (String name : request.names()) { if (Regex.isSimpleMatchPattern(name)) { - for (ObjectObjectCursor entry : state.metadata().templates()) { - if (Regex.simpleMatch(name, entry.key)) { - results.add(entry.value); + for (final Map.Entry entry : state.metadata().templates().entrySet()) { + if (Regex.simpleMatch(name, entry.getKey())) { + results.add(entry.getValue()); } } } else if (state.metadata().templates().containsKey(name)) { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/template/get/package-info.java new file mode 100644 index 0000000000000..e7173f4d6caaa --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Get Index Templates transport handlers. */ +package org.opensearch.action.admin.indices.template.get; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/template/package-info.java new file mode 100644 index 0000000000000..066a63f079232 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Index Templates transport handlers. */ +package org.opensearch.action.admin.indices.template; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateAction.java index 3e6a586638e58..66b15c8b807f8 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateAction.java @@ -34,6 +34,12 @@ import org.opensearch.action.ActionType; +/** + * Transport Action for handling simulating an index template either by name (looking it up in the + * cluster state), or by a provided template configuration + * + * @opensearch.internal + */ public class SimulateIndexTemplateAction extends ActionType { public static final SimulateIndexTemplateAction INSTANCE = new SimulateIndexTemplateAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateRequest.java index 9d6735751ac7e..028275778538e 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateRequest.java @@ -34,16 +34,22 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.admin.indices.template.put.PutComposableIndexTemplateAction; -import org.opensearch.action.support.master.MasterNodeReadRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; -public class SimulateIndexTemplateRequest extends MasterNodeReadRequest { +/** + * Transport Request for handling simulating an index template either by name (looking it up in the + * cluster state), or by a provided template configuration + * + * @opensearch.internal + */ +public class SimulateIndexTemplateRequest extends ClusterManagerNodeReadRequest { private String indexName; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateResponse.java index 9116db02459ef..8a370a6845db6 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateIndexTemplateResponse.java @@ -32,14 +32,14 @@ package org.opensearch.action.admin.indices.template.post; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.metadata.Template; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.HashMap; @@ -49,6 +49,8 @@ /** * Contains the information on what V2 templates would match a given index. + * + * @opensearch.internal */ public class SimulateIndexTemplateResponse extends ActionResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateTemplateAction.java index f53925e8abd50..0055676a7051a 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/post/SimulateTemplateAction.java @@ -36,10 +36,10 @@ import org.opensearch.action.ActionType; import org.opensearch.action.ValidateActions; import org.opensearch.action.admin.indices.template.put.PutComposableIndexTemplateAction; -import org.opensearch.action.support.master.MasterNodeReadRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; @@ -47,6 +47,8 @@ /** * An action for simulating the complete composed settings of the specified * index template name, or index template configuration + * + * @opensearch.internal */ public class SimulateTemplateAction extends ActionType { @@ -57,7 +59,12 @@ private SimulateTemplateAction() { super(NAME, SimulateIndexTemplateResponse::new); } - public static class Request extends MasterNodeReadRequest { + /** + * Request for simulating a template action + * + * @opensearch.internal + */ + public static class Request extends ClusterManagerNodeReadRequest { @Nullable private String templateName; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/post/TransportSimulateIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/post/TransportSimulateIndexTemplateAction.java index 598b5bdbf6d3b..c1a02d813ffb2 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/post/TransportSimulateIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/post/TransportSimulateIndexTemplateAction.java @@ -33,9 +33,8 @@ package org.opensearch.action.admin.indices.template.post; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -52,9 +51,10 @@ import org.opensearch.common.UUIDs; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.MapperService; import org.opensearch.indices.IndicesService; @@ -75,7 +75,13 @@ import static org.opensearch.cluster.metadata.MetadataIndexTemplateService.findV2Template; import static org.opensearch.cluster.metadata.MetadataIndexTemplateService.resolveSettings; -public class TransportSimulateIndexTemplateAction extends TransportMasterNodeReadAction< +/** + * Transport Action for handling simulating an index template either by name (looking it up in the + * cluster state), or by a provided template configuration + * + * @opensearch.internal + */ +public class TransportSimulateIndexTemplateAction extends TransportClusterManagerNodeReadAction< SimulateIndexTemplateRequest, SimulateIndexTemplateResponse> { @@ -121,7 +127,7 @@ protected SimulateIndexTemplateResponse read(StreamInput in) throws IOException } @Override - protected void masterOperation( + protected void clusterManagerOperation( SimulateIndexTemplateRequest request, ClusterState state, ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/post/TransportSimulateTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/post/TransportSimulateTemplateAction.java index e12544a02be4c..6565896fd3db2 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/post/TransportSimulateTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/post/TransportSimulateTemplateAction.java @@ -32,22 +32,22 @@ package org.opensearch.action.admin.indices.template.post; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.AliasValidator; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.ComposableIndexTemplate; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.MetadataIndexTemplateService; import org.opensearch.cluster.metadata.Template; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.UUIDs; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -64,8 +64,10 @@ /** * Handles simulating an index template either by name (looking it up in the * cluster state), or by a provided template configuration + * + * @opensearch.internal */ -public class TransportSimulateTemplateAction extends TransportMasterNodeReadAction< +public class TransportSimulateTemplateAction extends TransportClusterManagerNodeReadAction< SimulateTemplateAction.Request, SimulateIndexTemplateResponse> { @@ -111,7 +113,7 @@ protected SimulateIndexTemplateResponse read(StreamInput in) throws IOException } @Override - protected void masterOperation( + protected void clusterManagerOperation( SimulateTemplateAction.Request request, ClusterState state, ActionListener listener diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/post/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/template/post/package-info.java new file mode 100644 index 0000000000000..60b5a1d2abce8 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/post/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Post Index Templates transport handlers. */ +package org.opensearch.action.admin.indices.template.post; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutComponentTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutComponentTemplateAction.java index 69149b953cc08..d12f99ec345d3 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutComponentTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutComponentTemplateAction.java @@ -34,13 +34,13 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.ActionType; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeRequest; import org.opensearch.cluster.metadata.ComponentTemplate; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -48,6 +48,8 @@ /** * An action for putting a single component template into the cluster state + * + * @opensearch.internal */ public class PutComponentTemplateAction extends ActionType { @@ -60,8 +62,10 @@ private PutComponentTemplateAction() { /** * A request for putting a single component template into the cluster state + * + * @opensearch.internal */ - public static class Request extends MasterNodeRequest { + public static class Request extends ClusterManagerNodeRequest { private final String name; @Nullable private String cause; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutComposableIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutComposableIndexTemplateAction.java index 059f3be745bb0..1979cc39e32fb 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutComposableIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutComposableIndexTemplateAction.java @@ -36,21 +36,26 @@ import org.opensearch.action.ActionType; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeRequest; -import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.ComposableIndexTemplate; +import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.regex.Regex; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; import static org.opensearch.action.ValidateActions.addValidationError; +/** + * An action for putting a composable template into the cluster state + * + * @opensearch.internal + */ public class PutComposableIndexTemplateAction extends ActionType { public static final PutComposableIndexTemplateAction INSTANCE = new PutComposableIndexTemplateAction(); @@ -62,8 +67,10 @@ private PutComposableIndexTemplateAction() { /** * A request for putting a single index template into the cluster state + * + * @opensearch.internal */ - public static class Request extends MasterNodeRequest implements IndicesRequest { + public static class Request extends ClusterManagerNodeRequest implements IndicesRequest { private final String name; @Nullable private String cause; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateAction.java index e3c072908596f..06a9f6fbba409 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * An action for putting an index template into the cluster state + * + * @opensearch.internal + */ public class PutIndexTemplateAction extends ActionType { public static final PutIndexTemplateAction INSTANCE = new PutIndexTemplateAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateRequest.java index 608e3da699318..013e66b2292a0 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateRequest.java @@ -39,26 +39,26 @@ import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.DeprecationHandler; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.common.xcontent.support.XContentMapValues; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.DeprecationHandler; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import java.io.IOException; @@ -77,8 +77,13 @@ /** * A request to create an index template. + * + * @opensearch.internal */ -public class PutIndexTemplateRequest extends MasterNodeRequest implements IndicesRequest, ToXContentObject { +public class PutIndexTemplateRequest extends ClusterManagerNodeRequest + implements + IndicesRequest, + ToXContentObject { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(PutIndexTemplateRequest.class); @@ -220,8 +225,8 @@ public PutIndexTemplateRequest settings(Settings.Builder settings) { /** * The settings to create the index template with (either json/yaml format). */ - public PutIndexTemplateRequest settings(String source, XContentType xContentType) { - this.settings = Settings.builder().loadFromSource(source, xContentType).build(); + public PutIndexTemplateRequest settings(String source, MediaType mediaType) { + this.settings = Settings.builder().loadFromSource(source, mediaType).build(); return this; } @@ -253,10 +258,10 @@ public String cause() { * Adds mapping that will be added when the index gets created. * * @param source The mapping source - * @param xContentType The type of content contained within the source + * @param mediaType The type of content contained within the source */ - public PutIndexTemplateRequest mapping(String source, XContentType xContentType) { - return mapping(new BytesArray(source), xContentType); + public PutIndexTemplateRequest mapping(String source, MediaType mediaType) { + return mapping(new BytesArray(source), mediaType); } /** @@ -272,11 +277,11 @@ public PutIndexTemplateRequest mapping(XContentBuilder source) { * Adds mapping that will be added when the index gets created. * * @param source The mapping source - * @param xContentType the source content type + * @param mediaType the source content type */ - public PutIndexTemplateRequest mapping(BytesReference source, XContentType xContentType) { - Objects.requireNonNull(xContentType); - Map mappingAsMap = XContentHelper.convertToMap(source, false, xContentType).v2(); + public PutIndexTemplateRequest mapping(BytesReference source, MediaType mediaType) { + Objects.requireNonNull(mediaType); + Map mappingAsMap = XContentHelper.convertToMap(source, false, mediaType).v2(); return mapping(mappingAsMap); } @@ -292,7 +297,7 @@ public PutIndexTemplateRequest mapping(Map source) { try { XContentBuilder builder = XContentFactory.jsonBuilder(); builder.map(source); - mappings = Strings.toString(builder); + mappings = builder.toString(); return this; } catch (IOException e) { throw new OpenSearchGenerationException("Failed to generate [" + source + "]", e); @@ -392,22 +397,22 @@ public PutIndexTemplateRequest source(String templateSource, XContentType xConte /** * The template source definition. */ - public PutIndexTemplateRequest source(byte[] source, XContentType xContentType) { - return source(source, 0, source.length, xContentType); + public PutIndexTemplateRequest source(byte[] source, MediaType mediaType) { + return source(source, 0, source.length, mediaType); } /** * The template source definition. */ - public PutIndexTemplateRequest source(byte[] source, int offset, int length, XContentType xContentType) { - return source(new BytesArray(source, offset, length), xContentType); + public PutIndexTemplateRequest source(byte[] source, int offset, int length, MediaType mediaType) { + return source(new BytesArray(source, offset, length), mediaType); } /** * The template source definition. */ - public PutIndexTemplateRequest source(BytesReference source, XContentType xContentType) { - return source(XContentHelper.convertToMap(source, true, xContentType).v2()); + public PutIndexTemplateRequest source(BytesReference source, MediaType mediaType) { + return source(XContentHelper.convertToMap(source, true, mediaType).v2()); } public Set aliases() { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateRequestBuilder.java index e5a02acb4a6e9..04d2236e00e8f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/PutIndexTemplateRequestBuilder.java @@ -32,18 +32,24 @@ package org.opensearch.action.admin.indices.template.put; import org.opensearch.action.admin.indices.alias.Alias; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.XContentBuilder; import java.util.List; import java.util.Map; -public class PutIndexTemplateRequestBuilder extends MasterNodeOperationRequestBuilder< +/** + * A request builder for putting an index template into the cluster state + * + * @opensearch.internal + */ +public class PutIndexTemplateRequestBuilder extends ClusterManagerNodeOperationRequestBuilder< PutIndexTemplateRequest, AcknowledgedResponse, PutIndexTemplateRequestBuilder> { @@ -108,8 +114,8 @@ public PutIndexTemplateRequestBuilder setSettings(Settings.Builder settings) { /** * The settings to crete the index template with (either json or yaml format) */ - public PutIndexTemplateRequestBuilder setSettings(String source, XContentType xContentType) { - request.settings(source, xContentType); + public PutIndexTemplateRequestBuilder setSettings(String source, MediaType mediaType) { + request.settings(source, mediaType); return this; } @@ -125,10 +131,10 @@ public PutIndexTemplateRequestBuilder setSettings(Map source) { * Adds mapping that will be added when the index template gets created. * * @param source The mapping source - * @param xContentType The type/format of the source + * @param mediaType The type/format of the source */ - public PutIndexTemplateRequestBuilder setMapping(String source, XContentType xContentType) { - request.mapping(source, xContentType); + public PutIndexTemplateRequestBuilder setMapping(String source, MediaType mediaType) { + request.mapping(source, mediaType); return this; } @@ -221,16 +227,16 @@ public PutIndexTemplateRequestBuilder setSource(Map templateSour /** * The template source definition. */ - public PutIndexTemplateRequestBuilder setSource(BytesReference templateSource, XContentType xContentType) { - request.source(templateSource, xContentType); + public PutIndexTemplateRequestBuilder setSource(BytesReference templateSource, MediaType mediaType) { + request.source(templateSource, mediaType); return this; } /** * The template source definition. */ - public PutIndexTemplateRequestBuilder setSource(byte[] templateSource, XContentType xContentType) { - request.source(templateSource, xContentType); + public PutIndexTemplateRequestBuilder setSource(byte[] templateSource, MediaType mediaType) { + request.source(templateSource, mediaType); return this; } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java index 022ffbeb7e03a..6eb87bee9ffa7 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutComponentTemplateAction.java @@ -32,10 +32,9 @@ package org.opensearch.action.admin.indices.template.put; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -46,15 +45,21 @@ import org.opensearch.cluster.metadata.Template; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportPutComponentTemplateAction extends TransportMasterNodeAction< +/** + * An action for putting a single component template into the cluster state + * + * @opensearch.internal + */ +public class TransportPutComponentTemplateAction extends TransportClusterManagerNodeAction< PutComponentTemplateAction.Request, AcknowledgedResponse> { @@ -101,7 +106,7 @@ protected ClusterBlockException checkBlock(PutComponentTemplateAction.Request re } @Override - protected void masterOperation( + protected void clusterManagerOperation( final PutComponentTemplateAction.Request request, final ClusterState state, final ActionListener listener @@ -120,7 +125,7 @@ protected void masterOperation( request.cause(), request.create(), request.name(), - request.masterNodeTimeout(), + request.clusterManagerNodeTimeout(), componentTemplate, listener ); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutComposableIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutComposableIndexTemplateAction.java index 7739c66b4cbd4..8a31c36d723b4 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutComposableIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutComposableIndexTemplateAction.java @@ -32,25 +32,30 @@ package org.opensearch.action.admin.indices.template.put; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.ComposableIndexTemplate; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.metadata.MetadataIndexTemplateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportPutComposableIndexTemplateAction extends TransportMasterNodeAction< +/** + * An action for putting a composable index template into the cluster state + * + * @opensearch.internal + */ +public class TransportPutComposableIndexTemplateAction extends TransportClusterManagerNodeAction< PutComposableIndexTemplateAction.Request, AcknowledgedResponse> { @@ -94,7 +99,7 @@ protected ClusterBlockException checkBlock(PutComposableIndexTemplateAction.Requ } @Override - protected void masterOperation( + protected void clusterManagerOperation( final PutComposableIndexTemplateAction.Request request, final ClusterState state, final ActionListener listener @@ -104,7 +109,7 @@ protected void masterOperation( request.cause(), request.create(), request.name(), - request.masterNodeTimeout(), + request.clusterManagerNodeTimeout(), indexTemplate, listener ); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutIndexTemplateAction.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutIndexTemplateAction.java index 42d932c62da55..4431949c2e42b 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutIndexTemplateAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/TransportPutIndexTemplateAction.java @@ -34,10 +34,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; @@ -46,9 +45,10 @@ import org.opensearch.cluster.metadata.MetadataIndexTemplateService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -56,8 +56,10 @@ /** * Put index template action. + * + * @opensearch.internal */ -public class TransportPutIndexTemplateAction extends TransportMasterNodeAction { +public class TransportPutIndexTemplateAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportPutIndexTemplateAction.class); @@ -104,7 +106,7 @@ protected ClusterBlockException checkBlock(PutIndexTemplateRequest request, Clus } @Override - protected void masterOperation( + protected void clusterManagerOperation( final PutIndexTemplateRequest request, final ClusterState state, final ActionListener listener @@ -123,7 +125,7 @@ protected void masterOperation( .mappings(request.mappings()) .aliases(request.aliases()) .create(request.create()) - .masterTimeout(request.masterNodeTimeout()) + .clusterManagerTimeout(request.clusterManagerNodeTimeout()) .version(request.version()), new MetadataIndexTemplateService.PutListener() { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/template/put/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/template/put/package-info.java new file mode 100644 index 0000000000000..a0033a434b7b3 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/template/put/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Put Index Templates transport handlers. */ +package org.opensearch.action.admin.indices.template.put; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/IndexShardUpgradeStatus.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/IndexShardUpgradeStatus.java index 954ab28542904..32cf6d4bfe70d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/IndexShardUpgradeStatus.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/IndexShardUpgradeStatus.java @@ -32,11 +32,16 @@ package org.opensearch.action.admin.indices.upgrade.get; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import java.util.Arrays; import java.util.Iterator; +/** + * Status for an Index Shard Upgrade + * + * @opensearch.internal + */ public class IndexShardUpgradeStatus implements Iterable { private final ShardId shardId; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/IndexUpgradeStatus.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/IndexUpgradeStatus.java index 19f97faff3c61..2cff1f04d3fd2 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/IndexUpgradeStatus.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/IndexUpgradeStatus.java @@ -38,6 +38,11 @@ import java.util.List; import java.util.Map; +/** + * Status for an Index Upgrade + * + * @opensearch.internal + */ public class IndexUpgradeStatus implements Iterable { private final String index; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/ShardUpgradeStatus.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/ShardUpgradeStatus.java index 579bd5e969eef..57fb2513faf78 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/ShardUpgradeStatus.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/ShardUpgradeStatus.java @@ -34,11 +34,16 @@ import org.opensearch.action.support.broadcast.BroadcastShardResponse; import org.opensearch.cluster.routing.ShardRouting; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Status for a Shard Upgrade + * + * @opensearch.internal + */ public class ShardUpgradeStatus extends BroadcastShardResponse { private ShardRouting shardRouting; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/TransportUpgradeStatusAction.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/TransportUpgradeStatusAction.java index fcad2ccc6298d..e3afa342a933a 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/TransportUpgradeStatusAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/TransportUpgradeStatusAction.java @@ -34,7 +34,6 @@ import org.opensearch.Version; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; @@ -44,7 +43,8 @@ import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.IndexService; import org.opensearch.index.engine.Segment; import org.opensearch.index.shard.IndexShard; @@ -55,6 +55,11 @@ import java.io.IOException; import java.util.List; +/** + * Transport Action for Upgrading an Index + * + * @opensearch.internal + */ public class TransportUpgradeStatusAction extends TransportBroadcastByNodeAction< UpgradeStatusRequest, UpgradeStatusResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusAction.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusAction.java index 7612bcb4464d4..a43d7580a571f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Action for Upgrading an Index + * + * @opensearch.internal + */ public class UpgradeStatusAction extends ActionType { public static final UpgradeStatusAction INSTANCE = new UpgradeStatusAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusRequest.java index bc7f0f31a6197..987661d2129ac 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusRequest.java @@ -33,11 +33,16 @@ package org.opensearch.action.admin.indices.upgrade.get; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Transport Request for retrieving status of upgrading an Index + * + * @opensearch.internal + */ public class UpgradeStatusRequest extends BroadcastRequest { public UpgradeStatusRequest() { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusRequestBuilder.java index 6f4f601a81d8a..c698c38fe12d5 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.support.broadcast.BroadcastOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport Request Builder for retrieving status of upgrading an Index + * + * @opensearch.internal + */ public class UpgradeStatusRequestBuilder extends BroadcastOperationRequestBuilder< UpgradeStatusRequest, UpgradeStatusResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusResponse.java index a8bb5cfc8bba1..575e5ccfde027 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/UpgradeStatusResponse.java @@ -32,12 +32,12 @@ package org.opensearch.action.admin.indices.upgrade.get; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; @@ -47,6 +47,11 @@ import java.util.Map; import java.util.Set; +/** + * Transport Response for retrieving status of upgrading an Index + * + * @opensearch.internal + */ public class UpgradeStatusResponse extends BroadcastResponse { private ShardUpgradeStatus[] shards; @@ -200,6 +205,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Fields for parsing and toXContent + * + * @opensearch.internal + */ static final class Fields { static final String INDICES = "indices"; static final String SHARDS = "shards"; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/package-info.java new file mode 100644 index 0000000000000..58ad55e715d94 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/get/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Get Upgrade Indices transport handlers. */ +package org.opensearch.action.admin.indices.upgrade.get; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/package-info.java new file mode 100644 index 0000000000000..ee5633c39fc2f --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Upgrade Indices transport handlers. */ +package org.opensearch.action.admin.indices.upgrade; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/ShardUpgradeRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/ShardUpgradeRequest.java deleted file mode 100644 index fcc85e1a9cb5c..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/ShardUpgradeRequest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.upgrade.post; - -import org.opensearch.action.support.broadcast.BroadcastShardRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; - -import java.io.IOException; - -public final class ShardUpgradeRequest extends BroadcastShardRequest { - - private UpgradeRequest request; - - public ShardUpgradeRequest(StreamInput in) throws IOException { - super(in); - request = new UpgradeRequest(in); - } - - ShardUpgradeRequest(ShardId shardId, UpgradeRequest request) { - super(shardId, request); - this.request = request; - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - super.writeTo(out); - request.writeTo(out); - } - - public UpgradeRequest upgradeRequest() { - return this.request; - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/ShardUpgradeResult.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/ShardUpgradeResult.java index a4fcbaca55299..ebcb269725a6d 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/ShardUpgradeResult.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/ShardUpgradeResult.java @@ -33,14 +33,19 @@ package org.opensearch.action.admin.indices.upgrade.post; import org.opensearch.Version; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; import java.text.ParseException; +/** + * Result for Upgrading a Shard + * + * @opensearch.internal + */ class ShardUpgradeResult implements Writeable { private ShardId shardId; @@ -61,7 +66,7 @@ class ShardUpgradeResult implements Writeable { ShardUpgradeResult(StreamInput in) throws IOException { shardId = new ShardId(in); primary = in.readBoolean(); - upgradeVersion = Version.readVersion(in); + upgradeVersion = in.readVersion(); try { oldestLuceneSegment = org.apache.lucene.util.Version.parse(in.readString()); } catch (ParseException ex) { @@ -89,7 +94,7 @@ public boolean primary() { public void writeTo(StreamOutput out) throws IOException { shardId.writeTo(out); out.writeBoolean(primary); - Version.writeVersion(upgradeVersion, out); + out.writeVersion(upgradeVersion); out.writeString(oldestLuceneSegment.toString()); } } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/TransportUpgradeAction.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/TransportUpgradeAction.java index 1048de90b214e..2dcd030093a67 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/TransportUpgradeAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/TransportUpgradeAction.java @@ -33,10 +33,8 @@ package org.opensearch.action.admin.indices.upgrade.post; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.PrimaryMissingActionException; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.node.TransportBroadcastByNodeAction; import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.ClusterState; @@ -51,7 +49,9 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.collect.Tuple; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.shard.IndexShard; import org.opensearch.indices.IndicesService; import org.opensearch.tasks.Task; @@ -67,6 +67,8 @@ /** * Upgrade index/indices action. + * + * @opensearch.internal */ public class TransportUpgradeAction extends TransportBroadcastByNodeAction { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/TransportUpgradeSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/TransportUpgradeSettingsAction.java index cacd341730e12..286724d78bb63 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/TransportUpgradeSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/TransportUpgradeSettingsAction.java @@ -35,10 +35,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.block.ClusterBlockException; @@ -47,13 +46,19 @@ import org.opensearch.cluster.metadata.MetadataUpdateSettingsService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class TransportUpgradeSettingsAction extends TransportMasterNodeAction { +/** + * Transport action for upgrading index settings + * + * @opensearch.internal + */ +public class TransportUpgradeSettingsAction extends TransportClusterManagerNodeAction { private static final Logger logger = LogManager.getLogger(TransportUpgradeSettingsAction.class); @@ -97,14 +102,14 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation( + protected void clusterManagerOperation( final UpgradeSettingsRequest request, final ClusterState state, final ActionListener listener ) { UpgradeSettingsClusterStateUpdateRequest clusterStateUpdateRequest = new UpgradeSettingsClusterStateUpdateRequest().ackTimeout( request.timeout() - ).versions(request.versions()).masterNodeTimeout(request.masterNodeTimeout()); + ).versions(request.versions()).masterNodeTimeout(request.clusterManagerNodeTimeout()); updateSettingsService.upgradeIndexSettings(clusterStateUpdateRequest, new ActionListener() { @Override diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeAction.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeAction.java index ee9da454ad141..369503bb54ecf 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeAction.java @@ -36,6 +36,8 @@ /** * Upgrade index/indices action. + * + * @opensearch.internal */ public class UpgradeAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeRequest.java index 94d07075a8f3e..98c307c37ea54 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeRequest.java @@ -33,8 +33,8 @@ package org.opensearch.action.admin.indices.upgrade.post; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; @@ -44,9 +44,16 @@ * @see org.opensearch.client.Requests#upgradeRequest(String...) * @see org.opensearch.client.IndicesAdminClient#upgrade(UpgradeRequest) * @see UpgradeResponse + * + * @opensearch.internal */ public class UpgradeRequest extends BroadcastRequest { + /** + * Default config for Upgrade Requests + * + * @opensearch.internal + */ public static final class Defaults { public static final boolean UPGRADE_ONLY_ANCIENT_SEGMENTS = false; } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeRequestBuilder.java index 5deff45aadc9b..8203f9d51b8e4 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeRequestBuilder.java @@ -38,6 +38,8 @@ /** * A request to upgrade one or more indices. In order to optimize on all the indices, pass an empty array or * {@code null} for the indices. + * + * @opensearch.internal */ public class UpgradeRequestBuilder extends BroadcastOperationRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeResponse.java index 07f9debbbf97b..d08cc65832f8f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeResponse.java @@ -33,12 +33,12 @@ package org.opensearch.action.admin.indices.upgrade.post; import org.opensearch.Version; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.List; @@ -47,7 +47,7 @@ /** * A response for the upgrade action. * - * + * @opensearch.internal */ public class UpgradeResponse extends BroadcastResponse { @@ -55,7 +55,7 @@ public class UpgradeResponse extends BroadcastResponse { UpgradeResponse(StreamInput in) throws IOException { super(in); - versions = in.readMap(StreamInput::readString, i -> Tuple.tuple(Version.readVersion(i), i.readString())); + versions = in.readMap(StreamInput::readString, i -> Tuple.tuple(i.readVersion(), i.readString())); } UpgradeResponse( @@ -73,7 +73,7 @@ public class UpgradeResponse extends BroadcastResponse { public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeMap(versions, StreamOutput::writeString, (o, v) -> { - Version.writeVersion(v.v1(), o); + o.writeVersion(v.v1()); o.writeString(v.v2()); }); } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsAction.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsAction.java index f9ecc9ace9171..05944e781d109 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action for upgrading index settings + * + * @opensearch.internal + */ public class UpgradeSettingsAction extends ActionType { public static final UpgradeSettingsAction INSTANCE = new UpgradeSettingsAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsClusterStateUpdateRequest.java index cf24e20a44ee3..ee6c6161713ac 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsClusterStateUpdateRequest.java @@ -40,6 +40,8 @@ /** * Cluster state update request that allows to change minimum compatibility settings for some indices + * + * @opensearch.internal */ public class UpgradeSettingsClusterStateUpdateRequest extends ClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsRequest.java index dc3a60c62033b..306d29dd84f13 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsRequest.java @@ -36,8 +36,8 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Map; @@ -46,6 +46,8 @@ /** * Request for an update index settings action + * + * @opensearch.internal */ public class UpgradeSettingsRequest extends AcknowledgedRequest { @@ -53,7 +55,7 @@ public class UpgradeSettingsRequest extends AcknowledgedRequest new Tuple<>(Version.readVersion(i), i.readString())); + versions = in.readMap(StreamInput::readString, i -> new Tuple<>(i.readVersion(), i.readString())); } public UpgradeSettingsRequest() {} @@ -92,7 +94,7 @@ public UpgradeSettingsRequest versions(Map> versi public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeMap(versions, StreamOutput::writeString, (o, v) -> { - Version.writeVersion(v.v1(), out); + out.writeVersion(v.v1()); out.writeString(v.v2()); }); } diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsRequestBuilder.java deleted file mode 100644 index d3a8cc311bb8a..0000000000000 --- a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/UpgradeSettingsRequestBuilder.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -package org.opensearch.action.admin.indices.upgrade.post; - -import org.opensearch.Version; -import org.opensearch.action.support.master.AcknowledgedRequestBuilder; -import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.collect.Tuple; - -import java.util.Map; - -/** - * Builder for an update index settings request - */ -public class UpgradeSettingsRequestBuilder extends AcknowledgedRequestBuilder< - UpgradeSettingsRequest, - AcknowledgedResponse, - UpgradeSettingsRequestBuilder> { - - public UpgradeSettingsRequestBuilder(OpenSearchClient client, UpgradeSettingsAction action) { - super(client, action, new UpgradeSettingsRequest()); - } - - /** - * Sets the index versions to be updated - */ - public UpgradeSettingsRequestBuilder setVersions(Map> versions) { - request.versions(versions); - return this; - } -} diff --git a/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/package-info.java new file mode 100644 index 0000000000000..ae8c8839acaba --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/upgrade/post/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Post Upgrade Indices transport handlers. */ +package org.opensearch.action.admin.indices.upgrade.post; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/package-info.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/package-info.java new file mode 100644 index 0000000000000..ee210f7495e70 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Query Validation transport handlers. */ +package org.opensearch.action.admin.indices.validate; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/QueryExplanation.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/QueryExplanation.java index 3764cfd1e608b..248cab5e40eaf 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/QueryExplanation.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/QueryExplanation.java @@ -32,21 +32,26 @@ package org.opensearch.action.admin.indices.validate.query; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +/** + * Query Explanation + * + * @opensearch.internal + */ public class QueryExplanation implements Writeable, ToXContentFragment { public static final String INDEX_FIELD = "index"; diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ShardValidateQueryRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ShardValidateQueryRequest.java index 4d6525d002381..fb6e78d3ca4fc 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ShardValidateQueryRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ShardValidateQueryRequest.java @@ -34,10 +34,10 @@ import org.opensearch.Version; import org.opensearch.action.support.broadcast.BroadcastShardRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.query.QueryBuilder; -import org.opensearch.index.shard.ShardId; import org.opensearch.search.internal.AliasFilter; import java.io.IOException; @@ -45,6 +45,8 @@ /** * Internal validate request executed directly against a specific index shard. + * + * @opensearch.internal */ public class ShardValidateQueryRequest extends BroadcastShardRequest { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ShardValidateQueryResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ShardValidateQueryResponse.java index 88080bd7667f3..f72352cd8cd95 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ShardValidateQueryResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ShardValidateQueryResponse.java @@ -33,16 +33,16 @@ package org.opensearch.action.admin.indices.validate.query; import org.opensearch.action.support.broadcast.BroadcastShardResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; /** * Internal validate response of a shard validate request executed directly against a specific shard. * - * + * @opensearch.internal */ class ShardValidateQueryResponse extends BroadcastShardResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/TransportValidateQueryAction.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/TransportValidateQueryAction.java index 1849b41ce707f..2b46fbcb9d105 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/TransportValidateQueryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/TransportValidateQueryAction.java @@ -34,9 +34,7 @@ import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastShardOperationFailedException; import org.opensearch.action.support.broadcast.TransportBroadcastAction; import org.opensearch.cluster.ClusterState; @@ -46,11 +44,13 @@ import org.opensearch.cluster.routing.GroupShardsIterator; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.ParsingException; import org.opensearch.common.Randomness; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.lease.Releasables; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.query.ParsedQuery; import org.opensearch.index.query.QueryShardException; @@ -72,6 +72,11 @@ import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.function.LongSupplier; +/** + * Transport Action to Validate a Query + * + * @opensearch.internal + */ public class TransportValidateQueryAction extends TransportBroadcastAction< ValidateQueryRequest, ValidateQueryResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryAction.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryAction.java index a9b0954287c30..cdeb9e818a52f 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryAction.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Action to Validate a Query + * + * @opensearch.internal + */ public class ValidateQueryAction extends ActionType { public static final ValidateQueryAction INSTANCE = new ValidateQueryAction(); diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryRequest.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryRequest.java index 1bb85c4e84483..ef0d14502af23 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryRequest.java @@ -37,11 +37,11 @@ import org.opensearch.action.ValidateActions; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.broadcast.BroadcastRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.MatchAllQueryBuilder; import org.opensearch.index.query.QueryBuilder; @@ -52,6 +52,8 @@ * A request to validate a specific query. *

    * The request requires the query to be set using {@link #query(QueryBuilder)} + * + * @opensearch.internal */ public class ValidateQueryRequest extends BroadcastRequest implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryRequestBuilder.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryRequestBuilder.java index 88261e6536240..6209f41d88be2 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryRequestBuilder.java @@ -36,6 +36,11 @@ import org.opensearch.client.OpenSearchClient; import org.opensearch.index.query.QueryBuilder; +/** + * Transport Request Builder to Validate a Query + * + * @opensearch.internal + */ public class ValidateQueryRequestBuilder extends BroadcastOperationRequestBuilder< ValidateQueryRequest, ValidateQueryResponse, diff --git a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryResponse.java b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryResponse.java index 53d1463c23351..44d6d637cdc51 100644 --- a/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryResponse.java +++ b/server/src/main/java/org/opensearch/action/admin/indices/validate/query/ValidateQueryResponse.java @@ -32,27 +32,27 @@ package org.opensearch.action.admin.indices.validate.query; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.broadcast.BroadcastResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * The response of the validate action. * - * + * @opensearch.internal */ public class ValidateQueryResponse extends BroadcastResponse { diff --git a/server/src/main/java/org/opensearch/action/admin/package-info.java b/server/src/main/java/org/opensearch/action/admin/package-info.java index 1ee6ebacef3a5..4be3b7cf12e9c 100644 --- a/server/src/main/java/org/opensearch/action/admin/package-info.java +++ b/server/src/main/java/org/opensearch/action/admin/package-info.java @@ -25,12 +25,12 @@ * under the License. */ -/** - * Administrative Actions. - */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ +/** + * Administrative Actions. + */ package org.opensearch.action.admin; diff --git a/server/src/main/java/org/opensearch/action/bulk/BackoffPolicy.java b/server/src/main/java/org/opensearch/action/bulk/BackoffPolicy.java index 50acd82ae9002..0d6d122e31261 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BackoffPolicy.java +++ b/server/src/main/java/org/opensearch/action/bulk/BackoffPolicy.java @@ -31,6 +31,7 @@ package org.opensearch.action.bulk; +import org.opensearch.common.Randomness; import org.opensearch.common.unit.TimeValue; import java.util.Iterator; @@ -53,6 +54,8 @@ * * * Note that backoff policies are exposed as Iterables in order to be consumed multiple times. + * + * @opensearch.internal */ public abstract class BackoffPolicy implements Iterable { private static final BackoffPolicy NO_BACKOFF = new NoBackoff(); @@ -103,6 +106,30 @@ public static BackoffPolicy exponentialBackoff(TimeValue initialDelay, int maxNu return new ExponentialBackoff((int) checkDelay(initialDelay).millis(), maxNumberOfRetries); } + /** + * It provides exponential backoff between retries until it reaches maxDelayForRetry. + * It uses equal jitter scheme as it is being used for throttled exceptions. + * It will make random distribution and also guarantees a minimum delay. + * + * @param baseDelay BaseDelay for exponential Backoff + * @param maxDelayForRetry MaxDelay that can be returned from backoff policy + * @return A backoff policy with exponential backoff with equal jitter which can't return delay more than given max delay + */ + public static BackoffPolicy exponentialEqualJitterBackoff(long baseDelay, long maxDelayForRetry) { + return new ExponentialEqualJitterBackoff(baseDelay, maxDelayForRetry); + } + + /** + * It provides exponential backoff between retries until it reaches Integer.MAX_VALUE. + * It uses full jitter scheme for random distribution. + * + * @param baseDelay BaseDelay for exponential Backoff + * @return A backoff policy with exponential backoff with full jitter. + */ + public static BackoffPolicy exponentialFullJitterBackoff(long baseDelay) { + return new ExponentialFullJitterBackoff(baseDelay); + } + /** * Wraps the backoff policy in one that calls a method every time a new backoff is taken from the policy. */ @@ -117,6 +144,11 @@ private static TimeValue checkDelay(TimeValue delay) { return delay; } + /** + * Concrete No Back Off Policy + * + * @opensearch.internal + */ private static class NoBackoff extends BackoffPolicy { @Override public Iterator iterator() { @@ -134,6 +166,11 @@ public TimeValue next() { } } + /** + * Concrete Exponential Back Off Policy + * + * @opensearch.internal + */ private static class ExponentialBackoff extends BackoffPolicy { private final int start; @@ -152,6 +189,11 @@ public Iterator iterator() { } } + /** + * Concrete Exponential Back Off Iterator + * + * @opensearch.internal + */ private static class ExponentialBackoffIterator implements Iterator { private final int numberOfElements; @@ -180,6 +222,112 @@ public TimeValue next() { } } + private static class ExponentialEqualJitterBackoff extends BackoffPolicy { + private final long maxDelayForRetry; + private final long baseDelay; + + private ExponentialEqualJitterBackoff(long baseDelay, long maxDelayForRetry) { + this.maxDelayForRetry = maxDelayForRetry; + this.baseDelay = baseDelay; + } + + @Override + public Iterator iterator() { + return new ExponentialEqualJitterBackoffIterator(baseDelay, maxDelayForRetry); + } + } + + private static class ExponentialEqualJitterBackoffIterator implements Iterator { + /** + * Retry limit to avoids integer overflow issues. + * Post this limit, max delay will be returned with Equal Jitter. + * + * NOTE: If the value is greater than 30, there can be integer overflow + * issues during delay calculation. + **/ + private final int RETRIES_TILL_JITTER_INCREASE = 30; + + /** + * Exponential increase in delay will happen till it reaches maxDelayForRetry. + * Once delay has exceeded maxDelayForRetry, it will return maxDelayForRetry only + * and not increase the delay. + */ + private final long maxDelayForRetry; + private final long baseDelay; + private int retriesAttempted; + + private ExponentialEqualJitterBackoffIterator(long baseDelay, long maxDelayForRetry) { + this.baseDelay = baseDelay; + this.maxDelayForRetry = maxDelayForRetry; + } + + /** + * There is not any limit for this BackOff. + * This Iterator will always return back off delay. + * + * @return true + */ + @Override + public boolean hasNext() { + return true; + } + + @Override + public TimeValue next() { + int retries = Math.min(retriesAttempted, RETRIES_TILL_JITTER_INCREASE); + int exponentialDelay = (int) Math.min((1L << retries) * baseDelay, maxDelayForRetry); + retriesAttempted++; + return TimeValue.timeValueMillis((exponentialDelay / 2) + Randomness.get().nextInt(exponentialDelay / 2 + 1)); + } + } + + private static class ExponentialFullJitterBackoff extends BackoffPolicy { + private final long baseDelay; + + private ExponentialFullJitterBackoff(long baseDelay) { + this.baseDelay = baseDelay; + } + + @Override + public Iterator iterator() { + return new ExponentialFullJitterBackoffIterator(baseDelay); + } + } + + private static class ExponentialFullJitterBackoffIterator implements Iterator { + /** + * Current delay in exponential backoff + */ + private long currentDelay; + + private ExponentialFullJitterBackoffIterator(long baseDelay) { + this.currentDelay = baseDelay; + } + + /** + * There is not any limit for this BackOff. + * This Iterator will always return back off delay. + * + * @return true + */ + @Override + public boolean hasNext() { + return true; + } + + @Override + public TimeValue next() { + TimeValue delayToReturn = TimeValue.timeValueMillis(Randomness.get().nextInt(Math.toIntExact(currentDelay)) + 1); + currentDelay = Math.min(2 * currentDelay, Integer.MAX_VALUE); + return delayToReturn; + } + } + + /** + * Concrete Constant Back Off Policy + * + * @opensearch.internal + */ private static final class ConstantBackoff extends BackoffPolicy { private final TimeValue delay; @@ -197,6 +345,11 @@ public Iterator iterator() { } } + /** + * Concrete Constant Back Off Iterator + * + * @opensearch.internal + */ private static final class ConstantBackoffIterator implements Iterator { private final TimeValue delay; private final int numberOfElements; @@ -222,6 +375,11 @@ public TimeValue next() { } } + /** + * Concrete Wrapped Back Off Policy + * + * @opensearch.internal + */ private static final class WrappedBackoffPolicy extends BackoffPolicy { private final BackoffPolicy delegate; private final Runnable onBackoff; @@ -237,6 +395,11 @@ public Iterator iterator() { } } + /** + * Concrete Wrapped Back Off Iterator + * + * @opensearch.internal + */ private static final class WrappedBackoffIterator implements Iterator { private final Iterator delegate; private final Runnable onBackoff; diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkAction.java b/server/src/main/java/org/opensearch/action/bulk/BulkAction.java index 031c8efa8cb84..f80ec32628b8a 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkAction.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkAction.java @@ -36,6 +36,11 @@ import org.opensearch.common.settings.Settings; import org.opensearch.transport.TransportRequestOptions; +/** + * Transport action for bulk indexing + * + * @opensearch.internal + */ public class BulkAction extends ActionType { public static final BulkAction INSTANCE = new BulkAction(); diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkItemRequest.java b/server/src/main/java/org/opensearch/action/bulk/BulkItemRequest.java index 2002d5864e966..79201e0f7b38f 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkItemRequest.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkItemRequest.java @@ -36,15 +36,21 @@ import org.apache.lucene.util.RamUsageEstimator; import org.opensearch.action.DocWriteRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; import java.io.IOException; import java.util.Objects; +/** + * Transport request for a Single bulk item + * + * @opensearch.internal + */ public class BulkItemRequest implements Writeable, Accountable { private static final long SHALLOW_SIZE = RamUsageEstimator.shallowSizeOfInstance(BulkItemRequest.class); @@ -109,7 +115,7 @@ public void abort(String index, Exception cause) { setPrimaryResponse(new BulkItemResponse(id, request.opType(), failure)); } else { assert primaryResponse.isFailed() && primaryResponse.getFailure().isAborted() : "response [" - + Strings.toString(primaryResponse) + + Strings.toString(MediaTypeRegistry.JSON, primaryResponse) + "]; cause [" + cause + "]"; diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkItemResponse.java b/server/src/main/java/org/opensearch/action/bulk/BulkItemResponse.java index fdb27a00bac2d..72e22f6b72019 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkItemResponse.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkItemResponse.java @@ -42,31 +42,34 @@ import org.opensearch.action.index.IndexResponse; import org.opensearch.action.update.UpdateResponse; import org.opensearch.common.CheckedConsumer; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.seqno.SequenceNumbers; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; import java.io.IOException; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; -import static org.opensearch.common.xcontent.XContentParserUtils.throwUnknownField; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.throwUnknownField; /** * Represents a single item response for an action executed as part of the bulk API. Holds the index/type/id * of the relevant action, and if it has failed or not (with the failure message in case it failed). + * + * @opensearch.internal */ public class BulkItemResponse implements Writeable, StatusToXContentObject { @@ -176,6 +179,8 @@ public static BulkItemResponse fromXContent(XContentParser parser, int id) throw /** * Represents a failure. + * + * @opensearch.internal */ public static class Failure implements Writeable, ToXContentFragment { public static final String INDEX_FIELD = "index"; @@ -370,7 +375,7 @@ public static Failure fromXContent(XContentParser parser) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkPrimaryExecutionContext.java b/server/src/main/java/org/opensearch/action/bulk/BulkPrimaryExecutionContext.java index da8833fe49a29..896456089ee3e 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkPrimaryExecutionContext.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkPrimaryExecutionContext.java @@ -48,6 +48,8 @@ * This is a utility class that holds the per request state needed to perform bulk operations on the primary. * More specifically, it maintains an index to the current executing bulk item, which allows execution * to stop and wait for external events such as mapping updates. + * + * @opensearch.internal */ class BulkPrimaryExecutionContext { @@ -61,7 +63,7 @@ enum ItemProcessingState { TRANSLATED, /** * the request can not execute with the current mapping and should wait for a new mapping - * to arrive from the master. A mapping request for the needed changes has already been + * to arrive from the cluster-manager. A mapping request for the needed changes has already been * submitted */ WAIT_FOR_MAPPING_UPDATE, @@ -144,7 +146,7 @@ public boolean isOperationExecuted() { return currentItemState == ItemProcessingState.EXECUTED; } - /** returns true if the request needs to wait for a mapping update to arrive from the master */ + /** returns true if the request needs to wait for a mapping update to arrive from the cluster-manager */ public boolean requiresWaitingForMappingUpdate() { return currentItemState == ItemProcessingState.WAIT_FOR_MAPPING_UPDATE; } @@ -216,7 +218,7 @@ public > T getRequestToExecute() { return (T) requestToExecute; } - /** indicates that the current operation can not be completed and needs to wait for a new mapping from the master */ + /** indicates that the current operation can not be completed and needs to wait for a new mapping from the cluster-manager */ public void markAsRequiringMappingUpdate() { assert assertInvariants(ItemProcessingState.TRANSLATED); currentItemState = ItemProcessingState.WAIT_FOR_MAPPING_UPDATE; diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkProcessor.java b/server/src/main/java/org/opensearch/action/bulk/BulkProcessor.java index 90a177119cfd8..baf64b3e80af6 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkProcessor.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkProcessor.java @@ -32,19 +32,20 @@ package org.opensearch.action.bulk; -import org.opensearch.action.ActionListener; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.delete.DeleteRequest; import org.opensearch.action.index.IndexRequest; import org.opensearch.client.Client; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; -import org.opensearch.common.unit.ByteSizeUnit; -import org.opensearch.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.unit.ByteSizeUnit; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.MediaType; import org.opensearch.threadpool.Scheduler; import org.opensearch.threadpool.ThreadPool; @@ -63,6 +64,8 @@ * requests allowed to be executed in parallel. *

    * In order to create a new bulk processor, use the {@link Builder}. + * + * @opensearch.internal */ public class BulkProcessor implements Closeable { @@ -92,6 +95,8 @@ public interface Listener { /** * A builder used to create a build an instance of a bulk processor. + * + * @opensearch.internal */ public static class Builder { @@ -453,17 +458,13 @@ public BulkProcessor add(BytesReference data, @Nullable String defaultIndex, XCo /** * Adds the data from the bytes to be processed by the bulk processor */ - public BulkProcessor add( - BytesReference data, - @Nullable String defaultIndex, - @Nullable String defaultPipeline, - XContentType xContentType - ) throws Exception { + public BulkProcessor add(BytesReference data, @Nullable String defaultIndex, @Nullable String defaultPipeline, MediaType mediaType) + throws Exception { Tuple bulkRequestToExecute = null; lock.lock(); try { ensureOpen(); - bulkRequest.add(data, defaultIndex, null, null, defaultPipeline, null, true, xContentType); + bulkRequest.add(data, defaultIndex, null, null, defaultPipeline, null, true, mediaType); bulkRequestToExecute = newBulkRequestIfNeeded(); } finally { lock.unlock(); @@ -543,6 +544,11 @@ public void flush() { } } + /** + * Flush for bulk processor + * + * @opensearch.internal + */ class Flush implements Runnable { @Override public void run() { diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkRequest.java b/server/src/main/java/org/opensearch/action/bulk/BulkRequest.java index e3bf5bced5072..65043da6c2684 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkRequest.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkRequest.java @@ -45,13 +45,13 @@ import org.opensearch.action.support.replication.ReplicationRequest; import org.opensearch.action.update.UpdateRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; import org.opensearch.search.fetch.subphase.FetchSourceContext; import java.io.IOException; @@ -70,6 +70,8 @@ * * Note that we only support refresh on the bulk request not per item. * @see org.opensearch.client.Client#bulk(BulkRequest) + * + * @opensearch.internal */ public class BulkRequest extends ActionRequest implements CompositeIndicesRequest, WriteRequest, Accountable { @@ -234,30 +236,30 @@ public long estimatedSizeInBytes() { /** * Adds a framed data in binary format */ - public BulkRequest add(byte[] data, int from, int length, XContentType xContentType) throws IOException { - return add(data, from, length, null, xContentType); + public BulkRequest add(byte[] data, int from, int length, MediaType mediaType) throws IOException { + return add(data, from, length, null, mediaType); } /** * Adds a framed data in binary format */ - public BulkRequest add(byte[] data, int from, int length, @Nullable String defaultIndex, XContentType xContentType) throws IOException { - return add(new BytesArray(data, from, length), defaultIndex, xContentType); + public BulkRequest add(byte[] data, int from, int length, @Nullable String defaultIndex, MediaType mediaType) throws IOException { + return add(new BytesArray(data, from, length), defaultIndex, mediaType); } /** * Adds a framed data in binary format */ - public BulkRequest add(BytesReference data, @Nullable String defaultIndex, XContentType xContentType) throws IOException { - return add(data, defaultIndex, null, null, null, null, true, xContentType); + public BulkRequest add(BytesReference data, @Nullable String defaultIndex, MediaType mediaType) throws IOException { + return add(data, defaultIndex, null, null, null, null, true, mediaType); } /** * Adds a framed data in binary format */ - public BulkRequest add(BytesReference data, @Nullable String defaultIndex, boolean allowExplicitIndex, XContentType xContentType) + public BulkRequest add(BytesReference data, @Nullable String defaultIndex, boolean allowExplicitIndex, MediaType mediaType) throws IOException { - return add(data, defaultIndex, null, null, null, null, allowExplicitIndex, xContentType); + return add(data, defaultIndex, null, null, null, null, allowExplicitIndex, mediaType); } public BulkRequest add( @@ -267,9 +269,9 @@ public BulkRequest add( @Nullable FetchSourceContext defaultFetchSourceContext, @Nullable String defaultPipeline, boolean allowExplicitIndex, - XContentType xContentType + MediaType mediaType ) throws IOException { - return add(data, defaultIndex, defaultRouting, defaultFetchSourceContext, defaultPipeline, null, allowExplicitIndex, xContentType); + return add(data, defaultIndex, defaultRouting, defaultFetchSourceContext, defaultPipeline, null, allowExplicitIndex, mediaType); } public BulkRequest add( @@ -280,12 +282,12 @@ public BulkRequest add( @Nullable String defaultPipeline, @Nullable Boolean defaultRequireAlias, boolean allowExplicitIndex, - XContentType xContentType + MediaType mediaType ) throws IOException { String routing = valueOrDefault(defaultRouting, globalRouting); String pipeline = valueOrDefault(defaultPipeline, globalPipeline); Boolean requireAlias = valueOrDefault(defaultRequireAlias, globalRequireAlias); - new BulkRequestParser(true).parse( + new BulkRequestParser().parse( data, defaultIndex, routing, @@ -293,8 +295,8 @@ public BulkRequest add( pipeline, requireAlias, allowExplicitIndex, - xContentType, - (indexRequest, type) -> internalAdd(indexRequest), + mediaType, + this::internalAdd, this::internalAdd, this::add ); @@ -461,7 +463,7 @@ private void applyGlobalMandatoryParameters(DocWriteRequest request) { } private static String valueOrDefault(String value, String globalDefault) { - if (Strings.isNullOrEmpty(value) && !Strings.isNullOrEmpty(globalDefault)) { + if (Strings.isNullOrEmpty(value) && Strings.isNullOrEmpty(globalDefault) == false) { return globalDefault; } return value; diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkRequestBuilder.java b/server/src/main/java/org/opensearch/action/bulk/BulkRequestBuilder.java index c58877e48a7eb..08eee82a53cf9 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkRequestBuilder.java @@ -46,10 +46,13 @@ import org.opensearch.common.Nullable; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaType; /** * A bulk request holds an ordered {@link IndexRequest}s and {@link DeleteRequest}s and allows to executes * it in a single batch. + * + * @opensearch.internal */ public class BulkRequestBuilder extends ActionRequestBuilder implements WriteRequestBuilder { @@ -122,9 +125,8 @@ public BulkRequestBuilder add(byte[] data, int from, int length, XContentType xC /** * Adds a framed data in binary format */ - public BulkRequestBuilder add(byte[] data, int from, int length, @Nullable String defaultIndex, XContentType xContentType) - throws Exception { - request.add(data, from, length, defaultIndex, xContentType); + public BulkRequestBuilder add(byte[] data, int from, int length, @Nullable String defaultIndex, MediaType mediaType) throws Exception { + request.add(data, from, length, defaultIndex, mediaType); return this; } diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkRequestHandler.java b/server/src/main/java/org/opensearch/action/bulk/BulkRequestHandler.java index 5b4ec5cf8679a..ff40a12cb9087 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkRequestHandler.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkRequestHandler.java @@ -34,7 +34,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; import org.opensearch.threadpool.Scheduler; import java.util.concurrent.CountDownLatch; @@ -44,6 +44,8 @@ /** * Implements the low-level details of bulk request handling + * + * @opensearch.internal */ public final class BulkRequestHandler { private final Logger logger; diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkRequestParser.java b/server/src/main/java/org/opensearch/action/bulk/BulkRequestParser.java index 042e104f70c7f..3fadfe5f2cd6a 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkRequestParser.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkRequestParser.java @@ -37,15 +37,17 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.action.update.UpdateRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.VersionType; import org.opensearch.index.seqno.SequenceNumbers; import org.opensearch.search.fetch.subphase.FetchSourceContext; @@ -53,7 +55,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; -import java.util.function.BiConsumer; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; @@ -61,11 +63,12 @@ /** * Helper to parse bulk requests. This should be considered an internal class. + * + * @opensearch.internal */ public final class BulkRequestParser { private static final ParseField INDEX = new ParseField("_index"); - private static final ParseField TYPE = new ParseField("_type"); private static final ParseField ID = new ParseField("_id"); private static final ParseField ROUTING = new ParseField("routing"); private static final ParseField OP_TYPE = new ParseField("op_type"); @@ -78,16 +81,7 @@ public final class BulkRequestParser { private static final ParseField IF_PRIMARY_TERM = new ParseField("if_primary_term"); private static final ParseField REQUIRE_ALIAS = new ParseField(DocWriteRequest.REQUIRE_ALIAS); - // TODO: Remove this parameter once the BulkMonitoring endpoint has been removed - private final boolean errorOnType; - - /** - * Create a new parser. - * @param errorOnType whether to allow _type information in the index line; used by BulkMonitoring - */ - public BulkRequestParser(boolean errorOnType) { - this.errorOnType = errorOnType; - } + private static final Set VALID_ACTIONS = Set.of("create", "delete", "index", "update"); private static int findNextMarker(byte marker, int from, BytesReference data) { final int res = data.indexOf(marker, from); @@ -109,10 +103,10 @@ private static BytesReference sliceTrimmingCarriageReturn( BytesReference bytesReference, int from, int nextMarker, - XContentType xContentType + MediaType mediaType ) { final int length; - if (XContentType.JSON == xContentType && bytesReference.get(nextMarker - 1) == (byte) '\r') { + if (MediaTypeRegistry.JSON == mediaType && bytesReference.get(nextMarker - 1) == (byte) '\r') { length = nextMarker - from - 1; } else { length = nextMarker - from; @@ -133,12 +127,12 @@ public void parse( @Nullable String defaultPipeline, @Nullable Boolean defaultRequireAlias, boolean allowExplicitIndex, - XContentType xContentType, - BiConsumer indexRequestConsumer, + MediaType mediaType, + Consumer indexRequestConsumer, Consumer updateRequestConsumer, Consumer deleteRequestConsumer ) throws IOException { - XContent xContent = xContentType.xContent(); + XContent xContent = mediaType.xContent(); int line = 0; int from = 0; byte marker = xContent.streamSeparator(); @@ -188,9 +182,17 @@ public void parse( ); } String action = parser.currentName(); + if (action == null || VALID_ACTIONS.contains(action) == false) { + throw new IllegalArgumentException( + "Malformed action/metadata line [" + + line + + "], expected one of [create, delete, index, update] but found [" + + action + + "]" + ); + } String index = defaultIndex; - String type = null; String id = null; String routing = defaultRouting; FetchSourceContext fetchSourceContext = defaultFetchSourceContext; @@ -203,7 +205,7 @@ public void parse( String pipeline = defaultPipeline; boolean requireAlias = defaultRequireAlias != null && defaultRequireAlias; - // at this stage, next token can either be END_OBJECT (and use default index and type, with auto generated id) + // at this stage, next token can either be END_OBJECT (and use default index with auto generated id) // or START_OBJECT which will have another set of parameters token = parser.nextToken(); @@ -218,13 +220,6 @@ public void parse( throw new IllegalArgumentException("explicit index in bulk is not allowed"); } index = stringDeduplicator.computeIfAbsent(parser.text(), Function.identity()); - } else if (TYPE.match(currentFieldName, parser.getDeprecationHandler())) { - if (errorOnType) { - throw new IllegalArgumentException( - "Action/metadata line [" + line + "] contains an unknown parameter [" + currentFieldName + "]" - ); - } - type = stringDeduplicator.computeIfAbsent(parser.text(), Function.identity()); } else if (ID.match(currentFieldName, parser.getDeprecationHandler())) { id = parser.text(); } else if (ROUTING.match(currentFieldName, parser.getDeprecationHandler())) { @@ -319,9 +314,8 @@ public void parse( .setPipeline(pipeline) .setIfSeqNo(ifSeqNo) .setIfPrimaryTerm(ifPrimaryTerm) - .source(sliceTrimmingCarriageReturn(data, from, nextMarker, xContentType), xContentType) - .setRequireAlias(requireAlias), - type + .source(sliceTrimmingCarriageReturn(data, from, nextMarker, mediaType), mediaType) + .setRequireAlias(requireAlias) ); } else { indexRequestConsumer.accept( @@ -333,9 +327,8 @@ public void parse( .setPipeline(pipeline) .setIfSeqNo(ifSeqNo) .setIfPrimaryTerm(ifPrimaryTerm) - .source(sliceTrimmingCarriageReturn(data, from, nextMarker, xContentType), xContentType) - .setRequireAlias(requireAlias), - type + .source(sliceTrimmingCarriageReturn(data, from, nextMarker, mediaType), mediaType) + .setRequireAlias(requireAlias) ); } } else if ("create".equals(action)) { @@ -348,9 +341,8 @@ public void parse( .setPipeline(pipeline) .setIfSeqNo(ifSeqNo) .setIfPrimaryTerm(ifPrimaryTerm) - .source(sliceTrimmingCarriageReturn(data, from, nextMarker, xContentType), xContentType) - .setRequireAlias(requireAlias), - type + .source(sliceTrimmingCarriageReturn(data, from, nextMarker, mediaType), mediaType) + .setRequireAlias(requireAlias) ); } else if ("update".equals(action)) { if (version != Versions.MATCH_ANY || versionType != VersionType.INTERNAL) { @@ -368,7 +360,7 @@ public void parse( .routing(routing); try ( XContentParser sliceParser = createParser( - sliceTrimmingCarriageReturn(data, from, nextMarker, xContentType), + sliceTrimmingCarriageReturn(data, from, nextMarker, mediaType), xContent ) ) { diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkResponse.java b/server/src/main/java/org/opensearch/action/bulk/BulkResponse.java index 751ad567c8639..42d6a9f7f31b7 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkResponse.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkResponse.java @@ -32,14 +32,14 @@ package org.opensearch.action.bulk; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; @@ -47,14 +47,16 @@ import java.util.Iterator; import java.util.List; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; -import static org.opensearch.common.xcontent.XContentParserUtils.throwUnknownField; -import static org.opensearch.common.xcontent.XContentParserUtils.throwUnknownToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.throwUnknownField; +import static org.opensearch.core.xcontent.XContentParserUtils.throwUnknownToken; /** * A response of a bulk execution. Holding a response for each item responding (in order) of the * bulk requests. Each item holds the index/type/id is operated on, and if it failed or not (with the * failure message). + * + * @opensearch.internal */ public class BulkResponse extends ActionResponse implements Iterable, StatusToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkShardRequest.java b/server/src/main/java/org/opensearch/action/bulk/BulkShardRequest.java index b082395263875..0441fde4fe20c 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkShardRequest.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkShardRequest.java @@ -38,15 +38,20 @@ import org.opensearch.Version; import org.opensearch.action.support.replication.ReplicatedWriteRequest; import org.opensearch.action.support.replication.ReplicationRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.stream.Stream; +/** + * A bulk shard request targeting a specific shard ID + * + * @opensearch.internal + */ public class BulkShardRequest extends ReplicatedWriteRequest implements Accountable { public static final Version COMPACT_SHARD_ID_VERSION = LegacyESVersion.V_7_9_0; diff --git a/server/src/main/java/org/opensearch/action/bulk/BulkShardResponse.java b/server/src/main/java/org/opensearch/action/bulk/BulkShardResponse.java index 18d954a4feb23..d8414a57fcafd 100644 --- a/server/src/main/java/org/opensearch/action/bulk/BulkShardResponse.java +++ b/server/src/main/java/org/opensearch/action/bulk/BulkShardResponse.java @@ -37,12 +37,17 @@ import org.opensearch.action.DocWriteResponse; import org.opensearch.action.support.WriteResponse; import org.opensearch.action.support.replication.ReplicationResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; +/** + * Transport response for a bulk shard request + * + * @opensearch.internal + */ public class BulkShardResponse extends ReplicationResponse implements WriteResponse { private static final Version COMPACT_SHARD_ID_VERSION = LegacyESVersion.V_7_9_0; diff --git a/server/src/main/java/org/opensearch/action/bulk/MappingUpdatePerformer.java b/server/src/main/java/org/opensearch/action/bulk/MappingUpdatePerformer.java index c0eb29e4c112f..c0b6d7049a0e9 100644 --- a/server/src/main/java/org/opensearch/action/bulk/MappingUpdatePerformer.java +++ b/server/src/main/java/org/opensearch/action/bulk/MappingUpdatePerformer.java @@ -32,14 +32,19 @@ package org.opensearch.action.bulk; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.mapper.Mapping; -import org.opensearch.index.shard.ShardId; +/** + * Updates the mappings on the cluster manager + * + * @opensearch.internal + */ public interface MappingUpdatePerformer { /** - * Update the mappings on the master. + * Update the mappings on the cluster-manager. */ void updateMappings(Mapping update, ShardId shardId, ActionListener listener); diff --git a/server/src/main/java/org/opensearch/action/bulk/Retry.java b/server/src/main/java/org/opensearch/action/bulk/Retry.java index bb1197699d894..338355e214eb8 100644 --- a/server/src/main/java/org/opensearch/action/bulk/Retry.java +++ b/server/src/main/java/org/opensearch/action/bulk/Retry.java @@ -31,12 +31,12 @@ package org.opensearch.action.bulk; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; -import org.opensearch.action.ActionListener; +import org.apache.logging.log4j.Logger; import org.opensearch.action.support.PlainActionFuture; import org.opensearch.common.unit.TimeValue; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; import org.opensearch.threadpool.Scheduler; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.RemoteTransportException; @@ -49,6 +49,8 @@ /** * Encapsulates synchronous and asynchronous retry logic. + * + * @opensearch.internal */ public class Retry { private final BackoffPolicy backoffPolicy; @@ -92,6 +94,11 @@ public PlainActionFuture withBackoff( return future; } + /** + * Retry handler + * + * @opensearch.internal + */ static class RetryHandler implements ActionListener { private static final RestStatus RETRY_STATUS = RestStatus.TOO_MANY_REQUESTS; private static final Logger logger = LogManager.getLogger(RetryHandler.class); diff --git a/server/src/main/java/org/opensearch/action/bulk/TransportBulkAction.java b/server/src/main/java/org/opensearch/action/bulk/TransportBulkAction.java index 560fd1d8a45b3..387aff7194813 100644 --- a/server/src/main/java/org/opensearch/action/bulk/TransportBulkAction.java +++ b/server/src/main/java/org/opensearch/action/bulk/TransportBulkAction.java @@ -35,13 +35,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.SparseFixedBitSet; -import org.opensearch.Assertions; +import org.opensearch.ExceptionsHelper; import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchParseException; -import org.opensearch.ExceptionsHelper; import org.opensearch.ResourceAlreadyExistsException; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.DocWriteResponse; @@ -73,13 +71,17 @@ import org.opensearch.common.lease.Releasable; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AtomicArray; -import org.opensearch.index.Index; +import org.opensearch.core.Assertions; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.IndexingPressureService; import org.opensearch.index.VersionType; import org.opensearch.index.seqno.SequenceNumbers; -import org.opensearch.index.shard.ShardId; +import org.opensearch.index.shard.IndexingStats.Stats.DocStatusStats; import org.opensearch.indices.IndexClosedException; +import org.opensearch.indices.IndicesService; import org.opensearch.indices.SystemIndices; import org.opensearch.ingest.IngestService; import org.opensearch.node.NodeClosedException; @@ -112,6 +114,8 @@ /** * Groups bulk request items by shard, optionally creating non-existent indices and * delegates to {@link TransportShardBulkAction} for shard-level bulk execution + * + * @opensearch.internal */ public class TransportBulkAction extends HandledTransportAction { @@ -128,6 +132,7 @@ public class TransportBulkAction extends HandledTransportAction { private final Task task; private BulkRequest bulkRequest; // set to null once all requests are sent out @@ -596,14 +607,25 @@ protected void doRun() { } if (requestsByShard.isEmpty()) { - listener.onResponse( - new BulkResponse(responses.toArray(new BulkItemResponse[responses.length()]), buildTookInMillis(startTimeNanos)) - ); + BulkItemResponse[] response = responses.toArray(new BulkItemResponse[responses.length()]); + long tookMillis = buildTookInMillis(startTimeNanos); + + DocStatusStats stats = new DocStatusStats(); + for (BulkItemResponse itemResponse : response) { + if (itemResponse != null) { + stats.inc(itemResponse.status()); + } + } + + indicesService.addDocStatusStats(stats); + listener.onResponse(new BulkResponse(response, tookMillis)); return; } final AtomicInteger counter = new AtomicInteger(requestsByShard.size()); + final DocStatusStats docStatusStats = new DocStatusStats(); String nodeId = clusterService.localNode().getId(); + for (Map.Entry> entry : requestsByShard.entrySet()) { final ShardId shardId = entry.getKey(); final List requests = entry.getValue(); @@ -633,8 +655,11 @@ public void onResponse(BulkShardResponse bulkShardResponse) { if (bulkItemResponse.getResponse() != null) { bulkItemResponse.getResponse().setShardInfo(bulkShardResponse.getShardInfo()); } + + docStatusStats.inc(bulkItemResponse.status()); responses.set(bulkItemResponse.getItemId(), bulkItemResponse); } + if (counter.decrementAndGet() == 0) { finishHim(); } @@ -645,22 +670,24 @@ public void onFailure(Exception e) { // create failures for all relevant requests for (BulkItemRequest request : requests) { final String indexName = concreteIndices.getConcreteIndex(request.index()).getName(); - DocWriteRequest docWriteRequest = request.request(); - responses.set( + final DocWriteRequest docWriteRequest = request.request(); + final BulkItemResponse bulkItemResponse = new BulkItemResponse( request.id(), - new BulkItemResponse( - request.id(), - docWriteRequest.opType(), - new BulkItemResponse.Failure(indexName, docWriteRequest.id(), e) - ) + docWriteRequest.opType(), + new BulkItemResponse.Failure(indexName, docWriteRequest.id(), e) ); + + docStatusStats.inc(bulkItemResponse.status()); + responses.set(request.id(), bulkItemResponse); } + if (counter.decrementAndGet() == 0) { finishHim(); } } private void finishHim() { + indicesService.addDocStatusStats(docStatusStats); listener.onResponse( new BulkResponse(responses.toArray(new BulkItemResponse[responses.length()]), buildTookInMillis(startTimeNanos)) ); @@ -767,9 +794,18 @@ void executeBulk( final AtomicArray responses, Map indicesThatCannotBeCreated ) { + /* + * We are not wrapping the listener here to capture the response codes for performance benefits. It will + * be saving us an iteration over the responses array + */ new BulkOperation(task, bulkRequest, listener, responses, startTimeNanos, indicesThatCannotBeCreated).run(); } + /** + * Concrete indices + * + * @opensearch.internal + */ private static class ConcreteIndices { private final ClusterState state; private final IndexNameExpressionResolver indexNameExpressionResolver; @@ -872,6 +908,11 @@ public boolean isForceExecution() { ); } + /** + * A modifier for a bulk request + * + * @opensearch.internal + */ static final class BulkRequestModifier implements Iterator> { final BulkRequest bulkRequest; diff --git a/server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java b/server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java index cc9f20b7aa256..fddda0ef1f9a7 100644 --- a/server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java +++ b/server/src/main/java/org/opensearch/action/bulk/TransportShardBulkAction.java @@ -37,7 +37,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.MessageSupplier; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionListener; +import org.opensearch.action.ActionListenerResponseHandler; import org.opensearch.action.ActionRunnable; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.DocWriteResponse; @@ -46,55 +46,80 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.action.index.IndexResponse; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.ChannelActionListener; +import org.opensearch.action.support.replication.ReplicationMode; +import org.opensearch.action.support.replication.ReplicationOperation; +import org.opensearch.action.support.replication.ReplicationTask; import org.opensearch.action.support.replication.TransportReplicationAction; import org.opensearch.action.support.replication.TransportWriteAction; import org.opensearch.action.update.UpdateHelper; import org.opensearch.action.update.UpdateRequest; import org.opensearch.action.update.UpdateResponse; +import org.opensearch.client.transport.NoNodeAvailableException; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.action.index.MappingUpdatedAction; import org.opensearch.cluster.action.shard.ShardStateAction; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MappingMetadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.AllocationId; +import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContent; +import org.opensearch.common.util.concurrent.AbstractRunnable; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ToXContent; import org.opensearch.index.IndexingPressureService; +import org.opensearch.index.SegmentReplicationPressureService; import org.opensearch.index.engine.Engine; import org.opensearch.index.engine.VersionConflictEngineException; import org.opensearch.index.get.GetResult; import org.opensearch.index.mapper.MapperException; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.mapper.SourceToParse; +import org.opensearch.index.remote.RemoteStorePressureService; import org.opensearch.index.seqno.SequenceNumbers; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; +import org.opensearch.index.shard.ShardNotFoundException; import org.opensearch.index.translog.Translog; import org.opensearch.indices.IndicesService; import org.opensearch.indices.SystemIndices; import org.opensearch.node.NodeClosedException; +import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.threadpool.ThreadPool.Names; +import org.opensearch.transport.TransportChannel; +import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestOptions; import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.LongSupplier; -/** Performs shard-level bulk (index, delete or update) operations */ +/** + * Performs shard-level bulk (index, delete or update) operations + * + * @opensearch.internal + */ public class TransportShardBulkAction extends TransportWriteAction { public static final String ACTION_NAME = BulkAction.NAME + "[s]"; @@ -110,6 +135,17 @@ public class TransportShardBulkAction extends TransportWriteAction listener = new ChannelActionListener<>(channel, transportPrimaryTermValidationAction, request); + final ShardId shardId = request.getShardId(); + assert shardId != null : "request shardId must be set"; + IndexShard replica = getIndexShard(shardId); + try { + new PrimaryTermValidationReplicaAction(listener, replica, (ReplicationTask) task, request).run(); + } catch (RuntimeException e) { + listener.onFailure(e); + } + } + + /** + * This action is the primary term validation action which is used for doing primary term validation with replicas. + * This is only applicable for TransportShardBulkAction because all writes (delete/update/single write/bulk) + * ultimately boils down to TransportShardBulkAction and isolated primary could continue to acknowledge if it is not + * aware that the primary has changed. This helps achieve the same. More details in java doc of + * {@link TransportShardBulkAction#transportPrimaryTermValidationAction}. + * + * @opensearch.internal + */ + private static final class PrimaryTermValidationReplicaAction extends AbstractRunnable implements ActionListener { + + private final ActionListener onCompletionListener; + private final IndexShard replica; + private final ReplicationTask task; + private final PrimaryTermValidationRequest request; + + public PrimaryTermValidationReplicaAction( + ActionListener onCompletionListener, + IndexShard replica, + ReplicationTask task, + PrimaryTermValidationRequest request + ) { + this.onCompletionListener = onCompletionListener; + this.replica = replica; + this.task = task; + this.request = request; + } + + @Override + public void onResponse(Releasable releasable) { + setPhase(task, "finished"); + onCompletionListener.onResponse(new ReplicaResponse(SequenceNumbers.NO_OPS_PERFORMED, SequenceNumbers.NO_OPS_PERFORMED)); + } + + @Override + public void onFailure(Exception e) { + setPhase(task, "failed"); + onCompletionListener.onFailure(e); + } + + @Override + protected void doRun() throws Exception { + setPhase(task, "primary-term-validation"); + final String actualAllocationId = this.replica.routingEntry().allocationId().getId(); + if (actualAllocationId.equals(request.getTargetAllocationID()) == false) { + throw new ShardNotFoundException( + this.replica.shardId(), + "expected allocation id [{}] but found [{}]", + request.getTargetAllocationID(), + actualAllocationId + ); + } + // Check operation primary term against the incoming primary term + // If the request primary term is low, then trigger lister failure + if (request.getPrimaryTerm() < replica.getOperationPrimaryTerm()) { + final String message = String.format( + Locale.ROOT, + "%s operation primary term [%d] is too old (current [%d])", + request.getShardId(), + request.getPrimaryTerm(), + replica.getOperationPrimaryTerm() + ); + onFailure(new IllegalStateException(message)); + } else { + onResponse(null); + } + } + } + + /** + * Primary term validation request sent to a specific allocation id + * + * @opensearch.internal + */ + protected static final class PrimaryTermValidationRequest extends TransportRequest { + + /** + * {@link AllocationId#getId()} of the shard this request is sent to + **/ + private final String targetAllocationID; + private final long primaryTerm; + private final ShardId shardId; + + public PrimaryTermValidationRequest(String targetAllocationID, long primaryTerm, ShardId shardId) { + this.targetAllocationID = Objects.requireNonNull(targetAllocationID); + this.primaryTerm = primaryTerm; + this.shardId = Objects.requireNonNull(shardId); + } + + public PrimaryTermValidationRequest(StreamInput in) throws IOException { + super(in); + targetAllocationID = in.readString(); + primaryTerm = in.readVLong(); + shardId = new ShardId(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(targetAllocationID); + out.writeVLong(primaryTerm); + shardId.writeTo(out); + } + + @Override + public Task createTask(long id, String type, String action, TaskId parentTaskId, Map headers) { + return new ReplicationTask(id, type, action, getDescription(), parentTaskId, headers); + } + + public String getTargetAllocationID() { + return targetAllocationID; + } + + public long getPrimaryTerm() { + return primaryTerm; + } + + public ShardId getShardId() { + return shardId; + } + + @Override + public String getDescription() { + return toString(); + } + + @Override + public String toString() { + return "PrimaryTermValidationRequest [" + + shardId + + "] for targetAllocationID [" + + targetAllocationID + + "] with primaryTerm [" + + primaryTerm + + "]"; + } + } + + @Override + protected ReplicationOperation.Replicas primaryTermValidationReplicasProxy() { + return new PrimaryTermValidationProxy(); + } + + /** + * This {@link org.opensearch.action.support.replication.TransportReplicationAction.ReplicasProxy} implementation is + * used for primary term validation and is only relevant for TransportShardBulkAction replication action. + * + * @opensearch.internal + */ + private final class PrimaryTermValidationProxy extends WriteActionReplicasProxy { + + @Override + public void performOn( + ShardRouting replica, + BulkShardRequest request, + long primaryTerm, + long globalCheckpoint, + long maxSeqNoOfUpdatesOrDeletes, + ActionListener listener + ) { + String nodeId = replica.currentNodeId(); + final DiscoveryNode node = clusterService.state().nodes().get(nodeId); + if (node == null) { + listener.onFailure(new NoNodeAvailableException("unknown node [" + nodeId + "]")); + return; + } + final PrimaryTermValidationRequest validationRequest = new PrimaryTermValidationRequest( + replica.allocationId().getId(), + primaryTerm, + replica.shardId() + ); + final ActionListenerResponseHandler handler = new ActionListenerResponseHandler<>( + listener, + ReplicaResponse::new + ); + transportService.sendRequest(node, transportPrimaryTermValidationAction, validationRequest, transportOptions, handler); + } } @Override @@ -165,7 +411,7 @@ protected void dispatchedShardOperationOnPrimary( performOnPrimary(request, primary, updateHelper, threadPool::absoluteTimeInMillis, (update, shardId, mappingListener) -> { assert update != null; assert shardId != null; - mappingUpdatedAction.updateMappingOnMaster(shardId.getIndex(), update, mappingListener); + mappingUpdatedAction.updateMappingOnClusterManager(shardId.getIndex(), update, mappingListener); }, mappingUpdateListener -> observer.waitForNextChange(new ClusterStateObserver.Listener() { @Override public void onNewClusterState(ClusterState state) { @@ -189,6 +435,14 @@ protected long primaryOperationSize(BulkShardRequest request) { return request.ramBytesUsed(); } + @Override + public ReplicationMode getReplicationMode(IndexShard indexShard) { + if (indexShard.isRemoteTranslogEnabled()) { + return ReplicationMode.PRIMARY_TERM_VALIDATION; + } + return super.getReplicationMode(indexShard); + } + public static void performOnPrimary( BulkShardRequest request, IndexShard primary, @@ -276,6 +530,21 @@ private void finishRequest() { }.run(); } + @Override + protected Releasable checkPrimaryLimits(BulkShardRequest request, boolean rerouteWasLocal, boolean localRerouteInitiatedByNodeClient) { + if (force(request) == false) { + if (segmentReplicationPressureService.isSegmentReplicationBackpressureEnabled()) { + segmentReplicationPressureService.isSegrepLimitBreached(request.shardId()); + } + // TODO - While removing remote store flag, this can be encapsulated to single class with common interface for backpressure + // service + if (remoteStorePressureService.isSegmentsUploadBackpressureEnabled()) { + remoteStorePressureService.validateSegmentsUploadLag(request.shardId()); + } + } + return super.checkPrimaryLimits(request, rerouteWasLocal, localRerouteInitiatedByNodeClient); + } + /** * Executes bulk item requests and handles request execution exceptions. * @return {@code true} if request completed on this thread and the listener was invoked, {@code false} if the request triggered @@ -481,7 +750,7 @@ static BulkItemResponse processUpdateResponse( if (updateRequest.fetchSource() != null && updateRequest.fetchSource().fetchSource()) { final BytesReference indexSourceAsBytes = updateIndexRequest.source(); - final Tuple> sourceAndContent = XContentHelper.convertToMap( + final Tuple> sourceAndContent = XContentHelper.convertToMap( indexSourceAsBytes, true, updateIndexRequest.getContentType() @@ -599,6 +868,7 @@ private static Engine.Result performOpOnReplica( indexRequest.routing() ); result = replica.applyIndexOperationOnReplica( + primaryResponse.getId(), primaryResponse.getSeqNo(), primaryResponse.getPrimaryTerm(), primaryResponse.getVersion(), @@ -621,8 +891,8 @@ private static Engine.Result performOpOnReplica( throw new IllegalStateException("Unexpected request operation type on replica: " + docWriteRequest.opType().getLowercase()); } if (result.getResultType() == Engine.Result.Type.MAPPING_UPDATE_REQUIRED) { - // Even though the primary waits on all nodes to ack the mapping changes to the master - // (see MappingUpdatedAction.updateMappingOnMaster) we still need to protect against missing mappings + // Even though the primary waits on all nodes to ack the mapping changes to the cluster-manager + // (see MappingUpdatedAction.updateMappingOnClusterManager) we still need to protect against missing mappings // and wait for them. The reason is concurrent requests. Request r1 which has new field f triggers a // mapping update. Assume that that update is first applied on the primary, and only later on the replica // (it’s happening concurrently). Request r2, which now arrives on the primary and which also has the new diff --git a/server/src/main/java/org/opensearch/action/bulk/TransportSingleItemBulkWriteAction.java b/server/src/main/java/org/opensearch/action/bulk/TransportSingleItemBulkWriteAction.java index a833687d591de..9b901dda24c2b 100644 --- a/server/src/main/java/org/opensearch/action/bulk/TransportSingleItemBulkWriteAction.java +++ b/server/src/main/java/org/opensearch/action/bulk/TransportSingleItemBulkWriteAction.java @@ -32,7 +32,6 @@ package org.opensearch.action.bulk; -import org.opensearch.action.ActionListener; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.DocWriteResponse; import org.opensearch.action.support.ActionFilters; @@ -41,11 +40,16 @@ import org.opensearch.action.support.WriteResponse; import org.opensearch.action.support.replication.ReplicatedWriteRequest; import org.opensearch.action.support.replication.ReplicationResponse; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; -/** use transport bulk action directly */ +/** + * use transport bulk action directly + * + * @opensearch.internal + */ @Deprecated public abstract class TransportSingleItemBulkWriteAction< Request extends ReplicatedWriteRequest, diff --git a/server/src/main/java/org/opensearch/action/bulk/package-info.java b/server/src/main/java/org/opensearch/action/bulk/package-info.java new file mode 100644 index 0000000000000..45c42c0efa3f2 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/bulk/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Bulk API transport handlers. */ +package org.opensearch.action.bulk; diff --git a/server/src/main/java/org/opensearch/action/delete/DeleteAction.java b/server/src/main/java/org/opensearch/action/delete/DeleteAction.java index dc58ebee6c356..b93867ca12efe 100644 --- a/server/src/main/java/org/opensearch/action/delete/DeleteAction.java +++ b/server/src/main/java/org/opensearch/action/delete/DeleteAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to delete a document from an index + * + * @opensearch.internal + */ public class DeleteAction extends ActionType { public static final DeleteAction INSTANCE = new DeleteAction(); diff --git a/server/src/main/java/org/opensearch/action/delete/DeleteRequest.java b/server/src/main/java/org/opensearch/action/delete/DeleteRequest.java index c40933ba9c92e..cb8195b09593f 100644 --- a/server/src/main/java/org/opensearch/action/delete/DeleteRequest.java +++ b/server/src/main/java/org/opensearch/action/delete/DeleteRequest.java @@ -40,13 +40,13 @@ import org.opensearch.action.DocWriteRequest; import org.opensearch.action.support.replication.ReplicatedWriteRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.uid.Versions; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.MapperService; -import org.opensearch.index.shard.ShardId; import java.io.IOException; @@ -64,6 +64,8 @@ * @see DeleteResponse * @see org.opensearch.client.Client#delete(DeleteRequest) * @see org.opensearch.client.Requests#deleteRequest(String) + * + * @opensearch.internal */ public class DeleteRequest extends ReplicatedWriteRequest implements diff --git a/server/src/main/java/org/opensearch/action/delete/DeleteRequestBuilder.java b/server/src/main/java/org/opensearch/action/delete/DeleteRequestBuilder.java index f6ee0f4a7b278..0436962ce01d2 100644 --- a/server/src/main/java/org/opensearch/action/delete/DeleteRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/delete/DeleteRequestBuilder.java @@ -40,6 +40,8 @@ /** * A delete document action request builder. + * + * @opensearch.internal */ public class DeleteRequestBuilder extends ReplicationRequestBuilder implements diff --git a/server/src/main/java/org/opensearch/action/delete/DeleteResponse.java b/server/src/main/java/org/opensearch/action/delete/DeleteResponse.java index 6b000561ad282..e4d44197a8885 100644 --- a/server/src/main/java/org/opensearch/action/delete/DeleteResponse.java +++ b/server/src/main/java/org/opensearch/action/delete/DeleteResponse.java @@ -33,20 +33,22 @@ package org.opensearch.action.delete; import org.opensearch.action.DocWriteResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * The response of the delete action. * * @see org.opensearch.action.delete.DeleteRequest * @see org.opensearch.client.Client#delete(DeleteRequest) + * + * @opensearch.internal */ public class DeleteResponse extends DocWriteResponse { @@ -109,6 +111,8 @@ public static void parseXContentFields(XContentParser parser, Builder context) t * Builder class for {@link DeleteResponse}. This builder is usually used during xcontent parsing to * temporarily store the parsed values, then the {@link DocWriteResponse.Builder#build()} method is called to * instantiate the {@link DeleteResponse}. + * + * @opensearch.internal */ public static class Builder extends DocWriteResponse.Builder { diff --git a/server/src/main/java/org/opensearch/action/delete/TransportDeleteAction.java b/server/src/main/java/org/opensearch/action/delete/TransportDeleteAction.java index adeebcba28a89..039214459ac21 100644 --- a/server/src/main/java/org/opensearch/action/delete/TransportDeleteAction.java +++ b/server/src/main/java/org/opensearch/action/delete/TransportDeleteAction.java @@ -42,6 +42,8 @@ * Performs the delete operation. * * Deprecated use TransportBulkAction with a single item instead + * + * @opensearch.internal */ @Deprecated public class TransportDeleteAction extends TransportSingleItemBulkWriteAction { diff --git a/server/src/main/java/org/opensearch/action/explain/ExplainAction.java b/server/src/main/java/org/opensearch/action/explain/ExplainAction.java index ff534dbf82f6d..e955ecc8a1e45 100644 --- a/server/src/main/java/org/opensearch/action/explain/ExplainAction.java +++ b/server/src/main/java/org/opensearch/action/explain/ExplainAction.java @@ -36,6 +36,8 @@ /** * Entry point for the explain feature. + * + * @opensearch.internal */ public class ExplainAction extends ActionType { diff --git a/server/src/main/java/org/opensearch/action/explain/ExplainRequest.java b/server/src/main/java/org/opensearch/action/explain/ExplainRequest.java index 1543c2c95b269..aad5a9853a3c5 100644 --- a/server/src/main/java/org/opensearch/action/explain/ExplainRequest.java +++ b/server/src/main/java/org/opensearch/action/explain/ExplainRequest.java @@ -36,12 +36,12 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.ValidateActions; import org.opensearch.action.support.single.shard.SingleShardRequest; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.query.QueryBuilder; import org.opensearch.search.fetch.subphase.FetchSourceContext; @@ -53,6 +53,8 @@ /** * Explain request encapsulating the explain query and document identifier to get an explanation for. + * + * @opensearch.internal */ public class ExplainRequest extends SingleShardRequest implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/explain/ExplainRequestBuilder.java b/server/src/main/java/org/opensearch/action/explain/ExplainRequestBuilder.java index 6839479079845..3031cb6067469 100644 --- a/server/src/main/java/org/opensearch/action/explain/ExplainRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/explain/ExplainRequestBuilder.java @@ -35,12 +35,14 @@ import org.opensearch.action.support.single.shard.SingleShardOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import org.opensearch.index.query.QueryBuilder; import org.opensearch.search.fetch.subphase.FetchSourceContext; /** * A builder for {@link ExplainRequest}. + * + * @opensearch.internal */ public class ExplainRequestBuilder extends SingleShardOperationRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/explain/ExplainResponse.java b/server/src/main/java/org/opensearch/action/explain/ExplainResponse.java index 6dd1945a38ffc..895f87b83b6ff 100644 --- a/server/src/main/java/org/opensearch/action/explain/ExplainResponse.java +++ b/server/src/main/java/org/opensearch/action/explain/ExplainResponse.java @@ -34,17 +34,17 @@ import org.apache.lucene.search.Explanation; import org.opensearch.Version; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.get.GetResult; import org.opensearch.index.mapper.MapperService; -import org.opensearch.rest.RestStatus; import java.io.IOException; import java.util.Collection; @@ -55,6 +55,8 @@ /** * Response containing the score explanation. + * + * @opensearch.internal */ public class ExplainResponse extends ActionResponse implements StatusToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/explain/TransportExplainAction.java b/server/src/main/java/org/opensearch/action/explain/TransportExplainAction.java index 9fb16eec7d36b..fb2ccc6ebbf12 100644 --- a/server/src/main/java/org/opensearch/action/explain/TransportExplainAction.java +++ b/server/src/main/java/org/opensearch/action/explain/TransportExplainAction.java @@ -35,7 +35,6 @@ import org.apache.lucene.index.Term; import org.apache.lucene.search.Explanation; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.RoutingMissingException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.single.shard.TransportSingleShardAction; @@ -44,15 +43,16 @@ import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.lease.Releasables; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.engine.Engine; import org.opensearch.index.get.GetResult; import org.opensearch.index.mapper.IdFieldMapper; import org.opensearch.index.mapper.Uid; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.search.SearchService; import org.opensearch.search.internal.AliasFilter; import org.opensearch.search.internal.SearchContext; @@ -68,6 +68,8 @@ /** * Explain transport action. Computes the explain on the targeted shard. + * + * @opensearch.internal */ // TODO: AggregatedDfs. Currently the idf can be different then when executing a normal search with explain. public class TransportExplainAction extends TransportSingleShardAction { diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilities.java b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilities.java index ffc059f08c70f..260d3d8c4e33f 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilities.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilities.java @@ -33,15 +33,16 @@ package org.opensearch.action.fieldcaps; import org.opensearch.LegacyESVersion; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; @@ -59,6 +60,8 @@ /** * Describes the capabilities of a field optionally merged across multiple indices. + * + * @opensearch.internal */ public class FieldCapabilities implements Writeable, ToXContentObject { @@ -293,9 +296,14 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * Builder for field capabilities + * + * @opensearch.internal + */ static class Builder { private String name; private String type; @@ -385,6 +393,11 @@ FieldCapabilities build(boolean withIndices) { } } + /** + * Inner index capabilities + * + * @opensearch.internal + */ private static class IndexCaps { final String name; final boolean isSearchable; diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesAction.java b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesAction.java index d10fa5ee69811..e8585e60c8ffd 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesAction.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for a field capabilities request + * + * @opensearch.internal + */ public class FieldCapabilitiesAction extends ActionType { public static final FieldCapabilitiesAction INSTANCE = new FieldCapabilitiesAction(); diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesIndexRequest.java b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesIndexRequest.java index b349d8e2ca26f..09532e58e112f 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesIndexRequest.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesIndexRequest.java @@ -38,14 +38,19 @@ import org.opensearch.action.IndicesRequest; import org.opensearch.action.OriginalIndices; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.query.QueryBuilder; -import org.opensearch.index.shard.ShardId; import java.io.IOException; import java.util.Objects; +/** + * Transport Request for Retrieving Field Capabilities for an Index + * + * @opensearch.internal + */ public class FieldCapabilitiesIndexRequest extends ActionRequest implements IndicesRequest { public static final IndicesOptions INDICES_OPTIONS = IndicesOptions.strictSingleIndexNoExpandForbidClosed(); diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java index 0d7fd91a6bfd8..8baaa3e410a86 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesIndexResponse.java @@ -33,10 +33,10 @@ package org.opensearch.action.fieldcaps; import org.opensearch.LegacyESVersion; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; import java.util.Map; @@ -44,6 +44,8 @@ /** * Response for {@link TransportFieldCapabilitiesIndexAction}. + * + * @opensearch.internal */ public class FieldCapabilitiesIndexResponse extends ActionResponse implements Writeable { private final String indexName; diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesRequest.java b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesRequest.java index c8391fddf564d..c9bf962238ad8 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesRequest.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesRequest.java @@ -38,11 +38,11 @@ import org.opensearch.action.IndicesRequest; import org.opensearch.action.ValidateActions; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.query.QueryBuilder; import java.io.IOException; @@ -51,6 +51,9 @@ import java.util.Objects; import java.util.Set; +/** + * Transport request for retrieving field capabilities for an explicit list of fields + */ public final class FieldCapabilitiesRequest extends ActionRequest implements IndicesRequest.Replaceable, ToXContentObject { public static final String NAME = "field_caps_request"; diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesRequestBuilder.java b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesRequestBuilder.java index 3c1f11c047ef4..70a90b98bdf25 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesRequestBuilder.java @@ -36,6 +36,11 @@ import org.opensearch.client.OpenSearchClient; import org.opensearch.index.query.QueryBuilder; +/** + * Transport request builder for retrieving field capabilities + * + * @opensearch.internal + */ public class FieldCapabilitiesRequestBuilder extends ActionRequestBuilder { public FieldCapabilitiesRequestBuilder(OpenSearchClient client, FieldCapabilitiesAction action, String... indices) { super(client, action, new FieldCapabilitiesRequest().indices(indices)); diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesResponse.java b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesResponse.java index 90b35d6e14e4e..d919b96f5815e 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesResponse.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/FieldCapabilitiesResponse.java @@ -33,17 +33,18 @@ package org.opensearch.action.fieldcaps; import org.opensearch.LegacyESVersion; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParserUtils; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParserUtils; import java.io.IOException; import java.util.Arrays; @@ -56,6 +57,8 @@ /** * Response for {@link FieldCapabilitiesRequest} requests. + * + * @opensearch.internal */ public class FieldCapabilitiesResponse extends ActionResponse implements ToXContentObject { private static final ParseField INDICES_FIELD = new ParseField("indices"); @@ -217,6 +220,6 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/IndexFieldCapabilities.java b/server/src/main/java/org/opensearch/action/fieldcaps/IndexFieldCapabilities.java index d0629a46c9d4a..60c7c85cae55d 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/IndexFieldCapabilities.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/IndexFieldCapabilities.java @@ -33,9 +33,9 @@ package org.opensearch.action.fieldcaps; import org.opensearch.LegacyESVersion; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; import java.util.Collections; @@ -46,6 +46,8 @@ /** * Describes the capabilities of a field in a single index. + * + * @opensearch.internal */ public class IndexFieldCapabilities implements Writeable { diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/TransportFieldCapabilitiesAction.java b/server/src/main/java/org/opensearch/action/fieldcaps/TransportFieldCapabilitiesAction.java index 07254db46d567..4c9e10cba52e7 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/TransportFieldCapabilitiesAction.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/TransportFieldCapabilitiesAction.java @@ -32,7 +32,6 @@ package org.opensearch.action.fieldcaps; -import org.opensearch.action.ActionListener; import org.opensearch.action.OriginalIndices; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; @@ -40,9 +39,10 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.inject.Inject; import org.opensearch.common.util.concurrent.CountDown; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.RemoteClusterAware; @@ -58,6 +58,11 @@ import java.util.Map; import java.util.Set; +/** + * Transport action for field capabilities requests + * + * @opensearch.internal + */ public class TransportFieldCapabilitiesAction extends HandledTransportAction { private final ThreadPool threadPool; private final ClusterService clusterService; diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java b/server/src/main/java/org/opensearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java index a4807eff1acb4..b303dfea05c0a 100644 --- a/server/src/main/java/org/opensearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java +++ b/server/src/main/java/org/opensearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java @@ -35,7 +35,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.ActionType; import org.opensearch.action.NoShardAvailableActionException; @@ -48,19 +47,21 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.FailAwareWeightedRouting; import org.opensearch.cluster.routing.GroupShardsIterator; import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.logging.LoggerMessageFormat; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.logging.LoggerMessageFormat; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.mapper.MappedFieldType; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.mapper.ObjectMapper; import org.opensearch.index.query.MatchAllQueryBuilder; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.search.SearchService; import org.opensearch.search.builder.SearchSourceBuilder; @@ -85,6 +86,11 @@ import static org.opensearch.action.support.TransportActions.isShardNotAvailableException; +/** + * Transport action for field capabilities request in an index + * + * @opensearch.internal + */ public class TransportFieldCapabilitiesIndexAction extends HandledTransportAction< FieldCapabilitiesIndexRequest, FieldCapabilitiesIndexResponse> { @@ -212,6 +218,8 @@ private ClusterBlockException checkRequestBlock(ClusterState state, String concr * An action that executes on each shard sequentially until it finds one that can match the provided * {@link FieldCapabilitiesIndexRequest#indexFilter()}. In which case the shard is used * to create the final {@link FieldCapabilitiesIndexResponse}. + * + * @opensearch.internal */ class AsyncShardsAction { private final FieldCapabilitiesIndexRequest request; @@ -255,16 +263,18 @@ private void onFailure(ShardRouting shardRouting, Exception e) { tryNext(e, false); } - private ShardRouting nextRoutingOrNull() { + private ShardRouting nextRoutingOrNull(Exception failure) { if (shardsIt.size() == 0 || shardIndex >= shardsIt.size()) { return null; } - ShardRouting next = shardsIt.get(shardIndex).nextOrNull(); + ShardRouting next = FailAwareWeightedRouting.getInstance() + .findNext(shardsIt.get(shardIndex), clusterService.state(), failure, this::moveToNextShard); + if (next != null) { return next; } moveToNextShard(); - return nextRoutingOrNull(); + return nextRoutingOrNull(failure); } private void moveToNextShard() { @@ -272,7 +282,7 @@ private void moveToNextShard() { } private void tryNext(@Nullable final Exception lastFailure, boolean canMatchShard) { - ShardRouting shardRouting = nextRoutingOrNull(); + ShardRouting shardRouting = nextRoutingOrNull(lastFailure); if (shardRouting == null) { if (canMatchShard == false) { listener.onResponse(new FieldCapabilitiesIndexResponse(request.index(), Collections.emptyMap(), false)); @@ -336,6 +346,11 @@ public void handleException(TransportException exp) { } } + /** + * Shard transport handler for field capabilities index action + * + * @opensearch.internal + */ private class ShardTransportHandler implements TransportRequestHandler { @Override public void messageReceived(final FieldCapabilitiesIndexRequest request, final TransportChannel channel, Task task) diff --git a/server/src/main/java/org/opensearch/action/fieldcaps/package-info.java b/server/src/main/java/org/opensearch/action/fieldcaps/package-info.java new file mode 100644 index 0000000000000..c7b7c01875d2b --- /dev/null +++ b/server/src/main/java/org/opensearch/action/fieldcaps/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Field Capabilities transport handlers. */ +package org.opensearch.action.fieldcaps; diff --git a/server/src/main/java/org/opensearch/action/get/GetAction.java b/server/src/main/java/org/opensearch/action/get/GetAction.java index f90fbc9b3be1c..05544591f030e 100644 --- a/server/src/main/java/org/opensearch/action/get/GetAction.java +++ b/server/src/main/java/org/opensearch/action/get/GetAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to get a document + * + * @opensearch.internal + */ public class GetAction extends ActionType { public static final GetAction INSTANCE = new GetAction(); diff --git a/server/src/main/java/org/opensearch/action/get/GetRequest.java b/server/src/main/java/org/opensearch/action/get/GetRequest.java index 9badf2db92f67..83839e4ba3726 100644 --- a/server/src/main/java/org/opensearch/action/get/GetRequest.java +++ b/server/src/main/java/org/opensearch/action/get/GetRequest.java @@ -38,10 +38,10 @@ import org.opensearch.action.RealtimeRequest; import org.opensearch.action.ValidateActions; import org.opensearch.action.support.single.shard.SingleShardRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.uid.Versions; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.MapperService; import org.opensearch.search.fetch.subphase.FetchSourceContext; @@ -60,6 +60,8 @@ * @see GetResponse * @see org.opensearch.client.Requests#getRequest(String) * @see org.opensearch.client.Client#get(GetRequest) + * + * @opensearch.internal */ public class GetRequest extends SingleShardRequest implements RealtimeRequest { @@ -152,7 +154,8 @@ public GetRequest routing(String routing) { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public GetRequest preference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/get/GetRequestBuilder.java b/server/src/main/java/org/opensearch/action/get/GetRequestBuilder.java index 492a88b9d3821..6237cf73f0ca8 100644 --- a/server/src/main/java/org/opensearch/action/get/GetRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/get/GetRequestBuilder.java @@ -35,12 +35,14 @@ import org.opensearch.action.support.single.shard.SingleShardOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; import org.opensearch.index.VersionType; import org.opensearch.search.fetch.subphase.FetchSourceContext; /** * A get document action request builder. + * + * @opensearch.internal */ public class GetRequestBuilder extends SingleShardOperationRequestBuilder { @@ -71,7 +73,8 @@ public GetRequestBuilder setRouting(String routing) { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public GetRequestBuilder setPreference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/get/GetResponse.java b/server/src/main/java/org/opensearch/action/get/GetResponse.java index a15607d696195..c86128444d7eb 100644 --- a/server/src/main/java/org/opensearch/action/get/GetResponse.java +++ b/server/src/main/java/org/opensearch/action/get/GetResponse.java @@ -33,16 +33,17 @@ package org.opensearch.action.get; import org.opensearch.OpenSearchParseException; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParsingException; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.document.DocumentField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.get.GetResult; import java.io.IOException; @@ -56,6 +57,8 @@ * * @see GetRequest * @see org.opensearch.client.Client#get(GetRequest) + * + * @opensearch.internal */ public class GetResponse extends ActionResponse implements Iterable, ToXContentObject { @@ -235,6 +238,6 @@ public int hashCode() { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/action/get/MultiGetAction.java b/server/src/main/java/org/opensearch/action/get/MultiGetAction.java index f7df277585250..bed5d0bd9e55e 100644 --- a/server/src/main/java/org/opensearch/action/get/MultiGetAction.java +++ b/server/src/main/java/org/opensearch/action/get/MultiGetAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for a multi get + * + * @opensearch.internal + */ public class MultiGetAction extends ActionType { public static final MultiGetAction INSTANCE = new MultiGetAction(); diff --git a/server/src/main/java/org/opensearch/action/get/MultiGetItemResponse.java b/server/src/main/java/org/opensearch/action/get/MultiGetItemResponse.java index 1ff684fcc5872..19c9b785e7ea2 100644 --- a/server/src/main/java/org/opensearch/action/get/MultiGetItemResponse.java +++ b/server/src/main/java/org/opensearch/action/get/MultiGetItemResponse.java @@ -32,14 +32,16 @@ package org.opensearch.action.get; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; /** * A single multi get response. + * + * @opensearch.internal */ public class MultiGetItemResponse implements Writeable { diff --git a/server/src/main/java/org/opensearch/action/get/MultiGetRequest.java b/server/src/main/java/org/opensearch/action/get/MultiGetRequest.java index 974799dd7bf4c..8ccf5afb9fe4c 100644 --- a/server/src/main/java/org/opensearch/action/get/MultiGetRequest.java +++ b/server/src/main/java/org/opensearch/action/get/MultiGetRequest.java @@ -43,17 +43,18 @@ import org.opensearch.action.ValidateActions; import org.opensearch.action.support.IndicesOptions; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.ParsingException; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.lucene.uid.Versions; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.ParsingException; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.MapperService; import org.opensearch.search.fetch.subphase.FetchSourceContext; @@ -66,6 +67,11 @@ import java.util.List; import java.util.Locale; +/** + * Transport request for a multi get. + * + * @opensearch.internal + */ public class MultiGetRequest extends ActionRequest implements Iterable, @@ -85,6 +91,8 @@ public class MultiGetRequest extends ActionRequest /** * A single get item. + * + * @opensearch.internal */ public static class Item implements Writeable, IndicesRequest, ToXContentObject { @@ -260,7 +268,7 @@ public int hashCode() { } public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } @@ -315,7 +323,8 @@ public ActionRequestValidationException validate() { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public MultiGetRequest preference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/get/MultiGetRequestBuilder.java b/server/src/main/java/org/opensearch/action/get/MultiGetRequestBuilder.java index 56ac6cbd1b8c9..c317edc07da8b 100644 --- a/server/src/main/java/org/opensearch/action/get/MultiGetRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/get/MultiGetRequestBuilder.java @@ -37,6 +37,8 @@ /** * A multi get document action request builder. + * + * @opensearch.internal */ public class MultiGetRequestBuilder extends ActionRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/get/MultiGetResponse.java b/server/src/main/java/org/opensearch/action/get/MultiGetResponse.java index ca6249861dd50..73372b85be3b2 100644 --- a/server/src/main/java/org/opensearch/action/get/MultiGetResponse.java +++ b/server/src/main/java/org/opensearch/action/get/MultiGetResponse.java @@ -34,15 +34,15 @@ import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.index.get.GetResult; import org.opensearch.index.mapper.MapperService; @@ -52,15 +52,26 @@ import java.util.Iterator; import java.util.List; +/** + * Transport response for a multi get. + * + * @opensearch.internal + */ public class MultiGetResponse extends ActionResponse implements Iterable, ToXContentObject { private static final ParseField INDEX = new ParseField("_index"); private static final ParseField ID = new ParseField("_id"); private static final ParseField ERROR = new ParseField("error"); private static final ParseField DOCS = new ParseField("docs"); + // In mixed clusters, the 1.x cluster could still return the '_type' in the response payload, it has to + // be handled gracefully + @Deprecated(forRemoval = true) + private static final ParseField TYPE = new ParseField("_type"); /** * Represents a failure. + * + * @opensearch.internal */ public static class Failure implements Writeable, ToXContentObject { @@ -205,6 +216,7 @@ private static MultiGetItemResponse parseItem(XContentParser parser) throws IOEx currentFieldName = parser.currentName(); if (INDEX.match(currentFieldName, parser.getDeprecationHandler()) == false && ID.match(currentFieldName, parser.getDeprecationHandler()) == false + && TYPE.match(currentFieldName, parser.getDeprecationHandler()) == false && ERROR.match(currentFieldName, parser.getDeprecationHandler()) == false) { getResult = GetResult.fromXContentEmbedded(parser, index, id); } diff --git a/server/src/main/java/org/opensearch/action/get/MultiGetShardRequest.java b/server/src/main/java/org/opensearch/action/get/MultiGetShardRequest.java index 3f678d6db2407..c32b1da068e67 100644 --- a/server/src/main/java/org/opensearch/action/get/MultiGetShardRequest.java +++ b/server/src/main/java/org/opensearch/action/get/MultiGetShardRequest.java @@ -32,16 +32,20 @@ package org.opensearch.action.get; -import com.carrotsearch.hppc.IntArrayList; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.single.shard.SingleShardRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.ArrayList; import java.util.List; +/** + * Multi get shards. + * + * @opensearch.internal + */ public class MultiGetShardRequest extends SingleShardRequest { private int shardId; @@ -49,13 +53,13 @@ public class MultiGetShardRequest extends SingleShardRequest locations; List items; MultiGetShardRequest(StreamInput in) throws IOException { super(in); int size = in.readVInt(); - locations = new IntArrayList(size); + locations = new ArrayList<>(size); items = new ArrayList<>(size); for (int i = 0; i < size; i++) { @@ -71,7 +75,7 @@ public class MultiGetShardRequest extends SingleShardRequest(); items = new ArrayList<>(); preference = multiGetRequest.preference; realtime = multiGetRequest.realtime; @@ -89,7 +93,8 @@ public int shardId() { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public MultiGetShardRequest preference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/get/MultiGetShardResponse.java b/server/src/main/java/org/opensearch/action/get/MultiGetShardResponse.java index 2393fa4749876..ca020e74c7d93 100644 --- a/server/src/main/java/org/opensearch/action/get/MultiGetShardResponse.java +++ b/server/src/main/java/org/opensearch/action/get/MultiGetShardResponse.java @@ -32,23 +32,27 @@ package org.opensearch.action.get; -import com.carrotsearch.hppc.IntArrayList; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.ArrayList; import java.util.List; +/** + * Transport response for multi get shards. + * + * @opensearch.internal + */ public class MultiGetShardResponse extends ActionResponse { - final IntArrayList locations; + final List locations; final List responses; final List failures; MultiGetShardResponse() { - locations = new IntArrayList(); + locations = new ArrayList<>(); responses = new ArrayList<>(); failures = new ArrayList<>(); } @@ -56,7 +60,7 @@ public class MultiGetShardResponse extends ActionResponse { MultiGetShardResponse(StreamInput in) throws IOException { super(in); int size = in.readVInt(); - locations = new IntArrayList(size); + locations = new ArrayList<>(size); responses = new ArrayList<>(size); failures = new ArrayList<>(size); for (int i = 0; i < size; i++) { diff --git a/server/src/main/java/org/opensearch/action/get/TransportGetAction.java b/server/src/main/java/org/opensearch/action/get/TransportGetAction.java index 999cc8c12190b..00a795c86356f 100644 --- a/server/src/main/java/org/opensearch/action/get/TransportGetAction.java +++ b/server/src/main/java/org/opensearch/action/get/TransportGetAction.java @@ -32,20 +32,22 @@ package org.opensearch.action.get; -import org.opensearch.action.ActionListener; import org.opensearch.action.RoutingMissingException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.single.shard.TransportSingleShardAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.routing.Preference; import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.get.GetResult; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -54,6 +56,8 @@ /** * Performs the get operation. + * + * @opensearch.internal */ public class TransportGetAction extends TransportSingleShardAction { @@ -86,16 +90,30 @@ protected boolean resolveIndex(GetRequest request) { return true; } + /** + * Returns true if GET request should be routed to primary shards, else false. + */ + protected static boolean shouldForcePrimaryRouting(Metadata metadata, boolean realtime, String preference, String indexName) { + return metadata.isSegmentReplicationEnabled(indexName) && realtime && preference == null; + } + @Override protected ShardIterator shards(ClusterState state, InternalRequest request) { + final String preference; + // route realtime GET requests when segment replication is enabled to primary shards, + // iff there are no other preferences/routings enabled for routing to a specific shard + if (shouldForcePrimaryRouting( + state.getMetadata(), + request.request().realtime, + request.request().preference(), + request.concreteIndex() + )) { + preference = Preference.PRIMARY.type(); + } else { + preference = request.request().preference(); + } return clusterService.operationRouting() - .getShards( - clusterService.state(), - request.concreteIndex(), - request.request().id(), - request.request().routing(), - request.request().preference() - ); + .getShards(clusterService.state(), request.concreteIndex(), request.request().id(), request.request().routing(), preference); } @Override diff --git a/server/src/main/java/org/opensearch/action/get/TransportMultiGetAction.java b/server/src/main/java/org/opensearch/action/get/TransportMultiGetAction.java index 33f1e559a072c..8bbfef381aea8 100644 --- a/server/src/main/java/org/opensearch/action/get/TransportMultiGetAction.java +++ b/server/src/main/java/org/opensearch/action/get/TransportMultiGetAction.java @@ -32,17 +32,19 @@ package org.opensearch.action.get; -import org.opensearch.action.ActionListener; import org.opensearch.action.RoutingMissingException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.routing.Preference; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; import org.opensearch.common.util.concurrent.AtomicArray; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -50,6 +52,11 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +/** + * Perform the multi get action. + * + * @opensearch.internal + */ public class TransportMultiGetAction extends HandledTransportAction { private final ClusterService clusterService; @@ -70,6 +77,10 @@ public TransportMultiGetAction( this.indexNameExpressionResolver = resolver; } + protected static boolean shouldForcePrimaryRouting(Metadata metadata, boolean realtime, String preference, String indexName) { + return metadata.isSegmentReplicationEnabled(indexName) && realtime && preference == null; + } + @Override protected void doExecute(Task task, final MultiGetRequest request, final ActionListener listener) { ClusterState clusterState = clusterService.state(); @@ -104,6 +115,9 @@ protected void doExecute(Task task, final MultiGetRequest request, final ActionL MultiGetShardRequest shardRequest = shardRequests.get(shardId); if (shardRequest == null) { + if (shouldForcePrimaryRouting(clusterState.getMetadata(), request.realtime(), request.preference(), concreteSingleIndex)) { + request.preference(Preference.PRIMARY.type()); + } shardRequest = new MultiGetShardRequest(request, shardId.getIndexName(), shardId.getId()); shardRequests.put(shardId, shardRequest); } diff --git a/server/src/main/java/org/opensearch/action/get/TransportShardMultiGetAction.java b/server/src/main/java/org/opensearch/action/get/TransportShardMultiGetAction.java index db35af9c3e20f..27955098d96cd 100644 --- a/server/src/main/java/org/opensearch/action/get/TransportShardMultiGetAction.java +++ b/server/src/main/java/org/opensearch/action/get/TransportShardMultiGetAction.java @@ -33,7 +33,6 @@ package org.opensearch.action.get; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.TransportActions; import org.opensearch.action.support.single.shard.TransportSingleShardAction; @@ -42,17 +41,23 @@ import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.get.GetResult; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; +/** + * Perform the shard multi get action + * + * @opensearch.internal + */ public class TransportShardMultiGetAction extends TransportSingleShardAction { private static final String ACTION_NAME = MultiGetAction.NAME + "[shard]"; diff --git a/server/src/main/java/org/opensearch/action/index/IndexAction.java b/server/src/main/java/org/opensearch/action/index/IndexAction.java index 3f28af9b39d8a..0383cafba2958 100644 --- a/server/src/main/java/org/opensearch/action/index/IndexAction.java +++ b/server/src/main/java/org/opensearch/action/index/IndexAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for indexing a document. + * + * @opensearch.internal + */ public class IndexAction extends ActionType { public static final IndexAction INSTANCE = new IndexAction(); diff --git a/server/src/main/java/org/opensearch/action/index/IndexRequest.java b/server/src/main/java/org/opensearch/action/index/IndexRequest.java index 7bf6b876fa652..2500496103415 100644 --- a/server/src/main/java/org/opensearch/action/index/IndexRequest.java +++ b/server/src/main/java/org/opensearch/action/index/IndexRequest.java @@ -48,22 +48,23 @@ import org.opensearch.cluster.metadata.Metadata; import org.opensearch.common.Nullable; import org.opensearch.common.UUIDs; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.uid.Versions; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.MapperService; -import org.opensearch.index.shard.ShardId; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -77,17 +78,19 @@ * created using {@link org.opensearch.client.Requests#indexRequest(String)}. * * The index requires the {@link #index()}, {@link #id(String)} and - * {@link #source(byte[], XContentType)} to be set. + * {@link #source(byte[], MediaType)} to be set. * - * The source (content to index) can be set in its bytes form using ({@link #source(byte[], XContentType)}), - * its string form ({@link #source(String, XContentType)}) or using a {@link org.opensearch.common.xcontent.XContentBuilder} - * ({@link #source(org.opensearch.common.xcontent.XContentBuilder)}). + * The source (content to index) can be set in its bytes form using ({@link #source(byte[], MediaType)}), + * its string form ({@link #source(String, MediaType)}) or using a {@link XContentBuilder} + * ({@link #source(XContentBuilder)}). * * If the {@link #id(String)} is not set, it will be automatically generated. * * @see IndexResponse * @see org.opensearch.client.Requests#indexRequest(String) * @see org.opensearch.client.Client#index(IndexRequest) + * + * @opensearch.internal */ public class IndexRequest extends ReplicatedWriteRequest implements DocWriteRequest, CompositeIndicesRequest { @@ -113,7 +116,7 @@ public class IndexRequest extends ReplicatedWriteRequest implement private long version = Versions.MATCH_ANY; private VersionType versionType = VersionType.INTERNAL; - private XContentType contentType; + private MediaType contentType; private String pipeline; private String finalPipeline; @@ -163,7 +166,11 @@ public IndexRequest(@Nullable ShardId shardId, StreamInput in) throws IOExceptio isRetry = in.readBoolean(); autoGeneratedTimestamp = in.readLong(); if (in.readBoolean()) { - contentType = in.readEnum(XContentType.class); + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + contentType = in.readMediaType(); + } else { + contentType = in.readEnum(XContentType.class); + } } else { contentType = null; } @@ -182,7 +189,7 @@ public IndexRequest() { /** * Constructs a new index request against the specific index. The - * {@link #source(byte[], XContentType)} must be set. + * {@link #source(byte[], MediaType)} must be set. */ public IndexRequest(String index) { super(NO_SHARD_ID); @@ -235,12 +242,7 @@ public ActionRequestValidationException validate() { validationException = DocWriteRequest.validateSeqNoBasedCASParams(this, validationException); - if (id != null && id.getBytes(StandardCharsets.UTF_8).length > 512) { - validationException = addValidationError( - "id [" + id + "] is too long, must be no longer than 512 bytes but was: " + id.getBytes(StandardCharsets.UTF_8).length, - validationException - ); - } + validationException = DocWriteRequest.validateDocIdLength(id, validationException); if (pipeline != null && pipeline.isEmpty()) { validationException = addValidationError("pipeline cannot be an empty string", validationException); @@ -262,7 +264,7 @@ public IndicesOptions indicesOptions() { * The content type. This will be used when generating a document from user provided objects like Maps and when parsing the * source at index time */ - public XContentType getContentType() { + public MediaType getContentType() { return contentType; } @@ -385,9 +387,9 @@ public IndexRequest source(Map source) throws OpenSearchGenerationExc * * @param source The map to index */ - public IndexRequest source(Map source, XContentType contentType) throws OpenSearchGenerationException { + public IndexRequest source(Map source, MediaType contentType) throws OpenSearchGenerationException { try { - XContentBuilder builder = XContentFactory.contentBuilder(contentType); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(contentType); builder.map(source); return source(builder); } catch (IOException e) { @@ -398,11 +400,11 @@ public IndexRequest source(Map source, XContentType contentType) thro /** * Sets the document source to index. * - * Note, its preferable to either set it using {@link #source(org.opensearch.common.xcontent.XContentBuilder)} - * or using the {@link #source(byte[], XContentType)}. + * Note, its preferable to either set it using {@link #source(XContentBuilder)} + * or using the {@link #source(byte[], MediaType)}. */ - public IndexRequest source(String source, XContentType xContentType) { - return source(new BytesArray(source), xContentType); + public IndexRequest source(String source, MediaType mediaType) { + return source(new BytesArray(source), mediaType); } /** @@ -432,7 +434,7 @@ public IndexRequest source(Object... source) { * valid String representation. *

    */ - public IndexRequest source(XContentType xContentType, Object... source) { + public IndexRequest source(MediaType mediaType, Object... source) { if (source.length % 2 != 0) { throw new IllegalArgumentException("The number of object passed must be even but was [" + source.length + "]"); } @@ -443,7 +445,7 @@ public IndexRequest source(XContentType xContentType, Object... source) { ); } try { - XContentBuilder builder = XContentFactory.contentBuilder(xContentType); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(mediaType); builder.startObject(); for (int i = 0; i < source.length; i++) { builder.field(source[i++].toString(), source[i]); @@ -458,29 +460,30 @@ public IndexRequest source(XContentType xContentType, Object... source) { /** * Sets the document to index in bytes form. */ - public IndexRequest source(BytesReference source, XContentType xContentType) { + public IndexRequest source(BytesReference source, MediaType mediaType) { this.source = Objects.requireNonNull(source); - this.contentType = Objects.requireNonNull(xContentType); + this.contentType = Objects.requireNonNull(mediaType); return this; } /** * Sets the document to index in bytes form. */ - public IndexRequest source(byte[] source, XContentType xContentType) { - return source(source, 0, source.length, xContentType); + public IndexRequest source(byte[] source, MediaType mediaType) { + return source(source, 0, source.length, mediaType); } /** * Sets the document to index in bytes form (assumed to be safe to be used from different * threads). * - * @param source The source to index - * @param offset The offset in the byte array - * @param length The length of the data + * @param source The source to index + * @param offset The offset in the byte array + * @param length The length of the data + * @param mediaType The data format over the wire */ - public IndexRequest source(byte[] source, int offset, int length, XContentType xContentType) { - return source(new BytesArray(source, offset, length), xContentType); + public IndexRequest source(byte[] source, int offset, int length, MediaType mediaType) { + return source(new BytesArray(source, offset, length), mediaType); } /** @@ -685,7 +688,11 @@ private void writeBody(StreamOutput out) throws IOException { out.writeLong(autoGeneratedTimestamp); if (contentType != null) { out.writeBoolean(true); - out.writeEnum(contentType); + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + contentType.writeTo(out); + } else { + out.writeEnum((XContentType) contentType); + } } else { out.writeBoolean(false); } @@ -698,7 +705,7 @@ private void writeBody(StreamOutput out) throws IOException { @Override public String toString() { - String sSource = "_na_"; + String sSource = Strings.UNKNOWN_UUID_VALUE; try { if (source.length() > MAX_SOURCE_LENGTH_IN_TOSTRING) { sSource = "n/a, actual length: [" diff --git a/server/src/main/java/org/opensearch/action/index/IndexRequestBuilder.java b/server/src/main/java/org/opensearch/action/index/IndexRequestBuilder.java index cef5ef0f85c62..4bf7634dcb7e1 100644 --- a/server/src/main/java/org/opensearch/action/index/IndexRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/index/IndexRequestBuilder.java @@ -37,15 +37,17 @@ import org.opensearch.action.support.replication.ReplicationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.VersionType; import java.util.Map; /** * An index document action request builder. + * + * @opensearch.internal */ public class IndexRequestBuilder extends ReplicationRequestBuilder implements @@ -80,8 +82,8 @@ public IndexRequestBuilder setRouting(String routing) { /** * Sets the source. */ - public IndexRequestBuilder setSource(BytesReference source, XContentType xContentType) { - request.source(source, xContentType); + public IndexRequestBuilder setSource(BytesReference source, MediaType mediaType) { + request.source(source, mediaType); return this; } @@ -100,7 +102,7 @@ public IndexRequestBuilder setSource(Map source) { * * @param source The map to index */ - public IndexRequestBuilder setSource(Map source, XContentType contentType) { + public IndexRequestBuilder setSource(Map source, MediaType contentType) { request.source(source, contentType); return this; } @@ -108,11 +110,11 @@ public IndexRequestBuilder setSource(Map source, XContentType content /** * Sets the document source to index. *

    - * Note, its preferable to either set it using {@link #setSource(org.opensearch.common.xcontent.XContentBuilder)} - * or using the {@link #setSource(byte[], XContentType)}. + * Note, its preferable to either set it using {@link #setSource(XContentBuilder)} + * or using the {@link #setSource(byte[], MediaType)}. */ - public IndexRequestBuilder setSource(String source, XContentType xContentType) { - request.source(source, xContentType); + public IndexRequestBuilder setSource(String source, MediaType mediaType) { + request.source(source, mediaType); return this; } @@ -127,8 +129,8 @@ public IndexRequestBuilder setSource(XContentBuilder sourceBuilder) { /** * Sets the document to index in bytes form. */ - public IndexRequestBuilder setSource(byte[] source, XContentType xContentType) { - request.source(source, xContentType); + public IndexRequestBuilder setSource(byte[] source, MediaType mediaType) { + request.source(source, mediaType); return this; } @@ -139,10 +141,10 @@ public IndexRequestBuilder setSource(byte[] source, XContentType xContentType) { * @param source The source to index * @param offset The offset in the byte array * @param length The length of the data - * @param xContentType The type/format of the source + * @param mediaType The type/format of the source */ - public IndexRequestBuilder setSource(byte[] source, int offset, int length, XContentType xContentType) { - request.source(source, offset, length, xContentType); + public IndexRequestBuilder setSource(byte[] source, int offset, int length, MediaType mediaType) { + request.source(source, offset, length, mediaType); return this; } @@ -167,8 +169,8 @@ public IndexRequestBuilder setSource(Object... source) { * valid String representation. *

    */ - public IndexRequestBuilder setSource(XContentType xContentType, Object... source) { - request.source(xContentType, source); + public IndexRequestBuilder setSource(MediaType mediaType, Object... source) { + request.source(mediaType, source); return this; } diff --git a/server/src/main/java/org/opensearch/action/index/IndexResponse.java b/server/src/main/java/org/opensearch/action/index/IndexResponse.java index be0826ce84f96..d0aa9b57b6528 100644 --- a/server/src/main/java/org/opensearch/action/index/IndexResponse.java +++ b/server/src/main/java/org/opensearch/action/index/IndexResponse.java @@ -33,21 +33,24 @@ package org.opensearch.action.index; import org.opensearch.action.DocWriteResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * A response of an index operation, * * @see IndexRequest * @see org.opensearch.client.Client#index(IndexRequest) + * + * @opensearch.internal */ public class IndexResponse extends DocWriteResponse { @@ -87,7 +90,7 @@ public String toString() { builder.append(",result=").append(getResult().getLowercase()); builder.append(",seqNo=").append(getSeqNo()); builder.append(",primaryTerm=").append(getPrimaryTerm()); - builder.append(",shards=").append(Strings.toString(getShardInfo())); + builder.append(",shards=").append(Strings.toString(MediaTypeRegistry.JSON, getShardInfo())); return builder.append("]").toString(); } @@ -112,6 +115,8 @@ public static void parseXContentFields(XContentParser parser, Builder context) t * Builder class for {@link IndexResponse}. This builder is usually used during xcontent parsing to * temporarily store the parsed values, then the {@link Builder#build()} method is called to * instantiate the {@link IndexResponse}. + * + * @opensearch.internal */ public static class Builder extends DocWriteResponse.Builder { @Override diff --git a/server/src/main/java/org/opensearch/action/index/TransportIndexAction.java b/server/src/main/java/org/opensearch/action/index/TransportIndexAction.java index dcee695620498..fe4f80bf0c065 100644 --- a/server/src/main/java/org/opensearch/action/index/TransportIndexAction.java +++ b/server/src/main/java/org/opensearch/action/index/TransportIndexAction.java @@ -49,6 +49,8 @@ * * * Deprecated use TransportBulkAction with a single item instead + * + * @opensearch.internal */ @Deprecated public class TransportIndexAction extends TransportSingleItemBulkWriteAction { diff --git a/server/src/main/java/org/opensearch/action/ingest/DeletePipelineAction.java b/server/src/main/java/org/opensearch/action/ingest/DeletePipelineAction.java index 5ea978db3d5d4..6017be9747912 100644 --- a/server/src/main/java/org/opensearch/action/ingest/DeletePipelineAction.java +++ b/server/src/main/java/org/opensearch/action/ingest/DeletePipelineAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action to delete a pipeline + * + * @opensearch.internal + */ public class DeletePipelineAction extends ActionType { public static final DeletePipelineAction INSTANCE = new DeletePipelineAction(); diff --git a/server/src/main/java/org/opensearch/action/ingest/DeletePipelineRequest.java b/server/src/main/java/org/opensearch/action/ingest/DeletePipelineRequest.java index 98bc125ce40dc..2f05ce3a25320 100644 --- a/server/src/main/java/org/opensearch/action/ingest/DeletePipelineRequest.java +++ b/server/src/main/java/org/opensearch/action/ingest/DeletePipelineRequest.java @@ -34,12 +34,17 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.Objects; +/** + * transport request to delete a pipeline + * + * @opensearch.internal + */ public class DeletePipelineRequest extends AcknowledgedRequest { private String id; diff --git a/server/src/main/java/org/opensearch/action/ingest/DeletePipelineRequestBuilder.java b/server/src/main/java/org/opensearch/action/ingest/DeletePipelineRequestBuilder.java index 53b5080f414e0..6a2eb494e8d3f 100644 --- a/server/src/main/java/org/opensearch/action/ingest/DeletePipelineRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/ingest/DeletePipelineRequestBuilder.java @@ -36,6 +36,11 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder to delete a pipeline + * + * @opensearch.internal + */ public class DeletePipelineRequestBuilder extends ActionRequestBuilder { public DeletePipelineRequestBuilder(OpenSearchClient client, DeletePipelineAction action) { diff --git a/server/src/main/java/org/opensearch/action/ingest/DeletePipelineTransportAction.java b/server/src/main/java/org/opensearch/action/ingest/DeletePipelineTransportAction.java index de2cf1ca74254..fe68f06d0d32e 100644 --- a/server/src/main/java/org/opensearch/action/ingest/DeletePipelineTransportAction.java +++ b/server/src/main/java/org/opensearch/action/ingest/DeletePipelineTransportAction.java @@ -32,23 +32,28 @@ package org.opensearch.action.ingest; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.ingest.IngestService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class DeletePipelineTransportAction extends TransportMasterNodeAction { +/** + * Perform the action of deleting a pipeline + * + * @opensearch.internal + */ +public class DeletePipelineTransportAction extends TransportClusterManagerNodeAction { private final IngestService ingestService; @@ -83,7 +88,7 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(DeletePipelineRequest request, ClusterState state, ActionListener listener) + protected void clusterManagerOperation(DeletePipelineRequest request, ClusterState state, ActionListener listener) throws Exception { ingestService.delete(request, listener); } diff --git a/server/src/main/java/org/opensearch/action/ingest/GetPipelineAction.java b/server/src/main/java/org/opensearch/action/ingest/GetPipelineAction.java index 33b9d8f09d053..8371d82b8911e 100644 --- a/server/src/main/java/org/opensearch/action/ingest/GetPipelineAction.java +++ b/server/src/main/java/org/opensearch/action/ingest/GetPipelineAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to get a pipeline + * + * @opensearch.internal + */ public class GetPipelineAction extends ActionType { public static final GetPipelineAction INSTANCE = new GetPipelineAction(); diff --git a/server/src/main/java/org/opensearch/action/ingest/GetPipelineRequest.java b/server/src/main/java/org/opensearch/action/ingest/GetPipelineRequest.java index 322fd1b8e1a52..4bae98098777d 100644 --- a/server/src/main/java/org/opensearch/action/ingest/GetPipelineRequest.java +++ b/server/src/main/java/org/opensearch/action/ingest/GetPipelineRequest.java @@ -33,14 +33,19 @@ package org.opensearch.action.ingest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; -public class GetPipelineRequest extends MasterNodeReadRequest { +/** + * transport request to get a pipeline + * + * @opensearch.internal + */ +public class GetPipelineRequest extends ClusterManagerNodeReadRequest { private String[] ids; diff --git a/server/src/main/java/org/opensearch/action/ingest/GetPipelineRequestBuilder.java b/server/src/main/java/org/opensearch/action/ingest/GetPipelineRequestBuilder.java index 45d9abc6b0adc..bdc13523ffdc6 100644 --- a/server/src/main/java/org/opensearch/action/ingest/GetPipelineRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/ingest/GetPipelineRequestBuilder.java @@ -32,10 +32,15 @@ package org.opensearch.action.ingest; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -public class GetPipelineRequestBuilder extends MasterNodeReadOperationRequestBuilder< +/** + * Transport request builder to get a pipeline + * + * @opensearch.internal + */ +public class GetPipelineRequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder< GetPipelineRequest, GetPipelineResponse, GetPipelineRequestBuilder> { diff --git a/server/src/main/java/org/opensearch/action/ingest/GetPipelineResponse.java b/server/src/main/java/org/opensearch/action/ingest/GetPipelineResponse.java index bf7dd909e390b..bd1b18be9a828 100644 --- a/server/src/main/java/org/opensearch/action/ingest/GetPipelineResponse.java +++ b/server/src/main/java/org/opensearch/action/ingest/GetPipelineResponse.java @@ -32,17 +32,18 @@ package org.opensearch.action.ingest; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.ingest.PipelineConfiguration; -import org.opensearch.rest.RestStatus; import java.io.IOException; import java.util.ArrayList; @@ -51,8 +52,13 @@ import java.util.List; import java.util.Map; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; +/** + * transport response for getting a pipeline + * + * @opensearch.internal + */ public class GetPipelineResponse extends ActionResponse implements StatusToXContentObject { private List pipelines; @@ -140,7 +146,12 @@ public boolean equals(Object other) { GetPipelineResponse otherResponse = (GetPipelineResponse) other; if (pipelines == null) { return otherResponse.pipelines == null; + } else if (otherResponse.pipelines == null) { + return false; } else { + if (otherResponse.pipelines.size() != pipelines.size()) { + return false; + } // We need a map here because order does not matter for equality Map otherPipelineMap = new HashMap<>(); for (PipelineConfiguration pipeline : otherResponse.pipelines) { @@ -161,7 +172,7 @@ public boolean equals(Object other) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/server/src/main/java/org/opensearch/action/ingest/GetPipelineTransportAction.java b/server/src/main/java/org/opensearch/action/ingest/GetPipelineTransportAction.java index a62201d097d12..80333c7346f92 100644 --- a/server/src/main/java/org/opensearch/action/ingest/GetPipelineTransportAction.java +++ b/server/src/main/java/org/opensearch/action/ingest/GetPipelineTransportAction.java @@ -32,23 +32,28 @@ package org.opensearch.action.ingest; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.block.ClusterBlockException; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.ingest.IngestService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; import java.io.IOException; -public class GetPipelineTransportAction extends TransportMasterNodeReadAction { +/** + * Perform the action of getting a pipeline + * + * @opensearch.internal + */ +public class GetPipelineTransportAction extends TransportClusterManagerNodeReadAction { @Inject public GetPipelineTransportAction( @@ -80,7 +85,7 @@ protected GetPipelineResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(GetPipelineRequest request, ClusterState state, ActionListener listener) + protected void clusterManagerOperation(GetPipelineRequest request, ClusterState state, ActionListener listener) throws Exception { listener.onResponse(new GetPipelineResponse(IngestService.getPipelines(state, request.getIds()))); } diff --git a/server/src/main/java/org/opensearch/action/ingest/IngestActionForwarder.java b/server/src/main/java/org/opensearch/action/ingest/IngestActionForwarder.java index 3ab3e3367fc6d..9927affbc7442 100644 --- a/server/src/main/java/org/opensearch/action/ingest/IngestActionForwarder.java +++ b/server/src/main/java/org/opensearch/action/ingest/IngestActionForwarder.java @@ -32,14 +32,14 @@ package org.opensearch.action.ingest; -import org.opensearch.action.ActionType; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionListenerResponseHandler; import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionType; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterStateApplier; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.Randomness; +import org.opensearch.core.action.ActionListener; import org.opensearch.transport.TransportService; import java.util.concurrent.atomic.AtomicInteger; @@ -48,6 +48,8 @@ * A utility for forwarding ingest requests to ingest nodes in a round-robin fashion. * * TODO: move this into IngestService and make index/bulk actions call that + * + * @opensearch.internal */ public final class IngestActionForwarder implements ClusterStateApplier { @@ -80,6 +82,6 @@ private DiscoveryNode randomIngestNode() { @Override public void applyClusterState(ClusterChangedEvent event) { - ingestNodes = event.state().getNodes().getIngestNodes().values().toArray(DiscoveryNode.class); + ingestNodes = event.state().getNodes().getIngestNodes().values().toArray(new DiscoveryNode[0]); } } diff --git a/server/src/main/java/org/opensearch/action/ingest/PutPipelineAction.java b/server/src/main/java/org/opensearch/action/ingest/PutPipelineAction.java index d61a1ae1041cb..1fcbd783d246b 100644 --- a/server/src/main/java/org/opensearch/action/ingest/PutPipelineAction.java +++ b/server/src/main/java/org/opensearch/action/ingest/PutPipelineAction.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionType; import org.opensearch.action.support.master.AcknowledgedResponse; +/** + * Transport action to put a new pipeline + * + * @opensearch.internal + */ public class PutPipelineAction extends ActionType { public static final PutPipelineAction INSTANCE = new PutPipelineAction(); diff --git a/server/src/main/java/org/opensearch/action/ingest/PutPipelineRequest.java b/server/src/main/java/org/opensearch/action/ingest/PutPipelineRequest.java index 526da7c4a74dc..f764e4b23860a 100644 --- a/server/src/main/java/org/opensearch/action/ingest/PutPipelineRequest.java +++ b/server/src/main/java/org/opensearch/action/ingest/PutPipelineRequest.java @@ -32,38 +32,49 @@ package org.opensearch.action.ingest; +import org.opensearch.Version; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.master.AcknowledgedRequest; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Objects; +/** + * transport request to put a pipeline + * + * @opensearch.internal + */ public class PutPipelineRequest extends AcknowledgedRequest implements ToXContentObject { private String id; private BytesReference source; - private XContentType xContentType; + private MediaType mediaType; /** * Create a new pipeline request with the id and source along with the content type of the source */ - public PutPipelineRequest(String id, BytesReference source, XContentType xContentType) { + public PutPipelineRequest(String id, BytesReference source, MediaType mediaType) { this.id = Objects.requireNonNull(id); this.source = Objects.requireNonNull(source); - this.xContentType = Objects.requireNonNull(xContentType); + this.mediaType = Objects.requireNonNull(mediaType); } public PutPipelineRequest(StreamInput in) throws IOException { super(in); id = in.readString(); source = in.readBytesReference(); - xContentType = in.readEnum(XContentType.class); + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType = in.readMediaType(); + } else { + mediaType = in.readEnum(XContentType.class); + } } PutPipelineRequest() {} @@ -81,8 +92,8 @@ public BytesReference getSource() { return source; } - public XContentType getXContentType() { - return xContentType; + public MediaType getMediaType() { + return mediaType; } @Override @@ -90,13 +101,17 @@ public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeString(id); out.writeBytesReference(source); - out.writeEnum(xContentType); + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType.writeTo(out); + } else { + out.writeEnum((XContentType) mediaType); + } } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { if (source != null) { - builder.rawValue(source.streamInput(), xContentType); + builder.rawValue(source.streamInput(), mediaType); } else { builder.startObject().endObject(); } diff --git a/server/src/main/java/org/opensearch/action/ingest/PutPipelineRequestBuilder.java b/server/src/main/java/org/opensearch/action/ingest/PutPipelineRequestBuilder.java index 8279e2f3756e9..e734abb6d7969 100644 --- a/server/src/main/java/org/opensearch/action/ingest/PutPipelineRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/ingest/PutPipelineRequestBuilder.java @@ -35,9 +35,14 @@ import org.opensearch.action.ActionRequestBuilder; import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +/** + * Transport request builder to put a pipeline + * + * @opensearch.internal + */ public class PutPipelineRequestBuilder extends ActionRequestBuilder { public PutPipelineRequestBuilder(OpenSearchClient client, PutPipelineAction action) { @@ -49,8 +54,8 @@ public PutPipelineRequestBuilder( PutPipelineAction action, String id, BytesReference source, - XContentType xContentType + MediaType mediaType ) { - super(client, action, new PutPipelineRequest(id, source, xContentType)); + super(client, action, new PutPipelineRequest(id, source, mediaType)); } } diff --git a/server/src/main/java/org/opensearch/action/ingest/PutPipelineTransportAction.java b/server/src/main/java/org/opensearch/action/ingest/PutPipelineTransportAction.java index 89d1c1efed8a6..e2d206e8c4f6d 100644 --- a/server/src/main/java/org/opensearch/action/ingest/PutPipelineTransportAction.java +++ b/server/src/main/java/org/opensearch/action/ingest/PutPipelineTransportAction.java @@ -32,12 +32,11 @@ package org.opensearch.action.ingest; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.info.NodeInfo; import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.TransportMasterNodeAction; import org.opensearch.client.OriginSettingClient; import org.opensearch.client.node.NodeClient; import org.opensearch.cluster.ClusterState; @@ -46,7 +45,8 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.ingest.IngestInfo; import org.opensearch.ingest.IngestService; import org.opensearch.threadpool.ThreadPool; @@ -58,7 +58,12 @@ import static org.opensearch.ingest.IngestService.INGEST_ORIGIN; -public class PutPipelineTransportAction extends TransportMasterNodeAction { +/** + * Perform the action of putting a pipeline + * + * @opensearch.internal + */ +public class PutPipelineTransportAction extends TransportClusterManagerNodeAction { private final IngestService ingestService; private final OriginSettingClient client; @@ -98,7 +103,7 @@ protected AcknowledgedResponse read(StreamInput in) throws IOException { } @Override - protected void masterOperation(PutPipelineRequest request, ClusterState state, ActionListener listener) + protected void clusterManagerOperation(PutPipelineRequest request, ClusterState state, ActionListener listener) throws Exception { NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(); nodesInfoRequest.clear().addMetric(NodesInfoRequest.Metric.INGEST.metricName()); diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentBaseResult.java b/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentBaseResult.java index 10ccb2003969d..cb0c5f551d627 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentBaseResult.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentBaseResult.java @@ -33,20 +33,22 @@ import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.ingest.IngestDocument; import java.io.IOException; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Holds the end result of what a pipeline did to sample document provided via the simulate api. + * + * */ public final class SimulateDocumentBaseResult implements SimulateDocumentResult { private final WriteableIngestDocument ingestDocument; diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentResult.java b/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentResult.java index 3beaf43d2a1ae..98a03272aff42 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentResult.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentResult.java @@ -31,9 +31,14 @@ package org.opensearch.action.ingest; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentObject; +/** + * Interface to simulate a document result + * + * @opensearch.internal + */ public interface SimulateDocumentResult extends Writeable, ToXContentObject { } diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentVerboseResult.java b/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentVerboseResult.java index 07d9b628fe35d..f26bb106de273 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentVerboseResult.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulateDocumentVerboseResult.java @@ -31,22 +31,24 @@ package org.opensearch.action.ingest; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * Holds the result of what a pipeline did to a sample document via the simulate api, but instead of {@link SimulateDocumentBaseResult} * this result class holds the intermediate result each processor did to the sample document. + * + * @opensearch.internal */ public final class SimulateDocumentVerboseResult implements SimulateDocumentResult { public static final String PROCESSOR_RESULT_FIELD = "processor_results"; diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulateExecutionService.java b/server/src/main/java/org/opensearch/action/ingest/SimulateExecutionService.java index 8c620c707b3ac..c7c0f21eb0876 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulateExecutionService.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulateExecutionService.java @@ -32,8 +32,8 @@ package org.opensearch.action.ingest; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; +import org.opensearch.core.action.ActionListener; import org.opensearch.ingest.CompoundProcessor; import org.opensearch.ingest.IngestDocument; import org.opensearch.ingest.Pipeline; @@ -46,6 +46,11 @@ import static org.opensearch.ingest.TrackingResultProcessor.decorate; +/** + * Service to simulate pipeline execution + * + * @opensearch.internal + */ class SimulateExecutionService { private static final String THREAD_POOL_NAME = ThreadPool.Names.MANAGEMENT; @@ -71,10 +76,9 @@ void executeDocument( pipeline.getVersion(), verbosePipelineProcessor ); - ingestDocument.executePipeline( - verbosePipeline, - (result, e) -> { handler.accept(new SimulateDocumentVerboseResult(processorResultList), e); } - ); + ingestDocument.executePipeline(verbosePipeline, (result, e) -> { + handler.accept(new SimulateDocumentVerboseResult(processorResultList), e); + }); } else { ingestDocument.executePipeline(pipeline, (result, e) -> { if (e == null) { diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineAction.java b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineAction.java index 8039a83b5953e..df2cfda778d06 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineAction.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action to simulate a pipeline + * + * @opensearch.internal + */ public class SimulatePipelineAction extends ActionType { public static final SimulatePipelineAction INSTANCE = new SimulatePipelineAction(); diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineRequest.java b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineRequest.java index bc0317e076319..2234934499609 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineRequest.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineRequest.java @@ -32,15 +32,17 @@ package org.opensearch.action.ingest; +import org.opensearch.Version; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.logging.DeprecationLogger; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.VersionType; import org.opensearch.ingest.ConfigurationUtils; import org.opensearch.ingest.IngestDocument; @@ -55,20 +57,25 @@ import java.util.Map; import java.util.Objects; +/** + * transport request to simulate a pipeline + * + * @opensearch.internal + */ public class SimulatePipelineRequest extends ActionRequest implements ToXContentObject { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(SimulatePipelineRequest.class); private String id; private boolean verbose; private BytesReference source; - private XContentType xContentType; + private MediaType mediaType; /** * Creates a new request with the given source and its content type */ - public SimulatePipelineRequest(BytesReference source, XContentType xContentType) { + public SimulatePipelineRequest(BytesReference source, MediaType mediaType) { this.source = Objects.requireNonNull(source); - this.xContentType = Objects.requireNonNull(xContentType); + this.mediaType = Objects.requireNonNull(mediaType); } SimulatePipelineRequest() {} @@ -78,7 +85,11 @@ public SimulatePipelineRequest(BytesReference source, XContentType xContentType) id = in.readOptionalString(); verbose = in.readBoolean(); source = in.readBytesReference(); - xContentType = in.readEnum(XContentType.class); + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType = in.readMediaType(); + } else { + mediaType = in.readEnum(XContentType.class); + } } @Override @@ -106,8 +117,8 @@ public BytesReference getSource() { return source; } - public XContentType getXContentType() { - return xContentType; + public MediaType getXContentType() { + return mediaType; } @Override @@ -116,15 +127,24 @@ public void writeTo(StreamOutput out) throws IOException { out.writeOptionalString(id); out.writeBoolean(verbose); out.writeBytesReference(source); - out.writeEnum(xContentType); + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType.writeTo(out); + } else { + out.writeEnum((XContentType) mediaType); + } } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.rawValue(source.streamInput(), xContentType); + builder.rawValue(source.streamInput(), mediaType); return builder; } + /** + * Fields for parsing and toXContent + * + * @opensearch.internal + */ public static final class Fields { static final String PIPELINE = "pipeline"; static final String DOCS = "docs"; diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineRequestBuilder.java b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineRequestBuilder.java index 6befbea774685..55e6d95fde65c 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineRequestBuilder.java @@ -34,9 +34,14 @@ import org.opensearch.action.ActionRequestBuilder; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaType; +/** + * Transport request builder to simulate a pipeline + * + * @opensearch.internal + */ public class SimulatePipelineRequestBuilder extends ActionRequestBuilder { /** @@ -53,9 +58,9 @@ public SimulatePipelineRequestBuilder( OpenSearchClient client, SimulatePipelineAction action, BytesReference source, - XContentType xContentType + MediaType mediaType ) { - super(client, action, new SimulatePipelineRequest(source, xContentType)); + super(client, action, new SimulatePipelineRequest(source, mediaType)); } /** diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineResponse.java b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineResponse.java index c3540e4e9b8fd..3cbbc4350c3bd 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineResponse.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineResponse.java @@ -33,24 +33,29 @@ package org.opensearch.action.ingest; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; +/** + * transport response for simulating a pipeline + * + * @opensearch.internal + */ public class SimulatePipelineResponse extends ActionResponse implements ToXContentObject { private String pipelineId; private boolean verbose; @@ -173,6 +178,11 @@ public static SimulatePipelineResponse fromXContent(XContentParser parser) { return PARSER.apply(parser, null); } + /** + * Fields for parsing and toXContent + * + * @opensearch.internal + */ static final class Fields { static final String DOCUMENTS = "docs"; } diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineTransportAction.java b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineTransportAction.java index 7348035deaafe..4753679d370af 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineTransportAction.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulatePipelineTransportAction.java @@ -32,12 +32,12 @@ package org.opensearch.action.ingest; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.ingest.IngestService; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; @@ -45,6 +45,11 @@ import java.util.Map; +/** + * Perform the action of simulating a pipeline + * + * @opensearch.internal + */ public class SimulatePipelineTransportAction extends HandledTransportAction { private final IngestService ingestService; diff --git a/server/src/main/java/org/opensearch/action/ingest/SimulateProcessorResult.java b/server/src/main/java/org/opensearch/action/ingest/SimulateProcessorResult.java index eae36cc4efd7c..d933877fa214d 100644 --- a/server/src/main/java/org/opensearch/action/ingest/SimulateProcessorResult.java +++ b/server/src/main/java/org/opensearch/action/ingest/SimulateProcessorResult.java @@ -33,24 +33,29 @@ import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.common.ParseField; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.ingest.ConfigurationUtils; import org.opensearch.ingest.IngestDocument; import java.io.IOException; import java.util.Locale; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +/** + * Simulates an ingest processor result + * + * @opensearch.internal + */ public class SimulateProcessorResult implements Writeable, ToXContentObject { private static final String IGNORED_ERROR_FIELD = "ignored_error"; diff --git a/server/src/main/java/org/opensearch/action/ingest/WriteableIngestDocument.java b/server/src/main/java/org/opensearch/action/ingest/WriteableIngestDocument.java index 2f8c65486c22f..f8cc4736e87b0 100644 --- a/server/src/main/java/org/opensearch/action/ingest/WriteableIngestDocument.java +++ b/server/src/main/java/org/opensearch/action/ingest/WriteableIngestDocument.java @@ -32,14 +32,14 @@ package org.opensearch.action.ingest; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.ingest.IngestDocument; import org.opensearch.ingest.IngestDocument.Metadata; @@ -49,9 +49,14 @@ import java.util.Map; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +/** + * An ingest document that implements writeable + * + * @opensearch.internal + */ final class WriteableIngestDocument implements Writeable, ToXContentFragment { static final String SOURCE_FIELD = "_source"; diff --git a/server/src/main/java/org/opensearch/action/ingest/package-info.java b/server/src/main/java/org/opensearch/action/ingest/package-info.java new file mode 100644 index 0000000000000..42414f2090b41 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/ingest/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Ingest API transport handlers. */ +package org.opensearch.action.ingest; diff --git a/server/src/main/java/org/opensearch/action/main/MainAction.java b/server/src/main/java/org/opensearch/action/main/MainAction.java index 46de6f9e2b0f0..c5cbac824ec83 100644 --- a/server/src/main/java/org/opensearch/action/main/MainAction.java +++ b/server/src/main/java/org/opensearch/action/main/MainAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * The main OpenSearch Action + * + * @opensearch.internal + */ public class MainAction extends ActionType { public static final String NAME = "cluster:monitor/main"; diff --git a/server/src/main/java/org/opensearch/action/main/MainRequest.java b/server/src/main/java/org/opensearch/action/main/MainRequest.java index f5cafecb87375..459633fa63cb3 100644 --- a/server/src/main/java/org/opensearch/action/main/MainRequest.java +++ b/server/src/main/java/org/opensearch/action/main/MainRequest.java @@ -34,10 +34,15 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Transport request for main action + * + * @opensearch.internal + */ public class MainRequest extends ActionRequest { public MainRequest() {} diff --git a/server/src/main/java/org/opensearch/action/main/MainRequestBuilder.java b/server/src/main/java/org/opensearch/action/main/MainRequestBuilder.java index 8f73f316194ba..bd62882551c44 100644 --- a/server/src/main/java/org/opensearch/action/main/MainRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/main/MainRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * Transport request builder for the main opensearch action + * + * @opensearch.internal + */ public class MainRequestBuilder extends ActionRequestBuilder { public MainRequestBuilder(OpenSearchClient client, MainAction action) { diff --git a/server/src/main/java/org/opensearch/action/main/MainResponse.java b/server/src/main/java/org/opensearch/action/main/MainResponse.java index bd4be885fa210..254384a04f64f 100644 --- a/server/src/main/java/org/opensearch/action/main/MainResponse.java +++ b/server/src/main/java/org/opensearch/action/main/MainResponse.java @@ -35,19 +35,24 @@ import org.opensearch.Build; import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.action.ActionResponse; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; +/** + * The main response of opensearch + * + * @opensearch.internal + */ public class MainResponse extends ActionResponse implements ToXContentObject { private String nodeName; @@ -63,10 +68,10 @@ public class MainResponse extends ActionResponse implements ToXContentObject { MainResponse(StreamInput in) throws IOException { super(in); nodeName = in.readString(); - version = Version.readVersion(in); + version = in.readVersion(); clusterName = new ClusterName(in); clusterUuid = in.readString(); - build = Build.readBuild(in); + build = in.readBuild(); if (in.getVersion().before(LegacyESVersion.V_7_0_0)) { in.readBoolean(); } @@ -119,13 +124,13 @@ public String getVersionNumber() { public void writeTo(StreamOutput out) throws IOException { out.writeString(nodeName); if (out.getVersion().before(Version.V_1_0_0)) { - Version.writeVersion(LegacyESVersion.V_7_10_2, out); + out.writeVersion(LegacyESVersion.V_7_10_2); } else { - Version.writeVersion(version, out); + out.writeVersion(version); } clusterName.writeTo(out); out.writeString(clusterUuid); - Build.writeBuild(build, out); + out.writeBuild(build); if (out.getVersion().before(LegacyESVersion.V_7_0_0)) { out.writeBoolean(true); } diff --git a/server/src/main/java/org/opensearch/action/main/TransportMainAction.java b/server/src/main/java/org/opensearch/action/main/TransportMainAction.java index ef6ebb27c4505..7c92b9f41d5ab 100644 --- a/server/src/main/java/org/opensearch/action/main/TransportMainAction.java +++ b/server/src/main/java/org/opensearch/action/main/TransportMainAction.java @@ -35,7 +35,6 @@ import org.opensearch.Build; import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.cluster.ClusterState; @@ -44,10 +43,16 @@ import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; import org.opensearch.node.Node; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; +/** + * Performs the main action + * + * @opensearch.internal + */ public class TransportMainAction extends HandledTransportAction { private static final DeprecationLogger DEPRECATION_LOGGER = DeprecationLogger.getLogger(TransportMainAction.class); diff --git a/server/src/main/java/org/opensearch/action/main/package-info.java b/server/src/main/java/org/opensearch/action/main/package-info.java new file mode 100644 index 0000000000000..dc08f0a7b6303 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/main/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Main Response transport handlers. */ +package org.opensearch.action.main; diff --git a/server/src/main/java/org/opensearch/action/resync/ResyncReplicationRequest.java b/server/src/main/java/org/opensearch/action/resync/ResyncReplicationRequest.java index 2ecbc49384fe5..6a4f2f0607144 100644 --- a/server/src/main/java/org/opensearch/action/resync/ResyncReplicationRequest.java +++ b/server/src/main/java/org/opensearch/action/resync/ResyncReplicationRequest.java @@ -32,9 +32,9 @@ package org.opensearch.action.resync; import org.opensearch.action.support.replication.ReplicatedWriteRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.translog.Translog; import java.io.IOException; @@ -43,6 +43,8 @@ /** * Represents a batch of operations sent from the primary to its replicas during the primary-replica resync. + * + * @opensearch.internal */ public final class ResyncReplicationRequest extends ReplicatedWriteRequest { diff --git a/server/src/main/java/org/opensearch/action/resync/ResyncReplicationResponse.java b/server/src/main/java/org/opensearch/action/resync/ResyncReplicationResponse.java index 1e052c5d80e80..d1c7be3673645 100644 --- a/server/src/main/java/org/opensearch/action/resync/ResyncReplicationResponse.java +++ b/server/src/main/java/org/opensearch/action/resync/ResyncReplicationResponse.java @@ -33,10 +33,15 @@ import org.opensearch.action.support.WriteResponse; import org.opensearch.action.support.replication.ReplicationResponse; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; +/** + * Transport response for resyncing replication + * + * @opensearch.internal + */ public final class ResyncReplicationResponse extends ReplicationResponse implements WriteResponse { public ResyncReplicationResponse() {} diff --git a/server/src/main/java/org/opensearch/action/resync/TransportResyncReplicationAction.java b/server/src/main/java/org/opensearch/action/resync/TransportResyncReplicationAction.java index ea596b12f262f..032fe83e2220b 100644 --- a/server/src/main/java/org/opensearch/action/resync/TransportResyncReplicationAction.java +++ b/server/src/main/java/org/opensearch/action/resync/TransportResyncReplicationAction.java @@ -32,7 +32,6 @@ package org.opensearch.action.resync; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.replication.ReplicationOperation; import org.opensearch.action.support.replication.ReplicationResponse; @@ -43,8 +42,9 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.index.IndexingPressureService; import org.opensearch.index.engine.Engine; import org.opensearch.index.seqno.SequenceNumbers; @@ -64,6 +64,11 @@ import java.util.function.Function; import java.util.stream.Stream; +/** + * Perform replication resync + * + * @opensearch.internal + */ public class TransportResyncReplicationAction extends TransportWriteAction< ResyncReplicationRequest, ResyncReplicationRequest, @@ -253,6 +258,8 @@ public void handleException(TransportException exp) { * A proxy for primary-replica resync operations which are performed on replicas when a new primary is promoted. * Replica shards fail to execute resync operations will be failed but won't be marked as stale. * This avoids marking shards as stale during cluster restart but enforces primary-replica resync mandatory. + * + * @opensearch.internal */ class ResyncActionReplicasProxy extends ReplicasProxy { diff --git a/server/src/main/java/org/opensearch/action/resync/package-info.java b/server/src/main/java/org/opensearch/action/resync/package-info.java new file mode 100644 index 0000000000000..61c46d85aecda --- /dev/null +++ b/server/src/main/java/org/opensearch/action/resync/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Resync action transport handlers. */ +package org.opensearch.action.resync; diff --git a/server/src/main/java/org/opensearch/action/search/AbstractSearchAsyncAction.java b/server/src/main/java/org/opensearch/action/search/AbstractSearchAsyncAction.java index 190904145b091..1c0a1280ad550 100644 --- a/server/src/main/java/org/opensearch/action/search/AbstractSearchAsyncAction.java +++ b/server/src/main/java/org/opensearch/action/search/AbstractSearchAsyncAction.java @@ -34,28 +34,30 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.lucene.util.SetOnce; import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.NoShardAvailableActionException; -import org.opensearch.action.ShardOperationFailedException; import org.opensearch.action.support.TransportActions; import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.routing.FailAwareWeightedRouting; import org.opensearch.cluster.routing.GroupShardsIterator; import org.opensearch.common.Nullable; +import org.opensearch.common.SetOnce; import org.opensearch.common.lease.Releasable; import org.opensearch.common.lease.Releasables; import org.opensearch.common.util.concurrent.AbstractRunnable; import org.opensearch.common.util.concurrent.AtomicArray; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ShardOperationFailedException; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.internal.AliasFilter; import org.opensearch.search.internal.InternalSearchResponse; import org.opensearch.search.internal.SearchContext; import org.opensearch.search.internal.ShardSearchRequest; +import org.opensearch.search.pipeline.PipelinedRequest; import org.opensearch.transport.Transport; import java.util.ArrayDeque; @@ -63,6 +65,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -78,6 +81,8 @@ * referred to as the {@code shardIndex}. * The fan out and collect algorithm is traditionally used as the initial phase which can either be a query execution or collection of * distributed frequencies + * + * @opensearch.internal */ abstract class AbstractSearchAsyncAction extends SearchPhase implements SearchPhaseContext { private static final float DEFAULT_INDEX_BOOST = 1.0f; @@ -103,7 +108,6 @@ abstract class AbstractSearchAsyncAction exten private final AtomicInteger skippedOps = new AtomicInteger(); private final TransportSearchAction.SearchTimeProvider timeProvider; private final SearchResponse.Clusters clusters; - protected final GroupShardsIterator toSkipShardsIts; protected final GroupShardsIterator shardsIts; private final int expectedTotalOps; @@ -112,8 +116,12 @@ abstract class AbstractSearchAsyncAction exten private final Map pendingExecutionsPerNode = new ConcurrentHashMap<>(); private final boolean throttleConcurrentRequests; + private SearchPhase currentPhase; + private final List releasables = new ArrayList<>(); + private Optional searchRequestOperationsListener; + AbstractSearchAsyncAction( String name, Logger logger, @@ -131,7 +139,8 @@ abstract class AbstractSearchAsyncAction exten SearchTask task, SearchPhaseResults resultConsumer, int maxConcurrentRequestsPerNode, - SearchResponse.Clusters clusters + SearchResponse.Clusters clusters, + SearchRequestOperationsListener searchRequestOperationsListener ) { super(name); final List toSkipIterators = new ArrayList<>(); @@ -167,6 +176,7 @@ abstract class AbstractSearchAsyncAction exten this.indexRoutings = indexRoutings; this.results = resultConsumer; this.clusters = clusters; + this.searchRequestOperationsListener = Optional.ofNullable(searchRequestOperationsListener); } @Override @@ -300,7 +310,16 @@ public void onFailure(Exception t) { * It is possible to run into connection exceptions here because we are getting the connection early and might * run into nodes that are not connected. In this case, on shard failure will move us to the next shard copy. */ - fork(() -> onShardFailure(shardIndex, shard, shardIt, e)); + fork(() -> { + // It only happens when onPhaseDone() is called and executePhaseOnShard() fails hard with an exception. + // In this case calling onShardFailure() would overflow the operations counter, so the best we could do + // here is to fail the phase and move on to the next one. + if (totalOps.get() == expectedTotalOps) { + onPhaseFailure(this, "The phase has failed", e); + } else { + onShardFailure(shardIndex, shard, shardIt, e); + } + }); } finally { executeNext(pendingExecutions, thread); } @@ -358,6 +377,7 @@ public final void executeNextPhase(SearchPhase currentPhase, SearchPhase nextPha : OpenSearchException.guessRootCauses(shardSearchFailures[0].getCause())[0]; logger.debug(() -> new ParameterizedMessage("All shards failed for phase: [{}]", getName()), cause); onPhaseFailure(currentPhase, "all shards failed", cause); + } else { Boolean allowPartialResults = request.allowPartialSearchResults(); assert allowPartialResults != null : "SearchRequest missing setting for allowPartialSearchResults"; @@ -406,13 +426,24 @@ public final void executeNextPhase(SearchPhase currentPhase, SearchPhase nextPha clusterState.version() ); } + onPhaseEnd(); executePhase(nextPhase); } } + private void onPhaseEnd() { + this.searchRequestOperationsListener.ifPresent(searchRequestOperations -> { searchRequestOperations.onPhaseEnd(this); }); + } + + private void onPhaseStart(SearchPhase phase) { + setCurrentPhase(phase); + this.searchRequestOperationsListener.ifPresent(searchRequestOperations -> { searchRequestOperations.onPhaseStart(this); }); + } + private void executePhase(SearchPhase phase) { try { - phase.run(); + onPhaseStart(phase); + phase.recordAndRun(); } catch (Exception e) { if (logger.isDebugEnabled()) { logger.debug(new ParameterizedMessage("Failed to execute [{}] while moving to [{}] phase", request, phase.getName()), e); @@ -438,23 +469,41 @@ private void onShardFailure(final int shardIndex, @Nullable SearchShardTarget sh // we always add the shard failure for a specific shard instance // we do make sure to clean it on a successful response from a shard onShardFailure(shardIndex, shard, e); - final SearchShardTarget nextShard = shardIt.nextOrNull(); + SearchShardTarget nextShard = FailAwareWeightedRouting.getInstance() + .findNext(shardIt, clusterState, e, () -> totalOps.incrementAndGet()); + final boolean lastShard = nextShard == null; - logger.debug( - () -> new ParameterizedMessage( - "{}: Failed to execute [{}] lastShard [{}]", - shard != null ? shard : shardIt.shardId(), - request, - lastShard - ), - e - ); + if (logger.isTraceEnabled()) { + logger.trace( + () -> new ParameterizedMessage( + "{}: Failed to execute [{}] lastShard [{}]", + shard != null ? shard : shardIt.shardId(), + request, + lastShard + ), + e + ); + } else { + // Log the message without an exception. + logger.debug( + new ParameterizedMessage( + "{}: Failed to execute [{}] lastShard [{}]", + shard != null ? shard : shardIt.shardId(), + request, + lastShard + ) + ); + } if (lastShard) { onShardGroupFailure(shardIndex, shard, e); } final int totalOps = this.totalOps.incrementAndGet(); if (totalOps == expectedTotalOps) { - onPhaseDone(); + try { + onPhaseDone(); + } catch (final Exception ex) { + onPhaseFailure(this, "The phase has failed", ex); + } } else if (totalOps > expectedTotalOps) { throw new AssertionError( "unexpected higher total ops [" + totalOps + "] compared to expected [" + expectedTotalOps + "]", @@ -559,7 +608,11 @@ private void successfulShardExecution(SearchShardIterator shardsIt) { } final int xTotalOps = totalOps.addAndGet(remainingOpsOnIterator); if (xTotalOps == expectedTotalOps) { - onPhaseDone(); + try { + onPhaseDone(); + } catch (final Exception ex) { + onPhaseFailure(this, "The phase has failed", ex); + } } else if (xTotalOps > expectedTotalOps) { throw new AssertionError( "unexpected higher total ops [" + xTotalOps + "] compared to expected [" + expectedTotalOps + "]", @@ -568,6 +621,14 @@ private void successfulShardExecution(SearchShardIterator shardsIt) { } } + public SearchPhase getCurrentPhase() { + return currentPhase; + } + + private void setCurrentPhase(SearchPhase phase) { + currentPhase = phase; + } + @Override public final int getNumShards() { return results.getNumShards(); @@ -635,10 +696,13 @@ public void sendSearchResponse(InternalSearchResponse internalSearchResponse, At } listener.onResponse(buildSearchResponse(internalSearchResponse, failures, scrollId, searchContextId)); } + onPhaseEnd(); + setCurrentPhase(null); } @Override public final void onPhaseFailure(SearchPhase phase, String msg, Throwable cause) { + this.searchRequestOperationsListener.ifPresent(searchRequestOperations -> searchRequestOperations.onPhaseFailure(this)); raisePhaseFailure(new SearchPhaseExecutionException(phase.getName(), msg, cause, buildShardFailures())); } @@ -674,7 +738,11 @@ private void raisePhaseFailure(SearchPhaseExecutionException exception) { * @see #onShardResult(SearchPhaseResult, SearchShardIterator) */ final void onPhaseDone() { // as a tribute to @kimchy aka. finishHim() - executeNextPhase(this, getNextPhase(results, this)); + final SearchPhase nextPhase = getNextPhase(results, this); + if (request instanceof PipelinedRequest && nextPhase != null) { + ((PipelinedRequest) request).transformSearchPhaseResults(results, this, this.getName(), nextPhase.getName()); + } + executeNextPhase(this, nextPhase); } @Override @@ -749,6 +817,11 @@ void executeNext(Runnable runnable, Thread originalThread) { } } + /** + * Pending Executions + * + * @opensearch.internal + */ private static final class PendingExecutions { private final int permits; private int permitsTaken = 0; diff --git a/server/src/main/java/org/opensearch/action/search/ArraySearchPhaseResults.java b/server/src/main/java/org/opensearch/action/search/ArraySearchPhaseResults.java index 58cffe2ed5b8c..653b0e8aedb9d 100644 --- a/server/src/main/java/org/opensearch/action/search/ArraySearchPhaseResults.java +++ b/server/src/main/java/org/opensearch/action/search/ArraySearchPhaseResults.java @@ -39,6 +39,8 @@ /** * This class acts as a basic result collection that can be extended to do on-the-fly reduction or result processing + * + * @opensearch.internal */ class ArraySearchPhaseResults extends SearchPhaseResults { final AtomicArray results; @@ -64,7 +66,7 @@ boolean hasResult(int shardIndex) { } @Override - AtomicArray getAtomicArray() { + public AtomicArray getAtomicArray() { return results; } } diff --git a/server/src/main/java/org/opensearch/action/search/BottomSortValuesCollector.java b/server/src/main/java/org/opensearch/action/search/BottomSortValuesCollector.java index 64f50d2f37cdb..c831c80b6455c 100644 --- a/server/src/main/java/org/opensearch/action/search/BottomSortValuesCollector.java +++ b/server/src/main/java/org/opensearch/action/search/BottomSortValuesCollector.java @@ -41,6 +41,8 @@ /** * Utility class to keep track of the bottom doc's sort values in a distributed search. + * + * @opensearch.internal */ class BottomSortValuesCollector { private final int topNSize; diff --git a/server/src/main/java/org/opensearch/action/search/CanMatchPreFilterSearchPhase.java b/server/src/main/java/org/opensearch/action/search/CanMatchPreFilterSearchPhase.java index 2e67e6aa93a72..ae481736ad0aa 100644 --- a/server/src/main/java/org/opensearch/action/search/CanMatchPreFilterSearchPhase.java +++ b/server/src/main/java/org/opensearch/action/search/CanMatchPreFilterSearchPhase.java @@ -33,10 +33,10 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.util.FixedBitSet; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.routing.GroupShardsIterator; import org.opensearch.common.lease.Releasable; +import org.opensearch.core.action.ActionListener; import org.opensearch.search.SearchService.CanMatchResponse; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.builder.SearchSourceBuilder; @@ -67,6 +67,8 @@ * When the query primary sort is perform on a field, this phase extracts the min/max value in each shard and * sort them according to the provided order. This can be useful for instance to ensure that shards that contain recent * data are executed first when sorting by descending timestamp. + * + * @opensearch.internal */ final class CanMatchPreFilterSearchPhase extends AbstractSearchAsyncAction { @@ -88,11 +90,12 @@ final class CanMatchPreFilterSearchPhase extends AbstractSearchAsyncAction, SearchPhase> phaseFactory, - SearchResponse.Clusters clusters + SearchResponse.Clusters clusters, + SearchRequestOperationsListener searchRequestOperationsListener ) { // We set max concurrent shard requests to the number of shards so no throttling happens for can_match requests super( - "can_match", + SearchPhaseName.CAN_MATCH.getName(), logger, searchTransportService, nodeIdToConnection, @@ -108,7 +111,8 @@ final class CanMatchPreFilterSearchPhase extends AbstractSearchAsyncAction shardComparator( return comparator.thenComparing(index -> shardsIts.get(index).shardId()); } + /** + * Inner class for determining if canMatch search phase results + * + * @opensearch.internal + */ private static final class CanMatchSearchPhaseResults extends SearchPhaseResults { private final FixedBitSet possibleMatches; private final MinAndMax[] minAndMaxes; diff --git a/server/src/main/java/org/opensearch/action/search/ClearScrollAction.java b/server/src/main/java/org/opensearch/action/search/ClearScrollAction.java index 91ae4b2109ae1..70072e5ec9156 100644 --- a/server/src/main/java/org/opensearch/action/search/ClearScrollAction.java +++ b/server/src/main/java/org/opensearch/action/search/ClearScrollAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport Action for clearing a scroll + * + * @opensearch.internal + */ public class ClearScrollAction extends ActionType { public static final ClearScrollAction INSTANCE = new ClearScrollAction(); diff --git a/server/src/main/java/org/opensearch/action/search/ClearScrollController.java b/server/src/main/java/org/opensearch/action/search/ClearScrollController.java index 390f5e2b99e63..2ce94bfd682fe 100644 --- a/server/src/main/java/org/opensearch/action/search/ClearScrollController.java +++ b/server/src/main/java/org/opensearch/action/search/ClearScrollController.java @@ -33,15 +33,15 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.StepListener; import org.opensearch.action.support.GroupedActionListener; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.Strings; import org.opensearch.common.util.concurrent.CountDown; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.transport.Transport; -import org.opensearch.transport.TransportResponse; import java.util.ArrayList; import java.util.Collection; @@ -53,6 +53,11 @@ import java.util.function.BiFunction; import java.util.stream.Collectors; +/** + * Main controller for clearing a scroll + * + * @opensearch.internal + */ public final class ClearScrollController implements Runnable { private final DiscoveryNodes nodes; private final SearchTransportService searchTransportService; diff --git a/server/src/main/java/org/opensearch/action/search/ClearScrollRequest.java b/server/src/main/java/org/opensearch/action/search/ClearScrollRequest.java index 3fb1ebcd6fcc2..f71ddcf1691f8 100644 --- a/server/src/main/java/org/opensearch/action/search/ClearScrollRequest.java +++ b/server/src/main/java/org/opensearch/action/search/ClearScrollRequest.java @@ -34,11 +34,11 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; @@ -47,6 +47,11 @@ import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport request for clearing a search scroll + * + * @opensearch.internal + */ public class ClearScrollRequest extends ActionRequest implements ToXContentObject { private List scrollIds; diff --git a/server/src/main/java/org/opensearch/action/search/ClearScrollRequestBuilder.java b/server/src/main/java/org/opensearch/action/search/ClearScrollRequestBuilder.java index ba76b641c8f56..63f64e02a9dd2 100644 --- a/server/src/main/java/org/opensearch/action/search/ClearScrollRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/search/ClearScrollRequestBuilder.java @@ -37,6 +37,11 @@ import java.util.List; +/** + * Transport request builder for clearing a search scroll + * + * @opensearch.internal + */ public class ClearScrollRequestBuilder extends ActionRequestBuilder { public ClearScrollRequestBuilder(OpenSearchClient client, ClearScrollAction action) { diff --git a/server/src/main/java/org/opensearch/action/search/ClearScrollResponse.java b/server/src/main/java/org/opensearch/action/search/ClearScrollResponse.java index 7a989e646e4d0..4428f693763ed 100644 --- a/server/src/main/java/org/opensearch/action/search/ClearScrollResponse.java +++ b/server/src/main/java/org/opensearch/action/search/ClearScrollResponse.java @@ -32,23 +32,28 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.rest.RestStatus.NOT_FOUND; -import static org.opensearch.rest.RestStatus.OK; +import static org.opensearch.core.rest.RestStatus.NOT_FOUND; +import static org.opensearch.core.rest.RestStatus.OK; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +/** + * Transport response for clearing a search scroll + * + * @opensearch.internal + */ public class ClearScrollResponse extends ActionResponse implements StatusToXContentObject { private static final ParseField SUCCEEDED = new ParseField("succeeded"); diff --git a/server/src/main/java/org/opensearch/action/search/CountedCollector.java b/server/src/main/java/org/opensearch/action/search/CountedCollector.java index cb00902b52b7c..c73c4be2fa662 100644 --- a/server/src/main/java/org/opensearch/action/search/CountedCollector.java +++ b/server/src/main/java/org/opensearch/action/search/CountedCollector.java @@ -40,6 +40,8 @@ * This is a simple base class to simplify fan out to shards and collect their results. Each results passed to * {@link #onResult(SearchPhaseResult)} will be set to the provided result array * where the given index is used to set the result on the array. + * + * @opensearch.internal */ final class CountedCollector { private final ArraySearchPhaseResults resultConsumer; diff --git a/server/src/main/java/org/opensearch/action/search/CreatePitAction.java b/server/src/main/java/org/opensearch/action/search/CreatePitAction.java new file mode 100644 index 0000000000000..7ffa30a182458 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/CreatePitAction.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionType; + +/** + * Action type for creating PIT reader context + */ +public class CreatePitAction extends ActionType { + public static final CreatePitAction INSTANCE = new CreatePitAction(); + public static final String NAME = "indices:data/read/point_in_time/create"; + + private CreatePitAction() { + super(NAME, CreatePitResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/CreatePitController.java b/server/src/main/java/org/opensearch/action/search/CreatePitController.java new file mode 100644 index 0000000000000..1d11ad2760675 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/CreatePitController.java @@ -0,0 +1,329 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.OpenSearchException; +import org.opensearch.action.StepListener; +import org.opensearch.action.support.GroupedActionListener; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.search.SearchPhaseResult; +import org.opensearch.search.SearchShardTarget; +import org.opensearch.tasks.Task; +import org.opensearch.transport.Transport; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +import static org.opensearch.common.unit.TimeValue.timeValueSeconds; + +/** + * Controller for creating PIT reader context + * Phase 1 of create PIT request : Create PIT reader contexts in the associated shards with a temporary keep alive + * Phase 2 of create PIT : Update PIT reader context with PIT ID and keep alive from request and + * fail user request if any of the updates in this phase are failed - we clean up PITs in case of such failures. + * This two phase approach is used to save PIT ID as part of context which is later used for other use cases like list PIT etc. + */ +public class CreatePitController { + private final SearchTransportService searchTransportService; + private final ClusterService clusterService; + private final TransportSearchAction transportSearchAction; + private final NamedWriteableRegistry namedWriteableRegistry; + private final PitService pitService; + private static final Logger logger = LogManager.getLogger(CreatePitController.class); + public static final Setting PIT_INIT_KEEP_ALIVE = Setting.positiveTimeSetting( + "point_in_time.init.keep_alive", + timeValueSeconds(30), + Setting.Property.NodeScope + ); + + @Inject + public CreatePitController( + SearchTransportService searchTransportService, + ClusterService clusterService, + TransportSearchAction transportSearchAction, + NamedWriteableRegistry namedWriteableRegistry, + PitService pitService + ) { + this.searchTransportService = searchTransportService; + this.clusterService = clusterService; + this.transportSearchAction = transportSearchAction; + this.namedWriteableRegistry = namedWriteableRegistry; + this.pitService = pitService; + } + + /** + * This method creates PIT reader context + */ + public void executeCreatePit( + CreatePitRequest request, + Task task, + StepListener createPitListener, + ActionListener updatePitIdListener + ) { + SearchRequest searchRequest = new SearchRequest(request.getIndices()); + searchRequest.preference(request.getPreference()); + searchRequest.routing(request.getRouting()); + searchRequest.indicesOptions(request.getIndicesOptions()); + searchRequest.allowPartialSearchResults(request.shouldAllowPartialPitCreation()); + SearchTask searchTask = searchRequest.createTask( + task.getId(), + task.getType(), + task.getAction(), + task.getParentTaskId(), + Collections.emptyMap() + ); + /** + * This is needed for cross cluster functionality to work with PITs and current ccsMinimizeRoundTrips is + * not supported for point in time + */ + searchRequest.setCcsMinimizeRoundtrips(false); + /** + * Phase 1 of create PIT + */ + executeCreatePit(searchTask, searchRequest, createPitListener); + + /** + * Phase 2 of create PIT where we update pit id in pit contexts + */ + createPitListener.whenComplete( + searchResponse -> { executeUpdatePitId(request, searchRequest, searchResponse, updatePitIdListener); }, + updatePitIdListener::onFailure + ); + } + + /** + * Creates PIT reader context with temporary keep alive + */ + void executeCreatePit(Task task, SearchRequest searchRequest, StepListener createPitListener) { + logger.debug( + () -> new ParameterizedMessage("Executing creation of PIT context for indices [{}]", Arrays.toString(searchRequest.indices())) + ); + transportSearchAction.executeRequest( + task, + searchRequest, + TransportCreatePitAction.CREATE_PIT_ACTION, + true, + new TransportSearchAction.SinglePhaseSearchAction() { + @Override + public void executeOnShardTarget( + SearchTask searchTask, + SearchShardTarget target, + Transport.Connection connection, + ActionListener searchPhaseResultActionListener + ) { + searchTransportService.createPitContext( + connection, + new TransportCreatePitAction.CreateReaderContextRequest( + target.getShardId(), + PIT_INIT_KEEP_ALIVE.get(clusterService.getSettings()) + ), + searchTask, + ActionListener.wrap(r -> searchPhaseResultActionListener.onResponse(r), searchPhaseResultActionListener::onFailure) + ); + } + }, + createPitListener + ); + } + + /** + * Updates PIT ID, keep alive and createdTime of PIT reader context + */ + void executeUpdatePitId( + CreatePitRequest request, + SearchRequest searchRequest, + SearchResponse searchResponse, + ActionListener updatePitIdListener + ) { + logger.debug( + () -> new ParameterizedMessage( + "Updating PIT context with PIT ID [{}], creation time and keep alive", + searchResponse.pointInTimeId() + ) + ); + /** + * store the create time ( same create time for all PIT contexts across shards ) to be used + * for list PIT api + */ + final long relativeStartNanos = System.nanoTime(); + final TransportSearchAction.SearchTimeProvider timeProvider = new TransportSearchAction.SearchTimeProvider( + searchRequest.getOrCreateAbsoluteStartMillis(), + relativeStartNanos, + System::nanoTime + ); + final long creationTime = timeProvider.getAbsoluteStartMillis(); + CreatePitResponse createPITResponse = new CreatePitResponse( + searchResponse.pointInTimeId(), + creationTime, + searchResponse.getTotalShards(), + searchResponse.getSuccessfulShards(), + searchResponse.getSkippedShards(), + searchResponse.getFailedShards(), + searchResponse.getShardFailures() + ); + SearchContextId contextId = SearchContextId.decode(namedWriteableRegistry, createPITResponse.getId()); + final StepListener> lookupListener = getConnectionLookupListener(contextId); + lookupListener.whenComplete(nodelookup -> { + final ActionListener groupedActionListener = getGroupedListener( + updatePitIdListener, + createPITResponse, + contextId.shards().size(), + contextId.shards().values() + ); + for (Map.Entry entry : contextId.shards().entrySet()) { + DiscoveryNode node = nodelookup.apply(entry.getValue().getClusterAlias(), entry.getValue().getNode()); + if (node == null) { + node = this.clusterService.state().getNodes().get(entry.getValue().getNode()); + } + if (node == null) { + logger.error( + () -> new ParameterizedMessage( + "Create pit update phase for PIT ID [{}] failed " + "because node [{}] not found", + searchResponse.pointInTimeId(), + entry.getValue().getNode() + ) + ); + groupedActionListener.onFailure( + new OpenSearchException( + "Create pit update phase for PIT ID [" + + searchResponse.pointInTimeId() + + "] failed because node[" + + entry.getValue().getNode() + + "] " + + "not found" + ) + ); + return; + } + try { + final Transport.Connection connection = searchTransportService.getConnection(entry.getValue().getClusterAlias(), node); + searchTransportService.updatePitContext( + connection, + new UpdatePitContextRequest( + entry.getValue().getSearchContextId(), + createPITResponse.getId(), + request.getKeepAlive().millis(), + creationTime + ), + groupedActionListener + ); + } catch (Exception e) { + String nodeName = node.getName(); + logger.error( + () -> new ParameterizedMessage( + "Create pit update phase failed for PIT ID [{}] on node [{}]", + searchResponse.pointInTimeId(), + nodeName + ), + e + ); + groupedActionListener.onFailure( + new OpenSearchException( + "Create pit update phase for PIT ID [" + searchResponse.pointInTimeId() + "] failed on node[" + node + "]", + e + ) + ); + } + } + }, updatePitIdListener::onFailure); + } + + private StepListener> getConnectionLookupListener(SearchContextId contextId) { + ClusterState state = clusterService.state(); + final Set clusters = contextId.shards() + .values() + .stream() + .filter(ctx -> Strings.isEmpty(ctx.getClusterAlias()) == false) + .map(SearchContextIdForNode::getClusterAlias) + .collect(Collectors.toSet()); + return (StepListener>) SearchUtils.getConnectionLookupListener( + searchTransportService.getRemoteClusterService(), + state, + clusters + ); + } + + private ActionListener getGroupedListener( + ActionListener updatePitIdListener, + CreatePitResponse createPITResponse, + int size, + Collection contexts + ) { + return new GroupedActionListener<>(new ActionListener<>() { + @Override + public void onResponse(final Collection responses) { + updatePitIdListener.onResponse(createPITResponse); + } + + @Override + public void onFailure(final Exception e) { + cleanupContexts(contexts, createPITResponse.getId()); + updatePitIdListener.onFailure(e); + } + }, size); + } + + /** + * Cleanup all created PIT contexts in case of failure + */ + private void cleanupContexts(Collection contexts, String pitId) { + ActionListener deleteListener = new ActionListener<>() { + @Override + public void onResponse(DeletePitResponse response) { + // this is invoke and forget call + final StringBuilder failedPitsStringBuilder = new StringBuilder(); + response.getDeletePitResults() + .stream() + .filter(r -> !r.isSuccessful()) + .forEach(r -> failedPitsStringBuilder.append(r.getPitId()).append(",")); + logger.warn(() -> new ParameterizedMessage("Failed to delete PIT IDs {}", failedPitsStringBuilder.toString())); + if (logger.isDebugEnabled()) { + final StringBuilder successfulPitsStringBuilder = new StringBuilder(); + response.getDeletePitResults() + .stream() + .filter(r -> r.isSuccessful()) + .forEach(r -> successfulPitsStringBuilder.append(r.getPitId()).append(",")); + logger.debug(() -> new ParameterizedMessage("Deleted PIT with IDs {}", successfulPitsStringBuilder.toString())); + } + } + + @Override + public void onFailure(Exception e) { + logger.error("Cleaning up PIT contexts failed ", e); + } + }; + Map> nodeToContextsMap = new HashMap<>(); + for (SearchContextIdForNode context : contexts) { + List contextIdsForNode = nodeToContextsMap.getOrDefault(context.getNode(), new ArrayList<>()); + contextIdsForNode.add(new PitSearchContextIdForNode(pitId, context)); + nodeToContextsMap.put(context.getNode(), contextIdsForNode); + } + pitService.deletePitContexts(nodeToContextsMap, deleteListener); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/CreatePitRequest.java b/server/src/main/java/org/opensearch/action/search/CreatePitRequest.java new file mode 100644 index 0000000000000..a47127f18e18d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/CreatePitRequest.java @@ -0,0 +1,195 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.IndicesRequest; +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.common.Nullable; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.tasks.Task; + +import java.io.IOException; +import java.util.Map; +import java.util.Objects; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * A request to make create point in time against one or more indices. + */ +public class CreatePitRequest extends ActionRequest implements IndicesRequest.Replaceable, ToXContent { + + // keep alive for pit reader context + private TimeValue keepAlive; + + // this describes whether PIT can be created with partial failures + private Boolean allowPartialPitCreation; + @Nullable + private String routing = null; + @Nullable + private String preference = null; + private String[] indices = Strings.EMPTY_ARRAY; + private IndicesOptions indicesOptions = SearchRequest.DEFAULT_INDICES_OPTIONS; + + public CreatePitRequest(TimeValue keepAlive, Boolean allowPartialPitCreation, String... indices) { + this.keepAlive = keepAlive; + this.allowPartialPitCreation = allowPartialPitCreation; + this.indices = indices; + } + + public CreatePitRequest(StreamInput in) throws IOException { + super(in); + indices = in.readStringArray(); + indicesOptions = IndicesOptions.readIndicesOptions(in); + routing = in.readOptionalString(); + preference = in.readOptionalString(); + keepAlive = in.readTimeValue(); + allowPartialPitCreation = in.readOptionalBoolean(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeStringArray(indices); + indicesOptions.writeIndicesOptions(out); + out.writeOptionalString(routing); + out.writeOptionalString(preference); + out.writeTimeValue(keepAlive); + out.writeOptionalBoolean(allowPartialPitCreation); + } + + public String getRouting() { + return routing; + } + + public String getPreference() { + return preference; + } + + public String[] getIndices() { + return indices; + } + + public IndicesOptions getIndicesOptions() { + return indicesOptions; + } + + public TimeValue getKeepAlive() { + return keepAlive; + } + + /** + * Sets if this request should allow partial results. + */ + public void allowPartialPitCreation(Boolean allowPartialPitCreation) { + this.allowPartialPitCreation = allowPartialPitCreation; + } + + public boolean shouldAllowPartialPitCreation() { + return allowPartialPitCreation; + } + + public void setRouting(String routing) { + this.routing = routing; + } + + public void setPreference(String preference) { + this.preference = preference; + } + + public void setIndices(String[] indices) { + this.indices = indices; + } + + public void setIndicesOptions(IndicesOptions indicesOptions) { + this.indicesOptions = Objects.requireNonNull(indicesOptions, "indicesOptions must not be null"); + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (keepAlive == null) { + validationException = addValidationError("keep alive not specified", validationException); + } + return validationException; + } + + @Override + public String[] indices() { + return indices; + } + + @Override + public IndicesOptions indicesOptions() { + return indicesOptions; + } + + public CreatePitRequest indicesOptions(IndicesOptions indicesOptions) { + this.indicesOptions = Objects.requireNonNull(indicesOptions, "indicesOptions must not be null"); + return this; + } + + public void setKeepAlive(TimeValue keepAlive) { + this.keepAlive = keepAlive; + } + + public final String buildDescription() { + StringBuilder sb = new StringBuilder(); + sb.append("indices["); + Strings.arrayToDelimitedString(indices, ",", sb); + sb.append("], "); + sb.append("pointintime[").append(keepAlive).append("], "); + sb.append("allowPartialPitCreation[").append(allowPartialPitCreation).append("], "); + return sb.toString(); + } + + @Override + public Task createTask(long id, String type, String action, TaskId parentTaskId, Map headers) { + return new Task(id, type, action, this.buildDescription(), parentTaskId, headers); + } + + private void validateIndices(String... indices) { + Objects.requireNonNull(indices, "indices must not be null"); + for (String index : indices) { + Objects.requireNonNull(index, "index must not be null"); + } + } + + @Override + public CreatePitRequest indices(String... indices) { + validateIndices(indices); + this.indices = indices; + return this; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.field("keep_alive", keepAlive); + builder.field("allow_partial_pit_creation", allowPartialPitCreation); + if (indices != null) { + builder.startArray("indices"); + for (String index : indices) { + builder.value(index); + } + builder.endArray(); + } + if (indicesOptions != null) { + indicesOptions.toXContent(builder, params); + } + return builder; + } +} diff --git a/server/src/main/java/org/opensearch/action/search/CreatePitResponse.java b/server/src/main/java/org/opensearch/action/search/CreatePitResponse.java new file mode 100644 index 0000000000000..aa841a02f1d20 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/CreatePitResponse.java @@ -0,0 +1,232 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.common.xcontent.StatusToXContentObject; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.rest.action.RestActions; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * Create point in time response with point in time id and shard success / failures + */ +public class CreatePitResponse extends ActionResponse implements StatusToXContentObject { + private static final ParseField ID = new ParseField("pit_id"); + private static final ParseField CREATION_TIME = new ParseField("creation_time"); + + // point in time id + private final String id; + private final int totalShards; + private final int successfulShards; + private final int failedShards; + private final int skippedShards; + private final ShardSearchFailure[] shardFailures; + private final long creationTime; + + public CreatePitResponse(StreamInput in) throws IOException { + super(in); + id = in.readString(); + totalShards = in.readVInt(); + successfulShards = in.readVInt(); + failedShards = in.readVInt(); + skippedShards = in.readVInt(); + creationTime = in.readLong(); + int size = in.readVInt(); + if (size == 0) { + shardFailures = ShardSearchFailure.EMPTY_ARRAY; + } else { + shardFailures = new ShardSearchFailure[size]; + for (int i = 0; i < shardFailures.length; i++) { + shardFailures[i] = ShardSearchFailure.readShardSearchFailure(in); + } + } + } + + public CreatePitResponse( + String id, + long creationTime, + int totalShards, + int successfulShards, + int skippedShards, + int failedShards, + ShardSearchFailure[] shardFailures + ) { + this.id = id; + this.creationTime = creationTime; + this.totalShards = totalShards; + this.successfulShards = successfulShards; + this.skippedShards = skippedShards; + this.failedShards = failedShards; + this.shardFailures = shardFailures; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(ID.getPreferredName(), id); + RestActions.buildBroadcastShardsHeader( + builder, + params, + getTotalShards(), + getSuccessfulShards(), + getSkippedShards(), + getFailedShards(), + getShardFailures() + ); + builder.field(CREATION_TIME.getPreferredName(), creationTime); + builder.endObject(); + return builder; + } + + /** + * Parse the create PIT response body into a new {@link CreatePitResponse} object + */ + public static CreatePitResponse fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + parser.nextToken(); + return innerFromXContent(parser); + } + + public static CreatePitResponse innerFromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser); + String currentFieldName = parser.currentName(); + int successfulShards = -1; + int totalShards = -1; + int skippedShards = 0; + int failedShards = 0; + String id = null; + long creationTime = 0; + List failures = new ArrayList<>(); + for (XContentParser.Token token = parser.nextToken(); token != XContentParser.Token.END_OBJECT; token = parser.nextToken()) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (CREATION_TIME.match(currentFieldName, parser.getDeprecationHandler())) { + creationTime = parser.longValue(); + } else if (ID.match(currentFieldName, parser.getDeprecationHandler())) { + id = parser.text(); + } else { + parser.skipChildren(); + } + } else if (token == XContentParser.Token.START_OBJECT) { + if (RestActions._SHARDS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (RestActions.FAILED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + failedShards = parser.intValue(); // we don't need it but need to consume it + } else if (RestActions.SUCCESSFUL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + successfulShards = parser.intValue(); + } else if (RestActions.TOTAL_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + totalShards = parser.intValue(); + } else if (RestActions.SKIPPED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + skippedShards = parser.intValue(); + } else { + parser.skipChildren(); + } + } else if (token == XContentParser.Token.START_ARRAY) { + if (RestActions.FAILURES_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + failures.add(ShardSearchFailure.fromXContent(parser)); + } + } else { + parser.skipChildren(); + } + } else { + parser.skipChildren(); + } + } + } else { + parser.skipChildren(); + } + } + } + + return new CreatePitResponse( + id, + creationTime, + totalShards, + successfulShards, + skippedShards, + failedShards, + failures.toArray(ShardSearchFailure.EMPTY_ARRAY) + ); + } + + public long getCreationTime() { + return creationTime; + } + + /** + * The failed number of shards the search was executed on. + */ + public int getFailedShards() { + return shardFailures.length; + } + + /** + * The failures that occurred during the search. + */ + public ShardSearchFailure[] getShardFailures() { + return this.shardFailures; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(id); + out.writeVInt(totalShards); + out.writeVInt(successfulShards); + out.writeVInt(failedShards); + out.writeVInt(skippedShards); + out.writeLong(creationTime); + out.writeVInt(shardFailures.length); + for (ShardSearchFailure shardSearchFailure : shardFailures) { + shardSearchFailure.writeTo(out); + } + } + + public String getId() { + return id; + } + + /** + * The total number of shards the create pit operation was executed on. + */ + public int getTotalShards() { + return totalShards; + } + + /** + * The successful number of shards the create pit operation was executed on. + */ + public int getSuccessfulShards() { + return successfulShards; + } + + public int getSkippedShards() { + return skippedShards; + } + + @Override + public RestStatus status() { + return RestStatus.status(successfulShards, totalShards, shardFailures); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/DeletePitAction.java b/server/src/main/java/org/opensearch/action/search/DeletePitAction.java new file mode 100644 index 0000000000000..aa305ecfe73ab --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/DeletePitAction.java @@ -0,0 +1,24 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionType; + +/** + * Action type for deleting point in time searches + */ +public class DeletePitAction extends ActionType { + + public static final DeletePitAction INSTANCE = new DeletePitAction(); + public static final String NAME = "indices:data/read/point_in_time/delete"; + + private DeletePitAction() { + super(NAME, DeletePitResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/DeletePitInfo.java b/server/src/main/java/org/opensearch/action/search/DeletePitInfo.java new file mode 100644 index 0000000000000..40098c6670b65 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/DeletePitInfo.java @@ -0,0 +1,83 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.transport.TransportResponse; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; + +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; + +/** + * This class captures if deletion of pit is successful along with pit id + */ +public class DeletePitInfo extends TransportResponse implements Writeable, ToXContent { + /** + * This will be true if PIT reader contexts are deleted ond also if contexts are not found. + */ + private final boolean successful; + + private final String pitId; + + public DeletePitInfo(boolean successful, String pitId) { + this.successful = successful; + this.pitId = pitId; + } + + public DeletePitInfo(StreamInput in) throws IOException { + successful = in.readBoolean(); + pitId = in.readString(); + + } + + public boolean isSuccessful() { + return successful; + } + + public String getPitId() { + return pitId; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeBoolean(successful); + out.writeString(pitId); + } + + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "delete_pit_info", + true, + args -> new DeletePitInfo((boolean) args[0], (String) args[1]) + ); + + static { + PARSER.declareBoolean(constructorArg(), new ParseField("successful")); + PARSER.declareString(constructorArg(), new ParseField("pit_id")); + } + + private static final ParseField SUCCESSFUL = new ParseField("successful"); + private static final ParseField PIT_ID = new ParseField("pit_id"); + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(SUCCESSFUL.getPreferredName(), successful); + builder.field(PIT_ID.getPreferredName(), pitId); + builder.endObject(); + return builder; + } + +} diff --git a/server/src/main/java/org/opensearch/action/search/DeletePitRequest.java b/server/src/main/java/org/opensearch/action/search/DeletePitRequest.java new file mode 100644 index 0000000000000..62f67991083f2 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/DeletePitRequest.java @@ -0,0 +1,126 @@ + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.opensearch.action.ValidateActions.addValidationError; + +/** + * Request to delete one or more PIT search contexts based on IDs. + */ +public class DeletePitRequest extends ActionRequest implements ToXContentObject { + + /** + * List of PIT IDs to be deleted , and use "_all" to delete all PIT reader contexts + */ + private final List pitIds = new ArrayList<>(); + + public DeletePitRequest(StreamInput in) throws IOException { + super(in); + pitIds.addAll(Arrays.asList(in.readStringArray())); + } + + public DeletePitRequest(String... pitIds) { + this.pitIds.addAll(Arrays.asList(pitIds)); + } + + public DeletePitRequest(List pitIds) { + this.pitIds.addAll(pitIds); + } + + public void clearAndSetPitIds(List pitIds) { + this.pitIds.clear(); + this.pitIds.addAll(pitIds); + } + + public DeletePitRequest() {} + + public List getPitIds() { + return pitIds; + } + + @Override + public ActionRequestValidationException validate() { + ActionRequestValidationException validationException = null; + if (pitIds == null || pitIds.isEmpty()) { + validationException = addValidationError("no pit ids specified", validationException); + } + return validationException; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + if (pitIds == null) { + out.writeVInt(0); + } else { + out.writeStringArray(pitIds.toArray(new String[pitIds.size()])); + } + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { + builder.startObject(); + builder.startArray("pit_id"); + for (String pitId : pitIds) { + builder.value(pitId); + } + builder.endArray(); + builder.endObject(); + return builder; + } + + public void fromXContent(XContentParser parser) throws IOException { + pitIds.clear(); + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new IllegalArgumentException("Malformed content, must start with an object"); + } else { + XContentParser.Token token; + String currentFieldName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if ("pit_id".equals(currentFieldName)) { + if (token == XContentParser.Token.START_ARRAY) { + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (token.isValue() == false) { + throw new IllegalArgumentException("pit_id array element should only contain pit_id"); + } + pitIds.add(parser.text()); + } + } else { + if (token.isValue() == false) { + throw new IllegalArgumentException("pit_id element should only contain pit_id"); + } + pitIds.add(parser.text()); + } + } else { + throw new IllegalArgumentException( + "Unknown parameter [" + currentFieldName + "] in request body or parameter is of the wrong type[" + token + "] " + ); + } + } + } + } + +} diff --git a/server/src/main/java/org/opensearch/action/search/DeletePitResponse.java b/server/src/main/java/org/opensearch/action/search/DeletePitResponse.java new file mode 100644 index 0000000000000..00274f0e610a9 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/DeletePitResponse.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.common.xcontent.StatusToXContentObject; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.opensearch.core.rest.RestStatus.OK; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; + +/** + * Response class for delete pits flow which clears the point in time search contexts + */ +public class DeletePitResponse extends ActionResponse implements StatusToXContentObject { + + private final List deletePitResults; + + public DeletePitResponse(List deletePitResults) { + this.deletePitResults = deletePitResults; + } + + public DeletePitResponse(StreamInput in) throws IOException { + super(in); + int size = in.readVInt(); + deletePitResults = new ArrayList<>(); + for (int i = 0; i < size; i++) { + deletePitResults.add(new DeletePitInfo(in)); + } + + } + + public List getDeletePitResults() { + return deletePitResults; + } + + /** + * @return Whether the attempt to delete PIT was successful. + */ + @Override + public RestStatus status() { + return OK; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeVInt(deletePitResults.size()); + for (DeletePitInfo deletePitResult : deletePitResults) { + deletePitResult.writeTo(out); + } + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { + builder.startObject(); + builder.startArray("pits"); + for (DeletePitInfo response : deletePitResults) { + response.toXContent(builder, params); + } + builder.endArray(); + builder.endObject(); + return builder; + } + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "delete_pit_response", + true, + (Object[] parsedObjects) -> { + @SuppressWarnings("unchecked") + List deletePitInfoList = (List) parsedObjects[0]; + return new DeletePitResponse(deletePitInfoList); + } + ); + static { + PARSER.declareObjectArray(constructorArg(), DeletePitInfo.PARSER, new ParseField("pits")); + } + + public static DeletePitResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } + +} diff --git a/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineAction.java b/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineAction.java new file mode 100644 index 0000000000000..65f8cf3de9506 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineAction.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionType; +import org.opensearch.action.support.master.AcknowledgedResponse; + +/** + * Action type to delete a search pipeline + * + * @opensearch.internal + */ +public class DeleteSearchPipelineAction extends ActionType { + public static final DeleteSearchPipelineAction INSTANCE = new DeleteSearchPipelineAction(); + public static final String NAME = "cluster:admin/search/pipeline/delete"; + + public DeleteSearchPipelineAction() { + super(NAME, AcknowledgedResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineRequest.java b/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineRequest.java new file mode 100644 index 0000000000000..2c6ab6437fd2a --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineRequest.java @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.master.AcknowledgedRequest; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; +import java.util.Objects; + +/** + * Request to delete a search pipeline + * + * @opensearch.internal + */ +public class DeleteSearchPipelineRequest extends AcknowledgedRequest { + private String id; + + public DeleteSearchPipelineRequest(String id) { + this.id = Objects.requireNonNull(id); + } + + public DeleteSearchPipelineRequest() {} + + public DeleteSearchPipelineRequest(StreamInput in) throws IOException { + super(in); + id = in.readString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(id); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineTransportAction.java b/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineTransportAction.java new file mode 100644 index 0000000000000..ac83a6bb6b765 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/DeleteSearchPipelineTransportAction.java @@ -0,0 +1,80 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.search.pipeline.SearchPipelineService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Perform the action of deleting a search pipeline + * + * @opensearch.internal + */ +public class DeleteSearchPipelineTransportAction extends TransportClusterManagerNodeAction< + DeleteSearchPipelineRequest, + AcknowledgedResponse> { + private final SearchPipelineService searchPipelineService; + + @Inject + public DeleteSearchPipelineTransportAction( + ThreadPool threadPool, + SearchPipelineService searchPipelineService, + TransportService transportService, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + DeleteSearchPipelineAction.NAME, + transportService, + searchPipelineService.getClusterService(), + threadPool, + actionFilters, + DeleteSearchPipelineRequest::new, + indexNameExpressionResolver + ); + this.searchPipelineService = searchPipelineService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected AcknowledgedResponse read(StreamInput in) throws IOException { + return new AcknowledgedResponse(in); + } + + @Override + protected void clusterManagerOperation( + DeleteSearchPipelineRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + searchPipelineService.deletePipeline(request, listener); + } + + @Override + protected ClusterBlockException checkBlock(DeleteSearchPipelineRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/DfsQueryPhase.java b/server/src/main/java/org/opensearch/action/search/DfsQueryPhase.java index 6152f4cfdc599..6fe4eaabd6d17 100644 --- a/server/src/main/java/org/opensearch/action/search/DfsQueryPhase.java +++ b/server/src/main/java/org/opensearch/action/search/DfsQueryPhase.java @@ -50,6 +50,8 @@ * retry on another shard if any of the shards are failing. Failures are treated as shard failures and are counted as a non-successful * operation. * @see CountedCollector#onFailure(int, SearchShardTarget, Exception) + * + * @opensearch.internal */ final class DfsQueryPhase extends SearchPhase { private final ArraySearchPhaseResults queryResult; @@ -67,7 +69,7 @@ final class DfsQueryPhase extends SearchPhase { Function, SearchPhase> nextPhaseFactory, SearchPhaseContext context ) { - super("dfs_query"); + super(SearchPhaseName.DFS_QUERY.getName()); this.progressListener = context.getTask().getProgressListener(); this.queryResult = queryResult; this.searchResults = searchResults; diff --git a/server/src/main/java/org/opensearch/action/search/ExpandSearchPhase.java b/server/src/main/java/org/opensearch/action/search/ExpandSearchPhase.java index 6c131697cb31e..e249fb239dae9 100644 --- a/server/src/main/java/org/opensearch/action/search/ExpandSearchPhase.java +++ b/server/src/main/java/org/opensearch/action/search/ExpandSearchPhase.java @@ -32,8 +32,8 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionListener; import org.opensearch.common.util.concurrent.AtomicArray; +import org.opensearch.core.action.ActionListener; import org.opensearch.index.query.BoolQueryBuilder; import org.opensearch.index.query.InnerHitBuilder; import org.opensearch.index.query.QueryBuilder; @@ -53,6 +53,8 @@ * This search phase is an optional phase that will be executed once all hits are fetched from the shards that executes * field-collapsing on the inner hits. This phase only executes if field collapsing is requested in the search request and otherwise * forwards to the next phase immediately. + * + * @opensearch.internal */ final class ExpandSearchPhase extends SearchPhase { private final SearchPhaseContext context; @@ -60,7 +62,7 @@ final class ExpandSearchPhase extends SearchPhase { private final AtomicArray queryResults; ExpandSearchPhase(SearchPhaseContext context, InternalSearchResponse searchResponse, AtomicArray queryResults) { - super("expand"); + super(SearchPhaseName.EXPAND.getName()); this.context = context; this.searchResponse = searchResponse; this.queryResults = queryResults; diff --git a/server/src/main/java/org/opensearch/action/search/FetchSearchPhase.java b/server/src/main/java/org/opensearch/action/search/FetchSearchPhase.java index c106c5e2f8069..ebb2f33f8f37d 100644 --- a/server/src/main/java/org/opensearch/action/search/FetchSearchPhase.java +++ b/server/src/main/java/org/opensearch/action/search/FetchSearchPhase.java @@ -31,7 +31,6 @@ package org.opensearch.action.search; -import com.carrotsearch.hppc.IntArrayList; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.lucene.search.ScoreDoc; @@ -56,6 +55,8 @@ /** * This search phase merges the query results from the previous phase together and calculates the topN hits for this search. * Then it reaches out to all relevant shards to fetch the topN hits. + * + * @opensearch.internal */ final class FetchSearchPhase extends SearchPhase { private final ArraySearchPhaseResults fetchResults; @@ -90,7 +91,7 @@ final class FetchSearchPhase extends SearchPhase { SearchPhaseContext context, BiFunction, SearchPhase> nextPhaseFactory ) { - super("fetch"); + super(SearchPhaseName.FETCH.getName()); if (context.getNumShards() != resultConsumer.getNumShards()) { throw new IllegalStateException( "number of shards must match the length of the query results but doesn't:" @@ -149,7 +150,7 @@ private void innerRun() throws Exception { finishPhase.run(); } else { ScoreDoc[] scoreDocs = reducedQueryPhase.sortedTopDocs.scoreDocs; - final IntArrayList[] docIdsToLoad = searchPhaseController.fillDocIdsToLoad(numShards, scoreDocs); + final List[] docIdsToLoad = searchPhaseController.fillDocIdsToLoad(numShards, scoreDocs); // no docs to fetch -- sidestep everything and return if (scoreDocs.length == 0) { // we have to release contexts here to free up resources @@ -166,7 +167,7 @@ private void innerRun() throws Exception { context ); for (int i = 0; i < docIdsToLoad.length; i++) { - IntArrayList entry = docIdsToLoad[i]; + List entry = docIdsToLoad[i]; SearchPhaseResult queryResult = queryResults.get(i); if (entry == null) { // no results for this shard ID if (queryResult != null) { @@ -203,7 +204,7 @@ private void innerRun() throws Exception { protected ShardFetchSearchRequest createFetchRequest( ShardSearchContextId contextId, int index, - IntArrayList entry, + List entry, ScoreDoc[] lastEmittedDocPerShard, OriginalIndices originalIndices, ShardSearchRequest shardSearchRequest, diff --git a/server/src/main/java/org/opensearch/action/search/GetAllPitNodeRequest.java b/server/src/main/java/org/opensearch/action/search/GetAllPitNodeRequest.java new file mode 100644 index 0000000000000..4a5155f4e7b70 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetAllPitNodeRequest.java @@ -0,0 +1,35 @@ + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.support.nodes.BaseNodeRequest; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Inner node get all pits request + */ +public class GetAllPitNodeRequest extends BaseNodeRequest { + + public GetAllPitNodeRequest() { + super(); + } + + public GetAllPitNodeRequest(StreamInput in) throws IOException { + super(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/GetAllPitNodeResponse.java b/server/src/main/java/org/opensearch/action/search/GetAllPitNodeResponse.java new file mode 100644 index 0000000000000..9bbc81c0d6a4c --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetAllPitNodeResponse.java @@ -0,0 +1,69 @@ + +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.support.nodes.BaseNodeResponse; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * Inner node get all pits response + */ +public class GetAllPitNodeResponse extends BaseNodeResponse implements ToXContentFragment { + + /** + * List of active PITs in the associated node + */ + private final List pitInfos; + + public GetAllPitNodeResponse(DiscoveryNode node, List pitInfos) { + super(node); + if (pitInfos == null) { + throw new IllegalArgumentException("Pits info cannot be null"); + } + this.pitInfos = Collections.unmodifiableList(pitInfos); + } + + public GetAllPitNodeResponse(StreamInput in) throws IOException { + super(in); + this.pitInfos = Collections.unmodifiableList(in.readList(ListPitInfo::new)); + } + + public List getPitInfos() { + return pitInfos; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeList(pitInfos); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("node", this.getNode().getName()); + builder.startArray("pitInfos"); + for (ListPitInfo pit : pitInfos) { + pit.toXContent(builder, params); + } + + builder.endArray(); + builder.endObject(); + return builder; + } +} diff --git a/server/src/main/java/org/opensearch/action/search/GetAllPitNodesRequest.java b/server/src/main/java/org/opensearch/action/search/GetAllPitNodesRequest.java new file mode 100644 index 0000000000000..948fe72eae817 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetAllPitNodesRequest.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.support.nodes.BaseNodesRequest; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Request to get all active PIT IDs from all nodes of cluster + */ +public class GetAllPitNodesRequest extends BaseNodesRequest { + + @Inject + public GetAllPitNodesRequest(DiscoveryNode... concreteNodes) { + super(concreteNodes); + } + + public GetAllPitNodesRequest(StreamInput in) throws IOException { + super(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/GetAllPitNodesResponse.java b/server/src/main/java/org/opensearch/action/search/GetAllPitNodesResponse.java new file mode 100644 index 0000000000000..9bb3ab6407696 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetAllPitNodesResponse.java @@ -0,0 +1,139 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.FailedNodeException; +import org.opensearch.action.support.nodes.BaseNodesResponse; +import org.opensearch.cluster.ClusterName; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; + +/** + * This class transforms active PIT objects from all nodes to unique PIT objects + */ +public class GetAllPitNodesResponse extends BaseNodesResponse implements ToXContentObject { + + /** + * List of unique PITs across all nodes + */ + private final Set pitInfos = new HashSet<>(); + + public GetAllPitNodesResponse(StreamInput in) throws IOException { + super(in); + Set uniquePitIds = new HashSet<>(); + pitInfos.addAll( + getNodes().stream() + .flatMap(p -> p.getPitInfos().stream().filter(t -> uniquePitIds.add(t.getPitId()))) + .collect(Collectors.toList()) + ); + } + + public GetAllPitNodesResponse( + ClusterName clusterName, + List getAllPitNodeResponseList, + List failures + ) { + super(clusterName, getAllPitNodeResponseList, failures); + Set uniquePitIds = new HashSet<>(); + pitInfos.addAll( + getAllPitNodeResponseList.stream() + .flatMap(p -> p.getPitInfos().stream().filter(t -> uniquePitIds.add(t.getPitId()))) + .collect(Collectors.toList()) + ); + } + + /** + * Copy constructor that explicitly sets the list pit infos + */ + public GetAllPitNodesResponse(List listPitInfos, GetAllPitNodesResponse response) { + super(response.getClusterName(), response.getNodes(), response.failures()); + pitInfos.addAll(listPitInfos); + } + + public GetAllPitNodesResponse( + List listPitInfos, + ClusterName clusterName, + List getAllPitNodeResponseList, + List failures + ) { + super(clusterName, getAllPitNodeResponseList, failures); + pitInfos.addAll(listPitInfos); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.startArray("pits"); + for (ListPitInfo pit : pitInfos) { + pit.toXContent(builder, params); + } + builder.endArray(); + if (!failures().isEmpty()) { + builder.startArray("failures"); + for (FailedNodeException e : failures()) { + e.toXContent(builder, params); + } + } + builder.endObject(); + return builder; + } + + @Override + public List readNodesFrom(StreamInput in) throws IOException { + return in.readList(GetAllPitNodeResponse::new); + } + + @Override + public void writeNodesTo(StreamOutput out, List nodes) throws IOException { + out.writeList(nodes); + } + + public List getPitInfos() { + return Collections.unmodifiableList(new ArrayList<>(pitInfos)); + } + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "get_all_pits_response", + true, + (Object[] parsedObjects) -> { + @SuppressWarnings("unchecked") + List listPitInfos = (List) parsedObjects[0]; + List failures = null; + if (parsedObjects.length > 1) { + failures = (List) parsedObjects[1]; + } + if (failures == null) { + failures = new ArrayList<>(); + } + return new GetAllPitNodesResponse(listPitInfos, new ClusterName(""), new ArrayList<>(), failures); + } + ); + static { + PARSER.declareObjectArray(constructorArg(), ListPitInfo.PARSER, new ParseField("pits")); + } + + public static GetAllPitNodesResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/GetAllPitsAction.java b/server/src/main/java/org/opensearch/action/search/GetAllPitsAction.java new file mode 100644 index 0000000000000..8fe901add5e3a --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetAllPitsAction.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionType; + +/** + * Action type for retrieving all PIT reader contexts from nodes + */ +public class GetAllPitsAction extends ActionType { + public static final GetAllPitsAction INSTANCE = new GetAllPitsAction(); + public static final String NAME = "indices:data/read/point_in_time/readall"; + + private GetAllPitsAction() { + super(NAME, GetAllPitNodesResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/GetSearchPipelineAction.java b/server/src/main/java/org/opensearch/action/search/GetSearchPipelineAction.java new file mode 100644 index 0000000000000..b80ffa781b0ec --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetSearchPipelineAction.java @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionType; + +/** + * Action type to get search pipelines + * + * @opensearch.internal + */ +public class GetSearchPipelineAction extends ActionType { + public static final GetSearchPipelineAction INSTANCE = new GetSearchPipelineAction(); + public static final String NAME = "cluster:admin/search/pipeline/get"; + + public GetSearchPipelineAction() { + super(NAME, GetSearchPipelineResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/GetSearchPipelineRequest.java b/server/src/main/java/org/opensearch/action/search/GetSearchPipelineRequest.java new file mode 100644 index 0000000000000..f573a37fa5dab --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetSearchPipelineRequest.java @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; +import java.util.Objects; + +/** + * Request to get search pipelines + * + * @opensearch.internal + */ +public class GetSearchPipelineRequest extends ClusterManagerNodeReadRequest { + private final String[] ids; + + public GetSearchPipelineRequest(String... ids) { + this.ids = Objects.requireNonNull(ids); + } + + public GetSearchPipelineRequest() { + ids = Strings.EMPTY_ARRAY; + } + + public GetSearchPipelineRequest(StreamInput in) throws IOException { + super(in); + ids = in.readStringArray(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeStringArray(ids); + } + + public String[] getIds() { + return ids; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } +} diff --git a/server/src/main/java/org/opensearch/action/search/GetSearchPipelineResponse.java b/server/src/main/java/org/opensearch/action/search/GetSearchPipelineResponse.java new file mode 100644 index 0000000000000..64dde763e9ff8 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetSearchPipelineResponse.java @@ -0,0 +1,144 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.common.xcontent.StatusToXContentObject; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.search.pipeline.PipelineConfiguration; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * transport response for getting a search pipeline + * + * @opensearch.internal + */ +public class GetSearchPipelineResponse extends ActionResponse implements StatusToXContentObject { + + private final List pipelines; + + public GetSearchPipelineResponse(StreamInput in) throws IOException { + super(in); + int size = in.readVInt(); + pipelines = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + pipelines.add(PipelineConfiguration.readFrom(in)); + } + + } + + public GetSearchPipelineResponse(List pipelines) { + this.pipelines = pipelines; + } + + public List pipelines() { + return Collections.unmodifiableList(pipelines); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + for (PipelineConfiguration pipeline : pipelines) { + builder.field(pipeline.getId(), pipeline.getConfigAsMap()); + } + builder.endObject(); + return builder; + } + + /** + * + * @param parser the parser for the XContent that contains the serialized GetPipelineResponse. + * @return an instance of GetPipelineResponse read from the parser + * @throws IOException If the parsing fails + */ + public static GetSearchPipelineResponse fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + List pipelines = new ArrayList<>(); + while (parser.nextToken().equals(XContentParser.Token.FIELD_NAME)) { + String pipelineId = parser.currentName(); + parser.nextToken(); + try (XContentBuilder contentBuilder = XContentBuilder.builder(parser.contentType().xContent())) { + contentBuilder.generator().copyCurrentStructure(parser); + PipelineConfiguration pipeline = new PipelineConfiguration( + pipelineId, + BytesReference.bytes(contentBuilder), + contentBuilder.contentType() + ); + pipelines.add(pipeline); + } + } + ensureExpectedToken(XContentParser.Token.END_OBJECT, parser.currentToken(), parser); + return new GetSearchPipelineResponse(pipelines); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeVInt(pipelines.size()); + for (PipelineConfiguration pipeline : pipelines) { + pipeline.writeTo(out); + } + } + + public boolean isFound() { + return !pipelines.isEmpty(); + } + + @Override + public RestStatus status() { + return isFound() ? RestStatus.OK : RestStatus.NOT_FOUND; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GetSearchPipelineResponse otherResponse = (GetSearchPipelineResponse) o; + if (pipelines == null) { + return otherResponse.pipelines == null; + } else if (otherResponse.pipelines == null) { + return false; + } + // Convert to a map to ignore order; + return toMap(pipelines).equals(toMap(otherResponse.pipelines)); + } + + private static Map toMap(List pipelines) { + return pipelines.stream().collect(Collectors.toMap(PipelineConfiguration::getId, p -> p)); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this); + } + + @Override + public int hashCode() { + int result = 1; + for (PipelineConfiguration pipeline : pipelines) { + // We only take the sum here to ensure that the order does not matter. + result += (pipeline == null ? 0 : pipeline.hashCode()); + } + return result; + } +} diff --git a/server/src/main/java/org/opensearch/action/search/GetSearchPipelineTransportAction.java b/server/src/main/java/org/opensearch/action/search/GetSearchPipelineTransportAction.java new file mode 100644 index 0000000000000..a7fcb8f1cfbae --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/GetSearchPipelineTransportAction.java @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.search.pipeline.SearchPipelineService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; + +/** + * Perform the action of getting a search pipeline + * + * @opensearch.internal + */ +public class GetSearchPipelineTransportAction extends TransportClusterManagerNodeReadAction< + GetSearchPipelineRequest, + GetSearchPipelineResponse> { + + @Inject + public GetSearchPipelineTransportAction( + ThreadPool threadPool, + ClusterService clusterService, + TransportService transportService, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + GetSearchPipelineAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + GetSearchPipelineRequest::new, + indexNameExpressionResolver + ); + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected GetSearchPipelineResponse read(StreamInput in) throws IOException { + return new GetSearchPipelineResponse(in); + } + + @Override + protected void clusterManagerOperation( + GetSearchPipelineRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + listener.onResponse(new GetSearchPipelineResponse(SearchPipelineService.getPipelines(state, request.getIds()))); + } + + @Override + protected ClusterBlockException checkBlock(GetSearchPipelineRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_READ); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/ListPitInfo.java b/server/src/main/java/org/opensearch/action/search/ListPitInfo.java new file mode 100644 index 0000000000000..220b7247517b9 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/ListPitInfo.java @@ -0,0 +1,97 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Objects; + +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; + +/** + * This holds information about pit reader context such as pit id and creation time + */ +public class ListPitInfo implements ToXContentFragment, Writeable { + private final String pitId; + private final long creationTime; + private final long keepAlive; + + public ListPitInfo(String pitId, long creationTime, long keepAlive) { + this.pitId = pitId; + this.creationTime = creationTime; + this.keepAlive = keepAlive; + } + + public ListPitInfo(StreamInput in) throws IOException { + this.pitId = in.readString(); + this.creationTime = in.readLong(); + this.keepAlive = in.readLong(); + } + + public String getPitId() { + return pitId; + } + + public long getCreationTime() { + return creationTime; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(pitId); + out.writeLong(creationTime); + out.writeLong(keepAlive); + } + + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "list_pit_info", + true, + args -> new ListPitInfo((String) args[0], (long) args[1], (long) args[2]) + ); + + private static final ParseField CREATION_TIME = new ParseField("creation_time"); + private static final ParseField PIT_ID = new ParseField("pit_id"); + private static final ParseField KEEP_ALIVE = new ParseField("keep_alive"); + static { + PARSER.declareString(constructorArg(), PIT_ID); + PARSER.declareLong(constructorArg(), CREATION_TIME); + PARSER.declareLong(constructorArg(), KEEP_ALIVE); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field(PIT_ID.getPreferredName(), pitId); + builder.field(CREATION_TIME.getPreferredName(), creationTime); + builder.field(KEEP_ALIVE.getPreferredName(), keepAlive); + builder.endObject(); + return builder; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ListPitInfo that = (ListPitInfo) o; + return pitId.equals(that.pitId) && creationTime == that.creationTime && keepAlive == that.keepAlive; + } + + @Override + public int hashCode() { + return Objects.hash(pitId, creationTime, keepAlive); + } + +} diff --git a/server/src/main/java/org/opensearch/action/search/MaxScoreCollector.java b/server/src/main/java/org/opensearch/action/search/MaxScoreCollector.java index fce1de8cecbda..d7e0ba6d30fec 100644 --- a/server/src/main/java/org/opensearch/action/search/MaxScoreCollector.java +++ b/server/src/main/java/org/opensearch/action/search/MaxScoreCollector.java @@ -40,6 +40,8 @@ /** * A collector that computes the maximum score. + * + * @opensearch.internal */ public class MaxScoreCollector extends SimpleCollector { diff --git a/server/src/main/java/org/opensearch/action/search/MultiSearchAction.java b/server/src/main/java/org/opensearch/action/search/MultiSearchAction.java index fc8416536bf0a..84c18855324a9 100644 --- a/server/src/main/java/org/opensearch/action/search/MultiSearchAction.java +++ b/server/src/main/java/org/opensearch/action/search/MultiSearchAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for executing a multi search + * + * @opensearch.internal + */ public class MultiSearchAction extends ActionType { public static final MultiSearchAction INSTANCE = new MultiSearchAction(); diff --git a/server/src/main/java/org/opensearch/action/search/MultiSearchRequest.java b/server/src/main/java/org/opensearch/action/search/MultiSearchRequest.java index c45b6477d30f0..da8f8f144eaf2 100644 --- a/server/src/main/java/org/opensearch/action/search/MultiSearchRequest.java +++ b/server/src/main/java/org/opensearch/action/search/MultiSearchRequest.java @@ -38,19 +38,19 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.IndicesOptions.WildcardStates; import org.opensearch.common.CheckedBiConsumer; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.tasks.CancellableTask; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -70,6 +70,8 @@ /** * A multi search API request. + * + * @opensearch.internal */ public class MultiSearchRequest extends ActionRequest implements CompositeIndicesRequest { diff --git a/server/src/main/java/org/opensearch/action/search/MultiSearchRequestBuilder.java b/server/src/main/java/org/opensearch/action/search/MultiSearchRequestBuilder.java index e8db52b949425..f9bb90c69d925 100644 --- a/server/src/main/java/org/opensearch/action/search/MultiSearchRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/search/MultiSearchRequestBuilder.java @@ -38,6 +38,8 @@ /** * A request builder for multiple search requests. + * + * @opensearch.internal */ public class MultiSearchRequestBuilder extends ActionRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/search/MultiSearchResponse.java b/server/src/main/java/org/opensearch/action/search/MultiSearchResponse.java index 3869d909cce6e..89a053133abb9 100644 --- a/server/src/main/java/org/opensearch/action/search/MultiSearchResponse.java +++ b/server/src/main/java/org/opensearch/action/search/MultiSearchResponse.java @@ -32,32 +32,35 @@ package org.opensearch.action.search; +import org.opensearch.ExceptionsHelper; import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; import java.util.List; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * A multi search response. + * + * @opensearch.internal */ public class MultiSearchResponse extends ActionResponse implements Iterable, ToXContentObject { @@ -75,6 +78,8 @@ public class MultiSearchResponse extends ActionResponse implements Iterable> nodeToContextsMap, + ActionListener listener + ) { + if (nodeToContextsMap.size() == 0) { + listener.onResponse(new DeletePitResponse(Collections.emptyList())); + } + final Set clusters = nodeToContextsMap.values() + .stream() + .flatMap(Collection::stream) + .filter(ctx -> Strings.isEmpty(ctx.getSearchContextIdForNode().getClusterAlias()) == false) + .map(c -> c.getSearchContextIdForNode().getClusterAlias()) + .collect(Collectors.toSet()); + StepListener> lookupListener = (StepListener< + BiFunction>) SearchUtils.getConnectionLookupListener( + searchTransportService.getRemoteClusterService(), + clusterService.state(), + clusters + ); + lookupListener.whenComplete(nodeLookup -> { + final GroupedActionListener groupedListener = getDeletePitGroupedListener( + listener, + nodeToContextsMap.size() + ); + + for (Map.Entry> entry : nodeToContextsMap.entrySet()) { + String clusterAlias = entry.getValue().get(0).getSearchContextIdForNode().getClusterAlias(); + DiscoveryNode node = nodeLookup.apply(clusterAlias, entry.getValue().get(0).getSearchContextIdForNode().getNode()); + if (node == null) { + node = this.clusterService.state().getNodes().get(entry.getValue().get(0).getSearchContextIdForNode().getNode()); + } + if (node == null) { + logger.error( + () -> new ParameterizedMessage("node [{}] not found", entry.getValue().get(0).getSearchContextIdForNode().getNode()) + ); + List deletePitInfos = new ArrayList<>(); + for (PitSearchContextIdForNode pitSearchContextIdForNode : entry.getValue()) { + deletePitInfos.add(new DeletePitInfo(false, pitSearchContextIdForNode.getPitId())); + } + groupedListener.onResponse(new DeletePitResponse(deletePitInfos)); + } else { + try { + final Transport.Connection connection = searchTransportService.getConnection(clusterAlias, node); + searchTransportService.sendFreePITContexts(connection, entry.getValue(), groupedListener); + } catch (Exception e) { + String nodeName = node.getName(); + logger.error(() -> new ParameterizedMessage("Delete PITs failed on node [{}]", nodeName), e); + List deletePitInfos = new ArrayList<>(); + for (PitSearchContextIdForNode pitSearchContextIdForNode : entry.getValue()) { + deletePitInfos.add(new DeletePitInfo(false, pitSearchContextIdForNode.getPitId())); + } + groupedListener.onResponse(new DeletePitResponse(deletePitInfos)); + } + } + } + }, listener::onFailure); + } + + public GroupedActionListener getDeletePitGroupedListener(ActionListener listener, int size) { + return new GroupedActionListener<>(new ActionListener<>() { + @Override + public void onResponse(final Collection responses) { + Map pitIdToSucceededMap = new HashMap<>(); + for (DeletePitResponse response : responses) { + for (DeletePitInfo deletePitInfo : response.getDeletePitResults()) { + if (!pitIdToSucceededMap.containsKey(deletePitInfo.getPitId())) { + pitIdToSucceededMap.put(deletePitInfo.getPitId(), deletePitInfo.isSuccessful()); + } + if (!deletePitInfo.isSuccessful()) { + logger.debug(() -> new ParameterizedMessage("Deleting PIT with ID {} failed ", deletePitInfo.getPitId())); + pitIdToSucceededMap.put(deletePitInfo.getPitId(), deletePitInfo.isSuccessful()); + } + } + } + List deletePitResults = new ArrayList<>(); + for (Map.Entry entry : pitIdToSucceededMap.entrySet()) { + deletePitResults.add(new DeletePitInfo(entry.getValue(), entry.getKey())); + } + DeletePitResponse deletePitResponse = new DeletePitResponse(deletePitResults); + listener.onResponse(deletePitResponse); + } + + @Override + public void onFailure(final Exception e) { + logger.error("Delete PITs failed", e); + listener.onFailure(e); + } + }, size); + } + + /** + * This method returns indices associated for each pit + */ + public Map getIndicesForPits(List pitIds) { + Map pitToIndicesMap = new HashMap<>(); + for (String pitId : pitIds) { + pitToIndicesMap.put(pitId, SearchContextId.decode(nodeClient.getNamedWriteableRegistry(), pitId).getActualIndices()); + } + return pitToIndicesMap; + } + + /** + * Get all active point in time contexts + */ + public void getAllPits(ActionListener getAllPitsListener) { + final List nodes = new ArrayList<>(); + for (final DiscoveryNode node : clusterService.state().nodes().getDataNodes().values()) { + nodes.add(node); + } + DiscoveryNode[] disNodesArr = nodes.toArray(new DiscoveryNode[nodes.size()]); + GetAllPitNodesRequest getAllPitNodesRequest = new GetAllPitNodesRequest(disNodesArr); + transportService.sendRequest( + transportService.getLocalNode(), + GetAllPitsAction.NAME, + getAllPitNodesRequest, + new TransportResponseHandler() { + + @Override + public void handleResponse(GetAllPitNodesResponse response) { + getAllPitsListener.onResponse(response); + } + + @Override + public void handleException(TransportException exp) { + getAllPitsListener.onFailure(exp); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public GetAllPitNodesResponse read(StreamInput in) throws IOException { + return new GetAllPitNodesResponse(in); + } + } + ); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/PutSearchPipelineAction.java b/server/src/main/java/org/opensearch/action/search/PutSearchPipelineAction.java new file mode 100644 index 0000000000000..798c8211ee505 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/PutSearchPipelineAction.java @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.ActionType; +import org.opensearch.action.support.master.AcknowledgedResponse; + +/** + * Action type to put a new search pipeline + * + * @opensearch.internal + */ +public class PutSearchPipelineAction extends ActionType { + public static final PutSearchPipelineAction INSTANCE = new PutSearchPipelineAction(); + public static final String NAME = "cluster:admin/search/pipeline/put"; + + public PutSearchPipelineAction() { + super(NAME, AcknowledgedResponse::new); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/PutSearchPipelineRequest.java b/server/src/main/java/org/opensearch/action/search/PutSearchPipelineRequest.java new file mode 100644 index 0000000000000..48cf8e5028f90 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/PutSearchPipelineRequest.java @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.Version; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.action.support.master.AcknowledgedRequest; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Objects; + +/** + * Request to put a search pipeline + * + * @opensearch.internal + */ +public class PutSearchPipelineRequest extends AcknowledgedRequest implements ToXContentObject { + private String id; + private BytesReference source; + private MediaType mediaType; + + public PutSearchPipelineRequest(String id, BytesReference source, MediaType mediaType) { + this.id = Objects.requireNonNull(id); + this.source = Objects.requireNonNull(source); + if (mediaType instanceof XContentType == false) { + throw new IllegalArgumentException( + PutSearchPipelineRequest.class.getSimpleName() + " found unsupported media type [" + mediaType.getClass().getName() + "]" + ); + } + this.mediaType = Objects.requireNonNull(mediaType); + } + + public PutSearchPipelineRequest(StreamInput in) throws IOException { + super(in); + id = in.readString(); + source = in.readBytesReference(); + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType = in.readMediaType(); + } else { + mediaType = in.readEnum(XContentType.class); + } + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + public String getId() { + return id; + } + + public BytesReference getSource() { + return source; + } + + public MediaType getMediaType() { + return mediaType; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(id); + out.writeBytesReference(source); + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType.writeTo(out); + } else { + out.writeEnum((XContentType) mediaType); + } + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + if (source != null) { + builder.rawValue(source.streamInput(), mediaType); + } else { + builder.startObject().endObject(); + } + return builder; + } + +} diff --git a/server/src/main/java/org/opensearch/action/search/PutSearchPipelineTransportAction.java b/server/src/main/java/org/opensearch/action/search/PutSearchPipelineTransportAction.java new file mode 100644 index 0000000000000..a92961cdc3fd9 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/PutSearchPipelineTransportAction.java @@ -0,0 +1,99 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.admin.cluster.node.info.NodeInfo; +import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; +import org.opensearch.action.support.master.AcknowledgedResponse; +import org.opensearch.client.OriginSettingClient; +import org.opensearch.client.node.NodeClient; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.search.pipeline.SearchPipelineInfo; +import org.opensearch.search.pipeline.SearchPipelineService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static org.opensearch.search.pipeline.SearchPipelineService.SEARCH_PIPELINE_ORIGIN; + +/** + * Perform the action of putting a search pipeline + * + * @opensearch.internal + */ +public class PutSearchPipelineTransportAction extends TransportClusterManagerNodeAction { + + private final SearchPipelineService searchPipelineService; + private final OriginSettingClient client; + + @Inject + public PutSearchPipelineTransportAction( + ThreadPool threadPool, + TransportService transportService, + ActionFilters actionFilters, + IndexNameExpressionResolver indexNameExpressionResolver, + SearchPipelineService searchPipelineService, + NodeClient client + ) { + super( + PutSearchPipelineAction.NAME, + transportService, + searchPipelineService.getClusterService(), + threadPool, + actionFilters, + PutSearchPipelineRequest::new, + indexNameExpressionResolver + ); + this.client = new OriginSettingClient(client, SEARCH_PIPELINE_ORIGIN); + this.searchPipelineService = searchPipelineService; + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected AcknowledgedResponse read(StreamInput in) throws IOException { + return new AcknowledgedResponse(in); + } + + @Override + protected void clusterManagerOperation( + PutSearchPipelineRequest request, + ClusterState state, + ActionListener listener + ) throws Exception { + NodesInfoRequest nodesInfoRequest = new NodesInfoRequest(); + client.admin().cluster().nodesInfo(nodesInfoRequest, ActionListener.wrap(nodeInfos -> { + Map searchPipelineInfos = new HashMap<>(); + for (NodeInfo nodeInfo : nodeInfos.getNodes()) { + searchPipelineInfos.put(nodeInfo.getNode(), nodeInfo.getInfo(SearchPipelineInfo.class)); + } + searchPipelineService.putPipeline(searchPipelineInfos, request, listener); + }, listener::onFailure)); + } + + @Override + protected ClusterBlockException checkBlock(PutSearchPipelineRequest request, ClusterState state) { + return state.blocks().globalBlockedException(ClusterBlockLevel.METADATA_WRITE); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/QueryPhaseResultConsumer.java b/server/src/main/java/org/opensearch/action/search/QueryPhaseResultConsumer.java index 02e6b54777f7f..f1b06378bd579 100644 --- a/server/src/main/java/org/opensearch/action/search/QueryPhaseResultConsumer.java +++ b/server/src/main/java/org/opensearch/action/search/QueryPhaseResultConsumer.java @@ -35,13 +35,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.lucene.search.TopDocs; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.breaker.CircuitBreakingException; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.lease.Releasable; import org.opensearch.common.lease.Releasables; import org.opensearch.common.lucene.search.TopDocsAndMaxScore; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.breaker.CircuitBreakingException; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.aggregations.InternalAggregation.ReduceContextBuilder; @@ -66,6 +66,8 @@ * in the {@link CircuitBreaker#REQUEST} circuit breaker. Before any partial or final reduce, the memory * needed to reduce the aggregations is estimated and a {@link CircuitBreakingException} is thrown if it * exceeds the maximum memory allowed in this breaker. + * + * @opensearch.internal */ public class QueryPhaseResultConsumer extends ArraySearchPhaseResults implements Releasable { private static final Logger logger = LogManager.getLogger(QueryPhaseResultConsumer.class); @@ -240,6 +242,11 @@ public int getNumReducePhases() { return pendingMerges.numReducePhases; } + /** + * Class representing pending merges + * + * @opensearch.internal + */ private class PendingMerges implements Releasable { private final int batchReduceSize; private final List buffer = new ArrayList<>(); @@ -496,6 +503,11 @@ public synchronized List consumeAggs() { } } + /** + * A single merge result + * + * @opensearch.internal + */ private static class MergeResult { private final List processedShards; private final TopDocs reducedTopDocs; @@ -515,6 +527,11 @@ private MergeResult( } } + /** + * A single merge task + * + * @opensearch.internal + */ private static class MergeTask { private final List emptyResults; private QuerySearchResult[] buffer; diff --git a/server/src/main/java/org/opensearch/action/search/ReduceSearchPhaseException.java b/server/src/main/java/org/opensearch/action/search/ReduceSearchPhaseException.java index 88558ceb6f7d9..27522280336ea 100644 --- a/server/src/main/java/org/opensearch/action/search/ReduceSearchPhaseException.java +++ b/server/src/main/java/org/opensearch/action/search/ReduceSearchPhaseException.java @@ -32,7 +32,7 @@ package org.opensearch.action.search; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; @@ -40,7 +40,7 @@ * A failure during a reduce phase (when receiving results from several shards, and reducing them * into one or more results and possible actions). * - * + * @opensearch.internal */ public class ReduceSearchPhaseException extends SearchPhaseExecutionException { diff --git a/server/src/main/java/org/opensearch/action/search/SearchAction.java b/server/src/main/java/org/opensearch/action/search/SearchAction.java index e3a906f8b30ce..c94b3b7352196 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchAction.java +++ b/server/src/main/java/org/opensearch/action/search/SearchAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for executing a search + * + * @opensearch.internal + */ public class SearchAction extends ActionType { public static final SearchAction INSTANCE = new SearchAction(); diff --git a/server/src/main/java/org/opensearch/action/search/SearchActionListener.java b/server/src/main/java/org/opensearch/action/search/SearchActionListener.java index c081dba3fe9c5..c032012b0b45d 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchActionListener.java +++ b/server/src/main/java/org/opensearch/action/search/SearchActionListener.java @@ -31,13 +31,15 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchShardTarget; /** * A base action listener that ensures shard target and shard index is set on all responses * received by this listener. + * + * @opensearch.internal */ abstract class SearchActionListener implements ActionListener { diff --git a/server/src/main/java/org/opensearch/action/search/SearchContextId.java b/server/src/main/java/org/opensearch/action/search/SearchContextId.java index 59ebb128b924a..41887f09e3fca 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchContextId.java +++ b/server/src/main/java/org/opensearch/action/search/SearchContextId.java @@ -33,15 +33,15 @@ package org.opensearch.action.search; import org.opensearch.Version; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.ByteBufferStreamInput; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.ByteBufferStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.internal.AliasFilter; @@ -57,6 +57,11 @@ import java.util.Map; import java.util.Set; +/** + * Id for a serach context. + * + * @opensearch.internal + */ public class SearchContextId { private final Map shards; private final Map aliasFilter; @@ -85,7 +90,7 @@ public static String encode(List searchPhaseResults, Map k.writeTo(o), (o, v) -> v.writeTo(o)); out.writeMap(aliasFilter, StreamOutput::writeString, (o, v) -> v.writeTo(o)); return Base64.getUrlEncoder().encodeToString(BytesReference.toBytes(out.bytes())); @@ -102,7 +107,7 @@ public static SearchContextId decode(NamedWriteableRegistry namedWriteableRegist throw new IllegalArgumentException("invalid id: [" + id + "]", e); } try (StreamInput in = new NamedWriteableAwareStreamInput(new ByteBufferStreamInput(byteBuffer), namedWriteableRegistry)) { - final Version version = Version.readVersion(in); + final Version version = in.readVersion(); in.setVersion(version); final Map shards = in.readMap(ShardId::new, SearchContextIdForNode::new); final Map aliasFilters = in.readMap(StreamInput::readString, AliasFilter::new); @@ -111,7 +116,7 @@ public static SearchContextId decode(NamedWriteableRegistry namedWriteableRegist } return new SearchContextId(Collections.unmodifiableMap(shards), Collections.unmodifiableMap(aliasFilters)); } catch (IOException e) { - throw new IllegalArgumentException(e); + throw new IllegalArgumentException("invalid id: [" + id + "]", e); } } diff --git a/server/src/main/java/org/opensearch/action/search/SearchContextIdForNode.java b/server/src/main/java/org/opensearch/action/search/SearchContextIdForNode.java index 379b268042e52..59c0e54bb6cbc 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchContextIdForNode.java +++ b/server/src/main/java/org/opensearch/action/search/SearchContextIdForNode.java @@ -33,19 +33,24 @@ package org.opensearch.action.search; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.search.internal.ShardSearchContextId; import java.io.IOException; +/** + * Id for a search context per node. + * + * @opensearch.internal + */ public final class SearchContextIdForNode implements Writeable { private final String node; private final ShardSearchContextId searchContextId; private final String clusterAlias; - SearchContextIdForNode(@Nullable String clusterAlias, String node, ShardSearchContextId searchContextId) { + public SearchContextIdForNode(@Nullable String clusterAlias, String node, ShardSearchContextId searchContextId) { this.node = node; this.clusterAlias = clusterAlias; this.searchContextId = searchContextId; diff --git a/server/src/main/java/org/opensearch/action/search/SearchDfsQueryThenFetchAsyncAction.java b/server/src/main/java/org/opensearch/action/search/SearchDfsQueryThenFetchAsyncAction.java index 50aac3daed52d..eca9646ee6b7c 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchDfsQueryThenFetchAsyncAction.java +++ b/server/src/main/java/org/opensearch/action/search/SearchDfsQueryThenFetchAsyncAction.java @@ -33,9 +33,9 @@ package org.opensearch.action.search; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.routing.GroupShardsIterator; +import org.opensearch.core.action.ActionListener; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.builder.SearchSourceBuilder; import org.opensearch.search.dfs.AggregatedDfs; @@ -49,6 +49,11 @@ import java.util.concurrent.Executor; import java.util.function.BiFunction; +/** + * Async action for DFS Query Then Fetch + * + * @opensearch.internal + */ final class SearchDfsQueryThenFetchAsyncAction extends AbstractSearchAsyncAction { private final SearchPhaseController searchPhaseController; @@ -71,10 +76,11 @@ final class SearchDfsQueryThenFetchAsyncAction extends AbstractSearchAsyncAction final TransportSearchAction.SearchTimeProvider timeProvider, final ClusterState clusterState, final SearchTask task, - SearchResponse.Clusters clusters + SearchResponse.Clusters clusters, + SearchRequestOperationsListener searchRequestOperationsListener ) { super( - "dfs", + SearchPhaseName.DFS_PRE_QUERY.getName(), logger, searchTransportService, nodeIdToConnection, @@ -90,7 +96,8 @@ final class SearchDfsQueryThenFetchAsyncAction extends AbstractSearchAsyncAction task, new ArraySearchPhaseResults<>(shardsIts.size()), request.getMaxConcurrentShardRequests(), - clusters + clusters, + searchRequestOperationsListener ); this.queryPhaseResultConsumer = queryPhaseResultConsumer; this.searchPhaseController = searchPhaseController; diff --git a/server/src/main/java/org/opensearch/action/search/SearchExecutionStatsCollector.java b/server/src/main/java/org/opensearch/action/search/SearchExecutionStatsCollector.java index ce77a6368f2b6..842e87b3eb635 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchExecutionStatsCollector.java +++ b/server/src/main/java/org/opensearch/action/search/SearchExecutionStatsCollector.java @@ -32,7 +32,7 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; import org.opensearch.node.ResponseCollectorService; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.fetch.QueryFetchSearchResult; @@ -46,6 +46,8 @@ * A wrapper of search action listeners (search results) that unwraps the query * result to get the piggybacked queue size and service time EWMA, adding those * values to the coordinating nodes' {@link ResponseCollectorService}. + * + * @opensearch.internal */ public final class SearchExecutionStatsCollector implements ActionListener { diff --git a/server/src/main/java/org/opensearch/action/search/SearchPhase.java b/server/src/main/java/org/opensearch/action/search/SearchPhase.java index 18509a2694cd1..1c7b3c1f1563c 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchPhase.java +++ b/server/src/main/java/org/opensearch/action/search/SearchPhase.java @@ -34,22 +34,44 @@ import org.opensearch.common.CheckedRunnable; import java.io.IOException; +import java.util.Locale; import java.util.Objects; /** * Base class for all individual search phases like collecting distributed frequencies, fetching documents, querying shards. + * + * @opensearch.internal */ -abstract class SearchPhase implements CheckedRunnable { +public abstract class SearchPhase implements CheckedRunnable { private final String name; + private long startTimeInNanos; protected SearchPhase(String name) { this.name = Objects.requireNonNull(name, "name must not be null"); } + public long getStartTimeInNanos() { + return startTimeInNanos; + } + + public void recordAndRun() throws IOException { + this.startTimeInNanos = System.nanoTime(); + run(); + } + /** * Returns the phases name. */ public String getName() { return name; } + + /** + * Returns the SearchPhase name as {@link SearchPhaseName}. Exception will come if SearchPhase name is not defined + * in {@link SearchPhaseName} + * @return {@link SearchPhaseName} + */ + public SearchPhaseName getSearchPhaseName() { + return SearchPhaseName.valueOf(name.toUpperCase(Locale.ROOT)); + } } diff --git a/server/src/main/java/org/opensearch/action/search/SearchPhaseContext.java b/server/src/main/java/org/opensearch/action/search/SearchPhaseContext.java index 2f30d28a0924c..45d39a6f85ea2 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchPhaseContext.java +++ b/server/src/main/java/org/opensearch/action/search/SearchPhaseContext.java @@ -47,8 +47,10 @@ /** * This class provide contextual state and access to resources across multiple search phases. + * + * @opensearch.internal */ -interface SearchPhaseContext extends Executor { +public interface SearchPhaseContext extends Executor { // TODO maybe we can make this concrete later - for now we just implement this in the base class for all initial phases /** @@ -71,6 +73,8 @@ interface SearchPhaseContext extends Executor { */ SearchRequest getRequest(); + SearchPhase getCurrentPhase(); + /** * Builds and sends the final search response back to the user. * diff --git a/server/src/main/java/org/opensearch/action/search/SearchPhaseController.java b/server/src/main/java/org/opensearch/action/search/SearchPhaseController.java index 8c3b1d20b33a0..cca85f92d2676 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchPhaseController.java +++ b/server/src/main/java/org/opensearch/action/search/SearchPhaseController.java @@ -32,25 +32,22 @@ package org.opensearch.action.search; -import com.carrotsearch.hppc.IntArrayList; -import com.carrotsearch.hppc.ObjectObjectHashMap; - import org.apache.lucene.index.Term; import org.apache.lucene.search.CollectionStatistics; import org.apache.lucene.search.FieldDoc; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; +import org.apache.lucene.search.SortedNumericSortField; import org.apache.lucene.search.TermStatistics; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TopFieldDocs; import org.apache.lucene.search.TotalHits; import org.apache.lucene.search.TotalHits.Relation; import org.apache.lucene.search.grouping.CollapseTopFieldDocs; -import org.opensearch.common.breaker.CircuitBreaker; -import org.opensearch.common.collect.HppcMaps; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.lucene.search.TopDocsAndMaxScore; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.search.DocValueFormat; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; @@ -68,6 +65,7 @@ import org.opensearch.search.profile.ProfileShardResult; import org.opensearch.search.profile.SearchProfileShardResults; import org.opensearch.search.query.QuerySearchResult; +import org.opensearch.search.sort.SortedWiderNumericSortField; import org.opensearch.search.suggest.Suggest; import org.opensearch.search.suggest.Suggest.Suggestion; import org.opensearch.search.suggest.completion.CompletionSuggestion; @@ -84,23 +82,28 @@ import java.util.function.IntFunction; import java.util.stream.Collectors; +/** + * Controller for the search phase. + * + * @opensearch.internal + */ public final class SearchPhaseController { private static final ScoreDoc[] EMPTY_DOCS = new ScoreDoc[0]; private final NamedWriteableRegistry namedWriteableRegistry; - private final Function requestToAggReduceContextBuilder; + private final Function requestToAggReduceContextBuilder; public SearchPhaseController( NamedWriteableRegistry namedWriteableRegistry, - Function requestToAggReduceContextBuilder + Function requestToAggReduceContextBuilder ) { this.namedWriteableRegistry = namedWriteableRegistry; this.requestToAggReduceContextBuilder = requestToAggReduceContextBuilder; } public AggregatedDfs aggregateDfs(Collection results) { - ObjectObjectHashMap termStatistics = HppcMaps.newNoNullKeysMap(); - ObjectObjectHashMap fieldStatistics = HppcMaps.newNoNullKeysMap(); + final Map termStatistics = new HashMap<>(); + final Map fieldStatistics = new HashMap<>(); long aggMaxDoc = 0; for (DfsSearchResult lEntry : results) { final Term[] terms = lEntry.terms(); @@ -129,29 +132,25 @@ public AggregatedDfs aggregateDfs(Collection results) { } assert !lEntry.fieldStatistics().containsKey(null); - final Object[] keys = lEntry.fieldStatistics().keys; - final Object[] values = lEntry.fieldStatistics().values; - for (int i = 0; i < keys.length; i++) { - if (keys[i] != null) { - String key = (String) keys[i]; - CollectionStatistics value = (CollectionStatistics) values[i]; - if (value == null) { - continue; - } - assert key != null; - CollectionStatistics existing = fieldStatistics.get(key); - if (existing != null) { - CollectionStatistics merged = new CollectionStatistics( - key, - existing.maxDoc() + value.maxDoc(), - existing.docCount() + value.docCount(), - existing.sumTotalTermFreq() + value.sumTotalTermFreq(), - existing.sumDocFreq() + value.sumDocFreq() - ); - fieldStatistics.put(key, merged); - } else { - fieldStatistics.put(key, value); - } + for (var entry : lEntry.fieldStatistics().entrySet()) { + String key = entry.getKey(); + CollectionStatistics value = entry.getValue(); + if (value == null) { + continue; + } + assert key != null; + CollectionStatistics existing = fieldStatistics.get(key); + if (existing != null) { + CollectionStatistics merged = new CollectionStatistics( + key, + existing.maxDoc() + value.maxDoc(), + existing.docCount() + value.docCount(), + existing.sumTotalTermFreq() + value.sumTotalTermFreq(), + existing.sumDocFreq() + value.sumDocFreq() + ); + fieldStatistics.put(key, merged); + } else { + fieldStatistics.put(key, value); } } aggMaxDoc += lEntry.maxDoc(); @@ -230,14 +229,12 @@ static TopDocs mergeTopDocs(Collection results, int topN, int from) { if (numShards == 1 && from == 0) { // only one shard and no pagination we can just return the topDocs as we got them. return topDocs; } else if (topDocs instanceof CollapseTopFieldDocs) { - CollapseTopFieldDocs firstTopDocs = (CollapseTopFieldDocs) topDocs; - final Sort sort = new Sort(firstTopDocs.fields); final CollapseTopFieldDocs[] shardTopDocs = results.toArray(new CollapseTopFieldDocs[numShards]); + final Sort sort = createSort(shardTopDocs); mergedTopDocs = CollapseTopFieldDocs.merge(sort, from, topN, shardTopDocs, false); } else if (topDocs instanceof TopFieldDocs) { - TopFieldDocs firstTopDocs = (TopFieldDocs) topDocs; - final Sort sort = new Sort(firstTopDocs.fields); final TopFieldDocs[] shardTopDocs = results.toArray(new TopFieldDocs[numShards]); + final Sort sort = createSort(shardTopDocs); mergedTopDocs = TopDocs.merge(sort, from, topN, shardTopDocs); } else { final TopDocs[] shardTopDocs = results.toArray(new TopDocs[numShards]); @@ -272,12 +269,12 @@ public ScoreDoc[] getLastEmittedDocPerShard(ReducedQueryPhase reducedQueryPhase, /** * Builds an array, with potential null elements, with docs to load. */ - public IntArrayList[] fillDocIdsToLoad(int numShards, ScoreDoc[] shardDocs) { - IntArrayList[] docIdsToLoad = new IntArrayList[numShards]; + public List[] fillDocIdsToLoad(int numShards, ScoreDoc[] shardDocs) { + final List[] docIdsToLoad = (List[]) new ArrayList[numShards]; for (ScoreDoc shardDoc : shardDocs) { - IntArrayList shardDocIdsToLoad = docIdsToLoad[shardDoc.shardIndex]; + List shardDocIdsToLoad = docIdsToLoad[shardDoc.shardIndex]; if (shardDocIdsToLoad == null) { - shardDocIdsToLoad = docIdsToLoad[shardDoc.shardIndex] = new IntArrayList(); + shardDocIdsToLoad = docIdsToLoad[shardDoc.shardIndex] = new ArrayList<>(); } shardDocIdsToLoad.add(shardDoc.doc); } @@ -579,12 +576,14 @@ private static void validateMergeSortValueFormats(Collection { +public abstract class SearchPhaseResults { private final int numShards; SearchPhaseResults(int numShards) { @@ -73,7 +75,13 @@ final int getNumShards() { void consumeShardFailure(int shardIndex) {} - AtomicArray getAtomicArray() { + /** + * Returns an {@link AtomicArray} of {@link Result}, which are nothing but the SearchPhaseResults + * for shards. The {@link Result} are of type {@link SearchPhaseResult} + * + * @return an {@link AtomicArray} of {@link Result} + */ + public AtomicArray getAtomicArray() { throw new UnsupportedOperationException(); } diff --git a/server/src/main/java/org/opensearch/action/search/SearchProgressActionListener.java b/server/src/main/java/org/opensearch/action/search/SearchProgressActionListener.java index 088aac6d71d30..320bebfa6a9f4 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchProgressActionListener.java +++ b/server/src/main/java/org/opensearch/action/search/SearchProgressActionListener.java @@ -32,10 +32,12 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; /** * An {@link ActionListener} for search requests that allows to track progress of the {@link SearchAction}. * See {@link SearchProgressListener}. + * + * @opensearch.internal */ public abstract class SearchProgressActionListener extends SearchProgressListener implements ActionListener {} diff --git a/server/src/main/java/org/opensearch/action/search/SearchProgressListener.java b/server/src/main/java/org/opensearch/action/search/SearchProgressListener.java index 78b1dd19dadaa..ffc64682cb07d 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchProgressListener.java +++ b/server/src/main/java/org/opensearch/action/search/SearchProgressListener.java @@ -50,6 +50,8 @@ /** * A listener that allows to track progress of the {@link SearchAction}. + * + * @opensearch.api */ public abstract class SearchProgressListener { private static final Logger logger = LogManager.getLogger(SearchProgressListener.class); diff --git a/server/src/main/java/org/opensearch/action/search/SearchQueryThenFetchAsyncAction.java b/server/src/main/java/org/opensearch/action/search/SearchQueryThenFetchAsyncAction.java index 93ce3cfd8c715..ca5ad087d3089 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchQueryThenFetchAsyncAction.java +++ b/server/src/main/java/org/opensearch/action/search/SearchQueryThenFetchAsyncAction.java @@ -34,9 +34,9 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.search.TopFieldDocs; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.routing.GroupShardsIterator; +import org.opensearch.core.action.ActionListener; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.internal.AliasFilter; @@ -50,6 +50,11 @@ import java.util.concurrent.Executor; import java.util.function.BiFunction; +/** + * Async transport action for query then fetch + * + * @opensearch.internal + */ class SearchQueryThenFetchAsyncAction extends AbstractSearchAsyncAction { private final SearchPhaseController searchPhaseController; @@ -76,10 +81,11 @@ class SearchQueryThenFetchAsyncAction extends AbstractSearchAsyncAction headers) { return new SearchTask(id, type, action, this::buildDescription, parentTaskId, headers, cancelAfterTimeInterval); @@ -717,7 +737,8 @@ public boolean equals(Object o) { && Objects.equals(localClusterAlias, that.localClusterAlias) && absoluteStartMillis == that.absoluteStartMillis && ccsMinimizeRoundtrips == that.ccsMinimizeRoundtrips - && Objects.equals(cancelAfterTimeInterval, that.cancelAfterTimeInterval); + && Objects.equals(cancelAfterTimeInterval, that.cancelAfterTimeInterval) + && Objects.equals(pipeline, that.pipeline); } @Override @@ -779,6 +800,8 @@ public String toString() { + source + ", cancelAfterTimeInterval=" + cancelAfterTimeInterval + + ", pipeline=" + + pipeline + "}"; } } diff --git a/server/src/main/java/org/opensearch/action/search/SearchRequestBuilder.java b/server/src/main/java/org/opensearch/action/search/SearchRequestBuilder.java index 6def33f82b7bd..861e1df0203d7 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/search/SearchRequestBuilder.java @@ -57,6 +57,8 @@ /** * A search action request builder. + * + * @opensearch.internal */ public class SearchRequestBuilder extends ActionRequestBuilder { @@ -149,7 +151,8 @@ public SearchRequestBuilder setRouting(String... routing) { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public SearchRequestBuilder setPreference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/search/SearchRequestOperationsListener.java b/server/src/main/java/org/opensearch/action/search/SearchRequestOperationsListener.java new file mode 100644 index 0000000000000..89d725b56bded --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/SearchRequestOperationsListener.java @@ -0,0 +1,77 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; + +import java.util.List; + +/** + * A listener for search, fetch and context events at the coordinator node level + * + * @opensearch.internal + */ +public interface SearchRequestOperationsListener { + + void onPhaseStart(SearchPhaseContext context); + + void onPhaseEnd(SearchPhaseContext context); + + void onPhaseFailure(SearchPhaseContext context); + + /** + * Holder of Composite Listeners + * + * @opensearch.internal + */ + + final class CompositeListener implements SearchRequestOperationsListener { + private final List listeners; + private final Logger logger; + + public CompositeListener(List listeners, Logger logger) { + this.listeners = listeners; + this.logger = logger; + } + + @Override + public void onPhaseStart(SearchPhaseContext context) { + for (SearchRequestOperationsListener listener : listeners) { + try { + listener.onPhaseStart(context); + } catch (Exception e) { + logger.warn(() -> new ParameterizedMessage("onPhaseStart listener [{}] failed", listener), e); + } + } + } + + @Override + public void onPhaseEnd(SearchPhaseContext context) { + for (SearchRequestOperationsListener listener : listeners) { + try { + listener.onPhaseEnd(context); + } catch (Exception e) { + logger.warn(() -> new ParameterizedMessage("onPhaseEnd listener [{}] failed", listener), e); + } + } + } + + @Override + public void onPhaseFailure(SearchPhaseContext context) { + for (SearchRequestOperationsListener listener : listeners) { + try { + listener.onPhaseFailure(context); + } catch (Exception e) { + logger.warn(() -> new ParameterizedMessage("onPhaseFailure listener [{}] failed", listener), e); + } + } + } + } +} diff --git a/server/src/main/java/org/opensearch/action/search/SearchRequestStats.java b/server/src/main/java/org/opensearch/action/search/SearchRequestStats.java new file mode 100644 index 0000000000000..ad299c11b987d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/SearchRequestStats.java @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.common.inject.Inject; +import org.opensearch.common.metrics.CounterMetric; +import org.opensearch.common.metrics.MeanMetric; + +import java.util.EnumMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Request level search stats to track coordinator level node search latencies + * + * @opensearch.internal + */ +public final class SearchRequestStats implements SearchRequestOperationsListener { + Map phaseStatsMap = new EnumMap<>(SearchPhaseName.class); + + @Inject + public SearchRequestStats() { + for (SearchPhaseName searchPhaseName : SearchPhaseName.values()) { + phaseStatsMap.put(searchPhaseName, new StatsHolder()); + } + } + + public long getPhaseCurrent(SearchPhaseName searchPhaseName) { + return phaseStatsMap.get(searchPhaseName).current.count(); + } + + public long getPhaseTotal(SearchPhaseName searchPhaseName) { + return phaseStatsMap.get(searchPhaseName).total.count(); + } + + public long getPhaseMetric(SearchPhaseName searchPhaseName) { + return phaseStatsMap.get(searchPhaseName).timing.sum(); + } + + @Override + public void onPhaseStart(SearchPhaseContext context) { + phaseStatsMap.get(context.getCurrentPhase().getSearchPhaseName()).current.inc(); + } + + @Override + public void onPhaseEnd(SearchPhaseContext context) { + StatsHolder phaseStats = phaseStatsMap.get(context.getCurrentPhase().getSearchPhaseName()); + phaseStats.current.dec(); + phaseStats.total.inc(); + phaseStats.timing.inc(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - context.getCurrentPhase().getStartTimeInNanos())); + } + + @Override + public void onPhaseFailure(SearchPhaseContext context) { + phaseStatsMap.get(context.getCurrentPhase().getSearchPhaseName()).current.dec(); + } + + /** + * Holder of statistics values + * + * @opensearch.internal + */ + + public static final class StatsHolder { + CounterMetric current = new CounterMetric(); + CounterMetric total = new CounterMetric(); + MeanMetric timing = new MeanMetric(); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/SearchResponse.java b/server/src/main/java/org/opensearch/action/search/SearchResponse.java index e1175dc079ee0..3eb698f2c140d 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchResponse.java +++ b/server/src/main/java/org/opensearch/action/search/SearchResponse.java @@ -34,21 +34,25 @@ import org.apache.lucene.search.TotalHits; import org.opensearch.LegacyESVersion; -import org.opensearch.action.ActionResponse; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.StatusToXContentObject; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParseException; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.rest.action.RestActions; +import org.opensearch.search.GenericSearchExtBuilder; +import org.opensearch.search.SearchExtBuilder; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.aggregations.Aggregations; @@ -65,10 +69,13 @@ import java.util.Objects; import java.util.function.Supplier; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.action.search.SearchResponseSections.EXT_FIELD; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * A response of a search request. + * + * @opensearch.internal */ public class SearchResponse extends ActionResponse implements StatusToXContentObject { @@ -78,6 +85,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb private static final ParseField TIMED_OUT = new ParseField("timed_out"); private static final ParseField TERMINATED_EARLY = new ParseField("terminated_early"); private static final ParseField NUM_REDUCE_PHASES = new ParseField("num_reduce_phases"); + private static final ParseField EXT = new ParseField("ext"); private final SearchResponseSections internalResponse; private final String scrollId; @@ -89,6 +97,8 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb private final Clusters clusters; private final long tookInMillis; + private List searchExtBuilders = new ArrayList<>(); + public SearchResponse(StreamInput in) throws IOException { super(in); internalResponse = new InternalSearchResponse(in); @@ -314,6 +324,7 @@ public XContentBuilder innerToXContent(XContentBuilder builder, Params params) t ); clusters.toXContent(builder, params); internalResponse.toXContent(builder, params); + return builder; } @@ -341,6 +352,7 @@ public static SearchResponse innerFromXContent(XContentParser parser) throws IOE String searchContextId = null; List failures = new ArrayList<>(); Clusters clusters = Clusters.EMPTY; + List extBuilders = new ArrayList<>(); for (Token token = parser.nextToken(); token != Token.END_OBJECT; token = parser.nextToken()) { if (token == Token.FIELD_NAME) { currentFieldName = parser.currentName(); @@ -419,6 +431,33 @@ public static SearchResponse innerFromXContent(XContentParser parser) throws IOE } } clusters = new Clusters(total, successful, skipped); + } else if (EXT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + String extSectionName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + extSectionName = parser.currentName(); + } else { + SearchExtBuilder searchExtBuilder; + try { + searchExtBuilder = parser.namedObject(SearchExtBuilder.class, extSectionName, null); + if (!searchExtBuilder.getWriteableName().equals(extSectionName)) { + throw new IllegalStateException( + "The parsed [" + + searchExtBuilder.getClass().getName() + + "] object has a " + + "different writeable name compared to the name of the section that it was parsed from: found [" + + searchExtBuilder.getWriteableName() + + "] expected [" + + extSectionName + + "]" + ); + } + } catch (XContentParseException e) { + searchExtBuilder = GenericSearchExtBuilder.fromXContent(parser); + } + extBuilders.add(searchExtBuilder); + } + } } else { parser.skipChildren(); } @@ -431,7 +470,8 @@ public static SearchResponse innerFromXContent(XContentParser parser) throws IOE timedOut, terminatedEarly, profile, - numReducePhases + numReducePhases, + extBuilders ); return new SearchResponse( searchResponseSections, @@ -467,12 +507,18 @@ public void writeTo(StreamOutput out) throws IOException { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); + } + + public void addSearchExtBuilder(SearchExtBuilder searchExtBuilder) { + this.searchExtBuilders.add(searchExtBuilder); } /** * Holds info about the clusters that the search was executed on: how many in total, how many of them were successful * and how many of them were skipped. + * + * @opensearch.internal */ public static class Clusters implements ToXContentFragment, Writeable { diff --git a/server/src/main/java/org/opensearch/action/search/SearchResponseMerger.java b/server/src/main/java/org/opensearch/action/search/SearchResponseMerger.java index 528c0693fbcd8..f90e98106f93f 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchResponseMerger.java +++ b/server/src/main/java/org/opensearch/action/search/SearchResponseMerger.java @@ -41,7 +41,7 @@ import org.apache.lucene.search.grouping.CollapseTopFieldDocs; import org.opensearch.OpenSearchException; import org.opensearch.common.lucene.search.TopDocsAndMaxScore; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.search.SearchHit; import org.opensearch.search.SearchHits; import org.opensearch.search.SearchShardTarget; @@ -79,6 +79,8 @@ * - scroll requests are not supported * - field collapsing is supported, but whenever inner_hits are requested, they will be retrieved by each cluster locally after the fetch * phase, through the {@link ExpandSearchPhase}. Such inner_hits are not merged together as part of hits reduction. + * + * @opensearch.internal */ // TODO it may make sense to integrate the remote clusters responses as a shard response in the initial search phase and ignore hits coming // from the remote clusters in the fetch phase. This would be identical to the removed QueryAndFetch strategy except that only the remote @@ -408,6 +410,11 @@ private static SearchHits topDocsToSearchHits(TopDocs topDocs, SearchPhaseContro ); } + /** + * Holds a field search hit and doc + * + * @opensearch.internal + */ private static final class FieldDocAndSearchHit extends FieldDoc { private final SearchHit searchHit; @@ -424,6 +431,8 @@ private static final class FieldDocAndSearchHit extends FieldDoc { * (see TopDocs#tieBreakLessThan line 86). Generally, indices with same names on different clusters have different index uuids which * make their ShardIds different, which is not the case if the index is really the same one from the same cluster, in which case we * need to look at the cluster alias and make sure to assign a different shardIndex based on that. + * + * @opensearch.internal */ private static final class ShardIdAndClusterAlias implements Comparable { private final ShardId shardId; diff --git a/server/src/main/java/org/opensearch/action/search/SearchResponseSections.java b/server/src/main/java/org/opensearch/action/search/SearchResponseSections.java index e146859bb295f..2e447abd125c5 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchResponseSections.java +++ b/server/src/main/java/org/opensearch/action/search/SearchResponseSections.java @@ -32,9 +32,11 @@ package org.opensearch.action.search; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.search.SearchExtBuilder; import org.opensearch.search.SearchHits; import org.opensearch.search.aggregations.Aggregations; import org.opensearch.search.profile.ProfileShardResult; @@ -42,8 +44,11 @@ import org.opensearch.search.suggest.Suggest; import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.Objects; /** * Base class that holds the various sections which a search response is @@ -52,9 +57,13 @@ * The reason why this class exists is that the high level REST client uses its own classes * to parse aggregations into, which are not serializable. This is the common part that can be * shared between core and client. + * + * @opensearch.internal */ public class SearchResponseSections implements ToXContentFragment { + public static final ParseField EXT_FIELD = new ParseField("ext"); + protected final SearchHits hits; protected final Aggregations aggregations; protected final Suggest suggest; @@ -62,6 +71,7 @@ public class SearchResponseSections implements ToXContentFragment { protected final boolean timedOut; protected final Boolean terminatedEarly; protected final int numReducePhases; + protected final List searchExtBuilders = new ArrayList<>(); public SearchResponseSections( SearchHits hits, @@ -71,6 +81,19 @@ public SearchResponseSections( Boolean terminatedEarly, SearchProfileShardResults profileResults, int numReducePhases + ) { + this(hits, aggregations, suggest, timedOut, terminatedEarly, profileResults, numReducePhases, Collections.emptyList()); + } + + public SearchResponseSections( + SearchHits hits, + Aggregations aggregations, + Suggest suggest, + boolean timedOut, + Boolean terminatedEarly, + SearchProfileShardResults profileResults, + int numReducePhases, + List searchExtBuilders ) { this.hits = hits; this.aggregations = aggregations; @@ -79,6 +102,7 @@ public SearchResponseSections( this.timedOut = timedOut; this.terminatedEarly = terminatedEarly; this.numReducePhases = numReducePhases; + this.searchExtBuilders.addAll(Objects.requireNonNull(searchExtBuilders, "searchExtBuilders must not be null")); } public final boolean timedOut() { @@ -133,9 +157,20 @@ public final XContentBuilder toXContent(XContentBuilder builder, Params params) if (profileResults != null) { profileResults.toXContent(builder, params); } + if (!searchExtBuilders.isEmpty()) { + builder.startObject(EXT_FIELD.getPreferredName()); + for (SearchExtBuilder searchExtBuilder : searchExtBuilders) { + searchExtBuilder.toXContent(builder, params); + } + builder.endObject(); + } return builder; } + public List getSearchExtBuilders() { + return Collections.unmodifiableList(this.searchExtBuilders); + } + protected void writeTo(StreamOutput out) throws IOException { throw new UnsupportedOperationException(); } diff --git a/server/src/main/java/org/opensearch/action/search/SearchScrollAction.java b/server/src/main/java/org/opensearch/action/search/SearchScrollAction.java index 4aec3d39e6f47..1f466d35f0ac2 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchScrollAction.java +++ b/server/src/main/java/org/opensearch/action/search/SearchScrollAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Transport action for a search scroll + * + * @opensearch.internal + */ public class SearchScrollAction extends ActionType { public static final SearchScrollAction INSTANCE = new SearchScrollAction(); diff --git a/server/src/main/java/org/opensearch/action/search/SearchScrollAsyncAction.java b/server/src/main/java/org/opensearch/action/search/SearchScrollAsyncAction.java index f98a75d37988c..51d9a34937f79 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchScrollAsyncAction.java +++ b/server/src/main/java/org/opensearch/action/search/SearchScrollAsyncAction.java @@ -34,12 +34,12 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.common.Nullable; import org.opensearch.common.util.concurrent.AtomicArray; import org.opensearch.common.util.concurrent.CountDown; +import org.opensearch.core.action.ActionListener; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.internal.InternalScrollSearchRequest; @@ -62,6 +62,8 @@ * Abstract base class for scroll execution modes. This class encapsulates the basic logic to * fan out to nodes and execute the query part of the scroll request. Subclasses can for instance * run separate fetch phases etc. + * + * @opensearch.internal */ abstract class SearchScrollAsyncAction implements Runnable { protected final Logger logger; @@ -264,7 +266,7 @@ protected SearchPhase sendResponsePhase( SearchPhaseController.ReducedQueryPhase queryPhase, final AtomicArray fetchResults ) { - return new SearchPhase("fetch") { + return new SearchPhase(SearchPhaseName.FETCH.getName()) { @Override public void run() throws IOException { sendResponse(queryPhase, fetchResults); diff --git a/server/src/main/java/org/opensearch/action/search/SearchScrollQueryAndFetchAsyncAction.java b/server/src/main/java/org/opensearch/action/search/SearchScrollQueryAndFetchAsyncAction.java index 59ce2567bdf18..f7737fe59a975 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchScrollQueryAndFetchAsyncAction.java +++ b/server/src/main/java/org/opensearch/action/search/SearchScrollQueryAndFetchAsyncAction.java @@ -33,10 +33,10 @@ package org.opensearch.action.search; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.util.concurrent.AtomicArray; +import org.opensearch.core.action.ActionListener; import org.opensearch.search.fetch.QueryFetchSearchResult; import org.opensearch.search.fetch.ScrollQueryFetchSearchResult; import org.opensearch.search.internal.InternalScrollSearchRequest; @@ -44,6 +44,11 @@ import java.util.function.BiFunction; +/** + * Async action for a search scroll query then fetch + * + * @opensearch.internal + */ final class SearchScrollQueryAndFetchAsyncAction extends SearchScrollAsyncAction { private final SearchTask task; diff --git a/server/src/main/java/org/opensearch/action/search/SearchScrollQueryThenFetchAsyncAction.java b/server/src/main/java/org/opensearch/action/search/SearchScrollQueryThenFetchAsyncAction.java index effe5afd0cad6..87ba32016370f 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchScrollQueryThenFetchAsyncAction.java +++ b/server/src/main/java/org/opensearch/action/search/SearchScrollQueryThenFetchAsyncAction.java @@ -32,14 +32,13 @@ package org.opensearch.action.search; -import com.carrotsearch.hppc.IntArrayList; import org.apache.logging.log4j.Logger; import org.apache.lucene.search.ScoreDoc; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.util.concurrent.AtomicArray; import org.opensearch.common.util.concurrent.CountDown; +import org.opensearch.core.action.ActionListener; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.fetch.FetchSearchResult; import org.opensearch.search.fetch.ShardFetchRequest; @@ -48,8 +47,14 @@ import org.opensearch.search.query.ScrollQuerySearchResult; import org.opensearch.transport.Transport; +import java.util.List; import java.util.function.BiFunction; +/** + * async action for a search scroll query then fetch + * + * @opensearch.internal + */ final class SearchScrollQueryThenFetchAsyncAction extends SearchScrollAsyncAction { private final SearchTask task; @@ -87,7 +92,7 @@ protected void executeInitialPhase( @Override protected SearchPhase moveToNextPhase(BiFunction clusterNodeLookup) { - return new SearchPhase("fetch") { + return new SearchPhase(SearchPhaseName.FETCH.getName()) { @Override public void run() { final SearchPhaseController.ReducedQueryPhase reducedQueryPhase = searchPhaseController.reducedScrollQueryPhase( @@ -99,7 +104,7 @@ public void run() { return; } - final IntArrayList[] docIdsToLoad = searchPhaseController.fillDocIdsToLoad(queryResults.length(), scoreDocs); + final List[] docIdsToLoad = searchPhaseController.fillDocIdsToLoad(queryResults.length(), scoreDocs); final ScoreDoc[] lastEmittedDocPerShard = searchPhaseController.getLastEmittedDocPerShard( reducedQueryPhase, queryResults.length() @@ -107,7 +112,7 @@ public void run() { final CountDown counter = new CountDown(docIdsToLoad.length); for (int i = 0; i < docIdsToLoad.length; i++) { final int index = i; - final IntArrayList docIds = docIdsToLoad[index]; + final List docIds = docIdsToLoad[index]; if (docIds != null) { final QuerySearchResult querySearchResult = queryResults.get(index); ScoreDoc lastEmittedDoc = lastEmittedDocPerShard[index]; diff --git a/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java b/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java index 828228e869831..cda00811e5500 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java +++ b/server/src/main/java/org/opensearch/action/search/SearchScrollRequest.java @@ -34,15 +34,15 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.Scroll; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import java.io.IOException; import java.util.Map; @@ -50,6 +50,11 @@ import static org.opensearch.action.ValidateActions.addValidationError; +/** + * Transport request for a search scroll + * + * @opensearch.internal + */ public class SearchScrollRequest extends ActionRequest implements ToXContentObject { private String scrollId; diff --git a/server/src/main/java/org/opensearch/action/search/SearchScrollRequestBuilder.java b/server/src/main/java/org/opensearch/action/search/SearchScrollRequestBuilder.java index 79bd952333de2..638c595216631 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchScrollRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/search/SearchScrollRequestBuilder.java @@ -39,6 +39,8 @@ /** * A search scroll action request builder. + * + * @opensearch.internal */ public class SearchScrollRequestBuilder extends ActionRequestBuilder { diff --git a/server/src/main/java/org/opensearch/action/search/SearchShard.java b/server/src/main/java/org/opensearch/action/search/SearchShard.java index 110209e3e9af0..478788277bb19 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchShard.java +++ b/server/src/main/java/org/opensearch/action/search/SearchShard.java @@ -33,7 +33,7 @@ package org.opensearch.action.search; import org.opensearch.common.Nullable; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import java.util.Comparator; import java.util.Objects; @@ -41,6 +41,8 @@ /** * A class that encapsulates the {@link ShardId} and the cluster alias * of a shard used during the search action. + * + * @opensearch.internal */ public final class SearchShard implements Comparable { @Nullable diff --git a/server/src/main/java/org/opensearch/action/search/SearchShardIterator.java b/server/src/main/java/org/opensearch/action/search/SearchShardIterator.java index 1ae0d8f78aa9c..fbd85a3fc0b8f 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchShardIterator.java +++ b/server/src/main/java/org/opensearch/action/search/SearchShardIterator.java @@ -39,7 +39,7 @@ import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.Countable; import org.opensearch.common.util.PlainIterator; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.internal.ShardSearchContextId; @@ -53,6 +53,8 @@ * of the search request (useful especially with cross-cluster search, as each cluster has its own set of original indices) as well as * the cluster alias. * @see OriginalIndices + * + * @opensearch.internal */ public final class SearchShardIterator implements Comparable, Countable { @@ -117,7 +119,7 @@ public String getClusterAlias() { return clusterAlias; } - SearchShardTarget nextOrNull() { + public SearchShardTarget nextOrNull() { final String nodeId = targetNodesIterator.nextOrNull(); if (nodeId != null) { return new SearchShardTarget(nodeId, shardId, clusterAlias, originalIndices); diff --git a/server/src/main/java/org/opensearch/action/search/SearchShardTask.java b/server/src/main/java/org/opensearch/action/search/SearchShardTask.java index 2e506c6fe181b..54faaf363cb70 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchShardTask.java +++ b/server/src/main/java/org/opensearch/action/search/SearchShardTask.java @@ -32,21 +32,50 @@ package org.opensearch.action.search; +import org.opensearch.common.MemoizedSupplier; +import org.opensearch.core.tasks.TaskId; import org.opensearch.search.fetch.ShardFetchSearchRequest; import org.opensearch.search.internal.ShardSearchRequest; import org.opensearch.tasks.CancellableTask; -import org.opensearch.tasks.TaskId; +import org.opensearch.tasks.SearchBackpressureTask; import java.util.Map; +import java.util.function.Supplier; /** * Task storing information about a currently running search shard request. * See {@link ShardSearchRequest}, {@link ShardFetchSearchRequest}, ... + * + * @opensearch.internal */ -public class SearchShardTask extends CancellableTask { +public class SearchShardTask extends CancellableTask implements SearchBackpressureTask { + // generating metadata in a lazy way since source can be quite big + private final MemoizedSupplier metadataSupplier; public SearchShardTask(long id, String type, String action, String description, TaskId parentTaskId, Map headers) { + this(id, type, action, description, parentTaskId, headers, () -> ""); + } + + public SearchShardTask( + long id, + String type, + String action, + String description, + TaskId parentTaskId, + Map headers, + Supplier metadataSupplier + ) { super(id, type, action, description, parentTaskId, headers); + this.metadataSupplier = new MemoizedSupplier<>(metadataSupplier); + } + + public String getTaskMetadata() { + return metadataSupplier.get(); + } + + @Override + public boolean supportsResourceTracking() { + return true; } @Override diff --git a/server/src/main/java/org/opensearch/action/search/SearchTask.java b/server/src/main/java/org/opensearch/action/search/SearchTask.java index 7f80f7836be6c..20370b7b17a07 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchTask.java +++ b/server/src/main/java/org/opensearch/action/search/SearchTask.java @@ -33,8 +33,9 @@ package org.opensearch.action.search; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.CancellableTask; -import org.opensearch.tasks.TaskId; +import org.opensearch.tasks.SearchBackpressureTask; import java.util.Map; import java.util.function.Supplier; @@ -43,8 +44,10 @@ /** * Task storing information about a currently running {@link SearchRequest}. + * + * @opensearch.internal */ -public class SearchTask extends CancellableTask { +public class SearchTask extends CancellableTask implements SearchBackpressureTask { // generating description in a lazy way since source can be quite big private final Supplier descriptionSupplier; private SearchProgressListener progressListener = SearchProgressListener.NOOP; @@ -78,6 +81,11 @@ public final String getDescription() { return descriptionSupplier.get(); } + @Override + public boolean supportsResourceTracking() { + return true; + } + /** * Attach a {@link SearchProgressListener} to this task. */ diff --git a/server/src/main/java/org/opensearch/action/search/SearchTransportService.java b/server/src/main/java/org/opensearch/action/search/SearchTransportService.java index 121de6d1c80da..a25ff0a0201a1 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchTransportService.java +++ b/server/src/main/java/org/opensearch/action/search/SearchTransportService.java @@ -34,7 +34,6 @@ import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionListenerResponseHandler; import org.opensearch.action.IndicesRequest; import org.opensearch.action.OriginalIndices; @@ -42,10 +41,12 @@ import org.opensearch.action.support.IndicesOptions; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.util.concurrent.ConcurrentCollections; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchService; import org.opensearch.search.dfs.DfsSearchResult; @@ -67,11 +68,12 @@ import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestOptions; -import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportService; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.BiFunction; @@ -79,12 +81,16 @@ /** * An encapsulation of {@link org.opensearch.search.SearchService} operations exposed through * transport. + * + * @opensearch.internal */ public class SearchTransportService { public static final String FREE_CONTEXT_SCROLL_ACTION_NAME = "indices:data/read/search[free_context/scroll]"; public static final String FREE_CONTEXT_ACTION_NAME = "indices:data/read/search[free_context]"; public static final String CLEAR_SCROLL_CONTEXTS_ACTION_NAME = "indices:data/read/search[clear_scroll_contexts]"; + public static final String FREE_PIT_CONTEXT_ACTION_NAME = "indices:data/read/search[free_context/pit]"; + public static final String FREE_ALL_PIT_CONTEXTS_ACTION_NAME = "indices:data/read/search[free_pit_contexts]"; public static final String DFS_ACTION_NAME = "indices:data/read/search[phase/dfs]"; public static final String QUERY_ACTION_NAME = "indices:data/read/search[phase/query]"; public static final String QUERY_ID_ACTION_NAME = "indices:data/read/search[phase/query/id]"; @@ -93,6 +99,8 @@ public class SearchTransportService { public static final String FETCH_ID_SCROLL_ACTION_NAME = "indices:data/read/search[phase/fetch/id/scroll]"; public static final String FETCH_ID_ACTION_NAME = "indices:data/read/search[phase/fetch/id]"; public static final String QUERY_CAN_MATCH_NAME = "indices:data/read/search[can_match]"; + public static final String CREATE_READER_CONTEXT_ACTION_NAME = "indices:data/read/search[create_context]"; + public static final String UPDATE_READER_CONTEXT_ACTION_NAME = "indices:data/read/search[update_context]"; private final TransportService transportService; private final BiFunction responseWrapper; @@ -140,6 +148,36 @@ public void sendFreeContext( ); } + public void updatePitContext( + Transport.Connection connection, + UpdatePitContextRequest request, + ActionListener actionListener + ) { + transportService.sendRequest( + connection, + UPDATE_READER_CONTEXT_ACTION_NAME, + request, + TransportRequestOptions.EMPTY, + new ActionListenerResponseHandler<>(actionListener, UpdatePitContextResponse::new) + ); + } + + public void createPitContext( + Transport.Connection connection, + TransportCreatePitAction.CreateReaderContextRequest request, + SearchTask task, + ActionListener actionListener + ) { + transportService.sendChildRequest( + connection, + CREATE_READER_CONTEXT_ACTION_NAME, + request, + task, + TransportRequestOptions.EMPTY, + new ActionListenerResponseHandler<>(actionListener, TransportCreatePitAction.CreateReaderContextResponse::new) + ); + } + public void sendCanMatch( Transport.Connection connection, final ShardSearchRequest request, @@ -166,6 +204,20 @@ public void sendClearAllScrollContexts(Transport.Connection connection, final Ac ); } + public void sendFreePITContexts( + Transport.Connection connection, + List contextIds, + ActionListener listener + ) { + transportService.sendRequest( + connection, + FREE_PIT_CONTEXT_ACTION_NAME, + new PitFreeContextsRequest(contextIds), + TransportRequestOptions.EMPTY, + new ActionListenerResponseHandler<>(listener, DeletePitResponse::new) + ); + } + public void sendExecuteDfs( Transport.Connection connection, final ShardSearchRequest request, @@ -307,6 +359,11 @@ public Map getPendingSearchRequests() { return new HashMap<>(clientConnections); } + /** + * A scroll free context request + * + * @opensearch.internal + */ static class ScrollFreeContextRequest extends TransportRequest { private ShardSearchContextId contextId; @@ -331,6 +388,48 @@ public ShardSearchContextId id() { } + /** + * Request to free the PIT context based on id + */ + static class PitFreeContextsRequest extends TransportRequest { + private List contextIds; + + PitFreeContextsRequest(List contextIds) { + this.contextIds = new ArrayList<>(); + this.contextIds.addAll(contextIds); + } + + PitFreeContextsRequest(StreamInput in) throws IOException { + super(in); + int size = in.readVInt(); + if (size > 0) { + this.contextIds = new ArrayList<>(); + for (int i = 0; i < size; i++) { + PitSearchContextIdForNode contextId = new PitSearchContextIdForNode(in); + contextIds.add(contextId); + } + } + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeVInt(contextIds.size()); + for (PitSearchContextIdForNode contextId : contextIds) { + contextId.writeTo(out); + } + } + + public List getContextIds() { + return this.contextIds; + } + } + + /** + * A search free context request + * + * @opensearch.internal + */ static class SearchFreeContextRequest extends ScrollFreeContextRequest implements IndicesRequest { private OriginalIndices originalIndices; @@ -368,6 +467,11 @@ public IndicesOptions indicesOptions() { } + /** + * A search free context response + * + * @opensearch.internal + */ public static class SearchFreeContextResponse extends TransportResponse { private boolean freed; @@ -405,6 +509,17 @@ public static void registerRequestHandler(TransportService transportService, Sea } ); TransportActionProxy.registerProxyAction(transportService, FREE_CONTEXT_SCROLL_ACTION_NAME, SearchFreeContextResponse::new); + + transportService.registerRequestHandler( + FREE_PIT_CONTEXT_ACTION_NAME, + ThreadPool.Names.SAME, + PitFreeContextsRequest::new, + (request, channel, task) -> { + channel.sendResponse(searchService.freeReaderContextsIfFound(request.getContextIds())); + } + ); + TransportActionProxy.registerProxyAction(transportService, FREE_PIT_CONTEXT_ACTION_NAME, DeletePitResponse::new); + transportService.registerRequestHandler( FREE_CONTEXT_ACTION_NAME, ThreadPool.Names.SAME, @@ -545,6 +660,48 @@ public static void registerRequestHandler(TransportService transportService, Sea } ); TransportActionProxy.registerProxyAction(transportService, QUERY_CAN_MATCH_NAME, SearchService.CanMatchResponse::new); + transportService.registerRequestHandler( + CREATE_READER_CONTEXT_ACTION_NAME, + ThreadPool.Names.SAME, + TransportCreatePitAction.CreateReaderContextRequest::new, + (request, channel, task) -> { + ChannelActionListener< + TransportCreatePitAction.CreateReaderContextResponse, + TransportCreatePitAction.CreateReaderContextRequest> listener = new ChannelActionListener<>( + channel, + CREATE_READER_CONTEXT_ACTION_NAME, + request + ); + searchService.createPitReaderContext( + request.getShardId(), + request.getKeepAlive(), + ActionListener.wrap( + r -> listener.onResponse(new TransportCreatePitAction.CreateReaderContextResponse(r)), + listener::onFailure + ) + ); + } + ); + TransportActionProxy.registerProxyAction( + transportService, + CREATE_READER_CONTEXT_ACTION_NAME, + TransportCreatePitAction.CreateReaderContextResponse::new + ); + + transportService.registerRequestHandler( + UPDATE_READER_CONTEXT_ACTION_NAME, + ThreadPool.Names.SAME, + UpdatePitContextRequest::new, + (request, channel, task) -> { + ChannelActionListener listener = new ChannelActionListener<>( + channel, + UPDATE_READER_CONTEXT_ACTION_NAME, + request + ); + searchService.updatePitIdAndKeepAlive(request, listener); + } + ); + TransportActionProxy.registerProxyAction(transportService, UPDATE_READER_CONTEXT_ACTION_NAME, UpdatePitContextResponse::new); } /** @@ -562,6 +719,11 @@ public Transport.Connection getConnection(@Nullable String clusterAlias, Discove } } + /** + * A handler that counts connections + * + * @opensearch.internal + */ final class ConnectionCountingHandler extends ActionListenerResponseHandler { private final Map clientConnections; private final String nodeId; diff --git a/server/src/main/java/org/opensearch/action/search/SearchType.java b/server/src/main/java/org/opensearch/action/search/SearchType.java index ea50ea4b93491..cb86c0d6c1b4a 100644 --- a/server/src/main/java/org/opensearch/action/search/SearchType.java +++ b/server/src/main/java/org/opensearch/action/search/SearchType.java @@ -35,7 +35,7 @@ /** * Search type represent the manner at which the search operation is executed. * - * + * @opensearch.internal */ public enum SearchType { /** diff --git a/server/src/main/java/org/opensearch/action/search/SearchUtils.java b/server/src/main/java/org/opensearch/action/search/SearchUtils.java new file mode 100644 index 0000000000000..ad4ac8f2a9eec --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/SearchUtils.java @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.StepListener; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.core.action.ActionListener; +import org.opensearch.transport.RemoteClusterService; + +import java.util.Set; +import java.util.function.BiFunction; + +/** + * Helper class for common search functions + */ +public class SearchUtils { + + public SearchUtils() {} + + /** + * Get connection lookup listener for list of clusters passed + */ + public static ActionListener> getConnectionLookupListener( + RemoteClusterService remoteClusterService, + ClusterState state, + Set clusters + ) { + final StepListener> lookupListener = new StepListener<>(); + + if (clusters.isEmpty()) { + lookupListener.onResponse((cluster, nodeId) -> state.getNodes().get(nodeId)); + } else { + remoteClusterService.collectNodes(clusters, lookupListener); + } + return lookupListener; + } +} diff --git a/server/src/main/java/org/opensearch/action/search/ShardSearchFailure.java b/server/src/main/java/org/opensearch/action/search/ShardSearchFailure.java index 228eac89f48d5..4d3f9b71a919c 100644 --- a/server/src/main/java/org/opensearch/action/search/ShardSearchFailure.java +++ b/server/src/main/java/org/opensearch/action/search/ShardSearchFailure.java @@ -35,26 +35,28 @@ import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; import org.opensearch.action.OriginalIndices; -import org.opensearch.action.ShardOperationFailedException; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.index.Index; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.action.ShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.search.SearchException; import org.opensearch.search.SearchShardTarget; import org.opensearch.transport.RemoteClusterAware; import java.io.IOException; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * Represents a failure to search on a specific shard. + * + * @opensearch.internal */ public class ShardSearchFailure extends ShardOperationFailedException { @@ -83,17 +85,20 @@ public ShardSearchFailure(Exception e) { } public ShardSearchFailure(Exception e, @Nullable SearchShardTarget shardTarget) { + this(e, ExceptionsHelper.unwrapCause(e), shardTarget); + } + + private ShardSearchFailure(final Exception e, final Throwable unwrappedCause, @Nullable SearchShardTarget shardTarget) { super( shardTarget == null ? null : shardTarget.getFullyQualifiedIndexName(), shardTarget == null ? -1 : shardTarget.getShardId().getId(), ExceptionsHelper.detailedMessage(e), - ExceptionsHelper.status(ExceptionsHelper.unwrapCause(e)), - ExceptionsHelper.unwrapCause(e) + ExceptionsHelper.status(unwrappedCause), + unwrappedCause ); - final Throwable actual = ExceptionsHelper.unwrapCause(e); - if (actual instanceof SearchException) { - this.shardTarget = ((SearchException) actual).shard(); + if (unwrappedCause instanceof SearchException) { + this.shardTarget = ((SearchException) unwrappedCause).shard(); } else if (shardTarget != null) { this.shardTarget = shardTarget; } diff --git a/server/src/main/java/org/opensearch/action/search/TransportClearScrollAction.java b/server/src/main/java/org/opensearch/action/search/TransportClearScrollAction.java index 1158dbe2b06e8..e67005eb1fb87 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportClearScrollAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportClearScrollAction.java @@ -32,15 +32,20 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; +/** + * Perform transport action to clear a search scroll + * + * @opensearch.internal + */ public class TransportClearScrollAction extends HandledTransportAction { private final ClusterService clusterService; diff --git a/server/src/main/java/org/opensearch/action/search/TransportCreatePitAction.java b/server/src/main/java/org/opensearch/action/search/TransportCreatePitAction.java new file mode 100644 index 0000000000000..baa113997f243 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/TransportCreatePitAction.java @@ -0,0 +1,133 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.action.StepListener; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.search.SearchPhaseResult; +import org.opensearch.search.internal.ShardSearchContextId; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportRequest; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.Arrays; + +/** + * Transport action for creating PIT reader context + */ +public class TransportCreatePitAction extends HandledTransportAction { + + public static final String CREATE_PIT_ACTION = "create_pit"; + private final TransportService transportService; + private final SearchTransportService searchTransportService; + private final ClusterService clusterService; + private final TransportSearchAction transportSearchAction; + private final NamedWriteableRegistry namedWriteableRegistry; + private final CreatePitController createPitController; + + @Inject + public TransportCreatePitAction( + TransportService transportService, + ActionFilters actionFilters, + SearchTransportService searchTransportService, + ClusterService clusterService, + TransportSearchAction transportSearchAction, + NamedWriteableRegistry namedWriteableRegistry, + CreatePitController createPitController + ) { + super(CreatePitAction.NAME, transportService, actionFilters, in -> new CreatePitRequest(in)); + this.transportService = transportService; + this.searchTransportService = searchTransportService; + this.clusterService = clusterService; + this.transportSearchAction = transportSearchAction; + this.namedWriteableRegistry = namedWriteableRegistry; + this.createPitController = createPitController; + } + + @Override + protected void doExecute(Task task, CreatePitRequest request, ActionListener listener) { + final StepListener createPitListener = new StepListener<>(); + final ActionListener updatePitIdListener = ActionListener.wrap(r -> listener.onResponse(r), e -> { + logger.error( + () -> new ParameterizedMessage( + "PIT creation failed while updating PIT ID for indices [{}]", + Arrays.toString(request.indices()) + ) + ); + listener.onFailure(e); + }); + createPitController.executeCreatePit(request, task, createPitListener, updatePitIdListener); + } + + /** + * Request to create pit reader context with keep alive + */ + public static class CreateReaderContextRequest extends TransportRequest { + private final ShardId shardId; + private final TimeValue keepAlive; + + public CreateReaderContextRequest(ShardId shardId, TimeValue keepAlive) { + this.shardId = shardId; + this.keepAlive = keepAlive; + } + + public ShardId getShardId() { + return shardId; + } + + public TimeValue getKeepAlive() { + return keepAlive; + } + + public CreateReaderContextRequest(StreamInput in) throws IOException { + super(in); + this.shardId = new ShardId(in); + this.keepAlive = in.readTimeValue(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + shardId.writeTo(out); + out.writeTimeValue(keepAlive); + } + } + + /** + * Create pit reader context response which holds the contextId + */ + public static class CreateReaderContextResponse extends SearchPhaseResult { + public CreateReaderContextResponse(ShardSearchContextId shardSearchContextId) { + this.contextId = shardSearchContextId; + } + + public CreateReaderContextResponse(StreamInput in) throws IOException { + super(in); + contextId = new ShardSearchContextId(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + contextId.writeTo(out); + } + } + +} diff --git a/server/src/main/java/org/opensearch/action/search/TransportDeletePitAction.java b/server/src/main/java/org/opensearch/action/search/TransportDeletePitAction.java new file mode 100644 index 0000000000000..614d576324026 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/TransportDeletePitAction.java @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Transport action for deleting point in time searches - supports deleting list and all point in time searches + */ +public class TransportDeletePitAction extends HandledTransportAction { + private final NamedWriteableRegistry namedWriteableRegistry; + private final PitService pitService; + + @Inject + public TransportDeletePitAction( + TransportService transportService, + ActionFilters actionFilters, + NamedWriteableRegistry namedWriteableRegistry, + PitService pitService + ) { + super(DeletePitAction.NAME, transportService, actionFilters, DeletePitRequest::new); + this.namedWriteableRegistry = namedWriteableRegistry; + this.pitService = pitService; + } + + /** + * Invoke 'delete all pits' or 'delete list of pits' workflow based on request + */ + @Override + protected void doExecute(Task task, DeletePitRequest request, ActionListener listener) { + if (request.getPitIds().size() == 1 && "_all".equals(request.getPitIds().get(0))) { + deleteAllPits(listener); + } else { + deletePits(listener, request); + } + } + + /** + * Deletes one or more point in time search contexts. + */ + private void deletePits(ActionListener listener, DeletePitRequest request) { + Map> nodeToContextsMap = new HashMap<>(); + // remove duplicates from the request + Set uniquePitIds = new LinkedHashSet<>(request.getPitIds()); + for (String pitId : uniquePitIds) { + SearchContextId contextId = SearchContextId.decode(namedWriteableRegistry, pitId); + for (SearchContextIdForNode contextIdForNode : contextId.shards().values()) { + PitSearchContextIdForNode pitSearchContext = new PitSearchContextIdForNode(pitId, contextIdForNode); + List contexts = nodeToContextsMap.getOrDefault(contextIdForNode.getNode(), new ArrayList<>()); + contexts.add(pitSearchContext); + nodeToContextsMap.put(contextIdForNode.getNode(), contexts); + } + } + pitService.deletePitContexts(nodeToContextsMap, listener); + } + + /** + * Delete all active PIT reader contexts leveraging list all PITs + * + * For Cross cluster PITs : + * - mixed cluster PITs ( PIT comprising local and remote ) will be fully deleted. Since there will atleast be + * one reader context with PIT ID present in local cluster, 'Get all PITs' will retrieve the PIT ID with which + * we can completely delete the PIT contexts in both local and remote cluster. + * - fully remote PITs will not be deleted as 'Get all PITs' operates on local cluster only and no PIT info can + * be retrieved when it's fully remote. + */ + private void deleteAllPits(ActionListener listener) { + // Get all PITs and execute delete operation for the PITs. + pitService.getAllPits(ActionListener.wrap(getAllPitNodesResponse -> { + DeletePitRequest deletePitRequest = new DeletePitRequest( + getAllPitNodesResponse.getPitInfos().stream().map(r -> r.getPitId()).collect(Collectors.toList()) + ); + deletePits(listener, deletePitRequest); + }, listener::onFailure)); + } +} diff --git a/server/src/main/java/org/opensearch/action/search/TransportGetAllPitsAction.java b/server/src/main/java/org/opensearch/action/search/TransportGetAllPitsAction.java new file mode 100644 index 0000000000000..561ca034da469 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/TransportGetAllPitsAction.java @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.action.FailedNodeException; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.nodes.TransportNodesAction; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.search.SearchService; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.List; + +/** + * Transport action to get all active PIT contexts across all nodes + */ +public class TransportGetAllPitsAction extends TransportNodesAction< + GetAllPitNodesRequest, + GetAllPitNodesResponse, + GetAllPitNodeRequest, + GetAllPitNodeResponse> { + private final SearchService searchService; + + @Inject + public TransportGetAllPitsAction( + ThreadPool threadPool, + ClusterService clusterService, + TransportService transportService, + ActionFilters actionFilters, + SearchService searchService + ) { + super( + GetAllPitsAction.NAME, + threadPool, + clusterService, + transportService, + actionFilters, + GetAllPitNodesRequest::new, + GetAllPitNodeRequest::new, + ThreadPool.Names.SAME, + GetAllPitNodeResponse.class + ); + this.searchService = searchService; + } + + @Override + protected GetAllPitNodesResponse newResponse( + GetAllPitNodesRequest request, + List getAllPitNodeResponses, + List failures + ) { + return new GetAllPitNodesResponse(clusterService.getClusterName(), getAllPitNodeResponses, failures); + } + + @Override + protected GetAllPitNodeRequest newNodeRequest(GetAllPitNodesRequest request) { + return new GetAllPitNodeRequest(); + } + + @Override + protected GetAllPitNodeResponse newNodeResponse(StreamInput in) throws IOException { + return new GetAllPitNodeResponse(in); + } + + /** + * This retrieves all active PITs in the node + */ + @Override + protected GetAllPitNodeResponse nodeOperation(GetAllPitNodeRequest request) { + GetAllPitNodeResponse nodeResponse = new GetAllPitNodeResponse( + transportService.getLocalNode(), + searchService.getAllPITReaderContexts() + ); + return nodeResponse; + } +} diff --git a/server/src/main/java/org/opensearch/action/search/TransportMultiSearchAction.java b/server/src/main/java/org/opensearch/action/search/TransportMultiSearchAction.java index 6c54136254832..146b4010af4b3 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportMultiSearchAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportMultiSearchAction.java @@ -32,7 +32,6 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.client.node.NodeClient; @@ -40,10 +39,11 @@ import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.AtomicArray; import org.opensearch.common.util.concurrent.OpenSearchExecutors; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -54,6 +54,11 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.LongSupplier; +/** + * Perform action for a multi search + * + * @opensearch.internal + */ public class TransportMultiSearchAction extends HandledTransportAction { private final int allocatedProcessors; @@ -215,6 +220,11 @@ private long buildTookInMillis() { }); } + /** + * Slots a search request + * + * @opensearch.internal + */ static final class SearchRequestSlot { final SearchRequest request; diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchAction.java b/server/src/main/java/org/opensearch/action/search/TransportSearchAction.java index 73eb7b0a98fef..cff1005beff27 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchAction.java @@ -32,7 +32,6 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionListener; import org.opensearch.action.OriginalIndices; import org.opensearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsGroup; @@ -57,20 +56,23 @@ import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.breaker.CircuitBreaker; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AtomicArray; import org.opensearch.common.util.concurrent.CountDown; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.breaker.CircuitBreaker; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.indices.breaker.CircuitBreakerService; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.query.Rewriteable; -import org.opensearch.index.shard.ShardId; -import org.opensearch.indices.breaker.CircuitBreakerService; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchService; import org.opensearch.search.SearchShardTarget; @@ -80,11 +82,12 @@ import org.opensearch.search.internal.AliasFilter; import org.opensearch.search.internal.InternalSearchResponse; import org.opensearch.search.internal.SearchContext; +import org.opensearch.search.pipeline.PipelinedRequest; +import org.opensearch.search.pipeline.SearchPipelineService; import org.opensearch.search.profile.ProfileShardResult; import org.opensearch.search.profile.SearchProfileShardResults; import org.opensearch.tasks.CancellableTask; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.RemoteClusterAware; import org.opensearch.transport.RemoteClusterService; @@ -117,6 +120,11 @@ import static org.opensearch.action.search.SearchType.QUERY_THEN_FETCH; import static org.opensearch.search.sort.FieldSortBuilder.hasPrimaryFieldSort; +/** + * Perform search action + * + * @opensearch.internal + */ public class TransportSearchAction extends HandledTransportAction { /** The maximum number of shards for a single search request. */ @@ -138,6 +146,14 @@ public class TransportSearchAction extends HandledTransportAction SEARCH_REQUEST_STATS_ENABLED = Setting.boolSetting( + SEARCH_REQUEST_STATS_ENABLED_KEY, + false, + Property.Dynamic, + Property.NodeScope + ); + private final NodeClient client; private final ThreadPool threadPool; private final ClusterService clusterService; @@ -148,6 +164,11 @@ public class TransportSearchAction extends HandledTransportAction) SearchRequest::new); this.client = client; @@ -175,6 +198,14 @@ public TransportSearchAction( this.searchService = searchService; this.indexNameExpressionResolver = indexNameExpressionResolver; this.namedWriteableRegistry = namedWriteableRegistry; + this.searchPipelineService = searchPipelineService; + this.isRequestStatsEnabled = clusterService.getClusterSettings().get(SEARCH_REQUEST_STATS_ENABLED); + clusterService.getClusterSettings().addSettingsUpdateConsumer(SEARCH_REQUEST_STATS_ENABLED, this::setIsRequestStatsEnabled); + this.searchRequestStats = searchRequestStats; + } + + private void setIsRequestStatsEnabled(boolean isRequestStatsEnabled) { + this.isRequestStatsEnabled = isRequestStatsEnabled; } private Map buildPerIndexAliasFilter( @@ -227,6 +258,8 @@ private Map resolveIndexBoosts(SearchRequest searchRequest, Clust * clock for measuring how long an operation took (they often lack precision, they are subject * to moving backwards due to NTP and other such complexities, etc.). There are also issues with * using a relative clock for reporting real time. Thus, we simply separate these two uses. + * + * @opensearch.internal */ static final class SearchTimeProvider { @@ -277,6 +310,11 @@ protected void doExecute(Task task, SearchRequest searchRequest, ActionListener< executeRequest(task, searchRequest, this::searchAsyncAction, listener); } + /** + * The single phase search action. + * + * @opensearch.internal + */ public interface SinglePhaseSearchAction { void executeOnShardTarget( SearchTask searchTask, @@ -294,6 +332,13 @@ public void executeRequest( SinglePhaseSearchAction phaseSearchAction, ActionListener listener ) { + final List searchListenersList = createSearchListenerList(); + final SearchRequestOperationsListener searchRequestOperationsListener; + if (!CollectionUtils.isEmpty(searchListenersList)) { + searchRequestOperationsListener = new SearchRequestOperationsListener.CompositeListener(searchListenersList, logger); + } else { + searchRequestOperationsListener = null; + } executeRequest(task, searchRequest, new SearchAsyncActionProvider() { @Override public AbstractSearchAsyncAction asyncSearchAction( @@ -328,8 +373,9 @@ public AbstractSearchAsyncAction asyncSearchAction( clusterState, task, new ArraySearchPhaseResults<>(shardsIts.size()), - 1, - clusters + searchRequest.getMaxConcurrentShardRequests(), + clusters, + searchRequestOperationsListener ) { @Override protected void executePhaseOnShard( @@ -363,16 +409,29 @@ boolean buildPointInTimeFromSearchResults() { private void executeRequest( Task task, - SearchRequest searchRequest, + SearchRequest originalSearchRequest, SearchAsyncActionProvider searchAsyncActionProvider, - ActionListener listener + ActionListener originalListener ) { final long relativeStartNanos = System.nanoTime(); final SearchTimeProvider timeProvider = new SearchTimeProvider( - searchRequest.getOrCreateAbsoluteStartMillis(), + originalSearchRequest.getOrCreateAbsoluteStartMillis(), relativeStartNanos, System::nanoTime ); + PipelinedRequest searchRequest; + ActionListener listener; + try { + searchRequest = searchPipelineService.resolvePipeline(originalSearchRequest); + listener = ActionListener.wrap( + r -> originalListener.onResponse(searchRequest.transformResponse(r)), + originalListener::onFailure + ); + } catch (Exception e) { + originalListener.onFailure(e); + return; + } + ActionListener rewriteListener = ActionListener.wrap(source -> { if (source != searchRequest.source()) { // only set it if it changed - we don't allow null values to be set but it might be already null. this way we catch @@ -412,7 +471,7 @@ private void executeRequest( localIndices, remoteClusterIndices, timeProvider, - searchService.aggReduceContextBuilder(searchRequest), + searchService.aggReduceContextBuilder(searchRequest.source()), remoteClusterService, threadPool, listener, @@ -886,9 +945,7 @@ private void executeSearch( @Nullable SearchContextId searchContext, SearchAsyncActionProvider searchAsyncActionProvider ) { - clusterState.blocks().globalBlockedRaiseException(ClusterBlockLevel.READ); - // TODO: I think startTime() should become part of ActionRequest and that should be used both for index name // date math expressions and $now in scripts. This way all apis will deal with now in the same way instead // of just for the _search api @@ -938,11 +995,8 @@ private void executeSearch( indexRoutings = routingMap; } final GroupShardsIterator shardIterators = mergeShardsIterators(localShardIterators, remoteShardIterators); - failIfOverShardCountLimit(clusterService, shardIterators.size()); - Map concreteIndexBoosts = resolveIndexBoosts(searchRequest, clusterState); - // optimize search type for cases where there is only one shard group to search on if (shardIterators.size() == 1) { // if we only have one group, then we always want Q_T_F, no need for DFS, and no need to do THEN since we hit one shard @@ -1077,6 +1131,14 @@ AbstractSearchAsyncAction asyncSearchAction( ); } + private List createSearchListenerList() { + final List searchListenersList = new ArrayList<>(); + if (isRequestStatsEnabled) { + searchListenersList.add(searchRequestStats); + } + return searchListenersList; + } + private AbstractSearchAsyncAction searchAsyncAction( SearchTask task, SearchRequest searchRequest, @@ -1093,6 +1155,13 @@ private AbstractSearchAsyncAction searchAsyncAction ThreadPool threadPool, SearchResponse.Clusters clusters ) { + final List searchListenersList = createSearchListenerList(); + final SearchRequestOperationsListener searchRequestOperationsListener; + if (!CollectionUtils.isEmpty(searchListenersList)) { + searchRequestOperationsListener = new SearchRequestOperationsListener.CompositeListener(searchListenersList, logger); + } else { + searchRequestOperationsListener = null; + } if (preFilter) { return new CanMatchPreFilterSearchPhase( logger, @@ -1132,7 +1201,8 @@ public void run() { } }; }, - clusters + clusters, + searchRequestOperationsListener ); } else { final QueryPhaseResultConsumer queryResultConsumer = searchPhaseController.newSearchPhaseResults( @@ -1162,7 +1232,8 @@ public void run() { timeProvider, clusterState, task, - clusters + clusters, + searchRequestOperationsListener ); break; case QUERY_THEN_FETCH: @@ -1182,7 +1253,8 @@ public void run() { timeProvider, clusterState, task, - clusters + clusters, + searchRequestOperationsListener ); break; default: @@ -1217,6 +1289,11 @@ private static void failIfOverShardCountLimit(ClusterService clusterService, int } } + /** + * xcluster search listener + * + * @opensearch.internal + */ abstract static class CCSActionListener implements ActionListener { private final String clusterAlias; private final boolean skipUnavailable; diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java index 7ddfdfec34cb1..afe76138edef3 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchHelper.java @@ -34,10 +34,10 @@ import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.BytesStreamInput; import org.opensearch.common.io.stream.BytesStreamOutput; import org.opensearch.common.util.concurrent.AtomicArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.BytesStreamInput; import org.opensearch.search.SearchPhaseResult; import org.opensearch.search.SearchShardTarget; import org.opensearch.search.internal.InternalScrollSearchRequest; @@ -48,6 +48,11 @@ import java.io.UncheckedIOException; import java.util.Base64; +/** + * Helper class for the search transport + * + * @opensearch.internal + */ final class TransportSearchHelper { private static final String INCLUDE_CONTEXT_UUID = "include_context_uuid"; diff --git a/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java b/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java index cb7c0b4a1873b..4713d03c93bac 100644 --- a/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java +++ b/server/src/main/java/org/opensearch/action/search/TransportSearchScrollAction.java @@ -32,15 +32,20 @@ package org.opensearch.action.search; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; +/** + * Perform the search scroll + * + * @opensearch.internal + */ public class TransportSearchScrollAction extends HandledTransportAction { private final ClusterService clusterService; diff --git a/server/src/main/java/org/opensearch/action/search/UpdatePitContextRequest.java b/server/src/main/java/org/opensearch/action/search/UpdatePitContextRequest.java new file mode 100644 index 0000000000000..0d10390f1f660 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/UpdatePitContextRequest.java @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.search.internal.ShardSearchContextId; +import org.opensearch.transport.TransportRequest; + +import java.io.IOException; + +/** + * Request used to update PIT reader contexts with pitId, keepAlive and creationTime + */ +public class UpdatePitContextRequest extends TransportRequest { + private final String pitId; + private final long keepAlive; + + private final long creationTime; + private final ShardSearchContextId searchContextId; + + public UpdatePitContextRequest(ShardSearchContextId searchContextId, String pitId, long keepAlive, long creationTime) { + this.pitId = pitId; + this.searchContextId = searchContextId; + this.keepAlive = keepAlive; + this.creationTime = creationTime; + } + + UpdatePitContextRequest(StreamInput in) throws IOException { + super(in); + pitId = in.readString(); + keepAlive = in.readLong(); + creationTime = in.readLong(); + searchContextId = new ShardSearchContextId(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(pitId); + out.writeLong(keepAlive); + out.writeLong(creationTime); + searchContextId.writeTo(out); + } + + public ShardSearchContextId getSearchContextId() { + return searchContextId; + } + + public String getPitId() { + return pitId; + } + + public long getCreationTime() { + return creationTime; + } + + public long getKeepAlive() { + return keepAlive; + } +} diff --git a/server/src/main/java/org/opensearch/action/search/UpdatePitContextResponse.java b/server/src/main/java/org/opensearch/action/search/UpdatePitContextResponse.java new file mode 100644 index 0000000000000..1db8fc48c28bc --- /dev/null +++ b/server/src/main/java/org/opensearch/action/search/UpdatePitContextResponse.java @@ -0,0 +1,58 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.search; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse; + +import java.io.IOException; + +/** + * Update PIT context response with creation time, keep alive etc. + */ +public class UpdatePitContextResponse extends TransportResponse { + private final String pitId; + + private final long creationTime; + + private final long keepAlive; + + UpdatePitContextResponse(StreamInput in) throws IOException { + super(in); + pitId = in.readString(); + creationTime = in.readLong(); + keepAlive = in.readLong(); + } + + public UpdatePitContextResponse(String pitId, long creationTime, long keepAlive) { + this.pitId = pitId; + this.keepAlive = keepAlive; + this.creationTime = creationTime; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(pitId); + out.writeLong(creationTime); + out.writeLong(keepAlive); + } + + public String getPitId() { + return pitId; + } + + public long getKeepAlive() { + return keepAlive; + } + + public long getCreationTime() { + return creationTime; + } +} diff --git a/server/src/main/java/org/opensearch/action/support/ActionFilter.java b/server/src/main/java/org/opensearch/action/support/ActionFilter.java index 414ef2a329214..e936512004fd2 100644 --- a/server/src/main/java/org/opensearch/action/support/ActionFilter.java +++ b/server/src/main/java/org/opensearch/action/support/ActionFilter.java @@ -32,13 +32,15 @@ package org.opensearch.action.support; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; import org.opensearch.tasks.Task; /** * A filter allowing to filter transport actions + * + * @opensearch.internal */ public interface ActionFilter { diff --git a/server/src/main/java/org/opensearch/action/support/ActionFilterChain.java b/server/src/main/java/org/opensearch/action/support/ActionFilterChain.java index 65821f14167f7..1ebe1ee63abf9 100644 --- a/server/src/main/java/org/opensearch/action/support/ActionFilterChain.java +++ b/server/src/main/java/org/opensearch/action/support/ActionFilterChain.java @@ -32,13 +32,15 @@ package org.opensearch.action.support; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; import org.opensearch.tasks.Task; /** * A filter chain allowing to continue and process the transport action request + * + * @opensearch.internal */ public interface ActionFilterChain { diff --git a/server/src/main/java/org/opensearch/action/support/ActionFilters.java b/server/src/main/java/org/opensearch/action/support/ActionFilters.java index 452b7e7f5238f..95cafbea4d7c1 100644 --- a/server/src/main/java/org/opensearch/action/support/ActionFilters.java +++ b/server/src/main/java/org/opensearch/action/support/ActionFilters.java @@ -38,6 +38,8 @@ /** * Holds the action filters injected through plugins, properly sorted by {@link org.opensearch.action.support.ActionFilter#order()} + * + * @opensearch.internal */ public class ActionFilters { diff --git a/server/src/main/java/org/opensearch/action/support/ActiveShardCount.java b/server/src/main/java/org/opensearch/action/support/ActiveShardCount.java index 9edd9bba0ef6e..15275ba48fc6e 100644 --- a/server/src/main/java/org/opensearch/action/support/ActiveShardCount.java +++ b/server/src/main/java/org/opensearch/action/support/ActiveShardCount.java @@ -32,15 +32,13 @@ package org.opensearch.action.support; -import com.carrotsearch.hppc.cursors.IntObjectCursor; - import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.routing.IndexRoutingTable; import org.opensearch.cluster.routing.IndexShardRoutingTable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; @@ -49,6 +47,8 @@ /** * A class whose instances represent a value for counting the number * of active shard copies for a given shard in an index. + * + * @opensearch.internal */ public final class ActiveShardCount implements Writeable { @@ -184,8 +184,8 @@ public boolean enoughShardsActive(final ClusterState clusterState, final String. if (waitForActiveShards == ActiveShardCount.DEFAULT) { waitForActiveShards = SETTING_WAIT_FOR_ACTIVE_SHARDS.get(indexMetadata.getSettings()); } - for (final IntObjectCursor shardRouting : indexRoutingTable.getShards()) { - if (waitForActiveShards.enoughShardsActive(shardRouting.value) == false) { + for (final IndexShardRoutingTable shardRouting : indexRoutingTable.getShards().values()) { + if (waitForActiveShards.enoughShardsActive(shardRouting) == false) { // not enough active shard copies yet return false; } diff --git a/server/src/main/java/org/opensearch/action/support/ActiveShardsObserver.java b/server/src/main/java/org/opensearch/action/support/ActiveShardsObserver.java index 064ae2ee522f9..29468fe777707 100644 --- a/server/src/main/java/org/opensearch/action/support/ActiveShardsObserver.java +++ b/server/src/main/java/org/opensearch/action/support/ActiveShardsObserver.java @@ -34,11 +34,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; import org.opensearch.node.NodeClosedException; import org.opensearch.threadpool.ThreadPool; @@ -49,6 +49,8 @@ /** * This class provides primitives for waiting for a configured number of shards * to become active before sending a response on an {@link ActionListener}. + * + * @opensearch.internal */ public class ActiveShardsObserver { diff --git a/server/src/main/java/org/opensearch/action/support/AdapterActionFuture.java b/server/src/main/java/org/opensearch/action/support/AdapterActionFuture.java index aa197d1e255f9..cfc867c572a75 100644 --- a/server/src/main/java/org/opensearch/action/support/AdapterActionFuture.java +++ b/server/src/main/java/org/opensearch/action/support/AdapterActionFuture.java @@ -33,15 +33,20 @@ package org.opensearch.action.support; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.BaseFuture; import org.opensearch.common.util.concurrent.FutureUtils; import org.opensearch.common.util.concurrent.UncategorizedExecutionException; +import org.opensearch.core.action.ActionListener; import java.util.concurrent.TimeUnit; +/** + * Future adapter action + * + * @opensearch.internal + */ public abstract class AdapterActionFuture extends BaseFuture implements ActionFuture, ActionListener { @Override diff --git a/server/src/main/java/org/opensearch/action/support/AutoCreateIndex.java b/server/src/main/java/org/opensearch/action/support/AutoCreateIndex.java index 322dfc85fbf54..9e8cbd7bf40c2 100644 --- a/server/src/main/java/org/opensearch/action/support/AutoCreateIndex.java +++ b/server/src/main/java/org/opensearch/action/support/AutoCreateIndex.java @@ -35,13 +35,13 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.common.Booleans; -import org.opensearch.common.Strings; import org.opensearch.common.collect.Tuple; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.Strings; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.mapper.MapperService; import org.opensearch.indices.SystemIndices; @@ -52,6 +52,8 @@ /** * Encapsulates the logic of whether a new index should be automatically created when * a write operation is about to happen in a non existing index. + * + * @opensearch.internal */ public final class AutoCreateIndex { @@ -142,6 +144,11 @@ void setAutoCreate(AutoCreate autoCreate) { this.autoCreate = autoCreate; } + /** + * An auto create object + * + * @opensearch.internal + */ static class AutoCreate { private final boolean autoCreateIndex; private final List> expressions; diff --git a/server/src/main/java/org/opensearch/action/support/ChannelActionListener.java b/server/src/main/java/org/opensearch/action/support/ChannelActionListener.java index 288c91b243cb1..ac5dfeb4728d5 100644 --- a/server/src/main/java/org/opensearch/action/support/ChannelActionListener.java +++ b/server/src/main/java/org/opensearch/action/support/ChannelActionListener.java @@ -32,11 +32,16 @@ package org.opensearch.action.support; -import org.opensearch.action.ActionListener; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.transport.TransportChannel; import org.opensearch.transport.TransportRequest; -import org.opensearch.transport.TransportResponse; +/** + * Listener for transport channel actions + * + * @opensearch.internal + */ public final class ChannelActionListener implements ActionListener { diff --git a/server/src/main/java/org/opensearch/action/support/ContextPreservingActionListener.java b/server/src/main/java/org/opensearch/action/support/ContextPreservingActionListener.java index b83e0541cb6d0..a59c7fb45ca27 100644 --- a/server/src/main/java/org/opensearch/action/support/ContextPreservingActionListener.java +++ b/server/src/main/java/org/opensearch/action/support/ContextPreservingActionListener.java @@ -31,14 +31,16 @@ package org.opensearch.action.support; -import org.opensearch.action.ActionListener; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; import java.util.function.Supplier; /** * Restores the given {@link org.opensearch.common.util.concurrent.ThreadContext.StoredContext} * once the listener is invoked + * + * @opensearch.internal */ public final class ContextPreservingActionListener implements ActionListener { diff --git a/server/src/main/java/org/opensearch/action/support/DestructiveOperations.java b/server/src/main/java/org/opensearch/action/support/DestructiveOperations.java index ce9a0d7c7aece..84b3287322301 100644 --- a/server/src/main/java/org/opensearch/action/support/DestructiveOperations.java +++ b/server/src/main/java/org/opensearch/action/support/DestructiveOperations.java @@ -39,6 +39,8 @@ /** * Helper for dealing with destructive operations and wildcard usage. + * + * @opensearch.internal */ public final class DestructiveOperations { diff --git a/server/src/main/java/org/opensearch/action/support/GroupedActionListener.java b/server/src/main/java/org/opensearch/action/support/GroupedActionListener.java index 515ed73ae2392..fb9554cc4c3d2 100644 --- a/server/src/main/java/org/opensearch/action/support/GroupedActionListener.java +++ b/server/src/main/java/org/opensearch/action/support/GroupedActionListener.java @@ -31,9 +31,9 @@ package org.opensearch.action.support; -import org.opensearch.action.ActionListener; import org.opensearch.common.util.concurrent.AtomicArray; import org.opensearch.common.util.concurrent.CountDown; +import org.opensearch.core.action.ActionListener; import java.util.Collection; import java.util.Collections; @@ -46,6 +46,8 @@ * it has received N results (either successes or failures). This allows synchronous * tasks to be forked off in a loop with the same listener and respond to a * higher level listener once all tasks responded. + * + * @opensearch.internal */ public final class GroupedActionListener implements ActionListener { private final CountDown countDown; diff --git a/server/src/main/java/org/opensearch/action/support/HandledTransportAction.java b/server/src/main/java/org/opensearch/action/support/HandledTransportAction.java index 9613b55cc7641..786d8cfb6fa1d 100644 --- a/server/src/main/java/org/opensearch/action/support/HandledTransportAction.java +++ b/server/src/main/java/org/opensearch/action/support/HandledTransportAction.java @@ -32,8 +32,8 @@ package org.opensearch.action.support; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportChannel; @@ -42,6 +42,8 @@ /** * A TransportAction that self registers a handler into the transport service + * + * @opensearch.internal */ public abstract class HandledTransportAction extends TransportAction< Request, @@ -88,6 +90,11 @@ protected HandledTransportAction( transportService.registerRequestHandler(actionName, executor, false, canTripCircuitBreaker, requestReader, new TransportHandler()); } + /** + * Inner transport handler + * + * @opensearch.internal + */ class TransportHandler implements TransportRequestHandler { @Override public final void messageReceived(final Request request, final TransportChannel channel, Task task) { diff --git a/server/src/main/java/org/opensearch/action/support/IndicesOptions.java b/server/src/main/java/org/opensearch/action/support/IndicesOptions.java index 09b691787dffe..351868b704dde 100644 --- a/server/src/main/java/org/opensearch/action/support/IndicesOptions.java +++ b/server/src/main/java/org/opensearch/action/support/IndicesOptions.java @@ -33,14 +33,14 @@ import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchParseException; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentParser.Token; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; +import org.opensearch.core.xcontent.XContentParser.Token; import org.opensearch.rest.RestRequest; import java.io.IOException; @@ -55,9 +55,16 @@ /** * Controls how to deal with unavailable concrete indices (closed or missing), how wildcard expressions are expanded * to actual indices (all, closed or open indices) and how to deal with wildcard expressions that resolve to no indices. + * + * @opensearch.internal */ public class IndicesOptions implements ToXContentFragment { + /** + * The wildcard states. + * + * @opensearch.internal + */ public enum WildcardStates { OPEN, CLOSED, @@ -118,6 +125,11 @@ private static void updateSetForValue(EnumSet states, String wil } } + /** + * The options. + * + * @opensearch.internal + */ public enum Option { IGNORE_UNAVAILABLE, IGNORE_ALIASES, diff --git a/server/src/main/java/org/opensearch/action/support/ListenerTimeouts.java b/server/src/main/java/org/opensearch/action/support/ListenerTimeouts.java index c31ca9b100624..6020dc2bfd489 100644 --- a/server/src/main/java/org/opensearch/action/support/ListenerTimeouts.java +++ b/server/src/main/java/org/opensearch/action/support/ListenerTimeouts.java @@ -33,14 +33,19 @@ package org.opensearch.action.support; import org.opensearch.OpenSearchTimeoutException; -import org.opensearch.action.ActionListener; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; import org.opensearch.threadpool.Scheduler; import org.opensearch.threadpool.ThreadPool; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; +/** + * Supports timeouts on listeners + * + * @opensearch.internal + */ public class ListenerTimeouts { /** @@ -91,6 +96,11 @@ public static ActionListener wrapWithTimeout( return wrappedListener; } + /** + * Listener that can time out + * + * @opensearch.internal + */ private static class TimeoutableListener implements ActionListener, Runnable { private final AtomicBoolean isDone = new AtomicBoolean(false); diff --git a/server/src/main/java/org/opensearch/action/support/PlainActionFuture.java b/server/src/main/java/org/opensearch/action/support/PlainActionFuture.java index d7235a3b0ee46..abe63d8abd473 100644 --- a/server/src/main/java/org/opensearch/action/support/PlainActionFuture.java +++ b/server/src/main/java/org/opensearch/action/support/PlainActionFuture.java @@ -34,6 +34,11 @@ import org.opensearch.common.CheckedConsumer; +/** + * Creates a new future for a plain action. + * + * @opensearch.internal + */ public class PlainActionFuture extends AdapterActionFuture { public static PlainActionFuture newFuture() { diff --git a/server/src/main/java/org/opensearch/action/support/PlainListenableActionFuture.java b/server/src/main/java/org/opensearch/action/support/PlainListenableActionFuture.java index 16a82b91145ae..1b16249f70754 100644 --- a/server/src/main/java/org/opensearch/action/support/PlainListenableActionFuture.java +++ b/server/src/main/java/org/opensearch/action/support/PlainListenableActionFuture.java @@ -32,12 +32,17 @@ package org.opensearch.action.support; -import org.opensearch.action.ActionListener; import org.opensearch.action.ListenableActionFuture; +import org.opensearch.core.action.ActionListener; import java.util.ArrayList; import java.util.List; +/** + * Future for a plain listenable action + * + * @opensearch.internal + */ public class PlainListenableActionFuture extends AdapterActionFuture implements ListenableActionFuture { volatile Object listeners; diff --git a/server/src/main/java/org/opensearch/action/support/RetryableAction.java b/server/src/main/java/org/opensearch/action/support/RetryableAction.java index e330948209b5c..fc2ee277b538a 100644 --- a/server/src/main/java/org/opensearch/action/support/RetryableAction.java +++ b/server/src/main/java/org/opensearch/action/support/RetryableAction.java @@ -34,15 +34,16 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; -import org.opensearch.common.Randomness; +import org.opensearch.action.bulk.BackoffPolicy; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; import org.opensearch.threadpool.Scheduler; import org.opensearch.threadpool.ThreadPool; import java.util.ArrayDeque; +import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -50,6 +51,8 @@ * The executor the action will be executed on can be defined in the constructor. Otherwise, SAME is the * default. The action will be retried with exponentially increasing delay periods until the timeout period * has been reached. + * + * @opensearch.internal */ public abstract class RetryableAction { @@ -62,6 +65,7 @@ public abstract class RetryableAction { private final long startMillis; private final ActionListener finalListener; private final String executor; + private final BackoffPolicy backoffPolicy; private volatile Scheduler.ScheduledCancellable retryTask; @@ -72,7 +76,15 @@ public RetryableAction( TimeValue timeoutValue, ActionListener listener ) { - this(logger, threadPool, initialDelay, timeoutValue, listener, ThreadPool.Names.SAME); + this( + logger, + threadPool, + initialDelay, + timeoutValue, + listener, + BackoffPolicy.exponentialFullJitterBackoff(initialDelay.getMillis()), + ThreadPool.Names.SAME + ); } public RetryableAction( @@ -81,6 +93,7 @@ public RetryableAction( TimeValue initialDelay, TimeValue timeoutValue, ActionListener listener, + BackoffPolicy backoffPolicy, String executor ) { this.logger = logger; @@ -93,10 +106,11 @@ public RetryableAction( this.startMillis = threadPool.relativeTimeInMillis(); this.finalListener = listener; this.executor = executor; + this.backoffPolicy = backoffPolicy; } public void run() { - final RetryingListener retryingListener = new RetryingListener(initialDelayMillis, null); + final RetryingListener retryingListener = new RetryingListener(backoffPolicy.iterator(), null); final Runnable runnable = createRunnable(retryingListener); threadPool.executor(executor).execute(runnable); } @@ -140,16 +154,24 @@ public void onRejection(Exception e) { public void onFinished() {} + /** + * Retry able task may want to throw different Exception on timeout, + * they can override it method for that. + */ + public Exception getTimeoutException(Exception e) { + return e; + } + private class RetryingListener implements ActionListener { private static final int MAX_EXCEPTIONS = 4; - private final long delayMillisBound; private ArrayDeque caughtExceptions; + private Iterator backoffDelayIterator; - private RetryingListener(long delayMillisBound, ArrayDeque caughtExceptions) { - this.delayMillisBound = delayMillisBound; + private RetryingListener(Iterator backoffDelayIterator, ArrayDeque caughtExceptions) { this.caughtExceptions = caughtExceptions; + this.backoffDelayIterator = backoffDelayIterator; } @Override @@ -169,16 +191,13 @@ public void onFailure(Exception e) { () -> new ParameterizedMessage("retryable action timed out after {}", TimeValue.timeValueMillis(elapsedMillis)), e ); - onFinalFailure(e); + onFinalFailure(getTimeoutException(e)); } else { addException(e); - final long nextDelayMillisBound = Math.min(delayMillisBound * 2, Integer.MAX_VALUE); - final RetryingListener retryingListener = new RetryingListener(nextDelayMillisBound, caughtExceptions); - final Runnable runnable = createRunnable(retryingListener); - final long delayMillis = Randomness.get().nextInt(Math.toIntExact(delayMillisBound)) + 1; + final TimeValue delay = backoffDelayIterator.next(); + final Runnable runnable = createRunnable(this); if (isDone.get() == false) { - final TimeValue delay = TimeValue.timeValueMillis(delayMillis); logger.debug(() -> new ParameterizedMessage("retrying action that failed in {}", delay), e); try { retryTask = threadPool.schedule(runnable, delay, executor); diff --git a/server/src/main/java/org/opensearch/action/support/ThreadedActionListener.java b/server/src/main/java/org/opensearch/action/support/ThreadedActionListener.java index f6a4c87c57344..8b51535ef8d87 100644 --- a/server/src/main/java/org/opensearch/action/support/ThreadedActionListener.java +++ b/server/src/main/java/org/opensearch/action/support/ThreadedActionListener.java @@ -34,13 +34,15 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.action.ActionListener; import org.opensearch.threadpool.ThreadPool; /** * An action listener that wraps another action listener and threading its execution. + * + * @opensearch.internal */ public final class ThreadedActionListener implements ActionListener { private final Logger logger; diff --git a/server/src/main/java/org/opensearch/action/support/TimeoutTaskCancellationUtility.java b/server/src/main/java/org/opensearch/action/support/TimeoutTaskCancellationUtility.java index e132555a3c071..a317a45eab31f 100644 --- a/server/src/main/java/org/opensearch/action/support/TimeoutTaskCancellationUtility.java +++ b/server/src/main/java/org/opensearch/action/support/TimeoutTaskCancellationUtility.java @@ -11,15 +11,15 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.opensearch.client.OriginSettingClient; import org.opensearch.client.node.NodeClient; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.tasks.TaskId; import org.opensearch.search.SearchService; import org.opensearch.tasks.CancellableTask; -import org.opensearch.tasks.TaskId; import org.opensearch.threadpool.Scheduler; import org.opensearch.threadpool.ThreadPool; @@ -29,6 +29,11 @@ import static org.opensearch.action.admin.cluster.node.tasks.get.GetTaskAction.TASKS_ORIGIN; import static org.opensearch.action.search.TransportSearchAction.SEARCH_CANCEL_AFTER_TIME_INTERVAL_SETTING; +/** + * Utility to cancel a timeout task + * + * @opensearch.internal + */ public class TimeoutTaskCancellationUtility { private static final Logger logger = LogManager.getLogger(TimeoutTaskCancellationUtility.class); @@ -99,6 +104,8 @@ public static ActionListener wrapWithCancellationListener( * Timeout listener which executes the provided runnable after timeout is expired and if a response/failure is not yet received. * If either a response/failure is received before timeout then the scheduled task is cancelled and response/failure is sent back to * the original listener. + * + * @opensearch.internal */ private static class TimeoutRunnableListener implements ActionListener, Runnable { diff --git a/server/src/main/java/org/opensearch/action/support/TransportAction.java b/server/src/main/java/org/opensearch/action/support/TransportAction.java index 84ece8cfec530..daa11c2d7d80f 100644 --- a/server/src/main/java/org/opensearch/action/support/TransportAction.java +++ b/server/src/main/java/org/opensearch/action/support/TransportAction.java @@ -34,20 +34,26 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.action.ActionResponse; import org.opensearch.common.lease.Releasable; import org.opensearch.common.lease.Releasables; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.tasks.TaskCancelledException; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskCancelledException; -import org.opensearch.tasks.TaskId; import org.opensearch.tasks.TaskListener; import org.opensearch.tasks.TaskManager; import java.util.concurrent.atomic.AtomicInteger; +/** + * Base class for a transport action + * + * @opensearch.internal + */ public abstract class TransportAction { public final String actionName; @@ -88,31 +94,39 @@ public final Task execute(Request request, ActionListener listener) { */ final Releasable unregisterChildNode = registerChildNode(request.getParentTask()); final Task task; + try { task = taskManager.register("transport", actionName, request); } catch (TaskCancelledException e) { unregisterChildNode.close(); throw e; } - execute(task, request, new ActionListener() { - @Override - public void onResponse(Response response) { - try { - Releasables.close(unregisterChildNode, () -> taskManager.unregister(task)); - } finally { - listener.onResponse(response); + + ThreadContext.StoredContext storedContext = taskManager.taskExecutionStarted(task); + try { + execute(task, request, new ActionListener() { + @Override + public void onResponse(Response response) { + try { + Releasables.close(unregisterChildNode, () -> taskManager.unregister(task)); + } finally { + listener.onResponse(response); + } } - } - @Override - public void onFailure(Exception e) { - try { - Releasables.close(unregisterChildNode, () -> taskManager.unregister(task)); - } finally { - listener.onFailure(e); + @Override + public void onFailure(Exception e) { + try { + Releasables.close(unregisterChildNode, () -> taskManager.unregister(task)); + } finally { + listener.onFailure(e); + } } - } - }); + }); + } finally { + storedContext.close(); + } + return task; } @@ -129,25 +143,30 @@ public final Task execute(Request request, TaskListener listener) { unregisterChildNode.close(); throw e; } - execute(task, request, new ActionListener() { - @Override - public void onResponse(Response response) { - try { - Releasables.close(unregisterChildNode, () -> taskManager.unregister(task)); - } finally { - listener.onResponse(task, response); + ThreadContext.StoredContext storedContext = taskManager.taskExecutionStarted(task); + try { + execute(task, request, new ActionListener() { + @Override + public void onResponse(Response response) { + try { + Releasables.close(unregisterChildNode, () -> taskManager.unregister(task)); + } finally { + listener.onResponse(task, response); + } } - } - @Override - public void onFailure(Exception e) { - try { - Releasables.close(unregisterChildNode, () -> taskManager.unregister(task)); - } finally { - listener.onFailure(task, e); + @Override + public void onFailure(Exception e) { + try { + Releasables.close(unregisterChildNode, () -> taskManager.unregister(task)); + } finally { + listener.onFailure(task, e); + } } - } - }); + }); + } finally { + storedContext.close(); + } return task; } @@ -171,6 +190,11 @@ public final void execute(Task task, Request request, ActionListener l protected abstract void doExecute(Task task, Request request, ActionListener listener); + /** + * A request filter chain + * + * @opensearch.internal + */ private static class RequestFilterChain implements ActionFilterChain { @@ -205,6 +229,8 @@ public void proceed(Task task, String actionName, Request request, ActionListene /** * Wrapper for an action listener that stores the result at the end of the execution + * + * @opensearch.internal */ private static class TaskResultStoringActionListener implements ActionListener { private final ActionListener delegate; diff --git a/server/src/main/java/org/opensearch/action/support/TransportActions.java b/server/src/main/java/org/opensearch/action/support/TransportActions.java index d79059d39f407..03e7509b3b8e3 100644 --- a/server/src/main/java/org/opensearch/action/support/TransportActions.java +++ b/server/src/main/java/org/opensearch/action/support/TransportActions.java @@ -40,6 +40,11 @@ import org.opensearch.index.shard.IllegalIndexShardStateException; import org.opensearch.index.shard.ShardNotFoundException; +/** + * Utility class for transport actions + * + * @opensearch.internal + */ public class TransportActions { public static boolean isShardNotAvailableException(final Throwable e) { diff --git a/server/src/main/java/org/opensearch/action/support/WriteRequest.java b/server/src/main/java/org/opensearch/action/support/WriteRequest.java index 65737ffedeecb..f462464b99ce8 100644 --- a/server/src/main/java/org/opensearch/action/support/WriteRequest.java +++ b/server/src/main/java/org/opensearch/action/support/WriteRequest.java @@ -37,15 +37,17 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.replication.ReplicatedWriteRequest; import org.opensearch.action.update.UpdateRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; /** * Interface implemented by requests that modify the documents in an index like {@link IndexRequest}, {@link UpdateRequest}, and * {@link BulkRequest}. Rather than implement this directly most implementers should extend {@link ReplicatedWriteRequest}. + * + * @opensearch.internal */ public interface WriteRequest> extends Writeable { /** @@ -73,6 +75,11 @@ default R setRefreshPolicy(String refreshPolicy) { ActionRequestValidationException validate(); + /** + * The refresh policy of the request. + * + * @opensearch.internal + */ enum RefreshPolicy implements Writeable { /** * Don't refresh after this request. The default. diff --git a/server/src/main/java/org/opensearch/action/support/WriteRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/WriteRequestBuilder.java index c26a08ad6ab60..899d7a8cd65fe 100644 --- a/server/src/main/java/org/opensearch/action/support/WriteRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/WriteRequestBuilder.java @@ -34,6 +34,11 @@ import org.opensearch.action.support.WriteRequest.RefreshPolicy; +/** + * Builder for a write request operations + * + * @opensearch.internal + */ public interface WriteRequestBuilder> { WriteRequest request(); diff --git a/server/src/main/java/org/opensearch/action/support/WriteResponse.java b/server/src/main/java/org/opensearch/action/support/WriteResponse.java index 89b04bbdb8362..786f851d88542 100644 --- a/server/src/main/java/org/opensearch/action/support/WriteResponse.java +++ b/server/src/main/java/org/opensearch/action/support/WriteResponse.java @@ -42,6 +42,8 @@ /** * Interface implemented by responses for actions that modify the documents in an index like {@link IndexResponse}, {@link UpdateResponse}, * and {@link BulkResponse}. Rather than implement this directly most implementers should extend {@link DocWriteResponse}. + * + * @opensearch.internal */ public interface WriteResponse { /** diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastOperationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastOperationRequestBuilder.java index 2eaa029e687f4..8c756c1682913 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastOperationRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastOperationRequestBuilder.java @@ -32,11 +32,16 @@ package org.opensearch.action.support.broadcast; -import org.opensearch.action.ActionType; import org.opensearch.action.ActionRequestBuilder; +import org.opensearch.action.ActionType; import org.opensearch.action.support.IndicesOptions; import org.opensearch.client.OpenSearchClient; +/** + * Request builder for broadcast operations + * + * @opensearch.internal + */ public abstract class BroadcastOperationRequestBuilder< Request extends BroadcastRequest, Response extends BroadcastResponse, diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastRequest.java b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastRequest.java index 457f97acbe98b..8a27e032cec5e 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastRequest.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastRequest.java @@ -36,12 +36,17 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Transport request for broadcast operations + * + * @opensearch.internal + */ public class BroadcastRequest> extends ActionRequest implements IndicesRequest.Replaceable { protected String[] indices; diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastResponse.java b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastResponse.java index 89dcec7bf75ae..fc0210d3e69b8 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastResponse.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastResponse.java @@ -32,26 +32,28 @@ package org.opensearch.action.support.broadcast; -import org.opensearch.action.ActionResponse; -import org.opensearch.action.support.DefaultShardOperationFailedException; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.rest.action.RestActions; import java.io.IOException; import java.util.List; -import static org.opensearch.action.support.DefaultShardOperationFailedException.readShardOperationFailed; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.action.support.DefaultShardOperationFailedException.readShardOperationFailed; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Base class for all broadcast operation based responses. + * + * @opensearch.internal */ public class BroadcastResponse extends ActionResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardOperationFailedException.java b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardOperationFailedException.java index f91de7f3ced7a..03cdb1103343e 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardOperationFailedException.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardOperationFailedException.java @@ -34,15 +34,15 @@ import org.opensearch.OpenSearchException; import org.opensearch.OpenSearchWrapperException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; /** * An exception indicating that a failure occurred performing an operation on the shard. * - * + * @opensearch.internal */ public class BroadcastShardOperationFailedException extends OpenSearchException implements OpenSearchWrapperException { diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardRequest.java b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardRequest.java index e89a1437784b9..b426f6e41f81d 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardRequest.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardRequest.java @@ -35,13 +35,18 @@ import org.opensearch.action.IndicesRequest; import org.opensearch.action.OriginalIndices; import org.opensearch.action.support.IndicesOptions; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.transport.TransportRequest; import java.io.IOException; +/** + * Base class for broadcasting shard requests + * + * @opensearch.internal + */ public abstract class BroadcastShardRequest extends TransportRequest implements IndicesRequest { private ShardId shardId; diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardResponse.java b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardResponse.java index 96245ec4d91f1..9603f886366f2 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardResponse.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/BroadcastShardResponse.java @@ -32,13 +32,18 @@ package org.opensearch.action.support.broadcast; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; -import org.opensearch.transport.TransportResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.transport.TransportResponse; import java.io.IOException; +/** + * Base class for broadcasting response + * + * @opensearch.internal + */ public abstract class BroadcastShardResponse extends TransportResponse { ShardId shardId; diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/TransportBroadcastAction.java b/server/src/main/java/org/opensearch/action/support/broadcast/TransportBroadcastAction.java index 32dda0af3970d..8bf8555194976 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/TransportBroadcastAction.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/TransportBroadcastAction.java @@ -33,7 +33,6 @@ package org.opensearch.action.support.broadcast; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.NoShardAvailableActionException; import org.opensearch.action.support.ActionFilters; @@ -44,13 +43,15 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.FailAwareWeightedRouting; import org.opensearch.cluster.routing.GroupShardsIterator; import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportChannel; @@ -63,6 +64,11 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; +/** + * Base transport broadcast action class + * + * @opensearch.internal + */ public abstract class TransportBroadcastAction< Request extends BroadcastRequest, Response extends BroadcastResponse, @@ -119,6 +125,11 @@ protected void doExecute(Task task, Request request, ActionListener li protected abstract ClusterBlockException checkRequestBlock(ClusterState state, Request request, String[] concreteIndices); + /** + * Asynchronous broadcast action + * + * @opensearch.internal + */ protected class AsyncBroadcastAction { private final Task task; @@ -240,7 +251,9 @@ void onOperation(@Nullable ShardRouting shard, final ShardIterator shardIt, int // we set the shard failure always, even if its the first in the replication group, and the next one // will work (it will just override it...) setFailure(shardIt, shardIndex, e); - ShardRouting nextShard = shardIt.nextOrNull(); + ShardRouting nextShard = FailAwareWeightedRouting.getInstance() + .findNext(shardIt, clusterService.state(), e, () -> counterOps.incrementAndGet()); + if (nextShard != null) { if (e != null) { if (logger.isTraceEnabled()) { @@ -315,6 +328,11 @@ void setFailure(ShardIterator shardIt, int shardIndex, Exception e) { } } + /** + * A shard transport handler + * + * @opensearch.internal + */ class ShardTransportHandler implements TransportRequestHandler { @Override diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java b/server/src/main/java/org/opensearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java index 3950f3a9fef77..c08cfb7af0e3d 100644 --- a/server/src/main/java/org/opensearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java +++ b/server/src/main/java/org/opensearch/action/support/broadcast/node/TransportBroadcastByNodeAction.java @@ -33,12 +33,10 @@ package org.opensearch.action.support.broadcast.node; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.IndicesRequest; import org.opensearch.action.NoShardAvailableActionException; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.TransportActions; @@ -53,9 +51,12 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.NodeShouldNotConnectException; @@ -63,10 +64,9 @@ import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestHandler; -import org.opensearch.transport.TransportResponse; +import org.opensearch.transport.TransportRequestOptions; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; -import org.opensearch.transport.TransportRequestOptions; import java.io.IOException; import java.util.ArrayList; @@ -86,6 +86,8 @@ * @param the underlying client request * @param the response to the client request * @param per-shard operation results + * + * @opensearch.internal */ public abstract class TransportBroadcastByNodeAction< Request extends BroadcastRequest, @@ -271,6 +273,11 @@ protected void doExecute(Task task, Request request, ActionListener li new AsyncAction(task, request, listener).start(); } + /** + * Asynchronous action + * + * @opensearch.internal + */ protected class AsyncAction { private final Task task; private final Request request; @@ -310,9 +317,9 @@ protected AsyncAction(Task task, Request request, ActionListener liste for (ShardRouting shard : shardIt) { // send a request to the shard only if it is assigned to a node that is in the local node's cluster state // a scenario in which a shard can be assigned but to a node that is not in the local node's cluster state - // is when the shard is assigned to the master node, the local node has detected the master as failed - // and a new master has not yet been elected; in this situation the local node will have removed the - // master node from the local cluster state, but the shards assigned to the master will still be in the + // is when the shard is assigned to the cluster-manager node, the local node has detected the cluster-manager as failed + // and a new cluster-manager has not yet been elected; in this situation the local node will have removed the + // cluster-manager node from the local cluster state, but the shards assigned to the cluster-manager will still be in the // routing table as such if (shard.assignedToNode() && nodes.get(shard.currentNodeId()) != null) { String nodeId = shard.currentNodeId(); @@ -441,6 +448,11 @@ protected void onCompletion() { } } + /** + * Broadcast by a node's transport request handler + * + * @opensearch.internal + */ class BroadcastByNodeTransportRequestHandler implements TransportRequestHandler { @Override public void messageReceived(final NodeRequest request, TransportChannel channel, Task task) throws Exception { @@ -520,6 +532,18 @@ private void onShardOperation( } } + /** + * This method reads ShardRouting from input stream + */ + public List getShardRoutingsFromInputStream(StreamInput in) throws IOException { + return in.readList(ShardRouting::new); + } + + /** + * A node request + * + * @opensearch.internal + */ public class NodeRequest extends TransportRequest implements IndicesRequest { private String nodeId; @@ -530,7 +554,7 @@ public class NodeRequest extends TransportRequest implements IndicesRequest { public NodeRequest(StreamInput in) throws IOException { super(in); indicesLevelRequest = readRequestFrom(in); - shards = in.readList(ShardRouting::new); + shards = getShardRoutingsFromInputStream(in); nodeId = in.readString(); } @@ -567,6 +591,11 @@ public void writeTo(StreamOutput out) throws IOException { } } + /** + * A node response + * + * @opensearch.internal + */ class NodeResponse extends TransportResponse { protected String nodeId; protected int totalShards; @@ -631,6 +660,8 @@ public void writeTo(StreamOutput out) throws IOException { /** * Can be used for implementations of {@link #shardOperation(BroadcastRequest, ShardRouting) shardOperation} for * which there is no shard-level return value. + * + * @opensearch.internal */ public static final class EmptyResult implements Writeable { public static EmptyResult INSTANCE = new EmptyResult(); diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/node/package-info.java b/server/src/main/java/org/opensearch/action/support/broadcast/node/package-info.java new file mode 100644 index 0000000000000..1d5ebaf244f77 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/broadcast/node/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node Level Broadcast transport handlers. */ +package org.opensearch.action.support.broadcast.node; diff --git a/server/src/main/java/org/opensearch/action/support/broadcast/package-info.java b/server/src/main/java/org/opensearch/action/support/broadcast/package-info.java new file mode 100644 index 0000000000000..00b5c640fd3a1 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/broadcast/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Broadcast info transport handlers. */ +package org.opensearch.action.support.broadcast; diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeOperationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeOperationRequestBuilder.java new file mode 100644 index 0000000000000..05c06604725a1 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeOperationRequestBuilder.java @@ -0,0 +1,96 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager; + +import org.opensearch.action.ActionRequestBuilder; +import org.opensearch.action.ActionType; +import org.opensearch.client.OpenSearchClient; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionResponse; + +/** + * Base request builder for cluster-manager node operations + * + * @opensearch.internal + */ +public abstract class ClusterManagerNodeOperationRequestBuilder< + Request extends ClusterManagerNodeRequest, + Response extends ActionResponse, + RequestBuilder extends ClusterManagerNodeOperationRequestBuilder> extends ActionRequestBuilder< + Request, + Response> { + + protected ClusterManagerNodeOperationRequestBuilder(OpenSearchClient client, ActionType action, Request request) { + super(client, action, request); + } + + /** + * Sets the cluster-manager node timeout in case the cluster-manager has not yet been discovered. + */ + @SuppressWarnings("unchecked") + public final RequestBuilder setClusterManagerNodeTimeout(TimeValue timeout) { + request.clusterManagerNodeTimeout(timeout); + return (RequestBuilder) this; + } + + /** + * Sets the cluster-manager node timeout in case the cluster-manager has not yet been discovered. + * + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #setClusterManagerNodeTimeout(TimeValue)} + */ + @SuppressWarnings("unchecked") + @Deprecated + public final RequestBuilder setMasterNodeTimeout(TimeValue timeout) { + return setClusterManagerNodeTimeout(timeout); + } + + /** + * Sets the cluster-manager node timeout in case the cluster-manager has not yet been discovered. + */ + @SuppressWarnings("unchecked") + public final RequestBuilder setClusterManagerNodeTimeout(String timeout) { + request.clusterManagerNodeTimeout(timeout); + return (RequestBuilder) this; + } + + /** + * Sets the cluster-manager node timeout in case the cluster-manager has not yet been discovered. + * + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #setClusterManagerNodeTimeout(String)} + */ + @SuppressWarnings("unchecked") + @Deprecated + public final RequestBuilder setMasterNodeTimeout(String timeout) { + return setClusterManagerNodeTimeout(timeout); + } +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeReadOperationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeReadOperationRequestBuilder.java new file mode 100644 index 0000000000000..c261f44a0cd04 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeReadOperationRequestBuilder.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager; + +import org.opensearch.action.ActionType; +import org.opensearch.client.OpenSearchClient; +import org.opensearch.core.action.ActionResponse; + +/** + * Base request builder for cluster-manager node read operations that can be executed on the local node as well + * + * @opensearch.internal + */ +public abstract class ClusterManagerNodeReadOperationRequestBuilder< + Request extends ClusterManagerNodeReadRequest, + Response extends ActionResponse, + RequestBuilder extends ClusterManagerNodeReadOperationRequestBuilder> extends + ClusterManagerNodeOperationRequestBuilder { + + protected ClusterManagerNodeReadOperationRequestBuilder(OpenSearchClient client, ActionType action, Request request) { + super(client, action, request); + } + + /** + * Specifies if the request should be executed on local node rather than on master + */ + @SuppressWarnings("unchecked") + public final RequestBuilder setLocal(boolean local) { + request.local(local); + return (RequestBuilder) this; + } +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeReadRequest.java b/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeReadRequest.java new file mode 100644 index 0000000000000..6dcc6ed1b098e --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeReadRequest.java @@ -0,0 +1,77 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Base request for cluster-manager based read operations that allows to read the cluster state from the local node if needed + * + * @opensearch.internal + */ +public abstract class ClusterManagerNodeReadRequest> extends + ClusterManagerNodeRequest { + + protected boolean local = false; + + protected ClusterManagerNodeReadRequest() {} + + protected ClusterManagerNodeReadRequest(StreamInput in) throws IOException { + super(in); + local = in.readBoolean(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeBoolean(local); + } + + @SuppressWarnings("unchecked") + public final Request local(boolean local) { + this.local = local; + return (Request) this; + } + + /** + * Return local information, do not retrieve the state from cluster-manager node (default: false). + * @return true if local information is to be returned; + * false if information is to be retrieved from cluster-manager node (default). + */ + public final boolean local() { + return local; + } +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeRequest.java b/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeRequest.java new file mode 100644 index 0000000000000..a43d6fb0b1e7a --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/ClusterManagerNodeRequest.java @@ -0,0 +1,122 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager; + +import org.opensearch.action.ActionRequest; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * A based request for cluster-manager based operation. + * + * @opensearch.internal + */ +public abstract class ClusterManagerNodeRequest> extends ActionRequest { + + public static final TimeValue DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT = TimeValue.timeValueSeconds(30); + + /** @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT} */ + @Deprecated + public static final TimeValue DEFAULT_MASTER_NODE_TIMEOUT = DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; + + protected TimeValue clusterManagerNodeTimeout = DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; + + /** @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #clusterManagerNodeTimeout} */ + @Deprecated + protected TimeValue masterNodeTimeout = clusterManagerNodeTimeout; + + protected ClusterManagerNodeRequest() {} + + protected ClusterManagerNodeRequest(StreamInput in) throws IOException { + super(in); + clusterManagerNodeTimeout = in.readTimeValue(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeTimeValue(clusterManagerNodeTimeout); + } + + /** + * A timeout value in case the cluster-manager has not been discovered yet or disconnected. + */ + @SuppressWarnings("unchecked") + public final Request clusterManagerNodeTimeout(TimeValue timeout) { + this.clusterManagerNodeTimeout = timeout; + return (Request) this; + } + + /** + * A timeout value in case the cluster-manager has not been discovered yet or disconnected. + * + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #clusterManagerNodeTimeout(TimeValue)} + */ + @SuppressWarnings("unchecked") + @Deprecated + public final Request masterNodeTimeout(TimeValue timeout) { + return clusterManagerNodeTimeout(timeout); + } + + /** + * A timeout value in case the cluster-manager has not been discovered yet or disconnected. + */ + public final Request clusterManagerNodeTimeout(String timeout) { + return clusterManagerNodeTimeout( + TimeValue.parseTimeValue(timeout, null, getClass().getSimpleName() + ".clusterManagerNodeTimeout") + ); + } + + /** + * A timeout value in case the cluster-manager has not been discovered yet or disconnected. + * + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #clusterManagerNodeTimeout(String)} + */ + @Deprecated + public final Request masterNodeTimeout(String timeout) { + return clusterManagerNodeTimeout(timeout); + } + + public final TimeValue clusterManagerNodeTimeout() { + return this.clusterManagerNodeTimeout; + } + + /** @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #clusterManagerNodeTimeout()} */ + @Deprecated + public final TimeValue masterNodeTimeout() { + return clusterManagerNodeTimeout(); + } +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/TransportClusterManagerNodeAction.java b/server/src/main/java/org/opensearch/action/support/clustermanager/TransportClusterManagerNodeAction.java new file mode 100644 index 0000000000000..536ddcdd402e2 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/TransportClusterManagerNodeAction.java @@ -0,0 +1,375 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.action.ActionListenerResponseHandler; +import org.opensearch.action.ActionRunnable; +import org.opensearch.action.bulk.BackoffPolicy; +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.action.support.RetryableAction; +import org.opensearch.cluster.ClusterManagerNodeChangePredicate; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.NotClusterManagerException; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.coordination.FailedToCommitClusterStateException; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.metadata.ProcessClusterEventTimeoutException; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; +import org.opensearch.cluster.service.ClusterManagerThrottlingException; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.discovery.ClusterManagerNotDiscoveredException; +import org.opensearch.node.NodeClosedException; +import org.opensearch.tasks.Task; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.ConnectTransportException; +import org.opensearch.transport.RemoteTransportException; +import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.function.Predicate; + +/** + * A base class for operations that needs to be performed on the cluster-manager node. + * + * @opensearch.internal + */ +public abstract class TransportClusterManagerNodeAction, Response extends ActionResponse> + extends HandledTransportAction { + + private static final Logger logger = LogManager.getLogger(TransportClusterManagerNodeAction.class); + + protected final ThreadPool threadPool; + protected final TransportService transportService; + protected final ClusterService clusterService; + protected final IndexNameExpressionResolver indexNameExpressionResolver; + + private final String executor; + + protected TransportClusterManagerNodeAction( + String actionName, + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + Writeable.Reader request, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + this(actionName, true, transportService, clusterService, threadPool, actionFilters, request, indexNameExpressionResolver); + } + + protected TransportClusterManagerNodeAction( + String actionName, + boolean canTripCircuitBreaker, + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + Writeable.Reader request, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super(actionName, canTripCircuitBreaker, transportService, actionFilters, request); + this.transportService = transportService; + this.clusterService = clusterService; + this.threadPool = threadPool; + this.indexNameExpressionResolver = indexNameExpressionResolver; + this.executor = executor(); + } + + protected abstract String executor(); + + protected abstract Response read(StreamInput in) throws IOException; + + /** + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #clusterManagerOperation(ClusterManagerNodeRequest, ClusterState, ActionListener)} + */ + @Deprecated + protected void masterOperation(Request request, ClusterState state, ActionListener listener) throws Exception { + throw new UnsupportedOperationException("Must be overridden"); + } + + // TODO: Add abstract keyword after removing the deprecated masterOperation() + protected void clusterManagerOperation(Request request, ClusterState state, ActionListener listener) throws Exception { + masterOperation(request, state, listener); + } + + /** + * Override this operation if access to the task parameter is needed + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #clusterManagerOperation(Task, ClusterManagerNodeRequest, ClusterState, ActionListener)} + */ + @Deprecated + protected void masterOperation(Task task, Request request, ClusterState state, ActionListener listener) throws Exception { + clusterManagerOperation(request, state, listener); + } + + /** + * Override this operation if access to the task parameter is needed + */ + // TODO: Change the implementation to call 'clusterManagerOperation(request...)' after removing the deprecated masterOperation() + protected void clusterManagerOperation(Task task, Request request, ClusterState state, ActionListener listener) + throws Exception { + masterOperation(task, request, state, listener); + } + + protected boolean localExecute(Request request) { + return false; + } + + protected abstract ClusterBlockException checkBlock(Request request, ClusterState state); + + @Override + protected void doExecute(Task task, final Request request, ActionListener listener) { + if (task != null) { + request.setParentTask(clusterService.localNode().getId(), task.getId()); + } + new AsyncSingleAction(task, request, listener).run(); + } + + /** + * Asynchronous single action + * + * @opensearch.internal + */ + class AsyncSingleAction extends RetryableAction { + + private ActionListener listener; + private final Request request; + private ClusterStateObserver observer; + private final long startTime; + private final Task task; + + AsyncSingleAction(Task task, Request request, ActionListener listener) { + super( + logger, + threadPool, + ClusterManagerTaskThrottler.getBaseDelayForRetry(), + request.clusterManagerNodeTimeout, + listener, + BackoffPolicy.exponentialEqualJitterBackoff( + ClusterManagerTaskThrottler.getBaseDelayForRetry().millis(), + ClusterManagerTaskThrottler.getMaxDelayForRetry().millis() + ), + ThreadPool.Names.SAME + ); + this.task = task; + this.request = request; + this.startTime = threadPool.relativeTimeInMillis(); + } + + @Override + public void tryAction(ActionListener retryListener) { + ClusterState state = clusterService.state(); + logger.trace("starting processing request [{}] with cluster state version [{}]", request, state.version()); + this.listener = retryListener; + doStart(state); + } + + @Override + public boolean shouldRetry(Exception e) { + // If remote address is null, i.e request is generated from same node and we would want to perform retry for it + // If remote address is not null, i.e request is generated from remote node and received on this master node on transport layer + // in that case we would want throttling retry to perform on remote node only not on this master node. + if (request.remoteAddress() == null) { + if (e instanceof TransportException) { + return ((TransportException) e).unwrapCause() instanceof ClusterManagerThrottlingException; + } + return e instanceof ClusterManagerThrottlingException; + } + return false; + } + + /** + * If tasks gets timed out in retrying on throttling, + * it should send cluster event timeout exception. + */ + @Override + public Exception getTimeoutException(Exception e) { + return new ProcessClusterEventTimeoutException(request.masterNodeTimeout, actionName); + } + + protected void doStart(ClusterState clusterState) { + try { + final DiscoveryNodes nodes = clusterState.nodes(); + if (nodes.isLocalNodeElectedClusterManager() || localExecute(request)) { + // check for block, if blocked, retry, else, execute locally + final ClusterBlockException blockException = checkBlock(request, clusterState); + if (blockException != null) { + if (!blockException.retryable()) { + listener.onFailure(blockException); + } else { + logger.debug("can't execute due to a cluster block, retrying", blockException); + retry(clusterState, blockException, newState -> { + try { + ClusterBlockException newException = checkBlock(request, newState); + return (newException == null || !newException.retryable()); + } catch (Exception e) { + // accept state as block will be rechecked by doStart() and listener.onFailure() then called + logger.trace("exception occurred during cluster block checking, accepting state", e); + return true; + } + }); + } + } else { + ActionListener delegate = ActionListener.delegateResponse(listener, (delegatedListener, t) -> { + if (t instanceof FailedToCommitClusterStateException || t instanceof NotClusterManagerException) { + logger.debug( + () -> new ParameterizedMessage( + "master could not publish cluster state or " + + "stepped down before publishing action [{}], scheduling a retry", + actionName + ), + t + ); + retryOnMasterChange(clusterState, t); + } else { + delegatedListener.onFailure(t); + } + }); + threadPool.executor(executor) + .execute(ActionRunnable.wrap(delegate, l -> clusterManagerOperation(task, request, clusterState, l))); + } + } else { + if (nodes.getClusterManagerNode() == null) { + logger.debug("no known cluster-manager node, scheduling a retry"); + retryOnMasterChange(clusterState, null); + } else { + DiscoveryNode clusterManagerNode = nodes.getClusterManagerNode(); + final String actionName = getClusterManagerActionName(clusterManagerNode); + transportService.sendRequest( + clusterManagerNode, + actionName, + request, + new ActionListenerResponseHandler(listener, TransportClusterManagerNodeAction.this::read) { + @Override + public void handleException(final TransportException exp) { + Throwable cause = exp.unwrapCause(); + if (cause instanceof ConnectTransportException + || (exp instanceof RemoteTransportException && cause instanceof NodeClosedException)) { + // we want to retry here a bit to see if a new cluster-manager is elected + logger.debug( + "connection exception while trying to forward request with action name [{}] to " + + "master node [{}], scheduling a retry. Error: [{}]", + actionName, + nodes.getClusterManagerNode(), + exp.getDetailedMessage() + ); + retryOnMasterChange(clusterState, cause); + } else { + listener.onFailure(exp); + } + } + } + ); + } + } + } catch (Exception e) { + listener.onFailure(e); + } + } + + private void retryOnMasterChange(ClusterState state, Throwable failure) { + retry(state, failure, ClusterManagerNodeChangePredicate.build(state)); + } + + private void retry(ClusterState state, final Throwable failure, final Predicate statePredicate) { + if (observer == null) { + final long remainingTimeoutMS = request.clusterManagerNodeTimeout().millis() - (threadPool.relativeTimeInMillis() + - startTime); + if (remainingTimeoutMS <= 0) { + logger.debug(() -> new ParameterizedMessage("timed out before retrying [{}] after failure", actionName), failure); + listener.onFailure(new ClusterManagerNotDiscoveredException(failure)); + return; + } + this.observer = new ClusterStateObserver( + state, + clusterService, + TimeValue.timeValueMillis(remainingTimeoutMS), + logger, + threadPool.getThreadContext() + ); + } + observer.waitForNextChange(new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + doStart(state); + } + + @Override + public void onClusterServiceClose() { + listener.onFailure(new NodeClosedException(clusterService.localNode())); + } + + @Override + public void onTimeout(TimeValue timeout) { + logger.debug( + () -> new ParameterizedMessage("timed out while retrying [{}] after failure (timeout [{}])", actionName, timeout), + failure + ); + listener.onFailure(new ClusterManagerNotDiscoveredException(failure)); + } + }, statePredicate); + } + } + + /** + * Allows to conditionally return a different cluster-manager node action name in the case an action gets renamed. + * This mainly for backwards compatibility should be used rarely + */ + protected String getClusterManagerActionName(DiscoveryNode node) { + return actionName; + } + + /** + * Allows to conditionally return a different cluster-manager node action name in the case an action gets renamed. + * This mainly for backwards compatibility should be used rarely + * + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #getClusterManagerActionName(DiscoveryNode)} + */ + @Deprecated + protected String getMasterActionName(DiscoveryNode node) { + return getClusterManagerActionName(node); + } + +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/TransportClusterManagerNodeReadAction.java b/server/src/main/java/org/opensearch/action/support/clustermanager/TransportClusterManagerNodeReadAction.java new file mode 100644 index 0000000000000..d8cd5af992028 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/TransportClusterManagerNodeReadAction.java @@ -0,0 +1,91 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +/** + * A base class for read operations that needs to be performed on the cluster-manager node. + * Can also be executed on the local node if needed. + * + * @opensearch.internal + */ +public abstract class TransportClusterManagerNodeReadAction< + Request extends ClusterManagerNodeReadRequest, + Response extends ActionResponse> extends TransportClusterManagerNodeAction { + + protected TransportClusterManagerNodeReadAction( + String actionName, + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + Writeable.Reader request, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + this(actionName, true, transportService, clusterService, threadPool, actionFilters, request, indexNameExpressionResolver); + } + + protected TransportClusterManagerNodeReadAction( + String actionName, + boolean checkSizeLimit, + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + Writeable.Reader request, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super( + actionName, + checkSizeLimit, + transportService, + clusterService, + threadPool, + actionFilters, + request, + indexNameExpressionResolver + ); + } + + @Override + protected final boolean localExecute(Request request) { + return request.local(); + } +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/info/ClusterInfoRequest.java b/server/src/main/java/org/opensearch/action/support/clustermanager/info/ClusterInfoRequest.java new file mode 100644 index 0000000000000..5b7da57705ac3 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/info/ClusterInfoRequest.java @@ -0,0 +1,106 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager.info; + +import org.opensearch.Version; +import org.opensearch.action.IndicesRequest; +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; + +import java.io.IOException; + +/** + * Transport request for cluster information + * + * @opensearch.internal + */ +public abstract class ClusterInfoRequest> extends ClusterManagerNodeReadRequest + implements + IndicesRequest.Replaceable { + + private String[] indices = Strings.EMPTY_ARRAY; + + private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen(); + + public ClusterInfoRequest() {} + + public ClusterInfoRequest(StreamInput in) throws IOException { + super(in); + indices = in.readStringArray(); + if (in.getVersion().before(Version.V_2_0_0)) { + in.readStringArray(); + } + indicesOptions = IndicesOptions.readIndicesOptions(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + out.writeStringArray(indices); + if (out.getVersion().before(Version.V_2_0_0)) { + out.writeStringArray(Strings.EMPTY_ARRAY); + } + indicesOptions.writeIndicesOptions(out); + } + + @Override + @SuppressWarnings("unchecked") + public Request indices(String... indices) { + this.indices = indices; + return (Request) this; + } + + @SuppressWarnings("unchecked") + public Request indicesOptions(IndicesOptions indicesOptions) { + this.indicesOptions = indicesOptions; + return (Request) this; + } + + @Override + public String[] indices() { + return indices; + } + + @Override + public IndicesOptions indicesOptions() { + return indicesOptions; + } + + @Override + public boolean includeDataStreams() { + return true; + } +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/info/ClusterInfoRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/clustermanager/info/ClusterInfoRequestBuilder.java new file mode 100644 index 0000000000000..f22ff60b83a58 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/info/ClusterInfoRequestBuilder.java @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager.info; + +import org.opensearch.action.ActionType; +import org.opensearch.action.support.IndicesOptions; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; +import org.opensearch.client.OpenSearchClient; +import org.opensearch.common.util.ArrayUtils; +import org.opensearch.core.action.ActionResponse; + +/** + * Transport request builder for cluster information + * + * @opensearch.internal + */ +public abstract class ClusterInfoRequestBuilder< + Request extends ClusterInfoRequest, + Response extends ActionResponse, + Builder extends ClusterInfoRequestBuilder> extends ClusterManagerNodeReadOperationRequestBuilder< + Request, + Response, + Builder> { + + protected ClusterInfoRequestBuilder(OpenSearchClient client, ActionType action, Request request) { + super(client, action, request); + } + + @SuppressWarnings("unchecked") + public Builder setIndices(String... indices) { + request.indices(indices); + return (Builder) this; + } + + @SuppressWarnings("unchecked") + public Builder addIndices(String... indices) { + request.indices(ArrayUtils.concat(request.indices(), indices)); + return (Builder) this; + } + + @SuppressWarnings("unchecked") + public Builder setIndicesOptions(IndicesOptions indicesOptions) { + request.indicesOptions(indicesOptions); + return (Builder) this; + } +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/info/TransportClusterInfoAction.java b/server/src/main/java/org/opensearch/action/support/clustermanager/info/TransportClusterInfoAction.java new file mode 100644 index 0000000000000..65f00a4731ab5 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/info/TransportClusterInfoAction.java @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.action.support.clustermanager.info; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.block.ClusterBlockException; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +/** + * Perform cluster information action + * + * @opensearch.internal + */ +public abstract class TransportClusterInfoAction, Response extends ActionResponse> extends + TransportClusterManagerNodeReadAction { + + public TransportClusterInfoAction( + String actionName, + TransportService transportService, + ClusterService clusterService, + ThreadPool threadPool, + ActionFilters actionFilters, + Writeable.Reader request, + IndexNameExpressionResolver indexNameExpressionResolver + ) { + super(actionName, transportService, clusterService, threadPool, actionFilters, request, indexNameExpressionResolver); + } + + @Override + protected String executor() { + // read operation, lightweight... + return ThreadPool.Names.SAME; + } + + @Override + protected ClusterBlockException checkBlock(Request request, ClusterState state) { + return state.blocks() + .indicesBlockedException(ClusterBlockLevel.METADATA_READ, indexNameExpressionResolver.concreteIndexNames(state, request)); + } + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #clusterManagerOperation(ClusterInfoRequest, ClusterState, ActionListener)} */ + @Deprecated + protected final void masterOperation(final Request request, final ClusterState state, final ActionListener listener) { + clusterManagerOperation(request, state, listener); + } + + @Override + protected final void clusterManagerOperation(final Request request, final ClusterState state, final ActionListener listener) { + String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request); + doClusterManagerOperation(request, concreteIndices, state, listener); + } + + // TODO: Add abstract keyword after removing the deprecated doMasterOperation() + protected void doClusterManagerOperation( + Request request, + String[] concreteIndices, + ClusterState state, + ActionListener listener + ) { + doMasterOperation(request, concreteIndices, state, listener); + } + + /** + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #doClusterManagerOperation(ClusterInfoRequest, String[], ClusterState, ActionListener)} + */ + @Deprecated + protected void doMasterOperation(Request request, String[] concreteIndices, ClusterState state, ActionListener listener) { + throw new UnsupportedOperationException("Must be overridden"); + } + +} diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/info/package-info.java b/server/src/main/java/org/opensearch/action/support/clustermanager/info/package-info.java new file mode 100644 index 0000000000000..7f21d5d22ec2e --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/info/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Manager Node Information transport handlers. */ +package org.opensearch.action.support.clustermanager.info; diff --git a/server/src/main/java/org/opensearch/action/support/clustermanager/package-info.java b/server/src/main/java/org/opensearch/action/support/clustermanager/package-info.java new file mode 100644 index 0000000000000..13d604ed71e3d --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/clustermanager/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Manager Node transport handlers. */ +package org.opensearch.action.support.clustermanager; diff --git a/server/src/main/java/org/opensearch/action/support/master/AcknowledgedRequest.java b/server/src/main/java/org/opensearch/action/support/master/AcknowledgedRequest.java index 278c4f287b4f5..59f238a202788 100644 --- a/server/src/main/java/org/opensearch/action/support/master/AcknowledgedRequest.java +++ b/server/src/main/java/org/opensearch/action/support/master/AcknowledgedRequest.java @@ -32,23 +32,27 @@ package org.opensearch.action.support.master; import org.opensearch.cluster.ack.AckedRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +import static org.opensearch.common.unit.TimeValue.timeValueHours; import static org.opensearch.common.unit.TimeValue.timeValueSeconds; /** * Abstract class that allows to mark action requests that support acknowledgements. * Facilitates consistency across different api. + * + * @opensearch.internal */ public abstract class AcknowledgedRequest> extends MasterNodeRequest implements AckedRequest { public static final TimeValue DEFAULT_ACK_TIMEOUT = timeValueSeconds(30); + public static final TimeValue DEFAULT_TASK_EXECUTION_TIMEOUT = timeValueHours(1); protected TimeValue timeout = DEFAULT_ACK_TIMEOUT; diff --git a/server/src/main/java/org/opensearch/action/support/master/AcknowledgedRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/master/AcknowledgedRequestBuilder.java index 9337e646cebea..7a0824c6d30ca 100644 --- a/server/src/main/java/org/opensearch/action/support/master/AcknowledgedRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/master/AcknowledgedRequestBuilder.java @@ -36,7 +36,9 @@ import org.opensearch.common.unit.TimeValue; /** - * Base request builder for master node operations that support acknowledgements + * Base request builder for cluster-manager node operations that support acknowledgements + * + * @opensearch.internal */ public abstract class AcknowledgedRequestBuilder< Request extends AcknowledgedRequest, diff --git a/server/src/main/java/org/opensearch/action/support/master/AcknowledgedResponse.java b/server/src/main/java/org/opensearch/action/support/master/AcknowledgedResponse.java index dca3096b99440..5d09d880336f1 100644 --- a/server/src/main/java/org/opensearch/action/support/master/AcknowledgedResponse.java +++ b/server/src/main/java/org/opensearch/action/support/master/AcknowledgedResponse.java @@ -31,23 +31,25 @@ package org.opensearch.action.support.master; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; /** * A response that indicates that a request has been acknowledged + * + * @opensearch.internal */ public class AcknowledgedResponse extends ActionResponse implements ToXContentObject { diff --git a/server/src/main/java/org/opensearch/action/support/master/MasterNodeOperationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/master/MasterNodeOperationRequestBuilder.java index b39ec5fe4cc6b..0acbd998a6322 100644 --- a/server/src/main/java/org/opensearch/action/support/master/MasterNodeOperationRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/master/MasterNodeOperationRequestBuilder.java @@ -33,41 +33,24 @@ package org.opensearch.action.support.master; import org.opensearch.action.ActionType; -import org.opensearch.action.ActionRequestBuilder; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionResponse; /** - * Base request builder for master node operations + * Base request builder for cluster-manager node operations + * + * @opensearch.internal + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link ClusterManagerNodeOperationRequestBuilder} */ +@Deprecated public abstract class MasterNodeOperationRequestBuilder< Request extends MasterNodeRequest, Response extends ActionResponse, - RequestBuilder extends MasterNodeOperationRequestBuilder> extends ActionRequestBuilder< - Request, - Response> { + RequestBuilder extends MasterNodeOperationRequestBuilder> extends + ClusterManagerNodeOperationRequestBuilder { protected MasterNodeOperationRequestBuilder(OpenSearchClient client, ActionType action, Request request) { super(client, action, request); } - - /** - * Sets the master node timeout in case the master has not yet been discovered. - */ - @SuppressWarnings("unchecked") - public final RequestBuilder setMasterNodeTimeout(TimeValue timeout) { - request.masterNodeTimeout(timeout); - return (RequestBuilder) this; - } - - /** - * Sets the master node timeout in case the master has not yet been discovered. - */ - @SuppressWarnings("unchecked") - public final RequestBuilder setMasterNodeTimeout(String timeout) { - request.masterNodeTimeout(timeout); - return (RequestBuilder) this; - } - } diff --git a/server/src/main/java/org/opensearch/action/support/master/MasterNodeReadOperationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/master/MasterNodeReadOperationRequestBuilder.java index add5c5177df42..36a3fc1d2de73 100644 --- a/server/src/main/java/org/opensearch/action/support/master/MasterNodeReadOperationRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/master/MasterNodeReadOperationRequestBuilder.java @@ -33,28 +33,24 @@ package org.opensearch.action.support.master; import org.opensearch.action.ActionType; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; +import org.opensearch.core.action.ActionResponse; /** - * Base request builder for master node read operations that can be executed on the local node as well + * Base request builder for cluster-manager node read operations that can be executed on the local node as well + * + * @opensearch.internal + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link ClusterManagerNodeReadOperationRequestBuilder} */ +@Deprecated public abstract class MasterNodeReadOperationRequestBuilder< Request extends MasterNodeReadRequest, Response extends ActionResponse, RequestBuilder extends MasterNodeReadOperationRequestBuilder> extends - MasterNodeOperationRequestBuilder { + ClusterManagerNodeReadOperationRequestBuilder { protected MasterNodeReadOperationRequestBuilder(OpenSearchClient client, ActionType action, Request request) { super(client, action, request); } - - /** - * Specifies if the request should be executed on local node rather than on master - */ - @SuppressWarnings("unchecked") - public final RequestBuilder setLocal(boolean local) { - request.local(local); - return (RequestBuilder) this; - } } diff --git a/server/src/main/java/org/opensearch/action/support/master/MasterNodeReadRequest.java b/server/src/main/java/org/opensearch/action/support/master/MasterNodeReadRequest.java index eeafa148ca7c3..ea8f6a6e43cfe 100644 --- a/server/src/main/java/org/opensearch/action/support/master/MasterNodeReadRequest.java +++ b/server/src/main/java/org/opensearch/action/support/master/MasterNodeReadRequest.java @@ -32,43 +32,22 @@ package org.opensearch.action.support.master; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeReadRequest; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** - * Base request for master based read operations that allows to read the cluster state from the local node if needed + * Base request for cluster-manager based read operations that allows to read the cluster state from the local node if needed + * + * @opensearch.internal + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link ClusterManagerNodeReadRequest} */ -public abstract class MasterNodeReadRequest> extends MasterNodeRequest { - - protected boolean local = false; - +@Deprecated +public abstract class MasterNodeReadRequest> extends ClusterManagerNodeReadRequest { protected MasterNodeReadRequest() {} protected MasterNodeReadRequest(StreamInput in) throws IOException { super(in); - local = in.readBoolean(); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - super.writeTo(out); - out.writeBoolean(local); - } - - @SuppressWarnings("unchecked") - public final Request local(boolean local) { - this.local = local; - return (Request) this; - } - - /** - * Return local information, do not retrieve the state from master node (default: false). - * @return true if local information is to be returned; - * false if information is to be retrieved from master node (default). - */ - public final boolean local() { - return local; } } diff --git a/server/src/main/java/org/opensearch/action/support/master/MasterNodeRequest.java b/server/src/main/java/org/opensearch/action/support/master/MasterNodeRequest.java index d5be6c48e23b8..cfab63a845f7f 100644 --- a/server/src/main/java/org/opensearch/action/support/master/MasterNodeRequest.java +++ b/server/src/main/java/org/opensearch/action/support/master/MasterNodeRequest.java @@ -32,52 +32,23 @@ package org.opensearch.action.support.master; -import org.opensearch.action.ActionRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.unit.TimeValue; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** - * A based request for master based operation. + * A based request for cluster-manager based operation. + * + * @opensearch.internal + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link ClusterManagerNodeRequest} */ -public abstract class MasterNodeRequest> extends ActionRequest { - - public static final TimeValue DEFAULT_MASTER_NODE_TIMEOUT = TimeValue.timeValueSeconds(30); - - protected TimeValue masterNodeTimeout = DEFAULT_MASTER_NODE_TIMEOUT; +@Deprecated +public abstract class MasterNodeRequest> extends ClusterManagerNodeRequest { protected MasterNodeRequest() {} protected MasterNodeRequest(StreamInput in) throws IOException { super(in); - masterNodeTimeout = in.readTimeValue(); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - super.writeTo(out); - out.writeTimeValue(masterNodeTimeout); - } - - /** - * A timeout value in case the master has not been discovered yet or disconnected. - */ - @SuppressWarnings("unchecked") - public final Request masterNodeTimeout(TimeValue timeout) { - this.masterNodeTimeout = timeout; - return (Request) this; - } - - /** - * A timeout value in case the master has not been discovered yet or disconnected. - */ - public final Request masterNodeTimeout(String timeout) { - return masterNodeTimeout(TimeValue.parseTimeValue(timeout, null, getClass().getSimpleName() + ".masterNodeTimeout")); - } - - public final TimeValue masterNodeTimeout() { - return this.masterNodeTimeout; } } diff --git a/server/src/main/java/org/opensearch/action/support/master/ShardsAcknowledgedResponse.java b/server/src/main/java/org/opensearch/action/support/master/ShardsAcknowledgedResponse.java index 5964867d2d618..fd54e810528d3 100644 --- a/server/src/main/java/org/opensearch/action/support/master/ShardsAcknowledgedResponse.java +++ b/server/src/main/java/org/opensearch/action/support/master/ShardsAcknowledgedResponse.java @@ -32,18 +32,23 @@ package org.opensearch.action.support.master; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +/** + * Transport response for shard acknowledgements + * + * @opensearch.internal + */ public abstract class ShardsAcknowledgedResponse extends AcknowledgedResponse { protected static final ParseField SHARDS_ACKNOWLEDGED = new ParseField("shards_acknowledged"); diff --git a/server/src/main/java/org/opensearch/action/support/master/TransportMasterNodeAction.java b/server/src/main/java/org/opensearch/action/support/master/TransportMasterNodeAction.java index 62d08c23534af..eec7965bfed02 100644 --- a/server/src/main/java/org/opensearch/action/support/master/TransportMasterNodeAction.java +++ b/server/src/main/java/org/opensearch/action/support/master/TransportMasterNodeAction.java @@ -32,54 +32,24 @@ package org.opensearch.action.support.master; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; -import org.opensearch.action.ActionListenerResponseHandler; -import org.opensearch.action.ActionResponse; -import org.opensearch.action.ActionRunnable; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.HandledTransportAction; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.ClusterStateObserver; -import org.opensearch.cluster.MasterNodeChangePredicate; -import org.opensearch.cluster.NotMasterException; -import org.opensearch.cluster.block.ClusterBlockException; -import org.opensearch.cluster.coordination.FailedToCommitClusterStateException; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeAction; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.unit.TimeValue; -import org.opensearch.discovery.MasterNotDiscoveredException; -import org.opensearch.node.NodeClosedException; -import org.opensearch.tasks.Task; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.threadpool.ThreadPool; -import org.opensearch.transport.ConnectTransportException; -import org.opensearch.transport.RemoteTransportException; -import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportService; -import java.io.IOException; -import java.util.function.Predicate; - /** - * A base class for operations that needs to be performed on the master node. + * A base class for operations that needs to be performed on the cluster-manager node. + * + * @opensearch.internal + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link TransportClusterManagerNodeAction} */ +@Deprecated public abstract class TransportMasterNodeAction, Response extends ActionResponse> extends - HandledTransportAction { - - private static final Logger logger = LogManager.getLogger(TransportMasterNodeAction.class); - - protected final ThreadPool threadPool; - protected final TransportService transportService; - protected final ClusterService clusterService; - protected final IndexNameExpressionResolver indexNameExpressionResolver; - - private final String executor; + TransportClusterManagerNodeAction { protected TransportMasterNodeAction( String actionName, @@ -90,7 +60,7 @@ protected TransportMasterNodeAction( Writeable.Reader request, IndexNameExpressionResolver indexNameExpressionResolver ) { - this(actionName, true, transportService, clusterService, threadPool, actionFilters, request, indexNameExpressionResolver); + super(actionName, true, transportService, clusterService, threadPool, actionFilters, request, indexNameExpressionResolver); } protected TransportMasterNodeAction( @@ -103,186 +73,16 @@ protected TransportMasterNodeAction( Writeable.Reader request, IndexNameExpressionResolver indexNameExpressionResolver ) { - super(actionName, canTripCircuitBreaker, transportService, actionFilters, request); - this.transportService = transportService; - this.clusterService = clusterService; - this.threadPool = threadPool; - this.indexNameExpressionResolver = indexNameExpressionResolver; - this.executor = executor(); + super( + actionName, + canTripCircuitBreaker, + transportService, + clusterService, + threadPool, + actionFilters, + request, + indexNameExpressionResolver + ); } - protected abstract String executor(); - - protected abstract Response read(StreamInput in) throws IOException; - - protected abstract void masterOperation(Request request, ClusterState state, ActionListener listener) throws Exception; - - /** - * Override this operation if access to the task parameter is needed - */ - protected void masterOperation(Task task, Request request, ClusterState state, ActionListener listener) throws Exception { - masterOperation(request, state, listener); - } - - protected boolean localExecute(Request request) { - return false; - } - - protected abstract ClusterBlockException checkBlock(Request request, ClusterState state); - - @Override - protected void doExecute(Task task, final Request request, ActionListener listener) { - ClusterState state = clusterService.state(); - logger.trace("starting processing request [{}] with cluster state version [{}]", request, state.version()); - if (task != null) { - request.setParentTask(clusterService.localNode().getId(), task.getId()); - } - new AsyncSingleAction(task, request, listener).doStart(state); - } - - class AsyncSingleAction { - - private final ActionListener listener; - private final Request request; - private ClusterStateObserver observer; - private final long startTime; - private final Task task; - - AsyncSingleAction(Task task, Request request, ActionListener listener) { - this.task = task; - this.request = request; - this.listener = listener; - this.startTime = threadPool.relativeTimeInMillis(); - } - - protected void doStart(ClusterState clusterState) { - try { - final DiscoveryNodes nodes = clusterState.nodes(); - if (nodes.isLocalNodeElectedMaster() || localExecute(request)) { - // check for block, if blocked, retry, else, execute locally - final ClusterBlockException blockException = checkBlock(request, clusterState); - if (blockException != null) { - if (!blockException.retryable()) { - listener.onFailure(blockException); - } else { - logger.debug("can't execute due to a cluster block, retrying", blockException); - retry(clusterState, blockException, newState -> { - try { - ClusterBlockException newException = checkBlock(request, newState); - return (newException == null || !newException.retryable()); - } catch (Exception e) { - // accept state as block will be rechecked by doStart() and listener.onFailure() then called - logger.trace("exception occurred during cluster block checking, accepting state", e); - return true; - } - }); - } - } else { - ActionListener delegate = ActionListener.delegateResponse(listener, (delegatedListener, t) -> { - if (t instanceof FailedToCommitClusterStateException || t instanceof NotMasterException) { - logger.debug( - () -> new ParameterizedMessage( - "master could not publish cluster state or " - + "stepped down before publishing action [{}], scheduling a retry", - actionName - ), - t - ); - retryOnMasterChange(clusterState, t); - } else { - delegatedListener.onFailure(t); - } - }); - threadPool.executor(executor) - .execute(ActionRunnable.wrap(delegate, l -> masterOperation(task, request, clusterState, l))); - } - } else { - if (nodes.getMasterNode() == null) { - logger.debug("no known master node, scheduling a retry"); - retryOnMasterChange(clusterState, null); - } else { - DiscoveryNode masterNode = nodes.getMasterNode(); - final String actionName = getMasterActionName(masterNode); - transportService.sendRequest( - masterNode, - actionName, - request, - new ActionListenerResponseHandler(listener, TransportMasterNodeAction.this::read) { - @Override - public void handleException(final TransportException exp) { - Throwable cause = exp.unwrapCause(); - if (cause instanceof ConnectTransportException - || (exp instanceof RemoteTransportException && cause instanceof NodeClosedException)) { - // we want to retry here a bit to see if a new master is elected - logger.debug( - "connection exception while trying to forward request with action name [{}] to " - + "master node [{}], scheduling a retry. Error: [{}]", - actionName, - nodes.getMasterNode(), - exp.getDetailedMessage() - ); - retryOnMasterChange(clusterState, cause); - } else { - listener.onFailure(exp); - } - } - } - ); - } - } - } catch (Exception e) { - listener.onFailure(e); - } - } - - private void retryOnMasterChange(ClusterState state, Throwable failure) { - retry(state, failure, MasterNodeChangePredicate.build(state)); - } - - private void retry(ClusterState state, final Throwable failure, final Predicate statePredicate) { - if (observer == null) { - final long remainingTimeoutMS = request.masterNodeTimeout().millis() - (threadPool.relativeTimeInMillis() - startTime); - if (remainingTimeoutMS <= 0) { - logger.debug(() -> new ParameterizedMessage("timed out before retrying [{}] after failure", actionName), failure); - listener.onFailure(new MasterNotDiscoveredException(failure)); - return; - } - this.observer = new ClusterStateObserver( - state, - clusterService, - TimeValue.timeValueMillis(remainingTimeoutMS), - logger, - threadPool.getThreadContext() - ); - } - observer.waitForNextChange(new ClusterStateObserver.Listener() { - @Override - public void onNewClusterState(ClusterState state) { - doStart(state); - } - - @Override - public void onClusterServiceClose() { - listener.onFailure(new NodeClosedException(clusterService.localNode())); - } - - @Override - public void onTimeout(TimeValue timeout) { - logger.debug( - () -> new ParameterizedMessage("timed out while retrying [{}] after failure (timeout [{}])", actionName, timeout), - failure - ); - listener.onFailure(new MasterNotDiscoveredException(failure)); - } - }, statePredicate); - } - } - - /** - * Allows to conditionally return a different master node action name in the case an action gets renamed. - * This mainly for backwards compatibility should be used rarely - */ - protected String getMasterActionName(DiscoveryNode node) { - return actionName; - } } diff --git a/server/src/main/java/org/opensearch/action/support/master/TransportMasterNodeReadAction.java b/server/src/main/java/org/opensearch/action/support/master/TransportMasterNodeReadAction.java index b230901eb456e..b95459971737f 100644 --- a/server/src/main/java/org/opensearch/action/support/master/TransportMasterNodeReadAction.java +++ b/server/src/main/java/org/opensearch/action/support/master/TransportMasterNodeReadAction.java @@ -32,20 +32,25 @@ package org.opensearch.action.support.master; -import org.opensearch.action.ActionResponse; import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.clustermanager.TransportClusterManagerNodeReadAction; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; /** - * A base class for read operations that needs to be performed on the master node. + * A base class for read operations that needs to be performed on the cluster-manager node. * Can also be executed on the local node if needed. + * + * @opensearch.internal + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link TransportClusterManagerNodeReadAction} */ +@Deprecated public abstract class TransportMasterNodeReadAction, Response extends ActionResponse> extends - TransportMasterNodeAction { + TransportClusterManagerNodeReadAction { protected TransportMasterNodeReadAction( String actionName, @@ -56,7 +61,7 @@ protected TransportMasterNodeReadAction( Writeable.Reader request, IndexNameExpressionResolver indexNameExpressionResolver ) { - this(actionName, true, transportService, clusterService, threadPool, actionFilters, request, indexNameExpressionResolver); + super(actionName, true, transportService, clusterService, threadPool, actionFilters, request, indexNameExpressionResolver); } protected TransportMasterNodeReadAction( @@ -81,8 +86,4 @@ protected TransportMasterNodeReadAction( ); } - @Override - protected final boolean localExecute(Request request) { - return request.local(); - } } diff --git a/server/src/main/java/org/opensearch/action/support/master/info/ClusterInfoRequest.java b/server/src/main/java/org/opensearch/action/support/master/info/ClusterInfoRequest.java index 0b392caa3e588..0b66e3d932603 100644 --- a/server/src/main/java/org/opensearch/action/support/master/info/ClusterInfoRequest.java +++ b/server/src/main/java/org/opensearch/action/support/master/info/ClusterInfoRequest.java @@ -32,70 +32,22 @@ package org.opensearch.action.support.master.info; -import org.opensearch.Version; -import org.opensearch.action.IndicesRequest; -import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadRequest; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; -public abstract class ClusterInfoRequest> extends MasterNodeReadRequest - implements - IndicesRequest.Replaceable { - - private String[] indices = Strings.EMPTY_ARRAY; - - private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen(); +/** + * Transport request for cluster information + * + * @opensearch.internal + */ +public abstract class ClusterInfoRequest> extends + org.opensearch.action.support.clustermanager.info.ClusterInfoRequest { public ClusterInfoRequest() {} public ClusterInfoRequest(StreamInput in) throws IOException { super(in); - indices = in.readStringArray(); - if (in.getVersion().before(Version.V_2_0_0)) { - in.readStringArray(); - } - indicesOptions = IndicesOptions.readIndicesOptions(in); - } - - @Override - public void writeTo(StreamOutput out) throws IOException { - super.writeTo(out); - out.writeStringArray(indices); - if (out.getVersion().before(Version.V_2_0_0)) { - out.writeStringArray(Strings.EMPTY_ARRAY); - } - indicesOptions.writeIndicesOptions(out); - } - - @Override - @SuppressWarnings("unchecked") - public Request indices(String... indices) { - this.indices = indices; - return (Request) this; } - @SuppressWarnings("unchecked") - public Request indicesOptions(IndicesOptions indicesOptions) { - this.indicesOptions = indicesOptions; - return (Request) this; - } - - @Override - public String[] indices() { - return indices; - } - - @Override - public IndicesOptions indicesOptions() { - return indicesOptions; - } - - @Override - public boolean includeDataStreams() { - return true; - } } diff --git a/server/src/main/java/org/opensearch/action/support/master/info/ClusterInfoRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/master/info/ClusterInfoRequestBuilder.java index d806f96eb9ff2..091413c0df6d7 100644 --- a/server/src/main/java/org/opensearch/action/support/master/info/ClusterInfoRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/master/info/ClusterInfoRequestBuilder.java @@ -32,39 +32,21 @@ package org.opensearch.action.support.master.info; import org.opensearch.action.ActionType; -import org.opensearch.action.ActionResponse; -import org.opensearch.action.support.IndicesOptions; -import org.opensearch.action.support.master.MasterNodeReadOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.util.ArrayUtils; +import org.opensearch.core.action.ActionResponse; +/** + * Transport request builder for cluster information + * + * @opensearch.internal + */ public abstract class ClusterInfoRequestBuilder< Request extends ClusterInfoRequest, Response extends ActionResponse, - Builder extends ClusterInfoRequestBuilder> extends MasterNodeReadOperationRequestBuilder< - Request, - Response, - Builder> { + Builder extends ClusterInfoRequestBuilder> extends + org.opensearch.action.support.clustermanager.info.ClusterInfoRequestBuilder { protected ClusterInfoRequestBuilder(OpenSearchClient client, ActionType action, Request request) { super(client, action, request); } - - @SuppressWarnings("unchecked") - public Builder setIndices(String... indices) { - request.indices(indices); - return (Builder) this; - } - - @SuppressWarnings("unchecked") - public Builder addIndices(String... indices) { - request.indices(ArrayUtils.concat(request.indices(), indices)); - return (Builder) this; - } - - @SuppressWarnings("unchecked") - public Builder setIndicesOptions(IndicesOptions indicesOptions) { - request.indicesOptions(indicesOptions); - return (Builder) this; - } } diff --git a/server/src/main/java/org/opensearch/action/support/master/info/TransportClusterInfoAction.java b/server/src/main/java/org/opensearch/action/support/master/info/TransportClusterInfoAction.java index 9bcfaea056a89..2653e3a658674 100644 --- a/server/src/main/java/org/opensearch/action/support/master/info/TransportClusterInfoAction.java +++ b/server/src/main/java/org/opensearch/action/support/master/info/TransportClusterInfoAction.java @@ -31,21 +31,21 @@ package org.opensearch.action.support.master.info; -import org.opensearch.action.ActionListener; -import org.opensearch.action.ActionResponse; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.master.TransportMasterNodeReadAction; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.block.ClusterBlockException; -import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; +/** + * Perform cluster information action + * + * @opensearch.internal + */ public abstract class TransportClusterInfoAction, Response extends ActionResponse> extends - TransportMasterNodeReadAction { + org.opensearch.action.support.clustermanager.info.TransportClusterInfoAction { public TransportClusterInfoAction( String actionName, @@ -59,28 +59,4 @@ public TransportClusterInfoAction( super(actionName, transportService, clusterService, threadPool, actionFilters, request, indexNameExpressionResolver); } - @Override - protected String executor() { - // read operation, lightweight... - return ThreadPool.Names.SAME; - } - - @Override - protected ClusterBlockException checkBlock(Request request, ClusterState state) { - return state.blocks() - .indicesBlockedException(ClusterBlockLevel.METADATA_READ, indexNameExpressionResolver.concreteIndexNames(state, request)); - } - - @Override - protected final void masterOperation(final Request request, final ClusterState state, final ActionListener listener) { - String[] concreteIndices = indexNameExpressionResolver.concreteIndexNames(state, request); - doMasterOperation(request, concreteIndices, state, listener); - } - - protected abstract void doMasterOperation( - Request request, - String[] concreteIndices, - ClusterState state, - ActionListener listener - ); } diff --git a/server/src/main/java/org/opensearch/action/support/master/info/package-info.java b/server/src/main/java/org/opensearch/action/support/master/info/package-info.java new file mode 100644 index 0000000000000..8f21383c1b90c --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/master/info/package-info.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Master Node Information transport handlers. + * + * As of 2.1, because supporting inclusive language, replaced by {@link org.opensearch.action.support.clustermanager.info} + */ +@Deprecated +package org.opensearch.action.support.master.info; diff --git a/server/src/main/java/org/opensearch/action/support/master/package-info.java b/server/src/main/java/org/opensearch/action/support/master/package-info.java new file mode 100644 index 0000000000000..9e90d96986fe1 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/master/package-info.java @@ -0,0 +1,15 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Master Node transport handlers. + * + * As of 2.1, because supporting inclusive language, replaced by {@link org.opensearch.action.support.clustermanager} + */ +@Deprecated +package org.opensearch.action.support.master; diff --git a/server/src/main/java/org/opensearch/action/support/nodes/BaseNodeRequest.java b/server/src/main/java/org/opensearch/action/support/nodes/BaseNodeRequest.java index e91a659d331d1..eff5d99c90405 100644 --- a/server/src/main/java/org/opensearch/action/support/nodes/BaseNodeRequest.java +++ b/server/src/main/java/org/opensearch/action/support/nodes/BaseNodeRequest.java @@ -33,13 +33,21 @@ package org.opensearch.action.support.nodes; import org.opensearch.LegacyESVersion; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.transport.TransportRequest; import java.io.IOException; -// TODO: this class can be removed in master once 7.x is bumped to 7.4.0 +/** + * Base class for node transport requests + * + * @opensearch.internal + * + * @deprecated this class is deprecated and classes will extend TransportRequest directly + */ +// TODO: this class can be removed in main once 7.x is bumped to 7.4.0 +@Deprecated public abstract class BaseNodeRequest extends TransportRequest { public BaseNodeRequest() {} diff --git a/server/src/main/java/org/opensearch/action/support/nodes/BaseNodeResponse.java b/server/src/main/java/org/opensearch/action/support/nodes/BaseNodeResponse.java index 13d9293c21123..8a4e12567b515 100644 --- a/server/src/main/java/org/opensearch/action/support/nodes/BaseNodeResponse.java +++ b/server/src/main/java/org/opensearch/action/support/nodes/BaseNodeResponse.java @@ -33,14 +33,16 @@ package org.opensearch.action.support.nodes; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.transport.TransportResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse; import java.io.IOException; /** * A base class for node level operations. + * + * @opensearch.internal */ public abstract class BaseNodeResponse extends TransportResponse { diff --git a/server/src/main/java/org/opensearch/action/support/nodes/BaseNodesRequest.java b/server/src/main/java/org/opensearch/action/support/nodes/BaseNodesRequest.java index b36fc585e99a8..f5fb41dc5bae3 100644 --- a/server/src/main/java/org/opensearch/action/support/nodes/BaseNodesRequest.java +++ b/server/src/main/java/org/opensearch/action/support/nodes/BaseNodesRequest.java @@ -36,12 +36,17 @@ import org.opensearch.action.ActionRequestValidationException; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; +/** + * Base class for requests targeting a list of nodes + * + * @opensearch.internal + */ public abstract class BaseNodesRequest> extends ActionRequest { /** diff --git a/server/src/main/java/org/opensearch/action/support/nodes/BaseNodesResponse.java b/server/src/main/java/org/opensearch/action/support/nodes/BaseNodesResponse.java index 2ba00d77d0660..c51a4f6bb9399 100644 --- a/server/src/main/java/org/opensearch/action/support/nodes/BaseNodesResponse.java +++ b/server/src/main/java/org/opensearch/action/support/nodes/BaseNodesResponse.java @@ -32,11 +32,11 @@ package org.opensearch.action.support.nodes; -import org.opensearch.action.ActionResponse; import org.opensearch.action.FailedNodeException; import org.opensearch.cluster.ClusterName; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.HashMap; @@ -44,6 +44,11 @@ import java.util.Map; import java.util.Objects; +/** + * Transport response for nodes requests + * + * @opensearch.internal + */ public abstract class BaseNodesResponse extends ActionResponse { private ClusterName clusterName; diff --git a/server/src/main/java/org/opensearch/action/support/nodes/NodesOperationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/nodes/NodesOperationRequestBuilder.java index d8017ace7d3e6..ba1e214fe9d19 100644 --- a/server/src/main/java/org/opensearch/action/support/nodes/NodesOperationRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/nodes/NodesOperationRequestBuilder.java @@ -32,11 +32,16 @@ package org.opensearch.action.support.nodes; -import org.opensearch.action.ActionType; import org.opensearch.action.ActionRequestBuilder; +import org.opensearch.action.ActionType; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.unit.TimeValue; +/** + * Builder for Operation Requests + * + * @opensearch.internal + */ public abstract class NodesOperationRequestBuilder< Request extends BaseNodesRequest, Response extends BaseNodesResponse, diff --git a/server/src/main/java/org/opensearch/action/support/nodes/TransportNodesAction.java b/server/src/main/java/org/opensearch/action/support/nodes/TransportNodesAction.java index 030b14678c0e5..1b44ee562a4ed 100644 --- a/server/src/main/java/org/opensearch/action/support/nodes/TransportNodesAction.java +++ b/server/src/main/java/org/opensearch/action/support/nodes/TransportNodesAction.java @@ -33,7 +33,6 @@ package org.opensearch.action.support.nodes; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.FailedNodeException; import org.opensearch.action.support.ActionFilters; @@ -41,8 +40,9 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.NodeShouldNotConnectException; @@ -62,6 +62,11 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; +/** + * Base action class for transport nodes + * + * @opensearch.internal + */ public abstract class TransportNodesAction< NodesRequest extends BaseNodesRequest, NodesResponse extends BaseNodesResponse, @@ -211,6 +216,11 @@ protected String getTransportNodeAction(DiscoveryNode node) { return transportNodeAction; } + /** + * Asynchronous action + * + * @opensearch.internal + */ class AsyncAction { private final NodesRequest request; @@ -306,6 +316,11 @@ private void finishHim() { } } + /** + * A node transport handler + * + * @opensearch.internal + */ class NodeTransportHandler implements TransportRequestHandler { @Override diff --git a/server/src/main/java/org/opensearch/action/support/nodes/package-info.java b/server/src/main/java/org/opensearch/action/support/nodes/package-info.java new file mode 100644 index 0000000000000..f388527e30d8b --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/nodes/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Node request transport handlers. */ +package org.opensearch.action.support.nodes; diff --git a/server/src/main/java/org/opensearch/action/support/package-info.java b/server/src/main/java/org/opensearch/action/support/package-info.java new file mode 100644 index 0000000000000..58dd4c5190d8b --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Transport handler Support Classes. */ +package org.opensearch.action.support; diff --git a/server/src/main/java/org/opensearch/action/support/replication/BasicReplicationRequest.java b/server/src/main/java/org/opensearch/action/support/replication/BasicReplicationRequest.java index 1022a476bf885..b2a53fec5c281 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/BasicReplicationRequest.java +++ b/server/src/main/java/org/opensearch/action/support/replication/BasicReplicationRequest.java @@ -32,8 +32,8 @@ package org.opensearch.action.support.replication; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; @@ -42,6 +42,8 @@ * Unfortunately ReplicationRequest can't be declared as a type parameter * because it has a self referential type parameter of its own. So use this * instead. + * + * @opensearch.internal */ public class BasicReplicationRequest extends ReplicationRequest { /** diff --git a/server/src/main/java/org/opensearch/action/support/replication/FanoutReplicationProxy.java b/server/src/main/java/org/opensearch/action/support/replication/FanoutReplicationProxy.java new file mode 100644 index 0000000000000..95c4ef1e2b092 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/replication/FanoutReplicationProxy.java @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.support.replication; + +import org.opensearch.action.support.replication.ReplicationOperation.ReplicaResponse; +import org.opensearch.action.support.replication.ReplicationOperation.Replicas; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.core.action.ActionListener; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * This implementation of {@link ReplicationProxy} fans out the replication request to current shard routing if + * it is not the primary and has replication mode as {@link ReplicationMode#FULL_REPLICATION}. + * + * @opensearch.internal + */ +public class FanoutReplicationProxy> extends ReplicationProxy { + + public FanoutReplicationProxy(Replicas replicasProxy) { + super(replicasProxy); + } + + @Override + protected void performOnReplicaProxy( + ReplicationProxyRequest proxyRequest, + ReplicationMode replicationMode, + BiConsumer>, ReplicationProxyRequest> performOnReplicaConsumer + ) { + assert replicationMode == ReplicationMode.FULL_REPLICATION : "FanoutReplicationProxy allows only full replication mode"; + performOnReplicaConsumer.accept(getReplicasProxyConsumer(fullReplicationProxy, proxyRequest), proxyRequest); + } + + @Override + ReplicationMode determineReplicationMode(ShardRouting shardRouting, ShardRouting primaryRouting) { + return shardRouting.isSameAllocation(primaryRouting) == false ? ReplicationMode.FULL_REPLICATION : ReplicationMode.NO_REPLICATION; + } +} diff --git a/server/src/main/java/org/opensearch/action/support/replication/PendingReplicationActions.java b/server/src/main/java/org/opensearch/action/support/replication/PendingReplicationActions.java index 1e554913e527d..c52bc6c2c799d 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/PendingReplicationActions.java +++ b/server/src/main/java/org/opensearch/action/support/replication/PendingReplicationActions.java @@ -35,9 +35,10 @@ import org.opensearch.action.support.RetryableAction; import org.opensearch.common.lease.Releasable; import org.opensearch.common.util.concurrent.ConcurrentCollections; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.shard.IndexShardClosedException; +import org.opensearch.index.shard.PrimaryShardClosedException; import org.opensearch.index.shard.ReplicationGroup; -import org.opensearch.index.shard.ShardId; import org.opensearch.threadpool.ThreadPool; import java.util.ArrayList; @@ -45,7 +46,13 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Supplier; +/** + * Pending Replication Actions + * + * @opensearch.internal + */ public class PendingReplicationActions implements Consumer, Releasable { private final Map>> onGoingReplicationActions = ConcurrentCollections.newConcurrentMap(); @@ -116,7 +123,7 @@ synchronized void acceptNewTrackedAllocationIds(Set trackedAllocationIds } } - cancelActions(toCancel, "Replica left ReplicationGroup"); + cancelActions(toCancel, () -> new IndexShardClosedException(shardId, "Replica left ReplicationGroup")); } @Override @@ -124,15 +131,11 @@ public synchronized void close() { ArrayList>> toCancel = new ArrayList<>(onGoingReplicationActions.values()); onGoingReplicationActions.clear(); - cancelActions(toCancel, "Primary closed."); + cancelActions(toCancel, () -> new PrimaryShardClosedException(shardId)); } - private void cancelActions(ArrayList>> toCancel, String message) { + private void cancelActions(ArrayList>> toCancel, Supplier exceptionSupplier) { threadPool.executor(ThreadPool.Names.GENERIC) - .execute( - () -> toCancel.stream() - .flatMap(Collection::stream) - .forEach(action -> action.cancel(new IndexShardClosedException(shardId, message))) - ); + .execute(() -> toCancel.stream().flatMap(Collection::stream).forEach(action -> action.cancel(exceptionSupplier.get()))); } } diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicatedWriteRequest.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicatedWriteRequest.java index e18765d7a3546..55773c8721c2a 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/ReplicatedWriteRequest.java +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicatedWriteRequest.java @@ -37,15 +37,17 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.WriteRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; /** * Requests that are both {@linkplain ReplicationRequest}s (run on a shard's primary first, then the replica) and {@linkplain WriteRequest} * (modify documents on a shard), for example {@link BulkShardRequest}, {@link IndexRequest}, and {@link DeleteRequest}. + * + * @opensearch.internal */ public abstract class ReplicatedWriteRequest> extends ReplicationRequest implements WriteRequest { private RefreshPolicy refreshPolicy = RefreshPolicy.NONE; diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationMode.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationMode.java new file mode 100644 index 0000000000000..f9b85cc4bd7aa --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationMode.java @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.support.replication; + +/** + * The type of replication used for inter-node replication. + * + * @opensearch.internal + */ +public enum ReplicationMode { + /** + * In this mode, a {@code TransportReplicationAction} is fanned out to underlying concerned shard and is replicated logically. + * In short, this mode would replicate the {@link ReplicationRequest} to + * the replica shard along with primary term validation. + */ + FULL_REPLICATION, + /** + * In this mode, a {@code TransportReplicationAction} is fanned out to underlying concerned shard and used for + * primary term validation only. The request is not replicated logically. + */ + PRIMARY_TERM_VALIDATION, + /** + * In this mode, a {@code TransportReplicationAction} does not fan out to the underlying concerned shard. + */ + NO_REPLICATION; +} diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationModeAwareProxy.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationModeAwareProxy.java new file mode 100644 index 0000000000000..189bc82348a0c --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationModeAwareProxy.java @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.support.replication; + +import org.opensearch.action.support.replication.ReplicationOperation.ReplicaResponse; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.core.action.ActionListener; + +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * This implementation of {@link ReplicationProxy} fans out the replication request to current shard routing basis + * the shard routing's replication mode and replication override policy. + * + * @opensearch.internal + */ +public class ReplicationModeAwareProxy> extends ReplicationProxy { + + private final ReplicationMode replicationModeOverride; + + /** + * This ReplicasProxy is used for performing primary term validation. + */ + private final ReplicationOperation.Replicas primaryTermValidationProxy; + + public ReplicationModeAwareProxy( + ReplicationMode replicationModeOverride, + ReplicationOperation.Replicas replicasProxy, + ReplicationOperation.Replicas primaryTermValidationProxy + ) { + super(replicasProxy); + this.replicationModeOverride = Objects.requireNonNull(replicationModeOverride); + this.primaryTermValidationProxy = Objects.requireNonNull(primaryTermValidationProxy); + } + + @Override + protected void performOnReplicaProxy( + ReplicationProxyRequest proxyRequest, + ReplicationMode replicationMode, + BiConsumer>, ReplicationProxyRequest> performOnReplicaConsumer + ) { + assert replicationMode == ReplicationMode.FULL_REPLICATION || replicationMode == ReplicationMode.PRIMARY_TERM_VALIDATION; + + Consumer> replicasProxyConsumer; + if (replicationMode == ReplicationMode.FULL_REPLICATION) { + replicasProxyConsumer = getReplicasProxyConsumer(fullReplicationProxy, proxyRequest); + } else { + replicasProxyConsumer = getReplicasProxyConsumer(primaryTermValidationProxy, proxyRequest); + } + performOnReplicaConsumer.accept(replicasProxyConsumer, proxyRequest); + } + + @Override + ReplicationMode determineReplicationMode(ShardRouting shardRouting, ShardRouting primaryRouting) { + + // If the current routing is the primary, then it does not need to be replicated + if (shardRouting.isSameAllocation(primaryRouting)) { + return ReplicationMode.NO_REPLICATION; + } + + if (primaryRouting.relocating() && shardRouting.isSameAllocation(primaryRouting.getTargetRelocatingShard())) { + return ReplicationMode.FULL_REPLICATION; + } + + return replicationModeOverride; + } +} diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationOperation.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationOperation.java index 68c5416f3603e..60c490a50575a 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/ReplicationOperation.java +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationOperation.java @@ -34,27 +34,28 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.lucene.store.AlreadyClosedException; -import org.opensearch.Assertions; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionListener; +import org.opensearch.OpenSearchException; import org.opensearch.action.UnavailableShardsException; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.RetryableAction; import org.opensearch.action.support.TransportActions; +import org.opensearch.action.support.replication.ReplicationProxyRequest.Builder; import org.opensearch.cluster.action.shard.ShardStateAction; import org.opensearch.cluster.routing.IndexShardRoutingTable; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.common.Nullable; -import org.opensearch.common.breaker.CircuitBreakingException; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.Assertions; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.breaker.CircuitBreakingException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; import org.opensearch.index.seqno.SequenceNumbers; import org.opensearch.index.shard.ReplicationGroup; -import org.opensearch.index.shard.ShardId; import org.opensearch.node.NodeClosedException; -import org.opensearch.rest.RestStatus; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.ConnectTransportException; @@ -65,8 +66,14 @@ import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.LongSupplier; +/** + * Operation for a replication request + * + * @opensearch.internal + */ public class ReplicationOperation< Request extends ReplicationRequest, ReplicaRequest extends ReplicationRequest, @@ -94,6 +101,7 @@ public class ReplicationOperation< private final TimeValue initialRetryBackoffBound; private final TimeValue retryTimeout; private final long primaryTerm; + private final ReplicationProxy replicationProxy; // exposed for tests private final ActionListener resultListener; @@ -112,7 +120,8 @@ public ReplicationOperation( String opType, long primaryTerm, TimeValue initialRetryBackoffBound, - TimeValue retryTimeout + TimeValue retryTimeout, + ReplicationProxy replicationProxy ) { this.replicasProxy = replicas; this.primary = primary; @@ -124,6 +133,7 @@ public ReplicationOperation( this.primaryTerm = primaryTerm; this.initialRetryBackoffBound = initialRetryBackoffBound; this.retryTimeout = retryTimeout; + this.replicationProxy = replicationProxy; } public void execute() throws Exception { @@ -221,20 +231,28 @@ private void performOnReplicas( final ShardRouting primaryRouting = primary.routingEntry(); - for (final ShardRouting shard : replicationGroup.getReplicationTargets()) { - if (shard.isSameAllocation(primaryRouting) == false) { - performOnReplica(shard, replicaRequest, globalCheckpoint, maxSeqNoOfUpdatesOrDeletes, pendingReplicationActions); - } + for (final ShardRouting shardRouting : replicationGroup.getReplicationTargets()) { + ReplicationProxyRequest proxyRequest = new Builder( + shardRouting, + primaryRouting, + globalCheckpoint, + maxSeqNoOfUpdatesOrDeletes, + pendingReplicationActions, + replicaRequest, + primaryTerm + ).build(); + replicationProxy.performOnReplicaProxy(proxyRequest, this::performOnReplica); } } private void performOnReplica( - final ShardRouting shard, - final ReplicaRequest replicaRequest, - final long globalCheckpoint, - final long maxSeqNoOfUpdatesOrDeletes, - final PendingReplicationActions pendingReplicationActions + final Consumer> replicasProxyConsumer, + final ReplicationProxyRequest replicationProxyRequest ) { + final ShardRouting shard = replicationProxyRequest.getShardRouting(); + final ReplicaRequest replicaRequest = replicationProxyRequest.getReplicaRequest(); + final PendingReplicationActions pendingReplicationActions = replicationProxyRequest.getPendingReplicationActions(); + if (logger.isTraceEnabled()) { logger.trace("[{}] sending op [{}] to replica {} for request [{}]", shard.shardId(), opType, shard, replicaRequest); } @@ -263,7 +281,8 @@ public void onFailure(Exception replicaException) { ), replicaException ); - // Only report "critical" exceptions - TODO: Reach out to the master node to get the latest shard state then report. + // Only report "critical" exceptions + // TODO: Reach out to the cluster-manager node to get the latest shard state then report. if (TransportActions.isShardNotAvailableException(replicaException) == false) { RestStatus restStatus = ExceptionsHelper.status(replicaException); shardReplicaFailures.add( @@ -303,7 +322,7 @@ public String toString() { @Override public void tryAction(ActionListener listener) { - replicasProxy.performOn(shard, replicaRequest, primaryTerm, globalCheckpoint, maxSeqNoOfUpdatesOrDeletes, listener); + replicasProxyConsumer.accept(listener); } @Override @@ -590,6 +609,11 @@ public interface ReplicaResponse { } + /** + * Thrown if there are any errors retrying on primary + * + * @opensearch.internal + */ public static class RetryOnPrimaryException extends OpenSearchException { RetryOnPrimaryException(ShardId shardId, String msg) { this(shardId, msg, null); @@ -605,6 +629,11 @@ public RetryOnPrimaryException(StreamInput in) throws IOException { } } + /** + * The result of the primary. + * + * @opensearch.internal + */ public interface PrimaryResult> { /** diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationProxy.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationProxy.java new file mode 100644 index 0000000000000..4812984732134 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationProxy.java @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.support.replication; + +import org.opensearch.action.support.replication.ReplicationOperation.ReplicaResponse; +import org.opensearch.action.support.replication.ReplicationOperation.Replicas; +import org.opensearch.cluster.routing.ShardRouting; +import org.opensearch.core.action.ActionListener; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * Used for performing any replication operation on replicas. Depending on the implementation, the replication call + * can fanout or stops here. + * + * @opensearch.internal + */ +public abstract class ReplicationProxy> { + + /** + * This is the replicas proxy which is used for full replication. + */ + protected final Replicas fullReplicationProxy; + + public ReplicationProxy(Replicas fullReplicationProxy) { + this.fullReplicationProxy = fullReplicationProxy; + } + + /** + * Depending on the actual implementation and the passed {@link ReplicationMode}, the replication + * mode is determined using which the replication request is performed on the replica or not. + * + * @param proxyRequest replication proxy request + * @param performOnReplicaConsumer performOnReplicasProxy + */ + final void performOnReplicaProxy( + ReplicationProxyRequest proxyRequest, + BiConsumer>, ReplicationProxyRequest> performOnReplicaConsumer + ) { + ReplicationMode replicationMode = determineReplicationMode(proxyRequest.getShardRouting(), proxyRequest.getPrimaryRouting()); + // If the replication modes are 1. Logical replication or 2. Primary term validation, we let the call get performed on the + // replica shard. + if (replicationMode == ReplicationMode.NO_REPLICATION) { + return; + } + performOnReplicaProxy(proxyRequest, replicationMode, performOnReplicaConsumer); + } + + /** + * The implementor can decide the {@code Consumer>} basis the + * proxyRequest and replicationMode. This will ultimately make the calls to replica. + * + * @param proxyRequest replication proxy request + * @param replicationMode replication mode + * @param performOnReplicaConsumer performOnReplicasProxy + */ + protected abstract void performOnReplicaProxy( + ReplicationProxyRequest proxyRequest, + ReplicationMode replicationMode, + BiConsumer>, ReplicationProxyRequest> performOnReplicaConsumer + ); + + /** + * Determines what is the replication mode basis the constructor arguments of the implementation and the current + * replication mode aware shard routing. + * + * @param shardRouting replication mode aware ShardRouting + * @param primaryRouting primary ShardRouting + * @return the determined replication mode. + */ + abstract ReplicationMode determineReplicationMode(final ShardRouting shardRouting, final ShardRouting primaryRouting); + + protected Consumer> getReplicasProxyConsumer( + Replicas proxy, + ReplicationProxyRequest proxyRequest + ) { + return (listener) -> proxy.performOn( + proxyRequest.getShardRouting(), + proxyRequest.getReplicaRequest(), + proxyRequest.getPrimaryTerm(), + proxyRequest.getGlobalCheckpoint(), + proxyRequest.getMaxSeqNoOfUpdatesOrDeletes(), + listener + ); + } +} diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationProxyRequest.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationProxyRequest.java new file mode 100644 index 0000000000000..c65e55867f706 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationProxyRequest.java @@ -0,0 +1,128 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.action.support.replication; + +import org.opensearch.cluster.routing.ShardRouting; + +import java.util.Objects; + +/** + * This is proxy wrapper over the replication request whose object can be created using the Builder present inside. + * + * @opensearch.internal + */ +public class ReplicationProxyRequest { + + private final ShardRouting shardRouting; + + private final ShardRouting primaryRouting; + + private final long globalCheckpoint; + + private final long maxSeqNoOfUpdatesOrDeletes; + + private final PendingReplicationActions pendingReplicationActions; + + private final ReplicaRequest replicaRequest; + + private final long primaryTerm; + + private ReplicationProxyRequest( + ShardRouting shardRouting, + ShardRouting primaryRouting, + long globalCheckpoint, + long maxSeqNoOfUpdatesOrDeletes, + PendingReplicationActions pendingReplicationActions, + ReplicaRequest replicaRequest, + long primaryTerm + ) { + this.shardRouting = Objects.requireNonNull(shardRouting); + this.primaryRouting = Objects.requireNonNull(primaryRouting); + this.globalCheckpoint = globalCheckpoint; + this.maxSeqNoOfUpdatesOrDeletes = maxSeqNoOfUpdatesOrDeletes; + this.pendingReplicationActions = Objects.requireNonNull(pendingReplicationActions); + this.replicaRequest = Objects.requireNonNull(replicaRequest); + this.primaryTerm = primaryTerm; + } + + public ShardRouting getShardRouting() { + return shardRouting; + } + + public ShardRouting getPrimaryRouting() { + return primaryRouting; + } + + public long getGlobalCheckpoint() { + return globalCheckpoint; + } + + public long getMaxSeqNoOfUpdatesOrDeletes() { + return maxSeqNoOfUpdatesOrDeletes; + } + + public PendingReplicationActions getPendingReplicationActions() { + return pendingReplicationActions; + } + + public ReplicaRequest getReplicaRequest() { + return replicaRequest; + } + + public long getPrimaryTerm() { + return primaryTerm; + } + + /** + * Builder of ReplicationProxyRequest. + * + * @opensearch.internal + */ + public static class Builder { + + private final ShardRouting shardRouting; + private final ShardRouting primaryRouting; + private final long globalCheckpoint; + private final long maxSeqNoOfUpdatesOrDeletes; + private final PendingReplicationActions pendingReplicationActions; + private final ReplicaRequest replicaRequest; + private final long primaryTerm; + + public Builder( + ShardRouting shardRouting, + ShardRouting primaryRouting, + long globalCheckpoint, + long maxSeqNoOfUpdatesOrDeletes, + PendingReplicationActions pendingReplicationActions, + ReplicaRequest replicaRequest, + long primaryTerm + ) { + this.shardRouting = shardRouting; + this.primaryRouting = primaryRouting; + this.globalCheckpoint = globalCheckpoint; + this.maxSeqNoOfUpdatesOrDeletes = maxSeqNoOfUpdatesOrDeletes; + this.pendingReplicationActions = pendingReplicationActions; + this.replicaRequest = replicaRequest; + this.primaryTerm = primaryTerm; + } + + public ReplicationProxyRequest build() { + return new ReplicationProxyRequest<>( + shardRouting, + primaryRouting, + globalCheckpoint, + maxSeqNoOfUpdatesOrDeletes, + pendingReplicationActions, + replicaRequest, + primaryTerm + ); + } + + } +} diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationRequest.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationRequest.java index e7428e2913f1a..92e50f7a476f3 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/ReplicationRequest.java +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationRequest.java @@ -40,12 +40,12 @@ import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.IndicesOptions; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import java.io.IOException; import java.util.Map; @@ -56,6 +56,8 @@ /** * Requests that are run on a particular replica, first on the primary and then on the replicas like {@link IndexRequest} or * {@link TransportShardRefreshAction}. + * + * @opensearch.internal */ public abstract class ReplicationRequest> extends ActionRequest implements IndicesRequest { diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationRequestBuilder.java index 1994c70aec17c..920024b366a4c 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/ReplicationRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationRequestBuilder.java @@ -32,13 +32,18 @@ package org.opensearch.action.support.replication; -import org.opensearch.action.ActionType; import org.opensearch.action.ActionRequestBuilder; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionResponse; +/** + * Transport request builder for a replication operation + * + * @opensearch.internal + */ public abstract class ReplicationRequestBuilder< Request extends ReplicationRequest, Response extends ActionResponse, diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationResponse.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationResponse.java index 6434d36861c6f..400c68ea0e113 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/ReplicationResponse.java +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationResponse.java @@ -34,28 +34,30 @@ import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionResponse; -import org.opensearch.action.ShardOperationFailedException; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.action.ShardOperationFailedException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; /** * Base class for write action responses. + * + * @opensearch.internal */ public class ReplicationResponse extends ActionResponse { @@ -83,6 +85,11 @@ public void setShardInfo(ShardInfo shardInfo) { this.shardInfo = shardInfo; } + /** + * Holds shard information + * + * @opensearch.internal + */ public static class ShardInfo implements Writeable, ToXContentObject { private static final String TOTAL = "total"; @@ -225,6 +232,11 @@ public String toString() { return "ShardInfo{" + "total=" + total + ", successful=" + successful + ", failures=" + Arrays.toString(failures) + '}'; } + /** + * Holds failure information + * + * @opensearch.internal + */ public static class Failure extends ShardOperationFailedException implements ToXContentObject { private static final String _INDEX = "_index"; diff --git a/server/src/main/java/org/opensearch/action/support/replication/ReplicationTask.java b/server/src/main/java/org/opensearch/action/support/replication/ReplicationTask.java index b24dcd0648043..c92d2a6e8108e 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/ReplicationTask.java +++ b/server/src/main/java/org/opensearch/action/support/replication/ReplicationTask.java @@ -32,12 +32,13 @@ package org.opensearch.action.support.replication; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import java.io.IOException; import java.util.Map; @@ -46,6 +47,8 @@ /** * Task that tracks replication actions. + * + * @opensearch.internal */ public class ReplicationTask extends Task { private volatile String phase = "starting"; @@ -73,6 +76,11 @@ public Status getStatus() { return new Status(phase); } + /** + * Status of the replication task + * + * @opensearch.internal + */ public static class Status implements Task.Status { public static final String NAME = "replication"; @@ -106,7 +114,7 @@ public void writeTo(StreamOutput out) throws IOException { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } // Implements equals and hashcode for testing diff --git a/server/src/main/java/org/opensearch/action/support/replication/TransportBroadcastReplicationAction.java b/server/src/main/java/org/opensearch/action/support/replication/TransportBroadcastReplicationAction.java index dd1ce9f70a28a..e235adbc162fc 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/TransportBroadcastReplicationAction.java +++ b/server/src/main/java/org/opensearch/action/support/replication/TransportBroadcastReplicationAction.java @@ -32,11 +32,8 @@ package org.opensearch.action.support.replication; -import com.carrotsearch.hppc.cursors.IntObjectCursor; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.ActionFilters; -import org.opensearch.action.support.DefaultShardOperationFailedException; import org.opensearch.action.support.HandledTransportAction; import org.opensearch.action.support.TransportActions; import org.opensearch.action.support.broadcast.BroadcastRequest; @@ -47,9 +44,11 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.routing.IndexShardRoutingTable; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.util.concurrent.CountDown; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.support.DefaultShardOperationFailedException; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -61,6 +60,8 @@ /** * Base class for requests that should be executed on all shards of an index or several indices. * This action sends shard requests to all primary shards of the indices and they are then replicated like write requests + * + * @opensearch.internal */ public abstract class TransportBroadcastReplicationAction< Request extends BroadcastRequest, @@ -152,11 +153,12 @@ protected List shards(Request request, ClusterState clusterState) { for (String index : concreteIndices) { IndexMetadata indexMetadata = clusterState.metadata().getIndices().get(index); if (indexMetadata != null) { - for (IntObjectCursor shardRouting : clusterState.getRoutingTable() + for (IndexShardRoutingTable shardRouting : clusterState.getRoutingTable() .indicesRouting() .get(index) - .getShards()) { - shardIds.add(shardRouting.value.shardId()); + .getShards() + .values()) { + shardIds.add(shardRouting.shardId()); } } } diff --git a/server/src/main/java/org/opensearch/action/support/replication/TransportReplicationAction.java b/server/src/main/java/org/opensearch/action/support/replication/TransportReplicationAction.java index 0ea08eccae33d..de5a92fdcc4b1 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/TransportReplicationAction.java +++ b/server/src/main/java/org/opensearch/action/support/replication/TransportReplicationAction.java @@ -34,18 +34,16 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.lucene.store.AlreadyClosedException; -import org.opensearch.Assertions; import org.opensearch.ExceptionsHelper; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionListenerResponseHandler; -import org.opensearch.action.ActionResponse; import org.opensearch.action.UnavailableShardsException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.ChannelActionListener; import org.opensearch.action.support.TransportAction; import org.opensearch.action.support.TransportActions; +import org.opensearch.action.support.replication.ReplicationOperation.Replicas; import org.opensearch.client.transport.NoNodeAvailableException; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; @@ -58,9 +56,6 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.lease.Releasable; import org.opensearch.common.lease.Releasables; import org.opensearch.common.settings.ClusterSettings; @@ -68,20 +63,26 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.Assertions; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.IndexService; import org.opensearch.index.seqno.SequenceNumbers; import org.opensearch.index.shard.IndexShard; import org.opensearch.index.shard.IndexShardClosedException; import org.opensearch.index.shard.ReplicationGroup; -import org.opensearch.index.shard.ShardId; import org.opensearch.index.shard.ShardNotFoundException; import org.opensearch.index.shard.ShardNotInPrimaryModeException; import org.opensearch.indices.IndexClosedException; import org.opensearch.indices.IndicesService; import org.opensearch.node.NodeClosedException; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.ConnectTransportException; import org.opensearch.transport.TransportChannel; @@ -103,6 +104,8 @@ * The action samples cluster state on the receiving node to reroute to node with primary copy and on the * primary node to validate request before primary operation followed by sampling state again for resolving * nodes with replica copies to perform replication. + * + * @opensearch.internal */ public abstract class TransportReplicationAction< Request extends ReplicationRequest, @@ -252,10 +255,46 @@ private void runReroutePhase(Task task, Request request, ActionListener newReplicasProxy() { + protected Replicas newReplicasProxy() { return new ReplicasProxy(); } + /** + * This returns a ReplicaProxy that is used for primary term validation. The default behavior is that the control + * must not reach inside the performOn method for ReplicationActions. However, the implementations of the underlying + * class can provide primary term validation proxy that can allow performOn method to make calls to replica. + * + * @return Primary term validation replicas proxy. + */ + protected Replicas primaryTermValidationReplicasProxy() { + return new ReplicasProxy() { + @Override + public void performOn( + ShardRouting replica, + ReplicaRequest request, + long primaryTerm, + long globalCheckpoint, + long maxSeqNoOfUpdatesOrDeletes, + ActionListener listener + ) { + throw new UnsupportedOperationException("Primary term validation is not available for " + actionName); + } + }; + } + + /** + * This method is used for defining the {@link ReplicationMode} override per {@link TransportReplicationAction}. + * + * @param indexShard index shard used to determining the policy. + * @return the overridden replication mode. + */ + public ReplicationMode getReplicationMode(IndexShard indexShard) { + if (indexShard.isRemoteTranslogEnabled()) { + return ReplicationMode.NO_REPLICATION; + } + return ReplicationMode.FULL_REPLICATION; + } + protected abstract Response newResponseInstance(StreamInput in) throws IOException; /** @@ -384,6 +423,11 @@ protected Releasable checkPrimaryLimits(final Request request, boolean rerouteWa return () -> {}; } + /** + * Asynchronous primary action + * + * @opensearch.internal + */ class AsyncPrimaryAction extends AbstractRunnable { private final ActionListener onCompletionListener; private final ReplicationTask replicationTask; @@ -516,17 +560,24 @@ public void handleException(TransportException exp) { onCompletionListener.onResponse(response); }, e -> handleException(primaryShardReference, e)); + final Replicas replicasProxy = newReplicasProxy(); + final IndexShard indexShard = primaryShardReference.indexShard; + final Replicas termValidationProxy = primaryTermValidationReplicasProxy(); + new ReplicationOperation<>( primaryRequest.getRequest(), primaryShardReference, ActionListener.map(responseListener, result -> result.finalResponseIfSuccessful), - newReplicasProxy(), + replicasProxy, logger, threadPool, actionName, primaryRequest.getPrimaryTerm(), initialRetryBackoffBound, - retryTimeout + retryTimeout, + indexShard.isRemoteTranslogEnabled() + ? new ReplicationModeAwareProxy<>(getReplicationMode(indexShard), replicasProxy, termValidationProxy) + : new FanoutReplicationProxy<>(replicasProxy) ).execute(); } } catch (Exception e) { @@ -552,6 +603,11 @@ protected void adaptResponse(Response response, IndexShard indexShard) { } + /** + * The Primary Result + * + * @opensearch.internal + */ public static class PrimaryResult, Response extends ReplicationResponse> implements ReplicationOperation.PrimaryResult { @@ -601,6 +657,11 @@ public void runPostReplicationActions(ActionListener listener) { } } + /** + * The replica result + * + * @opensearch.internal + */ public static class ReplicaResult { final Exception finalFailure; @@ -643,6 +704,11 @@ protected Releasable checkReplicaLimits(final ReplicaRequest request) { return () -> {}; } + /** + * Thrown if there are any errors retrying on the replica + * + * @opensearch.internal + */ public static class RetryOnReplicaException extends OpenSearchException { public RetryOnReplicaException(ShardId shardId, String msg) { @@ -655,6 +721,11 @@ public RetryOnReplicaException(StreamInput in) throws IOException { } } + /** + * Asynchronous replica action + * + * @opensearch.internal + */ private final class AsyncReplicaAction extends AbstractRunnable implements ActionListener { private final ActionListener onCompletionListener; private final IndexShard replica; @@ -786,7 +857,7 @@ protected void doRun() throws Exception { } } - private IndexShard getIndexShard(final ShardId shardId) { + protected IndexShard getIndexShard(final ShardId shardId) { IndexService indexService = indicesService.indexServiceSafe(shardId.getIndex()); return indexService.getShard(shardId.id()); } @@ -797,6 +868,8 @@ private IndexShard getIndexShard(final ShardId shardId) { * node with primary copy. * * Resolves index and shard id for the request before routing it to target node + * + * @opensearch.internal */ final class ReroutePhase extends AbstractRunnable { private final ActionListener listener; @@ -1131,6 +1204,11 @@ protected void acquireReplicaOperationPermit( replica.acquireReplicaOperationPermit(primaryTerm, globalCheckpoint, maxSeqNoOfUpdatesOrDeletes, onAcquired, executor, request); } + /** + * The primary shard reference + * + * @opensearch.internal + */ class PrimaryShardReference implements Releasable, @@ -1223,11 +1301,16 @@ public PendingReplicationActions getPendingReplicationActions() { } } + /** + * The replica response + * + * @opensearch.internal + */ public static class ReplicaResponse extends ActionResponse implements ReplicationOperation.ReplicaResponse { private long localCheckpoint; private long globalCheckpoint; - ReplicaResponse(StreamInput in) throws IOException { + public ReplicaResponse(StreamInput in) throws IOException { super(in); localCheckpoint = in.readZLong(); globalCheckpoint = in.readZLong(); @@ -1279,8 +1362,10 @@ public int hashCode() { * interface that performs the actual {@code ReplicaRequest} on the replica * shards. It also encapsulates the logic required for failing the replica * if deemed necessary as well as marking it as stale when needed. + * + * @opensearch.internal */ - protected class ReplicasProxy implements ReplicationOperation.Replicas { + protected class ReplicasProxy implements Replicas { @Override public void performOn( @@ -1336,10 +1421,16 @@ public void markShardCopyAsStaleIfNeeded(ShardId shardId, String allocationId, l } } - /** a wrapper class to encapsulate a request when being sent to a specific allocation id **/ + /** + * a wrapper class to encapsulate a request when being sent to a specific allocation id + * + * @opensearch.internal + */ public static class ConcreteShardRequest extends TransportRequest { - /** {@link AllocationId#getId()} of the shard this request is sent to **/ + /** + * {@link AllocationId#getId()} of the shard this request is sent to + **/ private final String targetAllocationID; private final long primaryTerm; private final R request; @@ -1440,6 +1531,11 @@ public String toString() { } } + /** + * Internal request for concrete replica + * + * @opensearch.internal + */ protected static final class ConcreteReplicaRequest extends ConcreteShardRequest { private final long globalCheckpoint; @@ -1501,7 +1597,7 @@ public String toString() { * Sets the current phase on the task if it isn't null. Pulled into its own * method because its more convenient that way. */ - static void setPhase(ReplicationTask task, String phase) { + protected static void setPhase(ReplicationTask task, String phase) { if (task != null) { task.setPhase(phase); } diff --git a/server/src/main/java/org/opensearch/action/support/replication/TransportWriteAction.java b/server/src/main/java/org/opensearch/action/support/replication/TransportWriteAction.java index c9fb959306b9c..62cbfbde9780a 100644 --- a/server/src/main/java/org/opensearch/action/support/replication/TransportWriteAction.java +++ b/server/src/main/java/org/opensearch/action/support/replication/TransportWriteAction.java @@ -34,7 +34,6 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.TransportActions; @@ -46,14 +45,16 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexingPressureService; import org.opensearch.index.engine.Engine; import org.opensearch.index.mapper.MapperParsingException; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; +import org.opensearch.index.shard.PrimaryShardClosedException; import org.opensearch.index.translog.Translog; import org.opensearch.index.translog.Translog.Location; import org.opensearch.indices.IndicesService; @@ -69,6 +70,8 @@ /** * Base class for transport actions that modify data in some shard like index, delete, and shardBulk. * Allows performing async actions (e.g. refresh) after performing write operations on primary and replica shards + * + * @opensearch.internal */ public abstract class TransportWriteAction< Request extends ReplicatedWriteRequest, @@ -265,6 +268,8 @@ protected abstract void dispatchedShardOperationOnReplica( * Result of taking the action on the primary. * * NOTE: public for testing + * + * @opensearch.internal */ public static class WritePrimaryResult< ReplicaRequest extends ReplicatedWriteRequest, @@ -320,6 +325,8 @@ public void onFailure(Exception ex) { /** * Result of taking the action on the replica. + * + * @opensearch.internal */ public static class WriteReplicaResult> extends ReplicaResult { public final Location location; @@ -392,6 +399,8 @@ interface RespondingWriteResult { * This class encapsulates post write actions like async waits for * translog syncs or waiting for a refresh to happen making the write operation * visible. + * + * @opensearch.internal */ static final class AsyncAfterWriteAction { private final Location location; @@ -490,8 +499,10 @@ void run() { * * This extends {@code TransportReplicationAction.ReplicasProxy} to do the * failing and stale-ing. + * + * @opensearch.internal */ - class WriteActionReplicasProxy extends ReplicasProxy { + protected class WriteActionReplicasProxy extends ReplicasProxy { @Override public void failShardIfNeeded( @@ -504,15 +515,20 @@ public void failShardIfNeeded( if (TransportActions.isShardNotAvailableException(exception) == false) { logger.warn(new ParameterizedMessage("[{}] {}", replica.shardId(), message), exception); } - shardStateAction.remoteShardFailed( - replica.shardId(), - replica.allocationId().getId(), - primaryTerm, - true, - message, - exception, - listener - ); + // If a write action fails due to the closure of the primary shard + // then the replicas should not be marked as failed since they are + // still up-to-date with the (now closed) primary shard + if (exception instanceof PrimaryShardClosedException == false) { + shardStateAction.remoteShardFailed( + replica.shardId(), + replica.allocationId().getId(), + primaryTerm, + true, + message, + exception, + listener + ); + } } @Override diff --git a/server/src/main/java/org/opensearch/action/support/replication/package-info.java b/server/src/main/java/org/opensearch/action/support/replication/package-info.java new file mode 100644 index 0000000000000..912cd9197b10f --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/replication/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Replication transport handlers. */ +package org.opensearch.action.support.replication; diff --git a/server/src/main/java/org/opensearch/action/support/single/instance/InstanceShardOperationRequest.java b/server/src/main/java/org/opensearch/action/support/single/instance/InstanceShardOperationRequest.java index 0dd72671fb8c3..9422524133dbf 100644 --- a/server/src/main/java/org/opensearch/action/support/single/instance/InstanceShardOperationRequest.java +++ b/server/src/main/java/org/opensearch/action/support/single/instance/InstanceShardOperationRequest.java @@ -38,16 +38,21 @@ import org.opensearch.action.ValidateActions; import org.opensearch.action.support.IndicesOptions; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; import java.util.concurrent.TimeUnit; +/** + * Transport Request for an Index Shard Operation + * + * @opensearch.internal + */ // TODO: This request and its associated transport action can be folded into UpdateRequest which is its only concrete production code -// implementation +// implementation public abstract class InstanceShardOperationRequest> extends ActionRequest implements IndicesRequest { diff --git a/server/src/main/java/org/opensearch/action/support/single/instance/InstanceShardOperationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/single/instance/InstanceShardOperationRequestBuilder.java index 7bbbd7d11e6ab..30ebf1fb419bf 100644 --- a/server/src/main/java/org/opensearch/action/support/single/instance/InstanceShardOperationRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/single/instance/InstanceShardOperationRequestBuilder.java @@ -32,12 +32,17 @@ package org.opensearch.action.support.single.instance; -import org.opensearch.action.ActionType; import org.opensearch.action.ActionRequestBuilder; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionResponse; +/** + * Request builder for a shard operation + * + * @opensearch.internal + */ public abstract class InstanceShardOperationRequestBuilder< Request extends InstanceShardOperationRequest, Response extends ActionResponse, diff --git a/server/src/main/java/org/opensearch/action/support/single/instance/TransportInstanceSingleOperationAction.java b/server/src/main/java/org/opensearch/action/support/single/instance/TransportInstanceSingleOperationAction.java index f91b2e8ce2e98..21d4ba726e86f 100644 --- a/server/src/main/java/org/opensearch/action/support/single/instance/TransportInstanceSingleOperationAction.java +++ b/server/src/main/java/org/opensearch/action/support/single/instance/TransportInstanceSingleOperationAction.java @@ -32,8 +32,6 @@ package org.opensearch.action.support.single.instance; -import org.opensearch.action.ActionListener; -import org.opensearch.action.ActionResponse; import org.opensearch.action.UnavailableShardsException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; @@ -47,12 +45,14 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexNotFoundException; -import org.opensearch.index.shard.ShardId; import org.opensearch.node.NodeClosedException; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; @@ -69,6 +69,11 @@ import static org.opensearch.cluster.metadata.IndexNameExpressionResolver.EXCLUDED_DATA_STREAMS_KEY; +/** + * Base class for a single operation action + * + * @opensearch.internal + */ public abstract class TransportInstanceSingleOperationAction< Request extends InstanceShardOperationRequest, Response extends ActionResponse> extends HandledTransportAction { @@ -135,6 +140,11 @@ protected TransportRequestOptions transportOptions() { */ protected abstract ShardIterator shards(ClusterState clusterState, Request request); + /** + * Asynchronous single action + * + * @opensearch.internal + */ class AsyncSingleAction { private final ActionListener listener; @@ -286,6 +296,11 @@ public void onTimeout(TimeValue timeout) { } } + /** + * Transport handler per shard + * + * @opensearch.internal + */ private class ShardTransportHandler implements TransportRequestHandler { @Override diff --git a/server/src/main/java/org/opensearch/action/support/single/instance/package-info.java b/server/src/main/java/org/opensearch/action/support/single/instance/package-info.java new file mode 100644 index 0000000000000..1acd5c1503ed0 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/single/instance/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Single Instance transport handlers. */ +package org.opensearch.action.support.single.instance; diff --git a/server/src/main/java/org/opensearch/action/support/single/package-info.java b/server/src/main/java/org/opensearch/action/support/single/package-info.java new file mode 100644 index 0000000000000..918a755936fe3 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/single/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Single instance transport handlers. */ +package org.opensearch.action.support.single; diff --git a/server/src/main/java/org/opensearch/action/support/single/shard/SingleShardOperationRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/single/shard/SingleShardOperationRequestBuilder.java index d981bfaf60f2c..2c602cbc2d164 100644 --- a/server/src/main/java/org/opensearch/action/support/single/shard/SingleShardOperationRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/single/shard/SingleShardOperationRequestBuilder.java @@ -32,11 +32,16 @@ package org.opensearch.action.support.single.shard; -import org.opensearch.action.ActionType; import org.opensearch.action.ActionRequestBuilder; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; import org.opensearch.client.OpenSearchClient; +import org.opensearch.core.action.ActionResponse; +/** + * Request builder for a single shard operation request + * + * @opensearch.internal + */ public abstract class SingleShardOperationRequestBuilder< Request extends SingleShardRequest, Response extends ActionResponse, diff --git a/server/src/main/java/org/opensearch/action/support/single/shard/SingleShardRequest.java b/server/src/main/java/org/opensearch/action/support/single/shard/SingleShardRequest.java index faae069b4b7fa..c474096ff94e4 100644 --- a/server/src/main/java/org/opensearch/action/support/single/shard/SingleShardRequest.java +++ b/server/src/main/java/org/opensearch/action/support/single/shard/SingleShardRequest.java @@ -38,12 +38,17 @@ import org.opensearch.action.ValidateActions; import org.opensearch.action.support.IndicesOptions; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; import java.io.IOException; +/** + * Single shard request. + * + * @opensearch.internal + */ public abstract class SingleShardRequest> extends ActionRequest implements IndicesRequest { public static final IndicesOptions INDICES_OPTIONS = IndicesOptions.strictSingleIndexNoExpandForbidClosed(); diff --git a/server/src/main/java/org/opensearch/action/support/single/shard/TransportSingleShardAction.java b/server/src/main/java/org/opensearch/action/support/single/shard/TransportSingleShardAction.java index 2a03abac733cd..df91559a2f8cb 100644 --- a/server/src/main/java/org/opensearch/action/support/single/shard/TransportSingleShardAction.java +++ b/server/src/main/java/org/opensearch/action/support/single/shard/TransportSingleShardAction.java @@ -33,8 +33,6 @@ package org.opensearch.action.support.single.shard; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionRunnable; import org.opensearch.action.NoShardAvailableActionException; import org.opensearch.action.support.ActionFilters; @@ -47,14 +45,17 @@ import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.cluster.routing.FailAwareWeightedRouting; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.ShardsIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.logging.LoggerMessageFormat; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.logging.LoggerMessageFormat; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportChannel; @@ -71,6 +72,8 @@ * A base class for operations that need to perform a read operation on a single shard copy. If the operation fails, * the read operation can be performed on other shard copies. Concrete implementations can provide their own list * of candidate shards to try the read operation on. + * + * @opensearch.internal */ public abstract class TransportSingleShardAction, Response extends ActionResponse> extends TransportAction { @@ -151,6 +154,11 @@ protected void resolveRequest(ClusterState state, InternalRequest request) { @Nullable protected abstract ShardsIterator shards(ClusterState state, InternalRequest request); + /** + * Asynchronous single action + * + * @opensearch.internal + */ class AsyncSingleAction { private final ActionListener listener; @@ -237,7 +245,9 @@ private void perform(@Nullable final Exception currentFailure) { lastFailure = currentFailure; this.lastFailure = currentFailure; } - final ShardRouting shardRouting = shardIt.nextOrNull(); + ShardRouting shardRouting = FailAwareWeightedRouting.getInstance() + .findNext(shardIt, clusterService.state(), currentFailure, () -> {}); + if (shardRouting == null) { Exception failure = lastFailure; if (failure == null || isShardNotAvailableException(failure)) { @@ -266,6 +276,7 @@ private void perform(@Nullable final Exception currentFailure) { ); } final Writeable.Reader reader = getResponseReader(); + ShardRouting finalShardRouting = shardRouting; transportService.sendRequest( node, transportShardAction, @@ -289,7 +300,7 @@ public void handleResponse(final Response response) { @Override public void handleException(TransportException exp) { - onFailure(shardRouting, exp); + onFailure(finalShardRouting, exp); } } ); @@ -297,6 +308,11 @@ public void handleException(TransportException exp) { } } + /** + * Internal transport handler + * + * @opensearch.internal + */ private class TransportHandler implements TransportRequestHandler { @Override @@ -306,6 +322,11 @@ public void messageReceived(Request request, final TransportChannel channel, Tas } } + /** + * Shard level transport handler + * + * @opensearch.internal + */ private class ShardTransportHandler implements TransportRequestHandler { @Override @@ -319,6 +340,8 @@ public void messageReceived(final Request request, final TransportChannel channe /** * Internal request class that gets built on each node. Holds the original request plus additional info. + * + * @opensearch.internal */ protected class InternalRequest { final Request request; diff --git a/server/src/main/java/org/opensearch/action/support/single/shard/package-info.java b/server/src/main/java/org/opensearch/action/support/single/shard/package-info.java new file mode 100644 index 0000000000000..0d93688ece5f1 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/single/shard/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Single Shard transport handlers. */ +package org.opensearch.action.support.single.shard; diff --git a/server/src/main/java/org/opensearch/action/support/tasks/BaseTasksRequest.java b/server/src/main/java/org/opensearch/action/support/tasks/BaseTasksRequest.java index bbe7b918b26a5..a10f74f7c2aa8 100644 --- a/server/src/main/java/org/opensearch/action/support/tasks/BaseTasksRequest.java +++ b/server/src/main/java/org/opensearch/action/support/tasks/BaseTasksRequest.java @@ -34,14 +34,14 @@ import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionRequestValidationException; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.regex.Regex; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.CollectionUtils; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; import java.io.IOException; @@ -49,6 +49,8 @@ /** * A base class for task requests + * + * @opensearch.internal */ public class BaseTasksRequest> extends ActionRequest { diff --git a/server/src/main/java/org/opensearch/action/support/tasks/BaseTasksResponse.java b/server/src/main/java/org/opensearch/action/support/tasks/BaseTasksResponse.java index 3ba32cf2a3b12..d7b73361ee249 100644 --- a/server/src/main/java/org/opensearch/action/support/tasks/BaseTasksResponse.java +++ b/server/src/main/java/org/opensearch/action/support/tasks/BaseTasksResponse.java @@ -33,14 +33,14 @@ package org.opensearch.action.support.tasks; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionResponse; import org.opensearch.action.FailedNodeException; import org.opensearch.action.TaskOperationFailure; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.ArrayList; @@ -54,6 +54,8 @@ /** * Base class for responses of task-related operations + * + * @opensearch.internal */ public class BaseTasksResponse extends ActionResponse { protected static final String TASK_FAILURES = "task_failures"; diff --git a/server/src/main/java/org/opensearch/action/support/tasks/TasksRequestBuilder.java b/server/src/main/java/org/opensearch/action/support/tasks/TasksRequestBuilder.java index 0dd0f585ae9e6..a8bf264828122 100644 --- a/server/src/main/java/org/opensearch/action/support/tasks/TasksRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/support/tasks/TasksRequestBuilder.java @@ -31,14 +31,16 @@ package org.opensearch.action.support.tasks; -import org.opensearch.action.ActionType; import org.opensearch.action.ActionRequestBuilder; +import org.opensearch.action.ActionType; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.unit.TimeValue; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.tasks.TaskId; /** * Builder for task-based requests + * + * @opensearch.internal */ public class TasksRequestBuilder< Request extends BaseTasksRequest, diff --git a/server/src/main/java/org/opensearch/action/support/tasks/TransportTasksAction.java b/server/src/main/java/org/opensearch/action/support/tasks/TransportTasksAction.java index 7bd5a8503b123..f33d7161660a3 100644 --- a/server/src/main/java/org/opensearch/action/support/tasks/TransportTasksAction.java +++ b/server/src/main/java/org/opensearch/action/support/tasks/TransportTasksAction.java @@ -34,7 +34,6 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.ResourceNotFoundException; -import org.opensearch.action.ActionListener; import org.opensearch.action.FailedNodeException; import org.opensearch.action.NoSuchNodeException; import org.opensearch.action.TaskOperationFailure; @@ -44,12 +43,13 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.util.concurrent.AtomicArray; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.NodeShouldNotConnectException; @@ -58,13 +58,13 @@ import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestHandler; import org.opensearch.transport.TransportRequestOptions; -import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.function.Consumer; @@ -73,6 +73,8 @@ /** * The base class for transport actions that are interacting with currently running tasks. + * + * @opensearch.internal */ public abstract class TransportTasksAction< OperationTask extends Task, @@ -235,6 +237,11 @@ protected TasksResponse newResponse(TasksRequest request, AtomicReferenceArray r */ protected abstract void taskOperation(TasksRequest request, OperationTask task, ActionListener listener); + /** + * Asynchronous single action + * + * @opensearch.internal + */ private class AsyncAction { private final TasksRequest request; @@ -252,7 +259,7 @@ private AsyncAction(Task task, TasksRequest request, ActionListener nodes = clusterState.nodes().getNodes(); + final Map nodes = clusterState.nodes().getNodes(); this.nodes = new DiscoveryNode[nodesIds.length]; for (int i = 0; i < this.nodesIds.length; i++) { this.nodes[i] = nodes.get(this.nodesIds[i]); @@ -351,6 +358,11 @@ private void finishHim() { } } + /** + * Node level transport handler + * + * @opensearch.internal + */ class NodeTransportHandler implements TransportRequestHandler { @Override @@ -366,6 +378,11 @@ public void messageReceived(final NodeTaskRequest request, final TransportChanne } } + /** + * Node level task request + * + * @opensearch.internal + */ private class NodeTaskRequest extends TransportRequest { private TasksRequest tasksRequest; @@ -387,6 +404,11 @@ protected NodeTaskRequest(TasksRequest tasksRequest) { } + /** + * Node level task response + * + * @opensearch.internal + */ private class NodeTasksResponse extends TransportResponse { protected String nodeId; protected List exceptions; diff --git a/server/src/main/java/org/opensearch/action/support/tasks/package-info.java b/server/src/main/java/org/opensearch/action/support/tasks/package-info.java new file mode 100644 index 0000000000000..30924e1433c7b --- /dev/null +++ b/server/src/main/java/org/opensearch/action/support/tasks/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Base Task Request transport handlers. */ +package org.opensearch.action.support.tasks; diff --git a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsAction.java b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsAction.java index a14ae0ce94cc7..27d93f2a8e916 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsAction.java +++ b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * A single multi term action. + * + * @opensearch.internal + */ public class MultiTermVectorsAction extends ActionType { public static final MultiTermVectorsAction INSTANCE = new MultiTermVectorsAction(); diff --git a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsItemResponse.java b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsItemResponse.java index 037f4b95e3c73..80ca1629417ad 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsItemResponse.java +++ b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsItemResponse.java @@ -32,14 +32,16 @@ package org.opensearch.action.termvectors; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; /** - * A single multi get response. + * A single multi term response. + * + * @opensearch.internal */ public class MultiTermVectorsItemResponse implements Writeable { diff --git a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsRequest.java b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsRequest.java index aada19b081a1c..c055564c3fcbe 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsRequest.java +++ b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsRequest.java @@ -39,9 +39,9 @@ import org.opensearch.action.RealtimeRequest; import org.opensearch.action.ValidateActions; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; @@ -51,6 +51,11 @@ import java.util.List; import java.util.Set; +/** + * A single multi get request. + * + * @opensearch.internal + */ public class MultiTermVectorsRequest extends ActionRequest implements Iterable, diff --git a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsRequestBuilder.java b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsRequestBuilder.java index 51711c01beb44..04dfd39112d6e 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsRequestBuilder.java @@ -35,6 +35,11 @@ import org.opensearch.action.ActionRequestBuilder; import org.opensearch.client.OpenSearchClient; +/** + * A single multi get request builder. + * + * @opensearch.internal + */ public class MultiTermVectorsRequestBuilder extends ActionRequestBuilder { public MultiTermVectorsRequestBuilder(OpenSearchClient client, MultiTermVectorsAction action) { diff --git a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsResponse.java b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsResponse.java index 599c2fa883dc7..5d40e64df1e3e 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsResponse.java +++ b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsResponse.java @@ -34,21 +34,28 @@ import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Arrays; import java.util.Iterator; +/** + * A multi get response. + * + * @opensearch.internal + */ public class MultiTermVectorsResponse extends ActionResponse implements Iterable, ToXContentObject { /** * Represents a failure. + * + * @opensearch.internal */ public static class Failure implements Writeable { private final String index; @@ -146,10 +153,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Fields used for parsing and toXContent + * + * @opensearch.internal + */ static final class Fields { static final String DOCS = "docs"; static final String _INDEX = "_index"; - static final String _TYPE = "_type"; static final String _ID = "_id"; } diff --git a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsShardRequest.java b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsShardRequest.java index 85491b37b4914..a31e3085cbc17 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsShardRequest.java +++ b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsShardRequest.java @@ -32,28 +32,32 @@ package org.opensearch.action.termvectors; -import com.carrotsearch.hppc.IntArrayList; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.single.shard.SingleShardRequest; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.ArrayList; import java.util.List; +/** + * A multi get shard request. + * + * @opensearch.internal + */ public class MultiTermVectorsShardRequest extends SingleShardRequest { private int shardId; private String preference; - IntArrayList locations; + List locations; List requests; MultiTermVectorsShardRequest(StreamInput in) throws IOException { super(in); int size = in.readVInt(); - locations = new IntArrayList(size); + locations = new ArrayList<>(size); requests = new ArrayList<>(size); for (int i = 0; i < size; i++) { locations.add(in.readVInt()); @@ -66,7 +70,7 @@ public class MultiTermVectorsShardRequest extends SingleShardRequest(); requests = new ArrayList<>(); } @@ -81,7 +85,8 @@ public int shardId() { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public MultiTermVectorsShardRequest preference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsShardResponse.java b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsShardResponse.java index b01582230ed85..674b285a7b3ce 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsShardResponse.java +++ b/server/src/main/java/org/opensearch/action/termvectors/MultiTermVectorsShardResponse.java @@ -32,23 +32,27 @@ package org.opensearch.action.termvectors; -import com.carrotsearch.hppc.IntArrayList; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.ArrayList; import java.util.List; +/** + * A multi get shard response. + * + * @opensearch.internal + */ public class MultiTermVectorsShardResponse extends ActionResponse { - final IntArrayList locations; + final List locations; final List responses; final List failures; MultiTermVectorsShardResponse() { - locations = new IntArrayList(); + locations = new ArrayList<>(); responses = new ArrayList<>(); failures = new ArrayList<>(); } @@ -56,7 +60,7 @@ public class MultiTermVectorsShardResponse extends ActionResponse { MultiTermVectorsShardResponse(StreamInput in) throws IOException { super(in); int size = in.readVInt(); - locations = new IntArrayList(size); + locations = new ArrayList<>(size); responses = new ArrayList<>(size); failures = new ArrayList<>(size); for (int i = 0; i < size; i++) { diff --git a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsAction.java b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsAction.java index acdeb3d18c880..e810621b1a064 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsAction.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsAction.java @@ -34,6 +34,12 @@ import org.opensearch.action.ActionType; +/** + * Transport action for returning the term vector (doc frequency, positions, offsets) for a + * document. + * + * @opensearch.internal + */ public class TermVectorsAction extends ActionType { public static final TermVectorsAction INSTANCE = new TermVectorsAction(); diff --git a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsFields.java b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsFields.java index 0033e9299b76d..ed3dca815090d 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsFields.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsFields.java @@ -32,8 +32,6 @@ package org.opensearch.action.termvectors; -import com.carrotsearch.hppc.ObjectLongHashMap; -import com.carrotsearch.hppc.cursors.ObjectLongCursor; import org.apache.lucene.index.BaseTermsEnum; import org.apache.lucene.index.Fields; import org.apache.lucene.index.ImpactsEnum; @@ -46,12 +44,15 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; import org.apache.lucene.util.RamUsageEstimator; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; +import java.util.Map; import static org.apache.lucene.util.ArrayUtil.grow; @@ -130,11 +131,12 @@ *
  • BytesRef: payload_freqency (if payloads)
  • * * + * + * @opensearch.internal */ - public final class TermVectorsFields extends Fields { - private final ObjectLongHashMap fieldMap; + private final Map fieldMap; private final BytesReference termVectors; final boolean hasTermStatistic; final boolean hasFieldStatistic; @@ -156,10 +158,11 @@ public TermVectorsFields(BytesReference headerRef, BytesReference termVectors) t hasFieldStatistic = header.readBoolean(); hasScores = header.readBoolean(); final int numFields = header.readVInt(); - fieldMap = new ObjectLongHashMap<>(numFields); + final Map fieldMap = new HashMap<>(numFields); for (int i = 0; i < numFields; i++) { fieldMap.put((header.readString()), header.readVLong()); } + this.fieldMap = Collections.unmodifiableMap(fieldMap); } // reference to the term vector data this.termVectors = termVectors; @@ -167,8 +170,8 @@ public TermVectorsFields(BytesReference headerRef, BytesReference termVectors) t @Override public Iterator iterator() { - final Iterator> iterator = fieldMap.iterator(); - return new Iterator() { + final Iterator> iterator = fieldMap.entrySet().iterator(); + return new Iterator<>() { @Override public boolean hasNext() { return iterator.hasNext(); @@ -176,7 +179,7 @@ public boolean hasNext() { @Override public String next() { - return iterator.next().key; + return iterator.next().getKey(); } @Override @@ -190,12 +193,11 @@ public void remove() { public Terms terms(String field) throws IOException { // first, find where in the termVectors bytes the actual term vector for // this field is stored - final int keySlot = fieldMap.indexOf(field); - if (keySlot < 0) { + final Long keySlot = fieldMap.get(field); + if (keySlot == null) { return null; // we don't have it. } - long readOffset = fieldMap.indexGet(keySlot); - return new TermVector(termVectors, readOffset); + return new TermVector(termVectors, keySlot); } @Override @@ -203,6 +205,11 @@ public int size() { return fieldMap.size(); } + /** + * Internal term vector + * + * @opensearch.internal + */ private final class TermVector extends Terms { private final StreamInput perFieldTermVectorInput; @@ -419,6 +426,11 @@ public boolean hasPayloads() { } } + /** + * Internal postings enumerator for term vectors + * + * @opensearch.internal + */ private final class TermVectorPostingsEnum extends PostingsEnum { private boolean hasPositions; private boolean hasOffsets; diff --git a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsFilter.java b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsFilter.java index 2d2650e2b9389..0e0202777794b 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsFilter.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsFilter.java @@ -45,6 +45,12 @@ import java.util.Map; import java.util.Set; +/** + * Filter the term vector (doc frequency, positions, offsets) for a + * document. + * + * @opensearch.internal + */ public class TermVectorsFilter { public static final int DEFAULT_MAX_QUERY_TERMS = 25; public static final int DEFAULT_MIN_TERM_FREQ = 0; @@ -170,6 +176,11 @@ public void setMaxWordLength(int maxWordLength) { this.maxWordLength = maxWordLength; } + /** + * Internal score term + * + * @opensearch.internal + */ public static final class ScoreTerm { public String field; public String word; @@ -289,6 +300,11 @@ private float computeScore(long docFreq, int freq, long numDocs) { return freq * similarity.idf(docFreq, numDocs); } + /** + * Internal queue of score terms + * + * @opensearch.internal + */ private static class ScoreTermsQueue extends org.apache.lucene.util.PriorityQueue { private final int limit; diff --git a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsRequest.java b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsRequest.java index 214d5f0d6d4fa..d49d087316afa 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsRequest.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsRequest.java @@ -41,17 +41,18 @@ import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.support.single.shard.SingleShardRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.MapperService; @@ -73,6 +74,8 @@ *

    * Note, the {@link #index()}, and {@link #id(String)} are * required. + * + * @opensearch.internal */ public class TermVectorsRequest extends SingleShardRequest implements RealtimeRequest { private static final ParseField INDEX = new ParseField("_index"); @@ -92,7 +95,7 @@ public class TermVectorsRequest extends SingleShardRequest i private BytesReference doc; - private XContentType xContentType; + private MediaType mediaType; private String routing; @@ -113,6 +116,11 @@ public class TermVectorsRequest extends SingleShardRequest i private FilterSettings filterSettings; + /** + * Internal filter settings + * + * @opensearch.internal + */ public static final class FilterSettings { public Integer maxNumTerms; public Integer minTermFreq; @@ -179,7 +187,11 @@ public TermVectorsRequest() {} if (in.readBoolean()) { doc = in.readBytesReference(); - xContentType = in.readEnum(XContentType.class); + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType = in.readMediaType(); + } else { + mediaType = in.readEnum(XContentType.class); + } } routing = in.readOptionalString(); @@ -232,7 +244,7 @@ public TermVectorsRequest(TermVectorsRequest other) { this.id = other.id(); if (other.doc != null) { this.doc = new BytesArray(other.doc().toBytesRef(), true); - this.xContentType = other.xContentType; + this.mediaType = other.mediaType; } this.flagsEnum = other.getFlags().clone(); this.preference = other.preference(); @@ -282,8 +294,8 @@ public BytesReference doc() { return doc; } - public XContentType xContentType() { - return xContentType; + public MediaType xContentType() { + return mediaType; } /** @@ -295,23 +307,23 @@ public TermVectorsRequest doc(XContentBuilder documentBuilder) { /** * Sets an artificial document from which term vectors are requested for. - * @deprecated use {@link #doc(BytesReference, boolean, XContentType)} to avoid content auto detection + * @deprecated use {@link #doc(BytesReference, boolean, MediaType)} to avoid content auto detection */ @Deprecated public TermVectorsRequest doc(BytesReference doc, boolean generateRandomId) { - return this.doc(doc, generateRandomId, XContentHelper.xContentType(doc)); + return this.doc(doc, generateRandomId, MediaTypeRegistry.xContentType(doc)); } /** * Sets an artificial document from which term vectors are requested for. */ - public TermVectorsRequest doc(BytesReference doc, boolean generateRandomId, XContentType xContentType) { + public TermVectorsRequest doc(BytesReference doc, boolean generateRandomId, MediaType mediaType) { // assign a random id to this artificial document, for routing if (generateRandomId) { this.id(String.valueOf(randomInt.getAndAdd(1))); } this.doc = doc; - this.xContentType = xContentType; + this.mediaType = mediaType; return this; } @@ -333,8 +345,8 @@ public String preference() { /** * Sets the preference to execute the search. Defaults to randomize across - * shards. Can be set to {@code _local} to prefer local shards or a custom value, - * which guarantees that the same order will be used across different + * shards. Can be set to {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order will be used across different * requests. */ public TermVectorsRequest preference(String preference) { @@ -531,7 +543,11 @@ public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(doc != null); if (doc != null) { out.writeBytesReference(doc); - out.writeEnum(xContentType); + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + mediaType.writeTo(out); + } else { + out.writeEnum((XContentType) mediaType); + } } out.writeOptionalString(routing); if (out.getVersion().before(LegacyESVersion.V_7_0_0)) { @@ -561,6 +577,11 @@ public void writeTo(StreamOutput out) throws IOException { out.writeLong(version); } + /** + * The flags. + * + * @opensearch.internal + */ public enum Flag { // Do not change the order of these flags we use // the ordinal for encoding! Only append to the end! diff --git a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsRequestBuilder.java b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsRequestBuilder.java index 7294db072ad38..02cfff1a6682b 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsRequestBuilder.java @@ -34,7 +34,7 @@ import org.opensearch.action.ActionRequestBuilder; import org.opensearch.client.OpenSearchClient; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.VersionType; import java.util.Map; @@ -45,6 +45,8 @@ *

    * Note, the {@code index}, {@code type} and {@code id} are * required. + * + * @opensearch.internal */ public class TermVectorsRequestBuilder extends ActionRequestBuilder { @@ -94,7 +96,8 @@ public TermVectorsRequestBuilder setRouting(String routing) { /** * Sets the preference to execute the search. Defaults to randomize across shards. Can be set to - * {@code _local} to prefer local shards or a custom value, which guarantees that the same order + * {@code _local} to prefer local shards, {@code _primary} to execute only on primary shards, + * or a custom value, which guarantees that the same order * will be used across different requests. */ public TermVectorsRequestBuilder setPreference(String preference) { diff --git a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsResponse.java b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsResponse.java index 870609d526909..3c338ce7338bb 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsResponse.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsResponse.java @@ -41,15 +41,15 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.CharsRefBuilder; import org.opensearch.Version; -import org.opensearch.action.ActionResponse; -import org.opensearch.common.bytes.BytesArray; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.bytes.BytesArray; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.MapperService; import java.io.IOException; @@ -58,8 +58,19 @@ import java.util.Iterator; import java.util.Set; +/** + * Response returning the term vector (doc frequency, positions, offsets) for a + * document. + * + * @opensearch.internal + */ public class TermVectorsResponse extends ActionResponse implements ToXContentObject { + /** + * Fields used for parsing and toXContent + * + * @opensearch.internal + */ private static class FieldStrings { // term statistics strings public static final String TTF = "ttf"; diff --git a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsWriter.java b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsWriter.java index 2c1e543774da6..b60376d6f7e8d 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TermVectorsWriter.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TermVectorsWriter.java @@ -40,8 +40,8 @@ import org.apache.lucene.util.BytesRef; import org.opensearch.action.termvectors.TermVectorsRequest.Flag; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.bytes.BytesReference; import java.io.IOException; import java.util.ArrayList; @@ -49,6 +49,12 @@ import java.util.List; import java.util.Set; +/** + * Writer for the term vector (doc frequency, positions, offsets) for a + * document. + * + * @opensearch.internal + */ // package only - this is an internal class! final class TermVectorsWriter { final List fields = new ArrayList<>(); diff --git a/server/src/main/java/org/opensearch/action/termvectors/TransportMultiTermVectorsAction.java b/server/src/main/java/org/opensearch/action/termvectors/TransportMultiTermVectorsAction.java index 127b31f329d09..0364f36106cb0 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TransportMultiTermVectorsAction.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TransportMultiTermVectorsAction.java @@ -32,7 +32,6 @@ package org.opensearch.action.termvectors; -import org.opensearch.action.ActionListener; import org.opensearch.action.RoutingMissingException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.HandledTransportAction; @@ -42,8 +41,9 @@ import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; import org.opensearch.common.util.concurrent.AtomicArray; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexNotFoundException; -import org.opensearch.index.shard.ShardId; import org.opensearch.tasks.Task; import org.opensearch.transport.TransportService; @@ -51,6 +51,11 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +/** + * Performs the multi term get operation. + * + * @opensearch.internal + */ public class TransportMultiTermVectorsAction extends HandledTransportAction { private final ClusterService clusterService; diff --git a/server/src/main/java/org/opensearch/action/termvectors/TransportShardMultiTermsVectorAction.java b/server/src/main/java/org/opensearch/action/termvectors/TransportShardMultiTermsVectorAction.java index 511b68965ebdf..a298e267cca37 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TransportShardMultiTermsVectorAction.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TransportShardMultiTermsVectorAction.java @@ -41,15 +41,20 @@ import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.index.termvectors.TermVectorsService; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; +/** + * Performs the multi term shard level get operation. + * + * @opensearch.internal + */ public class TransportShardMultiTermsVectorAction extends TransportSingleShardAction< MultiTermVectorsShardRequest, MultiTermVectorsShardResponse> { diff --git a/server/src/main/java/org/opensearch/action/termvectors/TransportTermVectorsAction.java b/server/src/main/java/org/opensearch/action/termvectors/TransportTermVectorsAction.java index 3cfd9cf7da7c5..b7e8a29bd4027 100644 --- a/server/src/main/java/org/opensearch/action/termvectors/TransportTermVectorsAction.java +++ b/server/src/main/java/org/opensearch/action/termvectors/TransportTermVectorsAction.java @@ -32,20 +32,21 @@ package org.opensearch.action.termvectors; -import org.opensearch.action.ActionListener; import org.opensearch.action.RoutingMissingException; import org.opensearch.action.support.ActionFilters; import org.opensearch.action.support.single.shard.TransportSingleShardAction; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.routing.GroupShardsIterator; +import org.opensearch.cluster.routing.Preference; import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; import org.opensearch.index.IndexService; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.index.termvectors.TermVectorsService; import org.opensearch.indices.IndicesService; import org.opensearch.threadpool.ThreadPool; @@ -55,6 +56,8 @@ /** * Performs the get operation. + * + * @opensearch.internal */ public class TransportTermVectorsAction extends TransportSingleShardAction { @@ -85,15 +88,24 @@ public TransportTermVectorsAction( @Override protected ShardIterator shards(ClusterState state, InternalRequest request) { + + String preference = request.request().preference; + // For a real time request on a seg rep index, use primary shard as the preferred query shard. + if (request.request().realtime() + && preference == null + && state.getMetadata().isSegmentReplicationEnabled(request.concreteIndex())) { + preference = Preference.PRIMARY.type(); + } + if (request.request().doc() != null && request.request().routing() == null) { // artificial document without routing specified, ignore its "id" and use either random shard or according to preference GroupShardsIterator groupShardsIter = clusterService.operationRouting() - .searchShards(state, new String[] { request.concreteIndex() }, null, request.request().preference()); + .searchShards(state, new String[] { request.concreteIndex() }, null, preference); return groupShardsIter.iterator().next(); } return clusterService.operationRouting() - .getShards(state, request.concreteIndex(), request.request().id(), request.request().routing(), request.request().preference()); + .getShards(state, request.concreteIndex(), request.request().id(), request.request().routing(), preference); } @Override diff --git a/server/src/main/java/org/opensearch/action/update/TransportUpdateAction.java b/server/src/main/java/org/opensearch/action/update/TransportUpdateAction.java index 387c0d24ed4df..819112eb497f6 100644 --- a/server/src/main/java/org/opensearch/action/update/TransportUpdateAction.java +++ b/server/src/main/java/org/opensearch/action/update/TransportUpdateAction.java @@ -32,8 +32,8 @@ package org.opensearch.action.update; +import org.opensearch.ExceptionsHelper; import org.opensearch.ResourceAlreadyExistsException; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; import org.opensearch.action.DocWriteRequest; import org.opensearch.action.RoutingMissingException; @@ -55,18 +55,21 @@ import org.opensearch.cluster.routing.ShardIterator; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.NotSerializableExceptionWrapper; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NotSerializableExceptionWrapper; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaType; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.IndexService; import org.opensearch.index.engine.VersionConflictEngineException; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; +import org.opensearch.index.shard.IndexingStats.Stats.DocStatusStats; import org.opensearch.indices.IndicesService; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; @@ -81,6 +84,11 @@ import static org.opensearch.action.bulk.TransportSingleItemBulkWriteAction.toSingleItemBulkRequest; import static org.opensearch.action.bulk.TransportSingleItemBulkWriteAction.wrapBulkResponse; +/** + * Transport action for updating an index + * + * @opensearch.internal + */ public class TransportUpdateAction extends TransportInstanceSingleOperationAction { private final AutoCreateIndex autoCreateIndex; @@ -149,17 +157,20 @@ public static void resolveAndValidateRouting(Metadata metadata, String concreteI @Override protected void doExecute(Task task, final UpdateRequest request, final ActionListener listener) { if (request.isRequireAlias() && (clusterService.state().getMetadata().hasAlias(request.index()) == false)) { - throw new IndexNotFoundException( + IndexNotFoundException e = new IndexNotFoundException( "[" + DocWriteRequest.REQUIRE_ALIAS + "] request flag is [true] and [" + request.index() + "] is not an alias", request.index() ); + + incDocStatusStats(e); + throw e; } - // if we don't have a master, we don't have metadata, that's fine, let it find a master using create index API + // if we don't have a master, we don't have metadata, that's fine, let it find a cluster-manager using create index API if (autoCreateIndex.shouldAutoCreate(request.index(), clusterService.state())) { client.admin() .indices() .create( - new CreateIndexRequest().index(request.index()).cause("auto(update api)").masterNodeTimeout(request.timeout()), + new CreateIndexRequest().index(request.index()).cause("auto(update api)").clusterManagerNodeTimeout(request.timeout()), new ActionListener() { @Override public void onResponse(CreateIndexResponse result) { @@ -188,7 +199,10 @@ public void onFailure(Exception e) { } private void innerExecute(final Task task, final UpdateRequest request, final ActionListener listener) { - super.doExecute(task, request, listener); + super.doExecute(task, request, ActionListener.wrap(listener::onResponse, e -> { + incDocStatusStats(e); + listener.onFailure(e); + })); } @Override @@ -233,7 +247,7 @@ protected void shardOperation(final UpdateRequest request, final ActionListener< response.getResult() ); if (request.fetchSource() != null && request.fetchSource().fetchSource()) { - Tuple> sourceAndContent = XContentHelper.convertToMap( + Tuple> sourceAndContent = XContentHelper.convertToMap( upsertSourceBytes, true, upsertRequest.getContentType() @@ -325,7 +339,13 @@ protected void shardOperation(final UpdateRequest request, final ActionListener< shard.noopUpdate(); } } + + DocStatusStats stats = new DocStatusStats(); + stats.inc(RestStatus.OK); + + indicesService.addDocStatusStats(stats); listener.onResponse(update); + break; default: throw new IllegalStateException("Illegal result " + result.getResponseResult()); @@ -356,4 +376,10 @@ private void handleUpdateFailureWithRetry( } listener.onFailure(cause instanceof Exception ? (Exception) cause : new NotSerializableExceptionWrapper(cause)); } + + private void incDocStatusStats(final Exception e) { + DocStatusStats stats = new DocStatusStats(); + stats.inc(ExceptionsHelper.status(e)); + indicesService.addDocStatusStats(stats); + } } diff --git a/server/src/main/java/org/opensearch/action/update/UpdateAction.java b/server/src/main/java/org/opensearch/action/update/UpdateAction.java index c82cf96506de4..af520409f498c 100644 --- a/server/src/main/java/org/opensearch/action/update/UpdateAction.java +++ b/server/src/main/java/org/opensearch/action/update/UpdateAction.java @@ -34,6 +34,11 @@ import org.opensearch.action.ActionType; +/** + * Action for updating an index + * + * @opensearch.internal + */ public class UpdateAction extends ActionType { public static final UpdateAction INSTANCE = new UpdateAction(); diff --git a/server/src/main/java/org/opensearch/action/update/UpdateHelper.java b/server/src/main/java/org/opensearch/action/update/UpdateHelper.java index 0da41a3028edf..19c32f9336df8 100644 --- a/server/src/main/java/org/opensearch/action/update/UpdateHelper.java +++ b/server/src/main/java/org/opensearch/action/update/UpdateHelper.java @@ -40,20 +40,22 @@ import org.opensearch.action.index.IndexRequest; import org.opensearch.client.Requests; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.VersionType; import org.opensearch.index.engine.DocumentMissingException; import org.opensearch.index.engine.DocumentSourceMissingException; import org.opensearch.index.get.GetResult; import org.opensearch.index.mapper.RoutingFieldMapper; import org.opensearch.index.shard.IndexShard; -import org.opensearch.index.shard.ShardId; import org.opensearch.script.Script; import org.opensearch.script.ScriptService; import org.opensearch.script.UpdateScript; @@ -67,6 +69,8 @@ /** * Helper for translating an update request to an index, delete request or update response. + * + * @opensearch.internal */ public class UpdateHelper { @@ -162,7 +166,7 @@ Result prepareUpsert(ShardId shardId, UpdateRequest request, final GetResult get DocWriteResponse.Result.NOOP ); update.setGetResult(getResult); - return new Result(update, DocWriteResponse.Result.NOOP, upsertResult.v2(), XContentType.JSON); + return new Result(update, DocWriteResponse.Result.NOOP, upsertResult.v2(), MediaTypeRegistry.JSON); default: // It's fine to throw an exception here, the leniency is handled/logged by `executeScriptedUpsert` throw new IllegalArgumentException("unknown upsert operation, got: " + upsertResult.v1()); @@ -350,7 +354,7 @@ public static GetResult extractGetResult( long primaryTerm, long version, final Map source, - XContentType sourceContentType, + MediaType sourceContentType, @Nullable final BytesReference sourceAsBytes ) { if (request.fetchSource() == null || request.fetchSource().fetchSource() == false) { @@ -388,18 +392,23 @@ public static GetResult extractGetResult( ); } + /** + * Internal result + * + * @opensearch.internal + */ public static class Result { private final Writeable action; private final DocWriteResponse.Result result; private final Map updatedSourceAsMap; - private final XContentType updateSourceContentType; + private final MediaType updateSourceContentType; public Result( Writeable action, DocWriteResponse.Result result, Map updatedSourceAsMap, - XContentType updateSourceContentType + MediaType updateSourceContentType ) { this.action = action; this.result = result; @@ -420,7 +429,7 @@ public Map updatedSourceAsMap() { return updatedSourceAsMap; } - public XContentType updateSourceContentType() { + public MediaType updateSourceContentType() { return updateSourceContentType; } } @@ -466,6 +475,8 @@ public String toString() { /** * Field names used to populate the script context + * + * @opensearch.internal */ public static class ContextFields { public static final String CTX = "ctx"; @@ -473,7 +484,6 @@ public static class ContextFields { public static final String SOURCE = "_source"; public static final String NOW = "_now"; public static final String INDEX = "_index"; - public static final String TYPE = "_type"; public static final String ID = "_id"; public static final String VERSION = "_version"; public static final String ROUTING = "_routing"; diff --git a/server/src/main/java/org/opensearch/action/update/UpdateRequest.java b/server/src/main/java/org/opensearch/action/update/UpdateRequest.java index 36be9f0160c9a..ca38c0c4273c6 100644 --- a/server/src/main/java/org/opensearch/action/update/UpdateRequest.java +++ b/server/src/main/java/org/opensearch/action/update/UpdateRequest.java @@ -43,23 +43,23 @@ import org.opensearch.action.support.replication.ReplicationRequest; import org.opensearch.action.support.single.instance.InstanceShardOperationRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lucene.uid.Versions; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.VersionType; import org.opensearch.index.mapper.MapperService; -import org.opensearch.index.shard.ShardId; import org.opensearch.script.Script; import org.opensearch.script.ScriptType; import org.opensearch.search.fetch.subphase.FetchSourceContext; @@ -72,6 +72,11 @@ import static org.opensearch.index.seqno.SequenceNumbers.UNASSIGNED_PRIMARY_TERM; import static org.opensearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO; +/** + * Transport request for updating an index + * + * @opensearch.internal + */ public class UpdateRequest extends InstanceShardOperationRequest implements DocWriteRequest, @@ -102,12 +107,12 @@ public class UpdateRequest extends InstanceShardOperationRequest ); PARSER.declareBoolean(UpdateRequest::scriptedUpsert, SCRIPTED_UPSERT_FIELD); PARSER.declareObject((request, builder) -> request.safeUpsertRequest().source(builder), (parser, context) -> { - XContentBuilder builder = XContentFactory.contentBuilder(parser.contentType()); + XContentBuilder builder = MediaTypeRegistry.contentBuilder(parser.contentType()); builder.copyCurrentStructure(parser); return builder; }, UPSERT_FIELD); PARSER.declareObject((request, builder) -> request.safeDoc().source(builder), (parser, context) -> { - XContentBuilder docBuilder = XContentFactory.contentBuilder(parser.contentType()); + XContentBuilder docBuilder = MediaTypeRegistry.contentBuilder(parser.contentType()); docBuilder.copyCurrentStructure(parser); return docBuilder; }, DOC_FIELD); @@ -249,6 +254,9 @@ public ActionRequestValidationException validate() { if (doc == null && docAsUpsert) { validationException = addValidationError("doc must be specified if doc_as_upsert is enabled", validationException); } + + validationException = DocWriteRequest.validateDocIdLength(id, validationException); + return validationException; } @@ -677,32 +685,32 @@ public UpdateRequest doc(Map source) { /** * Sets the doc to use for updates when a script is not specified. */ - public UpdateRequest doc(Map source, XContentType contentType) { - safeDoc().source(source, contentType); + public UpdateRequest doc(Map source, MediaType mediaType) { + safeDoc().source(source, mediaType); return this; } /** * Sets the doc to use for updates when a script is not specified. */ - public UpdateRequest doc(String source, XContentType xContentType) { - safeDoc().source(source, xContentType); + public UpdateRequest doc(String source, MediaType mediaType) { + safeDoc().source(source, mediaType); return this; } /** * Sets the doc to use for updates when a script is not specified. */ - public UpdateRequest doc(byte[] source, XContentType xContentType) { - safeDoc().source(source, xContentType); + public UpdateRequest doc(byte[] source, MediaType mediaType) { + safeDoc().source(source, mediaType); return this; } /** * Sets the doc to use for updates when a script is not specified. */ - public UpdateRequest doc(byte[] source, int offset, int length, XContentType xContentType) { - safeDoc().source(source, offset, length, xContentType); + public UpdateRequest doc(byte[] source, int offset, int length, MediaType mediaType) { + safeDoc().source(source, offset, length, mediaType); return this; } @@ -719,8 +727,8 @@ public UpdateRequest doc(Object... source) { * Sets the doc to use for updates when a script is not specified, the doc provided * is a field and value pairs. */ - public UpdateRequest doc(XContentType xContentType, Object... source) { - safeDoc().source(xContentType, source); + public UpdateRequest doc(MediaType mediaType, Object... source) { + safeDoc().source(mediaType, source); return this; } @@ -763,32 +771,32 @@ public UpdateRequest upsert(Map source) { /** * Sets the doc source of the update request to be used when the document does not exists. */ - public UpdateRequest upsert(Map source, XContentType contentType) { - safeUpsertRequest().source(source, contentType); + public UpdateRequest upsert(Map source, MediaType mediaType) { + safeUpsertRequest().source(source, mediaType); return this; } /** * Sets the doc source of the update request to be used when the document does not exists. */ - public UpdateRequest upsert(String source, XContentType xContentType) { - safeUpsertRequest().source(source, xContentType); + public UpdateRequest upsert(String source, MediaType mediaType) { + safeUpsertRequest().source(source, mediaType); return this; } /** * Sets the doc source of the update request to be used when the document does not exists. */ - public UpdateRequest upsert(byte[] source, XContentType xContentType) { - safeUpsertRequest().source(source, xContentType); + public UpdateRequest upsert(byte[] source, MediaType mediaType) { + safeUpsertRequest().source(source, mediaType); return this; } /** * Sets the doc source of the update request to be used when the document does not exists. */ - public UpdateRequest upsert(byte[] source, int offset, int length, XContentType xContentType) { - safeUpsertRequest().source(source, offset, length, xContentType); + public UpdateRequest upsert(byte[] source, int offset, int length, MediaType mediaType) { + safeUpsertRequest().source(source, offset, length, mediaType); return this; } @@ -805,8 +813,8 @@ public UpdateRequest upsert(Object... source) { * Sets the doc source of the update request to be used when the document does not exists. The doc * includes field and value pairs. */ - public UpdateRequest upsert(XContentType xContentType, Object... source) { - safeUpsertRequest().source(xContentType, source); + public UpdateRequest upsert(MediaType mediaType, Object... source) { + safeUpsertRequest().source(mediaType, source); return this; } @@ -950,13 +958,13 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field("doc_as_upsert", docAsUpsert); } if (doc != null) { - XContentType xContentType = doc.getContentType(); + MediaType mediaType = doc.getContentType(); try ( XContentParser parser = XContentHelper.createParser( NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, doc.source(), - xContentType + mediaType ) ) { builder.field("doc"); @@ -973,13 +981,13 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field("script", script); } if (upsertRequest != null) { - XContentType xContentType = upsertRequest.getContentType(); + MediaType mediaType = upsertRequest.getContentType(); try ( XContentParser parser = XContentHelper.createParser( NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, upsertRequest.source(), - xContentType + mediaType ) ) { builder.field("upsert"); diff --git a/server/src/main/java/org/opensearch/action/update/UpdateRequestBuilder.java b/server/src/main/java/org/opensearch/action/update/UpdateRequestBuilder.java index 73e470bf8ba69..c97d0b4f5d13d 100644 --- a/server/src/main/java/org/opensearch/action/update/UpdateRequestBuilder.java +++ b/server/src/main/java/org/opensearch/action/update/UpdateRequestBuilder.java @@ -39,13 +39,18 @@ import org.opensearch.action.support.single.instance.InstanceShardOperationRequestBuilder; import org.opensearch.client.OpenSearchClient; import org.opensearch.common.Nullable; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.VersionType; import org.opensearch.script.Script; import java.util.Map; +/** + * Transport request builder for updating an index + * + * @opensearch.internal + */ public class UpdateRequestBuilder extends InstanceShardOperationRequestBuilder implements WriteRequestBuilder { @@ -225,7 +230,7 @@ public UpdateRequestBuilder setDoc(Map source) { /** * Sets the doc to use for updates when a script is not specified. */ - public UpdateRequestBuilder setDoc(Map source, XContentType contentType) { + public UpdateRequestBuilder setDoc(Map source, MediaType contentType) { request.doc(source, contentType); return this; } @@ -233,24 +238,24 @@ public UpdateRequestBuilder setDoc(Map source, XContentType cont /** * Sets the doc to use for updates when a script is not specified. */ - public UpdateRequestBuilder setDoc(String source, XContentType xContentType) { - request.doc(source, xContentType); + public UpdateRequestBuilder setDoc(String source, MediaType mediaType) { + request.doc(source, mediaType); return this; } /** * Sets the doc to use for updates when a script is not specified. */ - public UpdateRequestBuilder setDoc(byte[] source, XContentType xContentType) { - request.doc(source, xContentType); + public UpdateRequestBuilder setDoc(byte[] source, MediaType mediaType) { + request.doc(source, mediaType); return this; } /** * Sets the doc to use for updates when a script is not specified. */ - public UpdateRequestBuilder setDoc(byte[] source, int offset, int length, XContentType xContentType) { - request.doc(source, offset, length, xContentType); + public UpdateRequestBuilder setDoc(byte[] source, int offset, int length, MediaType mediaType) { + request.doc(source, offset, length, mediaType); return this; } @@ -267,8 +272,8 @@ public UpdateRequestBuilder setDoc(Object... source) { * Sets the doc to use for updates when a script is not specified, the doc provided * is a field and value pairs. */ - public UpdateRequestBuilder setDoc(XContentType xContentType, Object... source) { - request.doc(xContentType, source); + public UpdateRequestBuilder setDoc(MediaType mediaType, Object... source) { + request.doc(mediaType, source); return this; } @@ -300,32 +305,32 @@ public UpdateRequestBuilder setUpsert(Map source) { /** * Sets the doc source of the update request to be used when the document does not exists. */ - public UpdateRequestBuilder setUpsert(Map source, XContentType contentType) { - request.upsert(source, contentType); + public UpdateRequestBuilder setUpsert(Map source, MediaType mediaType) { + request.upsert(source, mediaType); return this; } /** * Sets the doc source of the update request to be used when the document does not exists. */ - public UpdateRequestBuilder setUpsert(String source, XContentType xContentType) { - request.upsert(source, xContentType); + public UpdateRequestBuilder setUpsert(String source, MediaType mediaType) { + request.upsert(source, mediaType); return this; } /** * Sets the doc source of the update request to be used when the document does not exists. */ - public UpdateRequestBuilder setUpsert(byte[] source, XContentType xContentType) { - request.upsert(source, xContentType); + public UpdateRequestBuilder setUpsert(byte[] source, MediaType mediaType) { + request.upsert(source, mediaType); return this; } /** * Sets the doc source of the update request to be used when the document does not exists. */ - public UpdateRequestBuilder setUpsert(byte[] source, int offset, int length, XContentType xContentType) { - request.upsert(source, offset, length, xContentType); + public UpdateRequestBuilder setUpsert(byte[] source, int offset, int length, MediaType mediaType) { + request.upsert(source, offset, length, mediaType); return this; } @@ -342,8 +347,8 @@ public UpdateRequestBuilder setUpsert(Object... source) { * Sets the doc source of the update request to be used when the document does not exists. The doc * includes field and value pairs. */ - public UpdateRequestBuilder setUpsert(XContentType xContentType, Object... source) { - request.upsert(xContentType, source); + public UpdateRequestBuilder setUpsert(MediaType mediaType, Object... source) { + request.upsert(mediaType, source); return this; } diff --git a/server/src/main/java/org/opensearch/action/update/UpdateResponse.java b/server/src/main/java/org/opensearch/action/update/UpdateResponse.java index 2c6efaf3c5f6b..d9f6ceacb0f3b 100644 --- a/server/src/main/java/org/opensearch/action/update/UpdateResponse.java +++ b/server/src/main/java/org/opensearch/action/update/UpdateResponse.java @@ -33,18 +33,23 @@ package org.opensearch.action.update; import org.opensearch.action.DocWriteResponse; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.get.GetResult; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; import java.io.IOException; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; +/** + * Transport response for updating an index + * + * @opensearch.internal + */ public class UpdateResponse extends DocWriteResponse { private static final String GET = "get"; @@ -167,6 +172,8 @@ public static void parseXContentFields(XContentParser parser, Builder context) t * Builder class for {@link UpdateResponse}. This builder is usually used during xcontent parsing to * temporarily store the parsed values, then the {@link DocWriteResponse.Builder#build()} method is called to * instantiate the {@link UpdateResponse}. + * + * @opensearch.internal */ public static class Builder extends DocWriteResponse.Builder { diff --git a/server/src/main/java/org/opensearch/action/update/package-info.java b/server/src/main/java/org/opensearch/action/update/package-info.java new file mode 100644 index 0000000000000..a8c8720c9ac10 --- /dev/null +++ b/server/src/main/java/org/opensearch/action/update/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Update Action transport handlers. */ +package org.opensearch.action.update; diff --git a/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java b/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java index 30a883454ba8c..827674c29b9e4 100644 --- a/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java +++ b/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java @@ -48,16 +48,15 @@ import org.opensearch.common.PidFile; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.inject.CreationException; -import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.logging.LogConfigurator; import org.opensearch.common.logging.Loggers; import org.opensearch.common.network.IfConfig; import org.opensearch.common.settings.KeyStoreWrapper; import org.opensearch.common.settings.SecureSettings; -import org.opensearch.common.settings.SecureString; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.BoundTransportAddress; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.common.settings.SecureString; +import org.opensearch.core.common.transport.BoundTransportAddress; import org.opensearch.env.Environment; import org.opensearch.monitor.jvm.JvmInfo; import org.opensearch.monitor.os.OsProbe; @@ -78,12 +77,13 @@ import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Internal startup code. + * + * @opensearch.internal */ final class Bootstrap { @@ -374,14 +374,6 @@ static void init(final boolean foreground, final Path pidFile, final boolean qui } catch (IOException e) { throw new BootstrapException(e); } - if (JavaVersion.current().compareTo(JavaVersion.parse("11")) < 0) { - final String message = String.format( - Locale.ROOT, - "future versions of OpenSearch will require Java 11; " + "your Java version from [%s] does not meet this requirement", - System.getProperty("java.home") - ); - DeprecationLogger.getLogger(Bootstrap.class).deprecate("java_version_11_required", message); - } if (environment.pidFile() != null) { try { PidFile.create(environment.pidFile(), true); diff --git a/server/src/main/java/org/opensearch/bootstrap/BootstrapCheck.java b/server/src/main/java/org/opensearch/bootstrap/BootstrapCheck.java index 230b1bd05e579..a695486bd084c 100644 --- a/server/src/main/java/org/opensearch/bootstrap/BootstrapCheck.java +++ b/server/src/main/java/org/opensearch/bootstrap/BootstrapCheck.java @@ -32,16 +32,22 @@ package org.opensearch.bootstrap; +import org.opensearch.common.annotation.PublicApi; + import java.util.Objects; /** * Encapsulates a bootstrap check. + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public interface BootstrapCheck { /** * Encapsulate the result of a bootstrap check. */ + @PublicApi(since = "1.0.0") final class BootstrapCheckResult { private final String message; diff --git a/server/src/main/java/org/opensearch/bootstrap/BootstrapChecks.java b/server/src/main/java/org/opensearch/bootstrap/BootstrapChecks.java index 79019a73c69e3..f9661e71d60e6 100644 --- a/server/src/main/java/org/opensearch/bootstrap/BootstrapChecks.java +++ b/server/src/main/java/org/opensearch/bootstrap/BootstrapChecks.java @@ -36,16 +36,20 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.lucene.util.Constants; +import org.opensearch.bootstrap.jvm.DenyJvmVersionsParser; import org.opensearch.cluster.coordination.ClusterBootstrapService; +import org.opensearch.cluster.node.DiscoveryNodeRole; import org.opensearch.common.SuppressForbidden; import org.opensearch.common.io.PathUtils; import org.opensearch.common.settings.Setting; -import org.opensearch.common.transport.BoundTransportAddress; -import org.opensearch.common.transport.TransportAddress; +import org.opensearch.core.common.transport.BoundTransportAddress; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.discovery.DiscoveryModule; +import org.opensearch.env.Environment; import org.opensearch.index.IndexModule; import org.opensearch.monitor.jvm.JvmInfo; import org.opensearch.monitor.process.ProcessProbe; +import org.opensearch.node.NodeRoleSettings; import org.opensearch.node.NodeValidationException; import java.io.BufferedReader; @@ -59,8 +63,6 @@ import java.util.List; import java.util.Locale; import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -73,6 +75,8 @@ * We enforce bootstrap checks once a node has the transport protocol bound to a non-loopback interface or if the system property {@code * opensearch.enforce.bootstrap.checks} is set to {@true}. In this case we assume the node is running in production and * all bootstrap checks must pass. + * + * @opensearch.internal */ final class BootstrapChecks { @@ -224,12 +228,33 @@ static List checks() { checks.add(new OnErrorCheck()); checks.add(new OnOutOfMemoryErrorCheck()); checks.add(new EarlyAccessCheck()); - checks.add(new G1GCCheck()); + checks.add(new JavaVersionCheck()); checks.add(new AllPermissionCheck()); checks.add(new DiscoveryConfiguredCheck()); + checks.add(new MultipleDataPathCheck()); return Collections.unmodifiableList(checks); } + static class JavaVersionCheck implements BootstrapCheck { + @Override + public BootstrapCheckResult check(BootstrapContext context) { + return DenyJvmVersionsParser.getDeniedJvmVersions() + .stream() + .filter(p -> p.test(getVersion())) + .findAny() + .map( + p -> BootstrapCheckResult.failure( + String.format(Locale.ROOT, "The current JVM version %s is not recommended for use: %s", getVersion(), p.getReason()) + ) + ) + .orElseGet(() -> BootstrapCheckResult.success()); + } + + Runtime.Version getVersion() { + return Runtime.version(); + } + } + static class HeapSizeCheck implements BootstrapCheck { @Override @@ -683,60 +708,6 @@ String javaVersion() { } - /** - * Bootstrap check for versions of HotSpot that are known to have issues that can lead to index corruption when G1GC is enabled. - */ - static class G1GCCheck implements BootstrapCheck { - - @Override - public BootstrapCheckResult check(BootstrapContext context) { - if ("Oracle Corporation".equals(jvmVendor()) && isJava8() && isG1GCEnabled()) { - final String jvmVersion = jvmVersion(); - // HotSpot versions on Java 8 match this regular expression; note that this changes with Java 9 after JEP-223 - final Pattern pattern = Pattern.compile("(\\d+)\\.(\\d+)-b\\d+"); - final Matcher matcher = pattern.matcher(jvmVersion); - final boolean matches = matcher.matches(); - assert matches : jvmVersion; - final int major = Integer.parseInt(matcher.group(1)); - final int update = Integer.parseInt(matcher.group(2)); - // HotSpot versions for Java 8 have major version 25, the bad versions are all versions prior to update 40 - if (major == 25 && update < 40) { - final String message = String.format( - Locale.ROOT, - "JVM version [%s] can cause data corruption when used with G1GC; upgrade to at least Java 8u40", - jvmVersion - ); - return BootstrapCheckResult.failure(message); - } - } - return BootstrapCheckResult.success(); - } - - // visible for testing - String jvmVendor() { - return Constants.JVM_VENDOR; - } - - // visible for testing - boolean isG1GCEnabled() { - assert "Oracle Corporation".equals(jvmVendor()); - return JvmInfo.jvmInfo().useG1GC().equals("true"); - } - - // visible for testing - String jvmVersion() { - assert "Oracle Corporation".equals(jvmVendor()); - return Constants.JVM_VERSION; - } - - // visible for testing - boolean isJava8() { - assert "Oracle Corporation".equals(jvmVendor()); - return JavaVersion.current().equals(JavaVersion.parse("1.8")); - } - - } - static class AllPermissionCheck implements BootstrapCheck { @Override @@ -784,4 +755,25 @@ public BootstrapCheckResult check(BootstrapContext context) { ); } } + + /** + * Bootstrap check that if a search node contains multiple data paths + */ + static class MultipleDataPathCheck implements BootstrapCheck { + + @Override + public BootstrapCheckResult check(BootstrapContext context) { + if (NodeRoleSettings.NODE_ROLES_SETTING.get(context.settings()).contains(DiscoveryNodeRole.SEARCH_ROLE) + && Environment.PATH_DATA_SETTING.get(context.settings()).size() > 1) { + return BootstrapCheckResult.failure("Multiple data paths are not allowed for search nodes"); + } + return BootstrapCheckResult.success(); + } + + @Override + public final boolean alwaysEnforce() { + return true; + } + + } } diff --git a/server/src/main/java/org/opensearch/bootstrap/BootstrapContext.java b/server/src/main/java/org/opensearch/bootstrap/BootstrapContext.java index b07a544e8521e..a7ffd701d07f0 100644 --- a/server/src/main/java/org/opensearch/bootstrap/BootstrapContext.java +++ b/server/src/main/java/org/opensearch/bootstrap/BootstrapContext.java @@ -32,12 +32,16 @@ package org.opensearch.bootstrap; import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.settings.Settings; import org.opensearch.env.Environment; /** * Context that is passed to every bootstrap check to make decisions on. + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public class BootstrapContext { /** * The node's environment diff --git a/server/src/main/java/org/opensearch/bootstrap/BootstrapException.java b/server/src/main/java/org/opensearch/bootstrap/BootstrapException.java index 8daa29aa3578b..77b66553cb46d 100644 --- a/server/src/main/java/org/opensearch/bootstrap/BootstrapException.java +++ b/server/src/main/java/org/opensearch/bootstrap/BootstrapException.java @@ -41,6 +41,8 @@ * these checked exceptions so that * {@link Bootstrap#init(boolean, Path, boolean, org.opensearch.env.Environment)} * does not have to declare all of these checked exceptions. + * + * @opensearch.internal */ class BootstrapException extends Exception { diff --git a/server/src/main/java/org/opensearch/bootstrap/BootstrapInfo.java b/server/src/main/java/org/opensearch/bootstrap/BootstrapInfo.java index d45d8ddab9c2c..0aa965ce46096 100644 --- a/server/src/main/java/org/opensearch/bootstrap/BootstrapInfo.java +++ b/server/src/main/java/org/opensearch/bootstrap/BootstrapInfo.java @@ -39,6 +39,8 @@ /** * Exposes system startup information + * + * @opensearch.internal */ @SuppressForbidden(reason = "exposes read-only view of system properties") public final class BootstrapInfo { diff --git a/server/src/main/java/org/opensearch/bootstrap/BootstrapSettings.java b/server/src/main/java/org/opensearch/bootstrap/BootstrapSettings.java index 208030b5d6d12..911bc92c433f1 100644 --- a/server/src/main/java/org/opensearch/bootstrap/BootstrapSettings.java +++ b/server/src/main/java/org/opensearch/bootstrap/BootstrapSettings.java @@ -35,6 +35,11 @@ import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; +/** + * Settings used for bootstrapping OpenSearch + * + * @opensearch.internal + */ public final class BootstrapSettings { private BootstrapSettings() {} diff --git a/server/src/main/java/org/opensearch/bootstrap/ConsoleCtrlHandler.java b/server/src/main/java/org/opensearch/bootstrap/ConsoleCtrlHandler.java index cbc1b6e761923..ae9b6ea7ce761 100644 --- a/server/src/main/java/org/opensearch/bootstrap/ConsoleCtrlHandler.java +++ b/server/src/main/java/org/opensearch/bootstrap/ConsoleCtrlHandler.java @@ -32,6 +32,11 @@ package org.opensearch.bootstrap; +/** + * Handler for ctrl events on the console + * + * @opensearch.internal + */ public interface ConsoleCtrlHandler { int CTRL_CLOSE_EVENT = 2; diff --git a/server/src/main/java/org/opensearch/bootstrap/FilePermissionUtils.java b/server/src/main/java/org/opensearch/bootstrap/FilePermissionUtils.java index b37aef5f9738d..18ff013b1e8cb 100644 --- a/server/src/main/java/org/opensearch/bootstrap/FilePermissionUtils.java +++ b/server/src/main/java/org/opensearch/bootstrap/FilePermissionUtils.java @@ -40,6 +40,11 @@ import java.nio.file.Path; import java.security.Permissions; +/** + * Utility for File Permissions during bootstrap + * + * @opensearch.internal + */ public class FilePermissionUtils { /** no instantiation */ diff --git a/server/src/main/java/org/opensearch/bootstrap/JNACLibrary.java b/server/src/main/java/org/opensearch/bootstrap/JNACLibrary.java index 7d3ce7728a9dc..aca2cb9656025 100644 --- a/server/src/main/java/org/opensearch/bootstrap/JNACLibrary.java +++ b/server/src/main/java/org/opensearch/bootstrap/JNACLibrary.java @@ -45,6 +45,8 @@ /** * java mapping to some libc functions + * + * @opensearch.internal */ final class JNACLibrary { diff --git a/server/src/main/java/org/opensearch/bootstrap/JNAKernel32Library.java b/server/src/main/java/org/opensearch/bootstrap/JNAKernel32Library.java index a0cd656f5fc01..8e556df4b2f9b 100644 --- a/server/src/main/java/org/opensearch/bootstrap/JNAKernel32Library.java +++ b/server/src/main/java/org/opensearch/bootstrap/JNAKernel32Library.java @@ -51,6 +51,8 @@ /** * Library for Windows/Kernel32 + * + * @opensearch.internal */ final class JNAKernel32Library { diff --git a/server/src/main/java/org/opensearch/bootstrap/JNANatives.java b/server/src/main/java/org/opensearch/bootstrap/JNANatives.java index 1e3c6c3af0386..033596033b0fd 100644 --- a/server/src/main/java/org/opensearch/bootstrap/JNANatives.java +++ b/server/src/main/java/org/opensearch/bootstrap/JNANatives.java @@ -48,6 +48,8 @@ /** * This class performs the actual work with JNA and library bindings to call native methods. It should only be used after * we are sure that the JNA classes are available to the JVM + * + * @opensearch.internal */ class JNANatives { diff --git a/server/src/main/java/org/opensearch/bootstrap/Natives.java b/server/src/main/java/org/opensearch/bootstrap/Natives.java index 65025e41958ce..aa5e29fbb2591 100644 --- a/server/src/main/java/org/opensearch/bootstrap/Natives.java +++ b/server/src/main/java/org/opensearch/bootstrap/Natives.java @@ -40,6 +40,8 @@ /** * The Natives class is a wrapper class that checks if the classes necessary for calling native methods are available on * startup. If they are not available, this class will avoid calling code that loads these classes. + * + * @opensearch.internal */ final class Natives { /** no instantiation */ diff --git a/server/src/main/java/org/opensearch/bootstrap/OpenSearch.java b/server/src/main/java/org/opensearch/bootstrap/OpenSearch.java index 4395ae98c9ba6..8df1d6c6df3da 100644 --- a/server/src/main/java/org/opensearch/bootstrap/OpenSearch.java +++ b/server/src/main/java/org/opensearch/bootstrap/OpenSearch.java @@ -55,6 +55,8 @@ /** * This class starts opensearch. + * + * @opensearch.internal */ class OpenSearch extends EnvironmentAwareCommand { diff --git a/server/src/main/java/org/opensearch/bootstrap/OpenSearchPolicy.java b/server/src/main/java/org/opensearch/bootstrap/OpenSearchPolicy.java index d31f11f153ae8..14435db64274c 100644 --- a/server/src/main/java/org/opensearch/bootstrap/OpenSearchPolicy.java +++ b/server/src/main/java/org/opensearch/bootstrap/OpenSearchPolicy.java @@ -48,7 +48,11 @@ import java.util.Map; import java.util.function.Predicate; -/** custom policy for union of static and dynamic permissions */ +/** + * custom policy for union of static and dynamic permissions + * + * @opensearch.internal + **/ final class OpenSearchPolicy extends Policy { /** template policy file, the one used in tests */ diff --git a/server/src/main/java/org/opensearch/bootstrap/OpenSearchUncaughtExceptionHandler.java b/server/src/main/java/org/opensearch/bootstrap/OpenSearchUncaughtExceptionHandler.java index 4834f4cb211fe..2b28260097ce1 100644 --- a/server/src/main/java/org/opensearch/bootstrap/OpenSearchUncaughtExceptionHandler.java +++ b/server/src/main/java/org/opensearch/bootstrap/OpenSearchUncaughtExceptionHandler.java @@ -41,6 +41,11 @@ import java.security.AccessController; import java.security.PrivilegedAction; +/** + * UncaughtException Handler used during bootstrapping + * + * @opensearch.internal + */ class OpenSearchUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final Logger logger = LogManager.getLogger(OpenSearchUncaughtExceptionHandler.class); diff --git a/server/src/main/java/org/opensearch/bootstrap/Security.java b/server/src/main/java/org/opensearch/bootstrap/Security.java index b3f4339f3e386..39614a786df82 100644 --- a/server/src/main/java/org/opensearch/bootstrap/Security.java +++ b/server/src/main/java/org/opensearch/bootstrap/Security.java @@ -116,6 +116,8 @@ * * See * Troubleshooting Security for information. + * + * @opensearch.internal */ final class Security { /** no instantiation */ diff --git a/server/src/main/java/org/opensearch/bootstrap/Spawner.java b/server/src/main/java/org/opensearch/bootstrap/Spawner.java index c5e7171946790..47d4ae4f05a50 100644 --- a/server/src/main/java/org/opensearch/bootstrap/Spawner.java +++ b/server/src/main/java/org/opensearch/bootstrap/Spawner.java @@ -33,7 +33,7 @@ package org.opensearch.bootstrap; import org.apache.lucene.util.Constants; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import org.opensearch.env.Environment; import org.opensearch.plugins.Platforms; import org.opensearch.plugins.PluginInfo; @@ -51,6 +51,8 @@ /** * Spawns native module controller processes if present. Will only work prior to a system call filter being installed. + * + * @opensearch.internal */ final class Spawner implements Closeable { diff --git a/server/src/main/java/org/opensearch/bootstrap/StartupException.java b/server/src/main/java/org/opensearch/bootstrap/StartupException.java index 735e8cfd287af..9ad89ab518006 100644 --- a/server/src/main/java/org/opensearch/bootstrap/StartupException.java +++ b/server/src/main/java/org/opensearch/bootstrap/StartupException.java @@ -44,6 +44,8 @@ * "reasonably". This means limits on stacktrace frames and * cleanup for guice, and some guidance about consulting full * logs for the whole exception. + * + * @opensearch.internal */ // TODO: remove this when guice is removed, and exceptions are cleaned up // this is horrible, but its what we must do diff --git a/server/src/main/java/org/opensearch/bootstrap/SystemCallFilter.java b/server/src/main/java/org/opensearch/bootstrap/SystemCallFilter.java index 8e179de9c28df..6347c37a7c7a5 100644 --- a/server/src/main/java/org/opensearch/bootstrap/SystemCallFilter.java +++ b/server/src/main/java/org/opensearch/bootstrap/SystemCallFilter.java @@ -43,7 +43,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.Constants; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.common.util.io.IOUtils; import java.io.IOException; import java.nio.ByteBuffer; @@ -102,6 +102,8 @@ * https://reverse.put.as/wp-content/uploads/2011/06/The-Apple-Sandbox-BHDC2011-Paper.pdf * @see * https://docs.oracle.com/cd/E23824_01/html/821-1456/prbac-2.html + * + * @opensearch.internal */ // not an example of how to write code!!! final class SystemCallFilter { @@ -257,6 +259,8 @@ static class Arch { Map m = new HashMap<>(); m.put("amd64", new Arch(0xC000003E, 0x3FFFFFFF, 57, 58, 59, 322, 317)); m.put("aarch64", new Arch(0xC00000B7, 0xFFFFFFFF, 1079, 1071, 221, 281, 277)); + m.put("s390x", new Arch(0x80000016, 0xFFFFFFFF, 2, 190, 11, 354, 348)); + m.put("ppc64le", new Arch(0xC0000015, 0xFFFFFFFF, 2, 189, 11, 362, 358)); ARCHITECTURES = Collections.unmodifiableMap(m); } diff --git a/server/src/main/java/org/opensearch/bootstrap/jvm/DenyJvmVersionsParser.java b/server/src/main/java/org/opensearch/bootstrap/jvm/DenyJvmVersionsParser.java new file mode 100644 index 0000000000000..65b50344b8e3a --- /dev/null +++ b/server/src/main/java/org/opensearch/bootstrap/jvm/DenyJvmVersionsParser.java @@ -0,0 +1,176 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.bootstrap.jvm; + +import org.opensearch.common.Nullable; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.lang.Runtime.Version; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Parses the list of JVM versions which should be denied to run Opensearch engine with due to discovered + * issues or flaws. + * + * @opensearch.internal + */ +public class DenyJvmVersionsParser { + /** + * Provides the reason for the denial + * + * @opensearch.internal + */ + public interface VersionPredicate extends Predicate { + String getReason(); + } + + private static class SingleVersion implements VersionPredicate { + final private Version version; + final private String reason; + + public SingleVersion(Version version, String reason) { + this.version = version; + this.reason = reason; + } + + @Override + public boolean test(Version v) { + return version.compareTo(v) == 0; + } + + @Override + public String getReason() { + return reason; + } + } + + private static class VersionRange implements VersionPredicate { + private Version lower; + private boolean lowerIncluded; + private Version upper; + private boolean upperIncluded; + private String reason; + + public VersionRange(@Nullable Version lower, boolean lowerIncluded, @Nullable Version upper, boolean upperIncluded, String reason) { + this.lower = lower; + this.lowerIncluded = lowerIncluded; + this.upper = upper; + this.upperIncluded = upperIncluded; + this.reason = reason; + } + + @Override + public boolean test(Version v) { + if (lower != null) { + int compare = lower.compareTo(v); + if (compare > 0 || (compare == 0 && lowerIncluded != true)) { + return false; + } + } + + if (upper != null) { + int compare = upper.compareTo(v); + if (compare < 0 || (compare == 0 && upperIncluded != true)) { + return false; + } + } + + return true; + } + + @Override + public String getReason() { + return reason; + } + } + + public static Collection getDeniedJvmVersions() { + try ( + final InputStreamReader in = new InputStreamReader( + DenyJvmVersionsParser.class.getResourceAsStream("deny-jvm-versions.txt"), + StandardCharsets.UTF_8 + ) + ) { + try (final BufferedReader reader = new BufferedReader(in)) { + return reader.lines() + .map(String::trim) + // filter empty lines + .filter(line -> line.isEmpty() == false) + // filter out all comments + .filter(line -> line.startsWith("//") == false) + .map(DenyJvmVersionsParser::parse) + .collect(Collectors.toList()); + } + } catch (final IOException ex) { + throw new UncheckedIOException("Unable to read the list of denied JVM versions", ex); + } + } + + /** + * Parse individual line from the list of denied JVM versions. Some version and version range examples are: + *

      + *
    • 11.0.2: "... reason ..." - version 11.0.2
    • + *
    • [11.0.2, 11.0.14): "... reason ..." - versions 11.0.2.2 (included) to 11.0.14 (not included)
    • + *
    • [11.0.2, 11.0.14]: "... reason ..." - versions 11.0.2 to 11.0.14 (both included)
    • + *
    • [11.0.2,): "... reason ..." - versions 11.0.2 and higher
    • + *
    + * @param line line to parse + * @return version or version range predicate + */ + static VersionPredicate parse(String line) { + final String[] parts = Arrays.stream(line.split("[:]", 2)).map(String::trim).toArray(String[]::new); + + if (parts.length != 2) { + throw new IllegalArgumentException("Unable to parse JVM version or version range: " + line); + } + + final String versionOrRange = parts[0]; + final String reason = parts[1]; + + // dealing with version range here + if (versionOrRange.startsWith("[") == true || versionOrRange.startsWith("(") == true) { + if (versionOrRange.endsWith("]") == false && versionOrRange.endsWith(")") == false) { + throw new IllegalArgumentException("Unable to parse JVM version range: " + versionOrRange); + } + + final boolean lowerIncluded = versionOrRange.startsWith("["); + final boolean upperIncluded = versionOrRange.endsWith("]"); + + final String[] range = Arrays.stream(versionOrRange.substring(1, versionOrRange.length() - 1).split("[,]", 2)) + .map(String::trim) + .toArray(String[]::new); + + if (range.length != 2) { + throw new IllegalArgumentException("Unable to parse JVM version range: " + versionOrRange); + } + + Version lower = null; + if (range[0].isEmpty() == false && range[0].equals("*") == false) { + lower = Version.parse(range[0]); + } + + Version upper = null; + if (range[1].isEmpty() == false && range[1].equals("*") == false) { + upper = Version.parse(range[1]); + } + + return new VersionRange(lower, lowerIncluded, upper, upperIncluded, reason); + } else { + // this is just a single version + return new SingleVersion(Version.parse(versionOrRange), reason); + } + } +} diff --git a/server/src/main/java/org/opensearch/bootstrap/jvm/package-info.java b/server/src/main/java/org/opensearch/bootstrap/jvm/package-info.java new file mode 100644 index 0000000000000..e171e705a29f7 --- /dev/null +++ b/server/src/main/java/org/opensearch/bootstrap/jvm/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** jvm specific bootstrapping */ +package org.opensearch.bootstrap.jvm; diff --git a/server/src/main/java/org/opensearch/bootstrap/package-info.java b/server/src/main/java/org/opensearch/bootstrap/package-info.java new file mode 100644 index 0000000000000..a1761d8d38315 --- /dev/null +++ b/server/src/main/java/org/opensearch/bootstrap/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * The bootstrap module implementing OpenSearch bootstrap operations. + */ +package org.opensearch.bootstrap; diff --git a/server/src/main/java/org/opensearch/cli/CommandLoggingConfigurator.java b/server/src/main/java/org/opensearch/cli/CommandLoggingConfigurator.java index 1705e9ebdd4b9..8918725472160 100644 --- a/server/src/main/java/org/opensearch/cli/CommandLoggingConfigurator.java +++ b/server/src/main/java/org/opensearch/cli/CommandLoggingConfigurator.java @@ -39,6 +39,8 @@ /** * Holder class for method to configure logging without OpenSearch configuration files for use in CLI tools that will not read such * files. + * + * @opensearch.internal */ public final class CommandLoggingConfigurator { diff --git a/server/src/main/java/org/opensearch/cli/EnvironmentAwareCommand.java b/server/src/main/java/org/opensearch/cli/EnvironmentAwareCommand.java index e85af87213fd0..10c59ef673050 100644 --- a/server/src/main/java/org/opensearch/cli/EnvironmentAwareCommand.java +++ b/server/src/main/java/org/opensearch/cli/EnvironmentAwareCommand.java @@ -46,7 +46,11 @@ import java.util.Locale; import java.util.Map; -/** A cli command which requires an {@link org.opensearch.env.Environment} to use current paths and settings. */ +/** + * A cli command which requires an {@link org.opensearch.env.Environment} to use current paths and settings. + * + * @opensearch.internal + */ public abstract class EnvironmentAwareCommand extends Command { private final OptionSpec settingOption; diff --git a/server/src/main/java/org/opensearch/cli/KeyStoreAwareCommand.java b/server/src/main/java/org/opensearch/cli/KeyStoreAwareCommand.java index 404dacd504e8a..6cd266252b369 100644 --- a/server/src/main/java/org/opensearch/cli/KeyStoreAwareCommand.java +++ b/server/src/main/java/org/opensearch/cli/KeyStoreAwareCommand.java @@ -34,7 +34,7 @@ import joptsimple.OptionSet; import org.opensearch.common.settings.KeyStoreWrapper; -import org.opensearch.common.settings.SecureString; +import org.opensearch.core.common.settings.SecureString; import org.opensearch.env.Environment; import java.io.IOException; @@ -44,6 +44,8 @@ /** * An {@link org.opensearch.cli.EnvironmentAwareCommand} that needs to access the opensearch keystore, possibly * decrypting it if it is password protected. + * + * @opensearch.internal */ public abstract class KeyStoreAwareCommand extends EnvironmentAwareCommand { public KeyStoreAwareCommand(String description) { diff --git a/server/src/main/java/org/opensearch/cli/LoggingAwareCommand.java b/server/src/main/java/org/opensearch/cli/LoggingAwareCommand.java index 7aee5e3fa1f76..07a6b7d523a33 100644 --- a/server/src/main/java/org/opensearch/cli/LoggingAwareCommand.java +++ b/server/src/main/java/org/opensearch/cli/LoggingAwareCommand.java @@ -35,6 +35,8 @@ /** * A command that is aware of logging. This class should be preferred over the base {@link Command} class for any CLI tools that depend on * core OpenSearch as they could directly or indirectly touch classes that touch logging and as such logging needs to be configured. + * + * @opensearch.internal */ public abstract class LoggingAwareCommand extends Command { diff --git a/server/src/main/java/org/opensearch/cli/LoggingAwareMultiCommand.java b/server/src/main/java/org/opensearch/cli/LoggingAwareMultiCommand.java index 2354a4978aec9..8453ed05a12d3 100644 --- a/server/src/main/java/org/opensearch/cli/LoggingAwareMultiCommand.java +++ b/server/src/main/java/org/opensearch/cli/LoggingAwareMultiCommand.java @@ -36,6 +36,8 @@ * A multi-command that is aware of logging. This class should be preferred over the base {@link MultiCommand} class for any CLI tools that * depend on core OpenSearch as they could directly or indirectly touch classes that touch logging and as such logging needs to be * configured. + * + * @opensearch.internal */ public abstract class LoggingAwareMultiCommand extends MultiCommand { diff --git a/server/src/main/java/org/opensearch/cli/package-info.java b/server/src/main/java/org/opensearch/cli/package-info.java new file mode 100644 index 0000000000000..8e9f9f6360870 --- /dev/null +++ b/server/src/main/java/org/opensearch/cli/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * The command line interface module. + */ +package org.opensearch.cli; diff --git a/server/src/main/java/org/opensearch/client/AdminClient.java b/server/src/main/java/org/opensearch/client/AdminClient.java index 995466ba5746c..1a5a39be4241a 100644 --- a/server/src/main/java/org/opensearch/client/AdminClient.java +++ b/server/src/main/java/org/opensearch/client/AdminClient.java @@ -32,12 +32,16 @@ package org.opensearch.client; +import org.opensearch.common.annotation.PublicApi; + /** * Administrative actions/operations against the cluster or the indices. * - * * @see org.opensearch.client.Client#admin() + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public interface AdminClient { /** diff --git a/server/src/main/java/org/opensearch/client/Client.java b/server/src/main/java/org/opensearch/client/Client.java index bca68834ca3cf..f4ae383249f61 100644 --- a/server/src/main/java/org/opensearch/client/Client.java +++ b/server/src/main/java/org/opensearch/client/Client.java @@ -32,8 +32,8 @@ package org.opensearch.client; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; +import org.opensearch.action.admin.indices.segments.IndicesSegmentResponse; +import org.opensearch.action.admin.indices.segments.PitSegmentsRequest; import org.opensearch.action.bulk.BulkRequest; import org.opensearch.action.bulk.BulkRequestBuilder; import org.opensearch.action.bulk.BulkResponse; @@ -58,6 +58,12 @@ import org.opensearch.action.search.ClearScrollRequest; import org.opensearch.action.search.ClearScrollRequestBuilder; import org.opensearch.action.search.ClearScrollResponse; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.action.search.GetAllPitNodesRequest; +import org.opensearch.action.search.GetAllPitNodesResponse; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchRequestBuilder; import org.opensearch.action.search.MultiSearchResponse; @@ -76,10 +82,13 @@ import org.opensearch.action.update.UpdateRequestBuilder; import org.opensearch.action.update.UpdateResponse; import org.opensearch.common.Nullable; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; import java.util.Map; @@ -87,13 +96,16 @@ * A client provides a one stop interface for performing actions/operations against the cluster. *

    * All operations performed are asynchronous by nature. Each action/operation has two flavors, the first - * simply returns an {@link org.opensearch.action.ActionFuture}, while the second accepts an + * simply returns an {@link ActionFuture}, while the second accepts an * {@link ActionListener}. *

    * A client can be retrieved from a started {@link org.opensearch.node.Node}. * * @see org.opensearch.node.Node#client() + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public interface Client extends OpenSearchClient, Releasable { Setting CLIENT_TYPE_SETTING_S = new Setting<>("client.type", "node", (s) -> { @@ -323,6 +335,26 @@ public interface Client extends OpenSearchClient, Releasable { */ SearchScrollRequestBuilder prepareSearchScroll(String scrollId); + /** + * Create point in time for one or more indices + */ + void createPit(CreatePitRequest createPITRequest, ActionListener listener); + + /** + * Delete one or more point in time contexts + */ + void deletePits(DeletePitRequest deletePITRequest, ActionListener listener); + + /** + * Get all active point in time searches + */ + void getAllPits(GetAllPitNodesRequest getAllPitNodesRequest, ActionListener listener); + + /** + * Get information of segments of one or more PITs + */ + void pitSegments(PitSegmentsRequest pitSegmentsRequest, ActionListener listener); + /** * Performs multiple search requests. */ diff --git a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java index 1088b85ae271f..05f09c1a6e661 100644 --- a/server/src/main/java/org/opensearch/client/ClusterAdminClient.java +++ b/server/src/main/java/org/opensearch/client/ClusterAdminClient.java @@ -32,11 +32,18 @@ package org.opensearch.client; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequestBuilder; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequestBuilder; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; @@ -62,6 +69,11 @@ import org.opensearch.action.admin.cluster.node.usage.NodesUsageRequest; import org.opensearch.action.admin.cluster.node.usage.NodesUsageRequestBuilder; import org.opensearch.action.admin.cluster.node.usage.NodesUsageResponse; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreRequest; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreResponse; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsRequest; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsRequestBuilder; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsResponse; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryRequest; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryRequestBuilder; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryResponse; @@ -84,6 +96,15 @@ import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsRequest; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsRequestBuilder; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingRequestBuilder; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingRequestBuilder; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingRequestBuilder; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingResponse; import org.opensearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequest; import org.opensearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequestBuilder; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; @@ -130,16 +151,26 @@ import org.opensearch.action.ingest.SimulatePipelineRequest; import org.opensearch.action.ingest.SimulatePipelineRequestBuilder; import org.opensearch.action.ingest.SimulatePipelineResponse; +import org.opensearch.action.search.DeleteSearchPipelineRequest; +import org.opensearch.action.search.GetSearchPipelineRequest; +import org.opensearch.action.search.GetSearchPipelineResponse; +import org.opensearch.action.search.PutSearchPipelineRequest; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.tasks.TaskId; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaType; /** * Administrative actions/operations against indices. * * @see AdminClient#cluster() + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public interface ClusterAdminClient extends OpenSearchClient { /** @@ -289,6 +320,10 @@ public interface ClusterAdminClient extends OpenSearchClient { */ NodesStatsRequestBuilder prepareNodesStats(String... nodesIds); + void remoteStoreStats(RemoteStoreStatsRequest request, ActionListener listener); + + RemoteStoreStatsRequestBuilder prepareRemoteStoreStats(String index, String shardId); + /** * Returns top N hot-threads samples per node. The hot-threads are only * sampled for the node ids specified in the request. Nodes usage of the @@ -575,6 +610,11 @@ public interface ClusterAdminClient extends OpenSearchClient { */ void restoreSnapshot(RestoreSnapshotRequest request, ActionListener listener); + /** + * Restores from remote store. + */ + void restoreRemoteStore(RestoreRemoteStoreRequest request, ActionListener listener); + /** * Restores a snapshot. */ @@ -631,7 +671,7 @@ public interface ClusterAdminClient extends OpenSearchClient { /** * Stores an ingest pipeline */ - PutPipelineRequestBuilder preparePutPipeline(String id, BytesReference source, XContentType xContentType); + PutPipelineRequestBuilder preparePutPipeline(String id, BytesReference source, MediaType mediaType); /** * Deletes a stored ingest pipeline @@ -681,7 +721,7 @@ public interface ClusterAdminClient extends OpenSearchClient { /** * Simulates an ingest pipeline */ - SimulatePipelineRequestBuilder prepareSimulatePipeline(BytesReference source, XContentType xContentType); + SimulatePipelineRequestBuilder prepareSimulatePipeline(BytesReference source, MediaType mediaType); /** * Explain the allocation of a shard @@ -782,4 +822,124 @@ public interface ClusterAdminClient extends OpenSearchClient { * Delete specified dangling indices. */ ActionFuture deleteDanglingIndex(DeleteDanglingIndexRequest request); + + /** + * Updates weights for weighted round-robin search routing policy. + */ + ActionFuture putWeightedRouting(ClusterPutWeightedRoutingRequest request); + + /** + * Updates weights for weighted round-robin search routing policy. + */ + void putWeightedRouting(ClusterPutWeightedRoutingRequest request, ActionListener listener); + + /** + * Updates weights for weighted round-robin search routing policy. + */ + ClusterPutWeightedRoutingRequestBuilder prepareWeightedRouting(); + + /** + * Gets weights for weighted round-robin search routing policy. + */ + ActionFuture getWeightedRouting(ClusterGetWeightedRoutingRequest request); + + /** + * Gets weights for weighted round-robin search routing policy. + */ + void getWeightedRouting(ClusterGetWeightedRoutingRequest request, ActionListener listener); + + /** + * Gets weights for weighted round-robin search routing policy. + */ + ClusterGetWeightedRoutingRequestBuilder prepareGetWeightedRouting(); + + /** + * Deletes weights for weighted round-robin search routing policy. + */ + ActionFuture deleteWeightedRouting(ClusterDeleteWeightedRoutingRequest request); + + /** + * Deletes weights for weighted round-robin search routing policy. + */ + void deleteWeightedRouting(ClusterDeleteWeightedRoutingRequest request, ActionListener listener); + + /** + * Deletes weights for weighted round-robin search routing policy. + */ + ClusterDeleteWeightedRoutingRequestBuilder prepareDeleteWeightedRouting(); + + /** + * Decommission awareness attribute + */ + ActionFuture decommission(DecommissionRequest request); + + /** + * Decommission awareness attribute + */ + void decommission(DecommissionRequest request, ActionListener listener); + + /** + * Decommission awareness attribute + */ + DecommissionRequestBuilder prepareDecommission(DecommissionRequest request); + + /** + * Get Decommissioned attribute + */ + ActionFuture getDecommissionState(GetDecommissionStateRequest request); + + /** + * Get Decommissioned attribute + */ + void getDecommissionState(GetDecommissionStateRequest request, ActionListener listener); + + /** + * Get Decommissioned attribute + */ + GetDecommissionStateRequestBuilder prepareGetDecommissionState(); + + /** + * Deletes the decommission metadata. + */ + ActionFuture deleteDecommissionState(DeleteDecommissionStateRequest request); + + /** + * Deletes the decommission metadata. + */ + void deleteDecommissionState(DeleteDecommissionStateRequest request, ActionListener listener); + + /** + * Deletes the decommission metadata. + */ + DeleteDecommissionStateRequestBuilder prepareDeleteDecommissionRequest(); + + /** + * Stores a search pipeline + */ + void putSearchPipeline(PutSearchPipelineRequest request, ActionListener listener); + + /** + * Stores a search pipeline + */ + ActionFuture putSearchPipeline(PutSearchPipelineRequest request); + + /** + * Returns a stored search pipeline + */ + void getSearchPipeline(GetSearchPipelineRequest request, ActionListener listener); + + /** + * Returns a stored search pipeline + */ + ActionFuture getSearchPipeline(GetSearchPipelineRequest request); + + /** + * Deletes a stored search pipeline + */ + void deleteSearchPipeline(DeleteSearchPipelineRequest request, ActionListener listener); + + /** + * Deletes a stored search pipeline + */ + ActionFuture deleteSearchPipeline(DeleteSearchPipelineRequest request); } diff --git a/server/src/main/java/org/opensearch/client/FilterClient.java b/server/src/main/java/org/opensearch/client/FilterClient.java index 4d7bf2ed30b00..2ce7146a794cb 100644 --- a/server/src/main/java/org/opensearch/client/FilterClient.java +++ b/server/src/main/java/org/opensearch/client/FilterClient.java @@ -31,18 +31,20 @@ package org.opensearch.client; -import org.opensearch.action.ActionType; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; import org.opensearch.client.support.AbstractClient; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; import org.opensearch.threadpool.ThreadPool; /** * A {@link Client} that contains another {@link Client} which it * uses as its basic source, possibly transforming the requests / responses along the * way or providing additional functionality. + * + * @opensearch.internal */ public abstract class FilterClient extends AbstractClient { diff --git a/server/src/main/java/org/opensearch/client/IndicesAdminClient.java b/server/src/main/java/org/opensearch/client/IndicesAdminClient.java index 7f51b8af19e4b..20dab1caa36c4 100644 --- a/server/src/main/java/org/opensearch/client/IndicesAdminClient.java +++ b/server/src/main/java/org/opensearch/client/IndicesAdminClient.java @@ -32,12 +32,8 @@ package org.opensearch.client; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequestBuilder; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistRequestBuilder; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistResponse; import org.opensearch.action.admin.indices.alias.get.GetAliasesRequest; import org.opensearch.action.admin.indices.alias.get.GetAliasesRequestBuilder; import org.opensearch.action.admin.indices.alias.get.GetAliasesResponse; @@ -60,9 +56,6 @@ import org.opensearch.action.admin.indices.exists.indices.IndicesExistsRequest; import org.opensearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder; import org.opensearch.action.admin.indices.exists.indices.IndicesExistsResponse; -import org.opensearch.action.admin.indices.exists.types.TypesExistsRequest; -import org.opensearch.action.admin.indices.exists.types.TypesExistsRequestBuilder; -import org.opensearch.action.admin.indices.exists.types.TypesExistsResponse; import org.opensearch.action.admin.indices.flush.FlushRequest; import org.opensearch.action.admin.indices.flush.FlushRequestBuilder; import org.opensearch.action.admin.indices.flush.FlushResponse; @@ -92,6 +85,9 @@ import org.opensearch.action.admin.indices.refresh.RefreshRequest; import org.opensearch.action.admin.indices.refresh.RefreshRequestBuilder; import org.opensearch.action.admin.indices.refresh.RefreshResponse; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsRequest; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsRequestBuilder; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsResponse; import org.opensearch.action.admin.indices.resolve.ResolveIndexAction; import org.opensearch.action.admin.indices.rollover.RolloverRequest; import org.opensearch.action.admin.indices.rollover.RolloverRequestBuilder; @@ -132,12 +128,18 @@ import org.opensearch.action.support.master.AcknowledgedResponse; import org.opensearch.cluster.metadata.IndexMetadata.APIBlock; import org.opensearch.common.Nullable; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.common.annotation.PublicApi; +import org.opensearch.core.action.ActionListener; /** * Administrative actions/operations against indices. * * @see AdminClient#indices() + * + * @opensearch.api */ +@PublicApi(since = "1.0.0") public interface IndicesAdminClient extends OpenSearchClient { /** @@ -163,34 +165,6 @@ public interface IndicesAdminClient extends OpenSearchClient { */ IndicesExistsRequestBuilder prepareExists(String... indices); - /** - * Types exists. - * - * @deprecated Types are deprecated and are in the process of being removed. - * @param request The types exists request - * @return The result future - */ - @Deprecated - ActionFuture typesExists(TypesExistsRequest request); - - /** - * Types exists. - * - * @deprecated Types are deprecated and are in the process of being removed. - * @param request The types exists - * @param listener A listener to be notified with a result - */ - @Deprecated - void typesExists(TypesExistsRequest request, ActionListener listener); - - /** - * Types exists. - * - * @deprecated Types are deprecated and are in the process of being removed. - */ - @Deprecated - TypesExistsRequestBuilder prepareTypesExists(String... index); - /** * Indices stats. */ @@ -216,11 +190,26 @@ public interface IndicesAdminClient extends OpenSearchClient { */ void recoveries(RecoveryRequest request, ActionListener listener); + /** + *Indices segment replication + */ + ActionFuture segmentReplicationStats(SegmentReplicationStatsRequest request); + + /** + *Indices segment replication + */ + void segmentReplicationStats(SegmentReplicationStatsRequest request, ActionListener listener); + /** * Indices recoveries */ RecoveryRequestBuilder prepareRecoveries(String... indices); + /** + * Indices segment replication + */ + SegmentReplicationStatsRequestBuilder prepareSegmentReplicationStats(String... indices); + /** * The segments of one or more indices. * @@ -595,26 +584,6 @@ public interface IndicesAdminClient extends OpenSearchClient { */ GetAliasesRequestBuilder prepareGetAliases(String... aliases); - /** - * Allows to check to existence of aliases from indices. - */ - AliasesExistRequestBuilder prepareAliasesExist(String... aliases); - - /** - * Check to existence of index aliases. - * - * @param request The result future - */ - ActionFuture aliasesExist(GetAliasesRequest request); - - /** - * Check the existence of specified index aliases. - * - * @param request The index aliases request - * @param listener A listener to be notified with a result - */ - void aliasesExist(GetAliasesRequest request, ActionListener listener); - /** * Get index metadata for particular indices. * diff --git a/server/src/main/java/org/opensearch/client/OpenSearchClient.java b/server/src/main/java/org/opensearch/client/OpenSearchClient.java index 9b092c98aa5f5..22b6436c93033 100644 --- a/server/src/main/java/org/opensearch/client/OpenSearchClient.java +++ b/server/src/main/java/org/opensearch/client/OpenSearchClient.java @@ -32,13 +32,18 @@ package org.opensearch.client; -import org.opensearch.action.ActionType; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; +import org.opensearch.common.action.ActionFuture; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; import org.opensearch.threadpool.ThreadPool; +/** + * Interface for an OpenSearch client implementation + * + * @opensearch.internal + */ public interface OpenSearchClient { /** diff --git a/server/src/main/java/org/opensearch/client/OriginSettingClient.java b/server/src/main/java/org/opensearch/client/OriginSettingClient.java index acb6352b06185..1b0e08cc489c4 100644 --- a/server/src/main/java/org/opensearch/client/OriginSettingClient.java +++ b/server/src/main/java/org/opensearch/client/OriginSettingClient.java @@ -32,12 +32,12 @@ package org.opensearch.client; -import org.opensearch.action.ActionType; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; import org.opensearch.action.support.ContextPreservingActionListener; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; import java.util.function.Supplier; @@ -46,6 +46,8 @@ * {@link ThreadContext#stashWithOrigin origin} set to a particular * value and calls its {@linkplain ActionListener} in its original * {@link ThreadContext}. + * + * @opensearch.internal */ public final class OriginSettingClient extends FilterClient { diff --git a/server/src/main/java/org/opensearch/client/ParentTaskAssigningClient.java b/server/src/main/java/org/opensearch/client/ParentTaskAssigningClient.java index 444f0ea778b05..17fda113b2fdc 100644 --- a/server/src/main/java/org/opensearch/client/ParentTaskAssigningClient.java +++ b/server/src/main/java/org/opensearch/client/ParentTaskAssigningClient.java @@ -32,17 +32,19 @@ package org.opensearch.client; -import org.opensearch.action.ActionType; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.tasks.TaskId; import org.opensearch.tasks.Task; -import org.opensearch.tasks.TaskId; /** * A {@linkplain Client} that sets the parent task on all requests that it makes. Use this to conveniently implement actions that cause * many other actions. + * + * @opensearch.internal */ public class ParentTaskAssigningClient extends FilterClient { private final TaskId parentTask; diff --git a/server/src/main/java/org/opensearch/client/Requests.java b/server/src/main/java/org/opensearch/client/Requests.java index d89f55a37a9cf..3607590826007 100644 --- a/server/src/main/java/org/opensearch/client/Requests.java +++ b/server/src/main/java/org/opensearch/client/Requests.java @@ -32,6 +32,9 @@ package org.opensearch.client; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; @@ -47,6 +50,9 @@ import org.opensearch.action.admin.cluster.reroute.ClusterRerouteRequest; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingRequest; import org.opensearch.action.admin.cluster.snapshots.create.CreateSnapshotRequest; import org.opensearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest; import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest; @@ -76,9 +82,13 @@ import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchScrollRequest; import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.xcontent.MediaType; +import org.opensearch.core.xcontent.MediaTypeRegistry; /** * A handy one stop shop for creating requests (make sure to import static this class). + * + * @opensearch.internal */ public class Requests { @@ -90,7 +100,7 @@ public class Requests { /** * The default content type to use to generate source documents when indexing. */ - public static XContentType INDEX_CONTENT_TYPE = XContentType.JSON; + public static MediaType INDEX_CONTENT_TYPE = MediaTypeRegistry.JSON; public static IndexRequest indexRequest() { return new IndexRequest(); @@ -546,4 +556,56 @@ public static DeleteSnapshotRequest deleteSnapshotRequest(String repository, Str public static SnapshotsStatusRequest snapshotsStatusRequest(String repository) { return new SnapshotsStatusRequest(repository); } + + /** + * Updates weights for weighted round-robin search routing policy + * + * @return update weight request + */ + public static ClusterPutWeightedRoutingRequest putWeightedRoutingRequest(String attributeName) { + return new ClusterPutWeightedRoutingRequest(attributeName); + } + + /** + * Gets weights for weighted round-robin search routing policy + * + * @return get weight request + */ + public static ClusterGetWeightedRoutingRequest getWeightedRoutingRequest(String attributeName) { + return new ClusterGetWeightedRoutingRequest(attributeName); + } + + /** + * Deletes weights for weighted round-robin search routing policy + * + * @return delete weight request + */ + public static ClusterDeleteWeightedRoutingRequest deleteWeightedRoutingRequest(String attributeName) { + return new ClusterDeleteWeightedRoutingRequest(attributeName); + } + + /** + * Creates a new decommission request. + * + * @return returns put decommission request + */ + public static DecommissionRequest decommissionRequest() { + return new DecommissionRequest(); + } + + /** + * Get decommissioned attribute from metadata + * + * @return returns get decommission request + */ + public static GetDecommissionStateRequest getDecommissionStateRequest() { + return new GetDecommissionStateRequest(); + } + + /** + * Creates a new delete decommission request. + */ + public static DeleteDecommissionStateRequest deleteDecommissionStateRequest() { + return new DeleteDecommissionStateRequest(); + } } diff --git a/server/src/main/java/org/opensearch/client/node/NodeClient.java b/server/src/main/java/org/opensearch/client/node/NodeClient.java index bda7cdca91015..6e1bb6ce79349 100644 --- a/server/src/main/java/org/opensearch/client/node/NodeClient.java +++ b/server/src/main/java/org/opensearch/client/node/NodeClient.java @@ -32,30 +32,32 @@ package org.opensearch.client.node; -import org.opensearch.action.ActionType; -import org.opensearch.action.ActionListener; +import org.opensearch.action.ActionModule.DynamicActionRegistry; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; +import org.opensearch.action.ActionType; import org.opensearch.action.support.TransportAction; import org.opensearch.client.Client; import org.opensearch.client.support.AbstractClient; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.NamedWriteableRegistry; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; import org.opensearch.tasks.Task; import org.opensearch.tasks.TaskListener; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.RemoteClusterService; -import java.util.Map; import java.util.function.Supplier; /** * Client that executes actions on the local node. + * + * @opensearch.internal */ public class NodeClient extends AbstractClient { - private Map actions; + private DynamicActionRegistry actionRegistry; /** * The id of the local {@link DiscoveryNode}. Useful for generating task ids from tasks returned by * {@link #executeLocally(ActionType, ActionRequest, TaskListener)}. @@ -69,12 +71,12 @@ public NodeClient(Settings settings, ThreadPool threadPool) { } public void initialize( - Map actions, + DynamicActionRegistry actionRegistry, Supplier localNodeId, RemoteClusterService remoteClusterService, NamedWriteableRegistry namedWriteableRegistry ) { - this.actions = actions; + this.actionRegistry = actionRegistry; this.localNodeId = localNodeId; this.remoteClusterService = remoteClusterService; this.namedWriteableRegistry = namedWriteableRegistry; @@ -135,10 +137,10 @@ public String getLocalNodeId() { private TransportAction transportAction( ActionType action ) { - if (actions == null) { + if (actionRegistry == null) { throw new IllegalStateException("NodeClient has not been initialized"); } - TransportAction transportAction = actions.get(action); + TransportAction transportAction = (TransportAction) actionRegistry.get(action); if (transportAction == null) { throw new IllegalStateException("failed to find action [" + action + "] to execute"); } diff --git a/server/src/main/java/org/opensearch/client/node/package-info.java b/server/src/main/java/org/opensearch/client/node/package-info.java new file mode 100644 index 0000000000000..0a3be1fad6bf0 --- /dev/null +++ b/server/src/main/java/org/opensearch/client/node/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * The node client module allowing to easily perform actions/operations at node level. + */ +package org.opensearch.client.node; diff --git a/server/src/main/java/org/opensearch/client/package-info.java b/server/src/main/java/org/opensearch/client/package-info.java index 24c744803a151..abfd9a9119971 100644 --- a/server/src/main/java/org/opensearch/client/package-info.java +++ b/server/src/main/java/org/opensearch/client/package-info.java @@ -25,12 +25,12 @@ * under the License. */ -/** - * The client module allowing to easily perform actions/operations. - */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ +/** + * The client module allowing to easily perform actions/operations. + */ package org.opensearch.client; diff --git a/server/src/main/java/org/opensearch/client/support/AbstractClient.java b/server/src/main/java/org/opensearch/client/support/AbstractClient.java index a37d293ee5dd2..786bfa38bb19c 100644 --- a/server/src/main/java/org/opensearch/client/support/AbstractClient.java +++ b/server/src/main/java/org/opensearch/client/support/AbstractClient.java @@ -34,15 +34,24 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionFuture; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRequest; -import org.opensearch.action.ActionResponse; import org.opensearch.action.ActionType; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainAction; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequest; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainRequestBuilder; import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateAction; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.get.GetDecommissionStateResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionAction; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequestBuilder; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; import org.opensearch.action.admin.cluster.health.ClusterHealthAction; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthRequestBuilder; @@ -77,6 +86,13 @@ import org.opensearch.action.admin.cluster.node.usage.NodesUsageRequest; import org.opensearch.action.admin.cluster.node.usage.NodesUsageRequestBuilder; import org.opensearch.action.admin.cluster.node.usage.NodesUsageResponse; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreAction; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreRequest; +import org.opensearch.action.admin.cluster.remotestore.restore.RestoreRemoteStoreResponse; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsAction; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsRequest; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsRequestBuilder; +import org.opensearch.action.admin.cluster.remotestore.stats.RemoteStoreStatsResponse; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryAction; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryRequest; import org.opensearch.action.admin.cluster.repositories.cleanup.CleanupRepositoryRequestBuilder; @@ -107,6 +123,18 @@ import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsRequest; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsRequestBuilder; import org.opensearch.action.admin.cluster.shards.ClusterSearchShardsResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingRequestBuilder; +import org.opensearch.action.admin.cluster.shards.routing.weighted.delete.ClusterDeleteWeightedRoutingResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingRequestBuilder; +import org.opensearch.action.admin.cluster.shards.routing.weighted.get.ClusterGetWeightedRoutingResponse; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterAddWeightedRoutingAction; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingRequest; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingRequestBuilder; +import org.opensearch.action.admin.cluster.shards.routing.weighted.put.ClusterPutWeightedRoutingResponse; import org.opensearch.action.admin.cluster.snapshots.clone.CloneSnapshotAction; import org.opensearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequest; import org.opensearch.action.admin.cluster.snapshots.clone.CloneSnapshotRequestBuilder; @@ -154,9 +182,6 @@ import org.opensearch.action.admin.indices.alias.IndicesAliasesAction; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; import org.opensearch.action.admin.indices.alias.IndicesAliasesRequestBuilder; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistAction; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistRequestBuilder; -import org.opensearch.action.admin.indices.alias.exists.AliasesExistResponse; import org.opensearch.action.admin.indices.alias.get.GetAliasesAction; import org.opensearch.action.admin.indices.alias.get.GetAliasesRequest; import org.opensearch.action.admin.indices.alias.get.GetAliasesRequestBuilder; @@ -192,10 +217,6 @@ import org.opensearch.action.admin.indices.exists.indices.IndicesExistsRequest; import org.opensearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder; import org.opensearch.action.admin.indices.exists.indices.IndicesExistsResponse; -import org.opensearch.action.admin.indices.exists.types.TypesExistsAction; -import org.opensearch.action.admin.indices.exists.types.TypesExistsRequest; -import org.opensearch.action.admin.indices.exists.types.TypesExistsRequestBuilder; -import org.opensearch.action.admin.indices.exists.types.TypesExistsResponse; import org.opensearch.action.admin.indices.flush.FlushAction; import org.opensearch.action.admin.indices.flush.FlushRequest; import org.opensearch.action.admin.indices.flush.FlushRequestBuilder; @@ -235,6 +256,10 @@ import org.opensearch.action.admin.indices.refresh.RefreshRequest; import org.opensearch.action.admin.indices.refresh.RefreshRequestBuilder; import org.opensearch.action.admin.indices.refresh.RefreshResponse; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsAction; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsRequest; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsRequestBuilder; +import org.opensearch.action.admin.indices.replication.SegmentReplicationStatsResponse; import org.opensearch.action.admin.indices.resolve.ResolveIndexAction; import org.opensearch.action.admin.indices.rollover.RolloverAction; import org.opensearch.action.admin.indices.rollover.RolloverRequest; @@ -244,6 +269,8 @@ import org.opensearch.action.admin.indices.segments.IndicesSegmentsAction; import org.opensearch.action.admin.indices.segments.IndicesSegmentsRequest; import org.opensearch.action.admin.indices.segments.IndicesSegmentsRequestBuilder; +import org.opensearch.action.admin.indices.segments.PitSegmentsAction; +import org.opensearch.action.admin.indices.segments.PitSegmentsRequest; import org.opensearch.action.admin.indices.settings.get.GetSettingsAction; import org.opensearch.action.admin.indices.settings.get.GetSettingsRequest; import org.opensearch.action.admin.indices.settings.get.GetSettingsRequestBuilder; @@ -331,10 +358,26 @@ import org.opensearch.action.search.ClearScrollRequest; import org.opensearch.action.search.ClearScrollRequestBuilder; import org.opensearch.action.search.ClearScrollResponse; +import org.opensearch.action.search.CreatePitAction; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.CreatePitResponse; +import org.opensearch.action.search.DeletePitAction; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.action.search.DeletePitResponse; +import org.opensearch.action.search.DeleteSearchPipelineAction; +import org.opensearch.action.search.DeleteSearchPipelineRequest; +import org.opensearch.action.search.GetAllPitNodesRequest; +import org.opensearch.action.search.GetAllPitNodesResponse; +import org.opensearch.action.search.GetAllPitsAction; +import org.opensearch.action.search.GetSearchPipelineAction; +import org.opensearch.action.search.GetSearchPipelineRequest; +import org.opensearch.action.search.GetSearchPipelineResponse; import org.opensearch.action.search.MultiSearchAction; import org.opensearch.action.search.MultiSearchRequest; import org.opensearch.action.search.MultiSearchRequestBuilder; import org.opensearch.action.search.MultiSearchResponse; +import org.opensearch.action.search.PutSearchPipelineAction; +import org.opensearch.action.search.PutSearchPipelineRequest; import org.opensearch.action.search.SearchAction; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.search.SearchRequestBuilder; @@ -364,15 +407,23 @@ import org.opensearch.client.OpenSearchClient; import org.opensearch.cluster.metadata.IndexMetadata.APIBlock; import org.opensearch.common.Nullable; -import org.opensearch.common.bytes.BytesReference; +import org.opensearch.common.action.ActionFuture; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.tasks.TaskId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.tasks.TaskId; +import org.opensearch.core.xcontent.MediaType; import org.opensearch.threadpool.ThreadPool; import java.util.Map; +/** + * Base client used to create concrete client implementations + * + * @opensearch.internal + */ public abstract class AbstractClient implements Client { protected final Logger logger; @@ -576,6 +627,26 @@ public SearchScrollRequestBuilder prepareSearchScroll(String scrollId) { return new SearchScrollRequestBuilder(this, SearchScrollAction.INSTANCE, scrollId); } + @Override + public void createPit(final CreatePitRequest createPITRequest, final ActionListener listener) { + execute(CreatePitAction.INSTANCE, createPITRequest, listener); + } + + @Override + public void deletePits(final DeletePitRequest deletePITRequest, final ActionListener listener) { + execute(DeletePitAction.INSTANCE, deletePITRequest, listener); + } + + @Override + public void getAllPits(final GetAllPitNodesRequest getAllPitNodesRequest, final ActionListener listener) { + execute(GetAllPitsAction.INSTANCE, getAllPitNodesRequest, listener); + } + + @Override + public void pitSegments(final PitSegmentsRequest request, final ActionListener listener) { + execute(PitSegmentsAction.INSTANCE, request, listener); + } + @Override public ActionFuture multiSearch(MultiSearchRequest request) { return execute(MultiSearchAction.INSTANCE, request); @@ -820,6 +891,23 @@ public NodesStatsRequestBuilder prepareNodesStats(String... nodesIds) { return new NodesStatsRequestBuilder(this, NodesStatsAction.INSTANCE).setNodesIds(nodesIds); } + @Override + public void remoteStoreStats(final RemoteStoreStatsRequest request, final ActionListener listener) { + execute(RemoteStoreStatsAction.INSTANCE, request, listener); + } + + @Override + public RemoteStoreStatsRequestBuilder prepareRemoteStoreStats(String index, String shardId) { + RemoteStoreStatsRequestBuilder remoteStoreStatsRequestBuilder = new RemoteStoreStatsRequestBuilder( + this, + RemoteStoreStatsAction.INSTANCE + ).setIndices(index); + if (shardId != null) { + remoteStoreStatsRequestBuilder.setShards(shardId); + } + return remoteStoreStatsRequestBuilder; + } + @Override public ActionFuture nodesUsage(final NodesUsageRequest request) { return execute(NodesUsageAction.INSTANCE, request); @@ -1095,6 +1183,11 @@ public void restoreSnapshot(RestoreSnapshotRequest request, ActionListener listener) { + execute(RestoreRemoteStoreAction.INSTANCE, request, listener); + } + @Override public RestoreSnapshotRequestBuilder prepareRestoreSnapshot(String repository, String snapshot) { return new RestoreSnapshotRequestBuilder(this, RestoreSnapshotAction.INSTANCE, repository, snapshot); @@ -1131,8 +1224,8 @@ public ActionFuture putPipeline(PutPipelineRequest request } @Override - public PutPipelineRequestBuilder preparePutPipeline(String id, BytesReference source, XContentType xContentType) { - return new PutPipelineRequestBuilder(this, PutPipelineAction.INSTANCE, id, source, xContentType); + public PutPipelineRequestBuilder preparePutPipeline(String id, BytesReference source, MediaType mediaType) { + return new PutPipelineRequestBuilder(this, PutPipelineAction.INSTANCE, id, source, mediaType); } @Override @@ -1181,8 +1274,8 @@ public ActionFuture simulatePipeline(SimulatePipelineR } @Override - public SimulatePipelineRequestBuilder prepareSimulatePipeline(BytesReference source, XContentType xContentType) { - return new SimulatePipelineRequestBuilder(this, SimulatePipelineAction.INSTANCE, source, xContentType); + public SimulatePipelineRequestBuilder prepareSimulatePipeline(BytesReference source, MediaType mediaType) { + return new SimulatePipelineRequestBuilder(this, SimulatePipelineAction.INSTANCE, source, mediaType); } @Override @@ -1235,6 +1328,60 @@ public ActionFuture deleteDanglingIndex(DeleteDanglingInde return execute(DeleteDanglingIndexAction.INSTANCE, request); } + @Override + public ActionFuture putWeightedRouting(ClusterPutWeightedRoutingRequest request) { + return execute(ClusterAddWeightedRoutingAction.INSTANCE, request); + } + + @Override + public void putWeightedRouting( + ClusterPutWeightedRoutingRequest request, + ActionListener listener + ) { + execute(ClusterAddWeightedRoutingAction.INSTANCE, request, listener); + } + + @Override + public ClusterPutWeightedRoutingRequestBuilder prepareWeightedRouting() { + return new ClusterPutWeightedRoutingRequestBuilder(this, ClusterAddWeightedRoutingAction.INSTANCE); + } + + @Override + public ActionFuture getWeightedRouting(ClusterGetWeightedRoutingRequest request) { + return execute(ClusterGetWeightedRoutingAction.INSTANCE, request); + } + + @Override + public void getWeightedRouting( + ClusterGetWeightedRoutingRequest request, + ActionListener listener + ) { + execute(ClusterGetWeightedRoutingAction.INSTANCE, request, listener); + } + + @Override + public ClusterGetWeightedRoutingRequestBuilder prepareGetWeightedRouting() { + return new ClusterGetWeightedRoutingRequestBuilder(this, ClusterGetWeightedRoutingAction.INSTANCE); + } + + @Override + public ActionFuture deleteWeightedRouting(ClusterDeleteWeightedRoutingRequest request) { + return execute(ClusterDeleteWeightedRoutingAction.INSTANCE, request); + } + + @Override + public void deleteWeightedRouting( + ClusterDeleteWeightedRoutingRequest request, + ActionListener listener + ) { + execute(ClusterDeleteWeightedRoutingAction.INSTANCE, request, listener); + } + + @Override + public ClusterDeleteWeightedRoutingRequestBuilder prepareDeleteWeightedRouting() { + return new ClusterDeleteWeightedRoutingRequestBuilder(this, ClusterDeleteWeightedRoutingAction.INSTANCE); + } + @Override public void deleteDanglingIndex(DeleteDanglingIndexRequest request, ActionListener listener) { execute(DeleteDanglingIndexAction.INSTANCE, request, listener); @@ -1285,6 +1432,84 @@ public DeleteStoredScriptRequestBuilder prepareDeleteStoredScript() { public DeleteStoredScriptRequestBuilder prepareDeleteStoredScript(String id) { return prepareDeleteStoredScript().setId(id); } + + @Override + public ActionFuture decommission(DecommissionRequest request) { + return execute(DecommissionAction.INSTANCE, request); + } + + @Override + public void decommission(DecommissionRequest request, ActionListener listener) { + execute(DecommissionAction.INSTANCE, request, listener); + } + + @Override + public DecommissionRequestBuilder prepareDecommission(DecommissionRequest request) { + return new DecommissionRequestBuilder(this, DecommissionAction.INSTANCE, request); + } + + @Override + public ActionFuture getDecommissionState(GetDecommissionStateRequest request) { + return execute(GetDecommissionStateAction.INSTANCE, request); + } + + @Override + public void getDecommissionState(GetDecommissionStateRequest request, ActionListener listener) { + execute(GetDecommissionStateAction.INSTANCE, request, listener); + } + + @Override + public GetDecommissionStateRequestBuilder prepareGetDecommissionState() { + return new GetDecommissionStateRequestBuilder(this, GetDecommissionStateAction.INSTANCE); + } + + @Override + public ActionFuture deleteDecommissionState(DeleteDecommissionStateRequest request) { + return execute(DeleteDecommissionStateAction.INSTANCE, request); + } + + @Override + public void deleteDecommissionState( + DeleteDecommissionStateRequest request, + ActionListener listener + ) { + execute(DeleteDecommissionStateAction.INSTANCE, request, listener); + } + + @Override + public DeleteDecommissionStateRequestBuilder prepareDeleteDecommissionRequest() { + return new DeleteDecommissionStateRequestBuilder(this, DeleteDecommissionStateAction.INSTANCE); + } + + @Override + public void putSearchPipeline(PutSearchPipelineRequest request, ActionListener listener) { + execute(PutSearchPipelineAction.INSTANCE, request, listener); + } + + @Override + public ActionFuture putSearchPipeline(PutSearchPipelineRequest request) { + return execute(PutSearchPipelineAction.INSTANCE, request); + } + + @Override + public void getSearchPipeline(GetSearchPipelineRequest request, ActionListener listener) { + execute(GetSearchPipelineAction.INSTANCE, request, listener); + } + + @Override + public ActionFuture getSearchPipeline(GetSearchPipelineRequest request) { + return execute(GetSearchPipelineAction.INSTANCE, request); + } + + @Override + public void deleteSearchPipeline(DeleteSearchPipelineRequest request, ActionListener listener) { + execute(DeleteSearchPipelineAction.INSTANCE, request, listener); + } + + @Override + public ActionFuture deleteSearchPipeline(DeleteSearchPipelineRequest request) { + return execute(DeleteSearchPipelineAction.INSTANCE, request); + } } static class IndicesAdmin implements IndicesAdminClient { @@ -1332,22 +1557,6 @@ public IndicesExistsRequestBuilder prepareExists(String... indices) { return new IndicesExistsRequestBuilder(this, IndicesExistsAction.INSTANCE, indices); } - @Deprecated - @Override - public ActionFuture typesExists(TypesExistsRequest request) { - return execute(TypesExistsAction.INSTANCE, request); - } - - @Override - public void typesExists(TypesExistsRequest request, ActionListener listener) { - execute(TypesExistsAction.INSTANCE, request, listener); - } - - @Override - public TypesExistsRequestBuilder prepareTypesExists(String... index) { - return new TypesExistsRequestBuilder(this, TypesExistsAction.INSTANCE, index); - } - @Override public ActionFuture aliases(final IndicesAliasesRequest request) { return execute(IndicesAliasesAction.INSTANCE, request); @@ -1383,21 +1592,6 @@ public ActionFuture clearCache(final ClearIndicesCach return execute(ClearIndicesCacheAction.INSTANCE, request); } - @Override - public void aliasesExist(GetAliasesRequest request, ActionListener listener) { - execute(AliasesExistAction.INSTANCE, request, listener); - } - - @Override - public ActionFuture aliasesExist(GetAliasesRequest request) { - return execute(AliasesExistAction.INSTANCE, request); - } - - @Override - public AliasesExistRequestBuilder prepareAliasesExist(String... aliases) { - return new AliasesExistRequestBuilder(this, AliasesExistAction.INSTANCE, aliases); - } - @Override public ActionFuture getIndex(GetIndexRequest request) { return execute(GetIndexAction.INSTANCE, request); @@ -1643,6 +1837,24 @@ public RecoveryRequestBuilder prepareRecoveries(String... indices) { return new RecoveryRequestBuilder(this, RecoveryAction.INSTANCE).setIndices(indices); } + @Override + public ActionFuture segmentReplicationStats(final SegmentReplicationStatsRequest request) { + return execute(SegmentReplicationStatsAction.INSTANCE, request); + } + + @Override + public void segmentReplicationStats( + final SegmentReplicationStatsRequest request, + final ActionListener listener + ) { + execute(SegmentReplicationStatsAction.INSTANCE, request, listener); + } + + @Override + public SegmentReplicationStatsRequestBuilder prepareSegmentReplicationStats(String... indices) { + return new SegmentReplicationStatsRequestBuilder(this, SegmentReplicationStatsAction.INSTANCE).setIndices(indices); + } + @Override public ActionFuture segments(final IndicesSegmentsRequest request) { return execute(IndicesSegmentsAction.INSTANCE, request); diff --git a/server/src/main/java/org/opensearch/client/support/package-info.java b/server/src/main/java/org/opensearch/client/support/package-info.java new file mode 100644 index 0000000000000..637f7f03101aa --- /dev/null +++ b/server/src/main/java/org/opensearch/client/support/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * The Abstract client for implementing new client types. + */ +package org.opensearch.client.support; diff --git a/server/src/main/java/org/opensearch/client/transport/NoNodeAvailableException.java b/server/src/main/java/org/opensearch/client/transport/NoNodeAvailableException.java index a4ffe2102037e..75dbd90275367 100644 --- a/server/src/main/java/org/opensearch/client/transport/NoNodeAvailableException.java +++ b/server/src/main/java/org/opensearch/client/transport/NoNodeAvailableException.java @@ -33,13 +33,15 @@ package org.opensearch.client.transport; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; /** * An exception indicating no node is available to perform the operation. + * + * @opensearch.internal */ public class NoNodeAvailableException extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/client/transport/package-info.java b/server/src/main/java/org/opensearch/client/transport/package-info.java new file mode 100644 index 0000000000000..a775d93350b19 --- /dev/null +++ b/server/src/main/java/org/opensearch/client/transport/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Transport Client support classes. + */ +package org.opensearch.client.transport; diff --git a/server/src/main/java/org/opensearch/cluster/AbstractDiffable.java b/server/src/main/java/org/opensearch/cluster/AbstractDiffable.java index 7b95545e58101..74af3472433ba 100644 --- a/server/src/main/java/org/opensearch/cluster/AbstractDiffable.java +++ b/server/src/main/java/org/opensearch/cluster/AbstractDiffable.java @@ -33,14 +33,16 @@ package org.opensearch.cluster; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * Abstract diffable object with simple diffs implementation that sends the entire object if object has changed or * nothing if object remained the same. + * + * @opensearch.internal */ public abstract class AbstractDiffable> implements Diffable { @@ -64,6 +66,11 @@ public static > Diff readDiffFrom(Reader reader, Str return (Diff) EMPTY; } + /** + * A complete diff. + * + * @opensearch.internal + */ private static class CompleteDiff> implements Diff { @Nullable diff --git a/server/src/main/java/org/opensearch/cluster/AbstractNamedDiffable.java b/server/src/main/java/org/opensearch/cluster/AbstractNamedDiffable.java index 43ff74f30a249..49837aea83708 100644 --- a/server/src/main/java/org/opensearch/cluster/AbstractNamedDiffable.java +++ b/server/src/main/java/org/opensearch/cluster/AbstractNamedDiffable.java @@ -34,15 +34,17 @@ import org.opensearch.Version; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.NamedWriteable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.NamedWriteable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** * Abstract diffable object with simple diffs implementation that sends the entire object if object has changed or * nothing is object remained the same. Comparing to AbstractDiffable, this class also works with NamedWriteables + * + * @opensearch.internal */ public abstract class AbstractNamedDiffable> implements Diffable, NamedWriteable { @@ -60,6 +62,11 @@ public static > NamedDiff readDiffFrom(Class(tClass, name, in); } + /** + * A complete named diff. + * + * @opensearch.internal + */ private static class CompleteNamedDiff> implements NamedDiff { @Nullable diff --git a/server/src/main/java/org/opensearch/cluster/AckedClusterStateTaskListener.java b/server/src/main/java/org/opensearch/cluster/AckedClusterStateTaskListener.java index 0f6531fb09be9..482087be1c8eb 100644 --- a/server/src/main/java/org/opensearch/cluster/AckedClusterStateTaskListener.java +++ b/server/src/main/java/org/opensearch/cluster/AckedClusterStateTaskListener.java @@ -35,6 +35,11 @@ import org.opensearch.common.Nullable; import org.opensearch.common.unit.TimeValue; +/** + * Listener when cluster state task is acknowledged + * + * @opensearch.internal + */ public interface AckedClusterStateTaskListener extends ClusterStateTaskListener { /** diff --git a/server/src/main/java/org/opensearch/cluster/AckedClusterStateUpdateTask.java b/server/src/main/java/org/opensearch/cluster/AckedClusterStateUpdateTask.java index 82ea05274d0d1..dfca0edcfbf5f 100644 --- a/server/src/main/java/org/opensearch/cluster/AckedClusterStateUpdateTask.java +++ b/server/src/main/java/org/opensearch/cluster/AckedClusterStateUpdateTask.java @@ -31,16 +31,18 @@ package org.opensearch.cluster; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ack.AckedRequest; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.Nullable; import org.opensearch.common.Priority; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; /** * An extension interface to {@link ClusterStateUpdateTask} that allows to be notified when * all the nodes have acknowledged a cluster state update request + * + * @opensearch.internal */ public abstract class AckedClusterStateUpdateTask extends ClusterStateUpdateTask implements AckedClusterStateTaskListener { diff --git a/server/src/main/java/org/opensearch/cluster/ClusterChangedEvent.java b/server/src/main/java/org/opensearch/cluster/ClusterChangedEvent.java index db54016a61f6d..fab104142e5bb 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterChangedEvent.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterChangedEvent.java @@ -32,27 +32,27 @@ package org.opensearch.cluster; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.cluster.metadata.IndexGraveyard; import org.opensearch.cluster.metadata.IndexGraveyard.IndexGraveyardDiff; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.collect.ImmutableOpenMap; +import org.opensearch.core.index.Index; import org.opensearch.gateway.GatewayService; -import org.opensearch.index.Index; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; /** * An event received by the local node, signaling that the cluster state has changed. + * + * @opensearch.internal */ public class ClusterChangedEvent { @@ -127,8 +127,7 @@ public List indicesCreated() { return Collections.emptyList(); } List created = null; - for (ObjectCursor cursor : state.metadata().indices().keys()) { - String index = cursor.value; + for (final String index : state.metadata().indices().keySet()) { if (!previousState.metadata().hasIndex(index)) { if (created == null) { created = new ArrayList<>(); @@ -168,20 +167,20 @@ public boolean metadataChanged() { */ public Set changedCustomMetadataSet() { Set result = new HashSet<>(); - ImmutableOpenMap currentCustoms = state.metadata().customs(); - ImmutableOpenMap previousCustoms = previousState.metadata().customs(); + Map currentCustoms = state.metadata().customs(); + Map previousCustoms = previousState.metadata().customs(); if (currentCustoms.equals(previousCustoms) == false) { - for (ObjectObjectCursor currentCustomMetadata : currentCustoms) { + for (Map.Entry currentCustomMetadata : currentCustoms.entrySet()) { // new custom md added or existing custom md changed - if (previousCustoms.containsKey(currentCustomMetadata.key) == false - || currentCustomMetadata.value.equals(previousCustoms.get(currentCustomMetadata.key)) == false) { - result.add(currentCustomMetadata.key); + if (previousCustoms.containsKey(currentCustomMetadata.getKey()) == false + || currentCustomMetadata.getValue().equals(previousCustoms.get(currentCustomMetadata.getKey())) == false) { + result.add(currentCustomMetadata.getKey()); } } // existing custom md deleted - for (ObjectObjectCursor previousCustomMetadata : previousCustoms) { - if (currentCustoms.containsKey(previousCustomMetadata.key) == false) { - result.add(previousCustomMetadata.key); + for (Map.Entry previousCustomMetadata : previousCustoms.entrySet()) { + if (currentCustoms.containsKey(previousCustomMetadata.getKey()) == false) { + result.add(previousCustomMetadata.getKey()); } } } @@ -211,8 +210,18 @@ public boolean blocksChanged() { /** * Returns true iff the local node is the mater node of the cluster. */ + public boolean localNodeClusterManager() { + return state.nodes().isLocalNodeElectedClusterManager(); + } + + /** + * Returns true iff the local node is the mater node of the cluster. + * + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #localNodeClusterManager()} + */ + @Deprecated public boolean localNodeMaster() { - return state.nodes().isLocalNodeElectedMaster(); + return localNodeClusterManager(); } /** @@ -248,7 +257,7 @@ public boolean nodesChanged() { * Determines whether or not the current cluster state represents an entirely * new cluster, either when a node joins a cluster for the first time or when * the node receives a cluster state update from a brand new cluster (different - * UUID from the previous cluster), which will happen when a master node is + * UUID from the previous cluster), which will happen when a cluster-manager node is * elected that has never been part of the cluster before. */ public boolean isNewCluster() { @@ -260,10 +269,10 @@ public boolean isNewCluster() { // Get the deleted indices by comparing the index metadatas in the previous and new cluster states. // If an index exists in the previous cluster state, but not in the new cluster state, it must have been deleted. private List indicesDeletedFromClusterState() { - // If the new cluster state has a new cluster UUID, the likely scenario is that a node was elected - // master that has had its data directory wiped out, in which case we don't want to delete the indices and lose data; + // If the new cluster state has a new cluster UUID, the likely scenario is that a node was elected cluster-manager + // that has had its data directory wiped out, in which case we don't want to delete the indices and lose data; // rather we want to import them as dangling indices instead. So we check here if the cluster UUID differs from the previous - // cluster UUID, in which case, we don't want to delete indices that the master erroneously believes shouldn't exist. + // cluster UUID, in which case, we don't want to delete indices that the cluster-manager erroneously believes shouldn't exist. // See test DiscoveryWithServiceDisruptionsIT.testIndicesDeleted() // See discussion on https://github.com/elastic/elasticsearch/pull/9952 and // https://github.com/elastic/elasticsearch/issues/11665 @@ -274,8 +283,7 @@ private List indicesDeletedFromClusterState() { final Metadata previousMetadata = previousState.metadata(); final Metadata currentMetadata = state.metadata(); - for (ObjectCursor cursor : previousMetadata.indices().values()) { - IndexMetadata index = cursor.value; + for (final IndexMetadata index : previousMetadata.indices().values()) { IndexMetadata current = currentMetadata.index(index.getIndex()); if (current == null) { if (deleted == null) { diff --git a/server/src/main/java/org/opensearch/cluster/ClusterInfo.java b/server/src/main/java/org/opensearch/cluster/ClusterInfo.java index 19e026c570c74..8f0937b17bbc0 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterInfo.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterInfo.java @@ -32,40 +32,44 @@ package org.opensearch.cluster; -import com.carrotsearch.hppc.ObjectHashSet; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; +import org.opensearch.Version; import org.opensearch.cluster.routing.ShardRouting; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.store.StoreStats; +import org.opensearch.index.store.remote.filecache.FileCacheStats; import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; /** * ClusterInfo is an object representing a map of nodes to {@link DiskUsage} * and a map of shard ids to shard sizes, see * InternalClusterInfoService.shardIdentifierFromRouting(String) * for the key used in the shardSizes map + * + * @opensearch.internal */ public class ClusterInfo implements ToXContentFragment, Writeable { - private final ImmutableOpenMap leastAvailableSpaceUsage; - private final ImmutableOpenMap mostAvailableSpaceUsage; - final ImmutableOpenMap shardSizes; + private final Map leastAvailableSpaceUsage; + private final Map mostAvailableSpaceUsage; + final Map shardSizes; // pkg-private for testing only public static final ClusterInfo EMPTY = new ClusterInfo(); - final ImmutableOpenMap routingToDataPath; - final ImmutableOpenMap reservedSpace; + final Map routingToDataPath; + final Map reservedSpace; + final Map nodeFileCacheStats; protected ClusterInfo() { - this(ImmutableOpenMap.of(), ImmutableOpenMap.of(), ImmutableOpenMap.of(), ImmutableOpenMap.of(), ImmutableOpenMap.of()); + this(Map.of(), Map.of(), Map.of(), Map.of(), Map.of(), Map.of()); } /** @@ -79,17 +83,19 @@ protected ClusterInfo() { * @see #shardIdentifierFromRouting */ public ClusterInfo( - ImmutableOpenMap leastAvailableSpaceUsage, - ImmutableOpenMap mostAvailableSpaceUsage, - ImmutableOpenMap shardSizes, - ImmutableOpenMap routingToDataPath, - ImmutableOpenMap reservedSpace + final Map leastAvailableSpaceUsage, + final Map mostAvailableSpaceUsage, + final Map shardSizes, + final Map routingToDataPath, + final Map reservedSpace, + final Map nodeFileCacheStats ) { this.leastAvailableSpaceUsage = leastAvailableSpaceUsage; this.shardSizes = shardSizes; this.mostAvailableSpaceUsage = mostAvailableSpaceUsage; this.routingToDataPath = routingToDataPath; this.reservedSpace = reservedSpace; + this.nodeFileCacheStats = nodeFileCacheStats; } public ClusterInfo(StreamInput in) throws IOException { @@ -101,51 +107,50 @@ public ClusterInfo(StreamInput in) throws IOException { if (in.getVersion().onOrAfter(StoreStats.RESERVED_BYTES_VERSION)) { reservedSpaceMap = in.readMap(NodeAndPath::new, ReservedSpace::new); } else { - reservedSpaceMap = org.opensearch.common.collect.Map.of(); + reservedSpaceMap = Map.of(); } - ImmutableOpenMap.Builder leastBuilder = ImmutableOpenMap.builder(); - this.leastAvailableSpaceUsage = leastBuilder.putAll(leastMap).build(); - ImmutableOpenMap.Builder mostBuilder = ImmutableOpenMap.builder(); - this.mostAvailableSpaceUsage = mostBuilder.putAll(mostMap).build(); - ImmutableOpenMap.Builder sizeBuilder = ImmutableOpenMap.builder(); - this.shardSizes = sizeBuilder.putAll(sizeMap).build(); - ImmutableOpenMap.Builder routingBuilder = ImmutableOpenMap.builder(); - this.routingToDataPath = routingBuilder.putAll(routingMap).build(); - ImmutableOpenMap.Builder reservedSpaceBuilder = ImmutableOpenMap.builder(); - this.reservedSpace = reservedSpaceBuilder.putAll(reservedSpaceMap).build(); + this.leastAvailableSpaceUsage = Collections.unmodifiableMap(leastMap); + this.mostAvailableSpaceUsage = Collections.unmodifiableMap(mostMap); + this.shardSizes = Collections.unmodifiableMap(sizeMap); + this.routingToDataPath = Collections.unmodifiableMap(routingMap); + this.reservedSpace = Collections.unmodifiableMap(reservedSpaceMap); + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + this.nodeFileCacheStats = in.readMap(StreamInput::readString, FileCacheStats::new); + } else { + this.nodeFileCacheStats = Map.of(); + } } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeVInt(this.leastAvailableSpaceUsage.size()); - for (ObjectObjectCursor c : this.leastAvailableSpaceUsage) { - out.writeString(c.key); - c.value.writeTo(out); - } + out.writeMap(this.leastAvailableSpaceUsage, StreamOutput::writeString, (o, v) -> v.writeTo(o)); out.writeMap(this.mostAvailableSpaceUsage, StreamOutput::writeString, (o, v) -> v.writeTo(o)); out.writeMap(this.shardSizes, StreamOutput::writeString, (o, v) -> out.writeLong(v == null ? -1 : v)); out.writeMap(this.routingToDataPath, (o, k) -> k.writeTo(o), StreamOutput::writeString); if (out.getVersion().onOrAfter(StoreStats.RESERVED_BYTES_VERSION)) { - out.writeMap(this.reservedSpace); + out.writeMap(this.reservedSpace, (o, v) -> v.writeTo(o), (o, v) -> v.writeTo(o)); + } + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + out.writeMap(this.nodeFileCacheStats, StreamOutput::writeString, (o, v) -> v.writeTo(o)); } } public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject("nodes"); { - for (ObjectObjectCursor c : this.leastAvailableSpaceUsage) { - builder.startObject(c.key); + for (Map.Entry c : this.leastAvailableSpaceUsage.entrySet()) { + builder.startObject(c.getKey()); { // node - builder.field("node_name", c.value.getNodeName()); + builder.field("node_name", c.getValue().getNodeName()); builder.startObject("least_available"); { - c.value.toShortXContent(builder); + c.getValue().toShortXContent(builder); } builder.endObject(); // end "least_available" builder.startObject("most_available"); { - DiskUsage most = this.mostAvailableSpaceUsage.get(c.key); + DiskUsage most = this.mostAvailableSpaceUsage.get(c.getKey()); if (most != null) { most.toShortXContent(builder); } @@ -158,26 +163,26 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endObject(); // end "nodes" builder.startObject("shard_sizes"); { - for (ObjectObjectCursor c : this.shardSizes) { - builder.humanReadableField(c.key + "_bytes", c.key, new ByteSizeValue(c.value)); + for (Map.Entry c : this.shardSizes.entrySet()) { + builder.humanReadableField(c.getKey() + "_bytes", c.getKey(), new ByteSizeValue(c.getValue())); } } builder.endObject(); // end "shard_sizes" builder.startObject("shard_paths"); { - for (ObjectObjectCursor c : this.routingToDataPath) { - builder.field(c.key.toString(), c.value); + for (Map.Entry c : this.routingToDataPath.entrySet()) { + builder.field(c.getKey().toString(), c.getValue()); } } builder.endObject(); // end "shard_paths" builder.startArray("reserved_sizes"); { - for (ObjectObjectCursor c : this.reservedSpace) { + for (Map.Entry c : this.reservedSpace.entrySet()) { builder.startObject(); { - builder.field("node_id", c.key.nodeId); - builder.field("path", c.key.path); - c.value.toXContent(builder, params); + builder.field("node_id", c.getKey().nodeId); + builder.field("path", c.getKey().path); + c.getValue().toXContent(builder, params); } builder.endObject(); // NodeAndPath } @@ -190,16 +195,23 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws * Returns a node id to disk usage mapping for the path that has the least available space on the node. * Note that this does not take account of reserved space: there may be another path with less available _and unreserved_ space. */ - public ImmutableOpenMap getNodeLeastAvailableDiskUsages() { - return this.leastAvailableSpaceUsage; + public Map getNodeLeastAvailableDiskUsages() { + return Collections.unmodifiableMap(this.leastAvailableSpaceUsage); } /** * Returns a node id to disk usage mapping for the path that has the most available space on the node. * Note that this does not take account of reserved space: there may be another path with more available _and unreserved_ space. */ - public ImmutableOpenMap getNodeMostAvailableDiskUsages() { - return this.mostAvailableSpaceUsage; + public Map getNodeMostAvailableDiskUsages() { + return Collections.unmodifiableMap(this.mostAvailableSpaceUsage); + } + + /** + * Returns a node id to file cache stats mapping for the nodes that have search roles assigned to it. + */ + public Map getNodeFileCacheStats() { + return Collections.unmodifiableMap(this.nodeFileCacheStats); } /** @@ -242,6 +254,8 @@ static String shardIdentifierFromRouting(ShardRouting shardRouting) { /** * Represents a data path on a node + * + * @opensearch.internal */ public static class NodeAndPath implements Writeable { public final String nodeId; @@ -279,34 +293,37 @@ public void writeTo(StreamOutput out) throws IOException { /** * Represents the total amount of "reserved" space on a particular data path, together with the set of shards considered. + * + * @opensearch.internal */ public static class ReservedSpace implements Writeable { - public static final ReservedSpace EMPTY = new ReservedSpace(0, new ObjectHashSet<>()); + public static final ReservedSpace EMPTY = new ReservedSpace(0, new HashSet<>()); private final long total; - private final ObjectHashSet shardIds; + private final Set shardIds; - private ReservedSpace(long total, ObjectHashSet shardIds) { + private ReservedSpace(long total, Set shardIds) { this.total = total; - this.shardIds = shardIds; + this.shardIds = Collections.unmodifiableSet(shardIds); } ReservedSpace(StreamInput in) throws IOException { total = in.readVLong(); final int shardIdCount = in.readVInt(); - shardIds = new ObjectHashSet<>(shardIdCount); + Set shardIds = new HashSet<>(shardIdCount); for (int i = 0; i < shardIdCount; i++) { shardIds.add(new ShardId(in)); } + this.shardIds = Collections.unmodifiableSet(shardIds); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVLong(total); out.writeVInt(shardIds.size()); - for (ObjectCursor shardIdCursor : shardIds) { - shardIdCursor.value.writeTo(out); + for (final ShardId shardIdCursor : shardIds) { + shardIdCursor.writeTo(out); } } @@ -335,16 +352,21 @@ void toXContent(XContentBuilder builder, Params params) throws IOException { builder.field("total", total); builder.startArray("shards"); { - for (ObjectCursor shardIdCursor : shardIds) { - shardIdCursor.value.toXContent(builder, params); + for (final ShardId shardIdCursor : shardIds) { + shardIdCursor.toXContent(builder, params); } } builder.endArray(); // end "shards" } + /** + * Builder for Reserved Space. + * + * @opensearch.internal + */ public static class Builder { private long total; - private ObjectHashSet shardIds = new ObjectHashSet<>(); + private Set shardIds = new HashSet<>(); public ReservedSpace build() { assert shardIds != null : "already built"; diff --git a/server/src/main/java/org/opensearch/cluster/ClusterInfoService.java b/server/src/main/java/org/opensearch/cluster/ClusterInfoService.java index 03ef68fbd1c4d..50675d11003bb 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterInfoService.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterInfoService.java @@ -36,6 +36,8 @@ /** * Interface for a class used to gather information about a cluster periodically. + * + * @opensearch.internal */ @FunctionalInterface public interface ClusterInfoService { diff --git a/server/src/main/java/org/opensearch/cluster/ClusterManagerNodeChangePredicate.java b/server/src/main/java/org/opensearch/cluster/ClusterManagerNodeChangePredicate.java new file mode 100644 index 0000000000000..b5c65dacb9542 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/ClusterManagerNodeChangePredicate.java @@ -0,0 +1,71 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster; + +import org.opensearch.cluster.node.DiscoveryNode; + +import java.util.function.Predicate; + +/** + * Utility class to build a predicate that accepts cluster state changes + * + * @opensearch.internal + */ +public final class ClusterManagerNodeChangePredicate { + + private ClusterManagerNodeChangePredicate() { + + } + + /** + * builds a predicate that will accept a cluster state only if it was generated after the current has + * (re-)joined the master + */ + public static Predicate build(ClusterState currentState) { + final long currentVersion = currentState.version(); + final DiscoveryNode clusterManagerNode = currentState.nodes().getClusterManagerNode(); + final String currentMasterId = clusterManagerNode == null ? null : clusterManagerNode.getEphemeralId(); + return newState -> { + final DiscoveryNode newClusterManager = newState.nodes().getClusterManagerNode(); + final boolean accept; + if (newClusterManager == null) { + accept = false; + } else if (newClusterManager.getEphemeralId().equals(currentMasterId) == false) { + accept = true; + } else { + accept = newState.version() > currentVersion; + } + return accept; + }; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/ClusterModule.java b/server/src/main/java/org/opensearch/cluster/ClusterModule.java index c85691b80d7c3..bad881f8bda76 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterModule.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterModule.java @@ -35,6 +35,7 @@ import org.opensearch.cluster.action.index.MappingUpdatedAction; import org.opensearch.cluster.action.index.NodeMappingRefreshAction; import org.opensearch.cluster.action.shard.ShardStateAction; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.cluster.metadata.ComponentTemplateMetadata; import org.opensearch.cluster.metadata.ComposableIndexTemplateMetadata; import org.opensearch.cluster.metadata.DataStreamMetadata; @@ -48,6 +49,7 @@ import org.opensearch.cluster.metadata.MetadataMappingService; import org.opensearch.cluster.metadata.MetadataUpdateSettingsService; import org.opensearch.cluster.metadata.RepositoriesMetadata; +import org.opensearch.cluster.metadata.WeightedRoutingMetadata; import org.opensearch.cluster.routing.DelayedAllocationService; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.routing.allocation.ExistingShardsAllocator; @@ -57,8 +59,8 @@ import org.opensearch.cluster.routing.allocation.decider.AllocationDeciders; import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.ClusterRebalanceAllocationDecider; -import org.opensearch.cluster.routing.allocation.decider.ConcurrentRecoveriesAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.ConcurrentRebalanceAllocationDecider; +import org.opensearch.cluster.routing.allocation.decider.ConcurrentRecoveriesAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.DiskThresholdDecider; import org.opensearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.FilterAllocationDecider; @@ -72,26 +74,28 @@ import org.opensearch.cluster.routing.allocation.decider.SameShardAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.ShardsLimitAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.SnapshotInProgressAllocationDecider; +import org.opensearch.cluster.routing.allocation.decider.TargetPoolAllocationDecider; import org.opensearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.ParseField; import org.opensearch.common.inject.AbstractModule; -import org.opensearch.common.io.stream.NamedWriteable; -import org.opensearch.common.io.stream.NamedWriteableRegistry.Entry; -import org.opensearch.common.io.stream.Writeable.Reader; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.NamedWriteable; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry.Entry; +import org.opensearch.core.common.io.stream.Writeable.Reader; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.gateway.GatewayAllocator; import org.opensearch.ingest.IngestMetadata; import org.opensearch.persistent.PersistentTasksCustomMetadata; import org.opensearch.persistent.PersistentTasksNodeService; import org.opensearch.plugins.ClusterPlugin; import org.opensearch.script.ScriptMetadata; +import org.opensearch.search.pipeline.SearchPipelineMetadata; import org.opensearch.snapshots.SnapshotsInfoService; import org.opensearch.tasks.Task; import org.opensearch.tasks.TaskResultsService; @@ -110,6 +114,8 @@ /** * Configures classes and services that affect the entire cluster. + * + * @opensearch.internal */ public class ClusterModule extends AbstractModule { @@ -167,6 +173,7 @@ public static List getNamedWriteables() { // Metadata registerMetadataCustom(entries, RepositoriesMetadata.TYPE, RepositoriesMetadata::new, RepositoriesMetadata::readDiffFrom); registerMetadataCustom(entries, IngestMetadata.TYPE, IngestMetadata::new, IngestMetadata::readDiffFrom); + registerMetadataCustom(entries, SearchPipelineMetadata.TYPE, SearchPipelineMetadata::new, SearchPipelineMetadata::readDiffFrom); registerMetadataCustom(entries, ScriptMetadata.TYPE, ScriptMetadata::new, ScriptMetadata::readDiffFrom); registerMetadataCustom(entries, IndexGraveyard.TYPE, IndexGraveyard::new, IndexGraveyard::readDiffFrom); registerMetadataCustom( @@ -188,6 +195,13 @@ public static List getNamedWriteables() { ComposableIndexTemplateMetadata::readDiffFrom ); registerMetadataCustom(entries, DataStreamMetadata.TYPE, DataStreamMetadata::new, DataStreamMetadata::readDiffFrom); + registerMetadataCustom(entries, WeightedRoutingMetadata.TYPE, WeightedRoutingMetadata::new, WeightedRoutingMetadata::readDiffFrom); + registerMetadataCustom( + entries, + DecommissionAttributeMetadata.TYPE, + DecommissionAttributeMetadata::new, + DecommissionAttributeMetadata::readDiffFrom + ); // Task Status (not Diffable) entries.add(new Entry(Task.Status.class, PersistentTasksNodeService.Status.NAME, PersistentTasksNodeService.Status::new)); return entries; @@ -210,13 +224,13 @@ public static List getNamedWriteables() { */ public static ClusterState filterCustomsForPre63Clients(ClusterState clusterState) { final ClusterState.Builder builder = ClusterState.builder(clusterState); - clusterState.customs().keysIt().forEachRemaining(name -> { + clusterState.customs().keySet().iterator().forEachRemaining(name -> { if (PRE_6_3_CLUSTER_CUSTOMS_WHITE_LIST.contains(name) == false) { builder.removeCustom(name); } }); final Metadata.Builder metaBuilder = Metadata.builder(clusterState.metadata()); - clusterState.metadata().customs().keysIt().forEachRemaining(name -> { + clusterState.metadata().customs().keySet().iterator().forEachRemaining(name -> { if (PRE_6_3_METADATA_CUSTOMS_WHITE_LIST.contains(name) == false) { metaBuilder.removeCustom(name); } @@ -237,6 +251,13 @@ public static List getNamedXWriteables() { entries.add( new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(IngestMetadata.TYPE), IngestMetadata::fromXContent) ); + entries.add( + new NamedXContentRegistry.Entry( + Metadata.Custom.class, + new ParseField(SearchPipelineMetadata.TYPE), + SearchPipelineMetadata::fromXContent + ) + ); entries.add( new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(ScriptMetadata.TYPE), ScriptMetadata::fromXContent) ); @@ -271,6 +292,20 @@ public static List getNamedXWriteables() { DataStreamMetadata::fromXContent ) ); + entries.add( + new NamedXContentRegistry.Entry( + Metadata.Custom.class, + new ParseField(WeightedRoutingMetadata.TYPE), + WeightedRoutingMetadata::fromXContent + ) + ); + entries.add( + new NamedXContentRegistry.Entry( + Metadata.Custom.class, + new ParseField(DecommissionAttributeMetadata.TYPE), + DecommissionAttributeMetadata::fromXContent + ) + ); return entries; } @@ -324,7 +359,7 @@ public static Collection createAllocationDeciders( addAllocationDecider(deciders, new ConcurrentRebalanceAllocationDecider(settings, clusterSettings)); addAllocationDecider(deciders, new ConcurrentRecoveriesAllocationDecider(settings, clusterSettings)); addAllocationDecider(deciders, new EnableAllocationDecider(settings, clusterSettings)); - addAllocationDecider(deciders, new NodeVersionAllocationDecider()); + addAllocationDecider(deciders, new NodeVersionAllocationDecider(settings)); addAllocationDecider(deciders, new SnapshotInProgressAllocationDecider()); addAllocationDecider(deciders, new RestoreInProgressAllocationDecider()); addAllocationDecider(deciders, new FilterAllocationDecider(settings, clusterSettings)); @@ -334,6 +369,7 @@ public static Collection createAllocationDeciders( addAllocationDecider(deciders, new ShardsLimitAllocationDecider(settings, clusterSettings)); addAllocationDecider(deciders, new AwarenessAllocationDecider(settings, clusterSettings)); addAllocationDecider(deciders, new NodeLoadAwareAllocationDecider(settings, clusterSettings)); + addAllocationDecider(deciders, new TargetPoolAllocationDecider()); clusterPlugins.stream() .flatMap(p -> p.createAllocationDeciders(settings, clusterSettings).stream()) diff --git a/server/src/main/java/org/opensearch/cluster/ClusterName.java b/server/src/main/java/org/opensearch/cluster/ClusterName.java index 86b182c6f6c2d..44321f7de9395 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterName.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterName.java @@ -32,16 +32,21 @@ package org.opensearch.cluster; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; import java.util.Objects; import java.util.function.Predicate; +/** + * Cluster Name + * + * @opensearch.internal + */ public class ClusterName implements Writeable { public static final Setting CLUSTER_NAME_SETTING = new Setting<>("cluster.name", "opensearch", (s) -> { diff --git a/server/src/main/java/org/opensearch/cluster/ClusterSettingsResponse.java b/server/src/main/java/org/opensearch/cluster/ClusterSettingsResponse.java new file mode 100644 index 0000000000000..3dc764cb5d520 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/ClusterSettingsResponse.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster; + +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse; + +import java.io.IOException; +import java.util.Objects; + +/** + * PluginSettings Response for Extensibility + * + * @opensearch.internal + */ +public class ClusterSettingsResponse extends TransportResponse { + private final Settings clusterSettings; + + public ClusterSettingsResponse(ClusterService clusterService) { + this.clusterSettings = clusterService.getSettings(); + } + + public ClusterSettingsResponse(StreamInput in) throws IOException { + super(in); + this.clusterSettings = Settings.readSettingsFromStream(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + Settings.writeSettingsToStream(clusterSettings, out); + } + + @Override + public String toString() { + return "ClusterSettingsResponse{" + "clusterSettings=" + clusterSettings + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClusterSettingsResponse that = (ClusterSettingsResponse) o; + return Objects.equals(clusterSettings, that.clusterSettings); + } + + @Override + public int hashCode() { + return Objects.hash(clusterSettings); + } + +} diff --git a/server/src/main/java/org/opensearch/cluster/ClusterState.java b/server/src/main/java/org/opensearch/cluster/ClusterState.java index 459c0b9502acf..1b87a60c2ccf5 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterState.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterState.java @@ -32,8 +32,6 @@ package org.opensearch.cluster; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.cluster.block.ClusterBlock; import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.coordination.CoordinationMetadata; @@ -50,28 +48,29 @@ import org.opensearch.cluster.routing.RoutingTable; import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.Strings; import org.opensearch.common.UUIDs; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.VersionedNamedWriteable; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.VersionedNamedWriteable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.discovery.Discovery; import java.io.IOException; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.Spliterators; import java.util.stream.StreamSupport; import static org.opensearch.cluster.coordination.Coordinator.ZEN1_BWC_TERM; @@ -81,7 +80,7 @@ *

    * The cluster state object is immutable with the exception of the {@link RoutingNodes} structure, which is * built on demand from the {@link RoutingTable}. - * The cluster state can be updated only on the master node. All updates are performed by on a + * The cluster state can be updated only on the cluster-manager node. All updates are performed by on a * single thread and controlled by the {@link ClusterService}. After every update the * {@link Discovery#publish} method publishes a new version of the cluster state to all other nodes in the * cluster. The actual publishing mechanism is delegated to the {@link Discovery#publish} method and depends on @@ -97,6 +96,8 @@ * make sure that the correct diffs are applied. If uuids don’t match, the {@link ClusterStateDiff#apply} method * throws the {@link IncompatibleClusterStateVersionException}, which causes the publishing mechanism to send * a full version of the cluster state to the node on which this exception was thrown. + * + * @opensearch.internal */ public class ClusterState implements ToXContentFragment, Diffable { @@ -104,6 +105,8 @@ public class ClusterState implements ToXContentFragment, Diffable /** * An interface that implementors use when a class requires a client to maybe have a feature. + * + * @opensearch.internal */ public interface FeatureAware { @@ -133,6 +136,11 @@ static boolean shouldSerializ } + /** + * Custom cluster state. + * + * @opensearch.internal + */ public interface Custom extends NamedDiffable, ToXContentFragment, FeatureAware { /** @@ -163,13 +171,13 @@ default boolean isPrivate() { private final ClusterBlocks blocks; - private final ImmutableOpenMap customs; + private final Map customs; private final ClusterName clusterName; private final boolean wasReadFromDiff; - private final int minimumMasterNodesOnPublishingMaster; + private final int minimumClusterManagerNodesOnPublishingClusterManager; // built on demand private volatile RoutingNodes routingNodes; @@ -197,8 +205,8 @@ public ClusterState( RoutingTable routingTable, DiscoveryNodes nodes, ClusterBlocks blocks, - ImmutableOpenMap customs, - int minimumMasterNodesOnPublishingMaster, + final Map customs, + int minimumClusterManagerNodesOnPublishingClusterManager, boolean wasReadFromDiff ) { this.version = version; @@ -208,8 +216,8 @@ public ClusterState( this.routingTable = routingTable; this.nodes = nodes; this.blocks = blocks; - this.customs = customs; - this.minimumMasterNodesOnPublishingMaster = minimumMasterNodesOnPublishingMaster; + this.customs = Collections.unmodifiableMap(customs); + this.minimumClusterManagerNodesOnPublishingClusterManager = minimumClusterManagerNodesOnPublishingClusterManager; this.wasReadFromDiff = wasReadFromDiff; } @@ -226,8 +234,9 @@ public long getVersion() { } public long getVersionOrMetadataVersion() { - // When following a Zen1 master, the cluster state version is not guaranteed to increase, so instead it is preferable to use the - // metadata version to determine the freshest node. However when following a Zen2 master the cluster state version should be used. + // When following a Zen1 cluster-manager, the cluster state version is not guaranteed to increase, + // so instead it is preferable to use the metadata version to determine the freshest node. + // However when following a Zen2 cluster-manager the cluster state version should be used. return term() == ZEN1_BWC_TERM ? metadata().version() : version(); } @@ -275,11 +284,11 @@ public ClusterBlocks getBlocks() { return blocks; } - public ImmutableOpenMap customs() { + public Map customs() { return this.customs; } - public ImmutableOpenMap getCustoms() { + public Map getCustoms() { return this.customs; } @@ -365,9 +374,9 @@ public String toString() { } if (metadata.customs().isEmpty() == false) { sb.append("metadata customs:\n"); - for (final ObjectObjectCursor cursor : metadata.customs()) { - final String type = cursor.key; - final Metadata.Custom custom = cursor.value; + for (final Map.Entry cursor : metadata.customs().entrySet()) { + final String type = cursor.getKey(); + final Metadata.Custom custom = cursor.getValue(); sb.append(TAB).append(type).append(": ").append(custom); } sb.append("\n"); @@ -378,9 +387,9 @@ public String toString() { sb.append(getRoutingNodes()); if (customs.isEmpty() == false) { sb.append("customs:\n"); - for (ObjectObjectCursor cursor : customs) { - final String type = cursor.key; - final Custom custom = cursor.value; + for (final Map.Entry cursor : customs.entrySet()) { + final String type = cursor.getKey(); + final Custom custom = cursor.getValue(); sb.append(TAB).append(type).append(": ").append(custom); } } @@ -388,18 +397,23 @@ public String toString() { } /** - * a cluster state supersedes another state if they are from the same master and the version of this state is higher than that of the + * a cluster state supersedes another state if they are from the same cluster-manager and the version of this state is higher than that of the * other state. *

    * In essence that means that all the changes from the other cluster state are also reflected by the current one */ public boolean supersedes(ClusterState other) { - return this.nodes().getMasterNodeId() != null - && this.nodes().getMasterNodeId().equals(other.nodes().getMasterNodeId()) + return this.nodes().getClusterManagerNodeId() != null + && this.nodes().getClusterManagerNodeId().equals(other.nodes().getClusterManagerNodeId()) && this.version() > other.version(); } + /** + * Metrics for cluster state. + * + * @opensearch.internal + */ public enum Metric { VERSION("version"), @@ -470,18 +484,18 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } if (metrics.contains(Metric.MASTER_NODE)) { - builder.field("master_node", nodes().getMasterNodeId()); + builder.field("master_node", nodes().getClusterManagerNodeId()); } // Value of the field is identical with the above, and aims to replace the above field. if (metrics.contains(Metric.CLUSTER_MANAGER_NODE)) { - builder.field("cluster_manager_node", nodes().getMasterNodeId()); + builder.field("cluster_manager_node", nodes().getClusterManagerNodeId()); } if (metrics.contains(Metric.BLOCKS)) { builder.startObject("blocks"); - if (!blocks().global().isEmpty()) { + if (blocks().global().isEmpty() == false) { builder.startObject("global"); for (ClusterBlock block : blocks().global()) { block.toXContent(builder, params); @@ -489,11 +503,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endObject(); } - if (!blocks().indices().isEmpty()) { + if (blocks().indices().isEmpty() == false) { builder.startObject("indices"); - for (ObjectObjectCursor> entry : blocks().indices()) { - builder.startObject(entry.key); - for (ClusterBlock block : entry.value) { + for (final Map.Entry> entry : blocks().indices().entrySet()) { + builder.startObject(entry.getKey()); + for (ClusterBlock block : entry.getValue()) { block.toXContent(builder, params); } builder.endObject(); @@ -561,9 +575,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endObject(); } if (metrics.contains(Metric.CUSTOMS)) { - for (ObjectObjectCursor cursor : customs) { - builder.startObject(cursor.key); - cursor.value.toXContent(builder, params); + for (final Map.Entry cursor : customs.entrySet()) { + builder.startObject(cursor.getKey()); + cursor.getValue().toXContent(builder, params); builder.endObject(); } } @@ -579,18 +593,23 @@ public static Builder builder(ClusterState state) { return new Builder(state); } + /** + * Builder for cluster state. + * + * @opensearch.internal + */ public static class Builder { private final ClusterName clusterName; private long version = 0; - private String uuid = UNKNOWN_UUID; + private String uuid = Strings.UNKNOWN_UUID_VALUE; private Metadata metadata = Metadata.EMPTY_METADATA; private RoutingTable routingTable = RoutingTable.EMPTY_ROUTING_TABLE; private DiscoveryNodes nodes = DiscoveryNodes.EMPTY_NODES; private ClusterBlocks blocks = ClusterBlocks.EMPTY_CLUSTER_BLOCK; - private final ImmutableOpenMap.Builder customs; + private final Map customs; private boolean fromDiff; - private int minimumMasterNodesOnPublishingMaster = -1; + private int minimumClusterManagerNodesOnPublishingClusterManager = -1; public Builder(ClusterState state) { this.clusterName = state.clusterName; @@ -600,13 +619,13 @@ public Builder(ClusterState state) { this.routingTable = state.routingTable(); this.metadata = state.metadata(); this.blocks = state.blocks(); - this.customs = ImmutableOpenMap.builder(state.customs()); - this.minimumMasterNodesOnPublishingMaster = state.minimumMasterNodesOnPublishingMaster; + this.customs = new HashMap<>(state.customs()); + this.minimumClusterManagerNodesOnPublishingClusterManager = state.minimumClusterManagerNodesOnPublishingClusterManager; this.fromDiff = false; } public Builder(ClusterName clusterName) { - customs = ImmutableOpenMap.builder(); + customs = new HashMap<>(); this.clusterName = clusterName; } @@ -662,8 +681,8 @@ public Builder stateUUID(String uuid) { return this; } - public Builder minimumMasterNodesOnPublishingMaster(int minimumMasterNodesOnPublishingMaster) { - this.minimumMasterNodesOnPublishingMaster = minimumMasterNodesOnPublishingMaster; + public Builder minimumClusterManagerNodesOnPublishingClusterManager(int minimumClusterManagerNodesOnPublishingClusterManager) { + this.minimumClusterManagerNodesOnPublishingClusterManager = minimumClusterManagerNodesOnPublishingClusterManager; return this; } @@ -677,8 +696,9 @@ public Builder removeCustom(String type) { return this; } - public Builder customs(ImmutableOpenMap customs) { - StreamSupport.stream(customs.spliterator(), false).forEach(cursor -> Objects.requireNonNull(cursor.value, cursor.key)); + public Builder customs(final Map customs) { + StreamSupport.stream(Spliterators.spliterator(customs.entrySet(), 0), false) + .forEach(cursor -> Objects.requireNonNull(cursor.getValue(), cursor.getKey())); this.customs.putAll(customs); return this; } @@ -700,8 +720,8 @@ public ClusterState build() { routingTable, nodes, blocks, - customs.build(), - minimumMasterNodesOnPublishingMaster, + customs, + minimumClusterManagerNodesOnPublishingClusterManager, fromDiff ); } @@ -746,7 +766,7 @@ public static ClusterState readFrom(StreamInput in, DiscoveryNode localNode) thr Custom customIndexMetadata = in.readNamedWriteable(Custom.class); builder.putCustom(customIndexMetadata.getWriteableName(), customIndexMetadata); } - builder.minimumMasterNodesOnPublishingMaster = in.readVInt(); + builder.minimumClusterManagerNodesOnPublishingClusterManager = in.readVInt(); return builder.build(); } @@ -761,20 +781,25 @@ public void writeTo(StreamOutput out) throws IOException { blocks.writeTo(out); // filter out custom states not supported by the other node int numberOfCustoms = 0; - for (final ObjectCursor cursor : customs.values()) { - if (FeatureAware.shouldSerialize(out, cursor.value)) { + for (final Custom custom : customs.values()) { + if (FeatureAware.shouldSerialize(out, custom)) { numberOfCustoms++; } } out.writeVInt(numberOfCustoms); - for (final ObjectCursor cursor : customs.values()) { - if (FeatureAware.shouldSerialize(out, cursor.value)) { - out.writeNamedWriteable(cursor.value); + for (final Custom custom : customs.values()) { + if (FeatureAware.shouldSerialize(out, custom)) { + out.writeNamedWriteable(custom); } } - out.writeVInt(minimumMasterNodesOnPublishingMaster); + out.writeVInt(minimumClusterManagerNodesOnPublishingClusterManager); } + /** + * The cluster state diff. + * + * @opensearch.internal + */ private static class ClusterStateDiff implements Diff { private final long toVersion; @@ -793,9 +818,9 @@ private static class ClusterStateDiff implements Diff { private final Diff blocks; - private final Diff> customs; + private final Diff> customs; - private final int minimumMasterNodesOnPublishingMaster; + private final int minimumClusterManagerNodesOnPublishingClusterManager; ClusterStateDiff(ClusterState before, ClusterState after) { fromUuid = before.stateUUID; @@ -807,7 +832,7 @@ private static class ClusterStateDiff implements Diff { metadata = after.metadata.diff(before.metadata); blocks = after.blocks.diff(before.blocks); customs = DiffableUtils.diff(before.customs, after.customs, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); - minimumMasterNodesOnPublishingMaster = after.minimumMasterNodesOnPublishingMaster; + minimumClusterManagerNodesOnPublishingClusterManager = after.minimumClusterManagerNodesOnPublishingClusterManager; } ClusterStateDiff(StreamInput in, DiscoveryNode localNode) throws IOException { @@ -819,8 +844,8 @@ private static class ClusterStateDiff implements Diff { nodes = DiscoveryNodes.readDiffFrom(in, localNode); metadata = Metadata.readDiffFrom(in); blocks = ClusterBlocks.readDiffFrom(in); - customs = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); - minimumMasterNodesOnPublishingMaster = in.readVInt(); + customs = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); + minimumClusterManagerNodesOnPublishingClusterManager = in.readVInt(); } @Override @@ -834,7 +859,7 @@ public void writeTo(StreamOutput out) throws IOException { metadata.writeTo(out); blocks.writeTo(out); customs.writeTo(out); - out.writeVInt(minimumMasterNodesOnPublishingMaster); + out.writeVInt(minimumClusterManagerNodesOnPublishingClusterManager); } @Override @@ -854,7 +879,7 @@ public ClusterState apply(ClusterState state) { builder.metadata(metadata.apply(state.metadata)); builder.blocks(blocks.apply(state.blocks)); builder.customs(customs.apply(state.customs)); - builder.minimumMasterNodesOnPublishingMaster(minimumMasterNodesOnPublishingMaster); + builder.minimumClusterManagerNodesOnPublishingClusterManager(minimumClusterManagerNodesOnPublishingClusterManager); builder.fromDiff(true); return builder.build(); } diff --git a/server/src/main/java/org/opensearch/cluster/ClusterStateApplier.java b/server/src/main/java/org/opensearch/cluster/ClusterStateApplier.java index 6ba7cba0b463d..140e6426bb801 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterStateApplier.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterStateApplier.java @@ -37,6 +37,8 @@ /** * A component that is in charge of applying an incoming cluster state to the node internal data structures. * The single apply method is called before the cluster state becomes visible via {@link ClusterService#state()}. + * + * @opensearch.internal */ public interface ClusterStateApplier { diff --git a/server/src/main/java/org/opensearch/cluster/ClusterStateListener.java b/server/src/main/java/org/opensearch/cluster/ClusterStateListener.java index bbd41508fd630..01a8e51a3d13e 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterStateListener.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterStateListener.java @@ -35,7 +35,7 @@ /** * A listener to be notified when a cluster state changes. * - * + * @opensearch.internal */ public interface ClusterStateListener { diff --git a/server/src/main/java/org/opensearch/cluster/ClusterStateObserver.java b/server/src/main/java/org/opensearch/cluster/ClusterStateObserver.java index 5d55ce70aec02..7945afd120350 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterStateObserver.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterStateObserver.java @@ -50,6 +50,8 @@ * A utility class which simplifies interacting with the cluster state in cases where * one tries to take action based on the current state but may want to wait for a new state * and retry upon failure. + * + * @opensearch.internal */ public class ClusterStateObserver { @@ -187,7 +189,7 @@ public void waitForNextChange(Listener listener, Predicate statePr // sample a new state. This state maybe *older* than the supplied state if we are called from an applier, // which wants to wait for something else to happen ClusterState newState = clusterApplierService.state(); - if (lastObservedState.get().isOlderOrDifferentMaster(newState) && statePredicate.test(newState)) { + if (lastObservedState.get().isOlderOrDifferentClusterManager(newState) && statePredicate.test(newState)) { // good enough, let's go. logger.trace("observer: sampled state accepted by predicate ({})", newState); lastObservedState.set(new StoredState(newState)); @@ -205,6 +207,11 @@ public void waitForNextChange(Listener listener, Predicate statePr } } + /** + * An observer of the cluster state for changes. + * + * @opensearch.internal + */ class ObserverClusterStateListener implements TimeoutClusterStateListener { @Override @@ -241,7 +248,7 @@ public void postAdded() { return; } ClusterState newState = clusterApplierService.state(); - if (lastObservedState.get().isOlderOrDifferentMaster(newState) && context.statePredicate.test(newState)) { + if (lastObservedState.get().isOlderOrDifferentClusterManager(newState) && context.statePredicate.test(newState)) { // double check we're still listening if (observingContext.compareAndSet(context, null)) { logger.trace("observer: post adding listener: accepting current cluster state ({})", newState); @@ -295,25 +302,33 @@ public String toString() { } /** - * The observer considers two cluster states to be the same if they have the same version and master node id (i.e. null or set) + * The observer considers two cluster states to be the same if they have the same version and cluster-manager node id (i.e. null or set) + * + * @opensearch.internal */ private static class StoredState { - private final String masterNodeId; + private final String clusterManagerNodeId; private final long version; StoredState(ClusterState clusterState) { - this.masterNodeId = clusterState.nodes().getMasterNodeId(); + this.clusterManagerNodeId = clusterState.nodes().getClusterManagerNodeId(); this.version = clusterState.version(); } /** - * returns true if stored state is older then given state or they are from a different master, meaning they can't be compared + * returns true if stored state is older then given state or they are from a different cluster-manager, meaning they can't be compared * */ - public boolean isOlderOrDifferentMaster(ClusterState clusterState) { - return version < clusterState.version() || Objects.equals(masterNodeId, clusterState.nodes().getMasterNodeId()) == false; + public boolean isOlderOrDifferentClusterManager(ClusterState clusterState) { + return version < clusterState.version() + || Objects.equals(clusterManagerNodeId, clusterState.nodes().getClusterManagerNodeId()) == false; } } + /** + * Listener for the observer. + * + * @opensearch.internal + */ public interface Listener { /** called when a new state is observed */ @@ -325,6 +340,11 @@ public interface Listener { void onTimeout(TimeValue timeout); } + /** + * Context for the observer. + * + * @opensearch.internal + */ static class ObservingContext { public final Listener listener; public final Predicate statePredicate; @@ -340,6 +360,11 @@ public String toString() { } } + /** + * A context preserving listener. + * + * @opensearch.internal + */ private static final class ContextPreservingListener implements Listener { private final Listener delegate; private final Supplier contextSupplier; diff --git a/server/src/main/java/org/opensearch/cluster/ClusterStateTaskConfig.java b/server/src/main/java/org/opensearch/cluster/ClusterStateTaskConfig.java index ec038df7a9096..9a4b708548a7d 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterStateTaskConfig.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterStateTaskConfig.java @@ -37,6 +37,8 @@ /** * Cluster state update task configuration for timeout and priority + * + * @opensearch.internal */ public interface ClusterStateTaskConfig { /** @@ -83,6 +85,11 @@ static ClusterStateTaskConfig build(Priority priority, TimeValue timeout) { return new Basic(priority, timeout); } + /** + * Basic task config. + * + * @opensearch.internal + */ class Basic implements ClusterStateTaskConfig { final TimeValue timeout; final Priority priority; diff --git a/server/src/main/java/org/opensearch/cluster/ClusterStateTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/ClusterStateTaskExecutor.java index 04002b31a8b3e..50beeb1f03deb 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterStateTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterStateTaskExecutor.java @@ -31,12 +31,18 @@ package org.opensearch.cluster; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.common.Nullable; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +/** + * Interface that updates the cluster state based on the task + * + * @opensearch.internal + */ public interface ClusterStateTaskExecutor { /** * Update the cluster state based on the current state and the given tasks. Return the *same instance* if no state @@ -45,12 +51,22 @@ public interface ClusterStateTaskExecutor { ClusterTasksResult execute(ClusterState currentState, List tasks) throws Exception; /** - * indicates whether this executor should only run if the current node is master + * indicates whether this executor should only run if the current node is cluster-manager */ - default boolean runOnlyOnMaster() { + default boolean runOnlyOnClusterManager() { return true; } + /** + * indicates whether this executor should only run if the current node is cluster-manager + * + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #runOnlyOnClusterManager()} + */ + @Deprecated + default boolean runOnlyOnMaster() { + return runOnlyOnClusterManager(); + } + /** * Callback invoked after new cluster state is published. Note that * this method is not invoked if the cluster state was not updated. @@ -73,9 +89,21 @@ default String describeTasks(List tasks) { return String.join(", ", tasks.stream().map(t -> (CharSequence) t.toString()).filter(t -> t.length() > 0)::iterator); } + /** + * Throttling key associated with the task, on which cluster manager node will do aggregation count + * and perform throttling based on configured threshold in cluster setting. + */ + default ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + // Default task is not registered with clusterService.registerClusterMangerTask, + // User can't configure throttling limit on it and will be bypassed while throttling on cluster manager + return ClusterManagerTaskThrottler.DEFAULT_THROTTLING_KEY; + } + /** * Represents the result of a batched execution of cluster state update tasks * @param the type of the cluster state update task + * + * @opensearch.internal */ class ClusterTasksResult { @Nullable @@ -96,6 +124,11 @@ public static Builder builder() { return new Builder<>(); } + /** + * Builder for cluster state task. + * + * @opensearch.internal + */ public static class Builder { private final Map executionResults = new IdentityHashMap<>(); @@ -137,6 +170,11 @@ ClusterTasksResult build(ClusterTasksResult result, ClusterState previousS } } + /** + * The task result. + * + * @opensearch.internal + */ final class TaskResult { private final Exception failure; diff --git a/server/src/main/java/org/opensearch/cluster/ClusterStateTaskListener.java b/server/src/main/java/org/opensearch/cluster/ClusterStateTaskListener.java index 718df33f8a2d2..d6c4abfad7b8d 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterStateTaskListener.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterStateTaskListener.java @@ -31,10 +31,15 @@ package org.opensearch.cluster; -import org.opensearch.cluster.service.MasterService; +import org.opensearch.cluster.service.ClusterManagerService; import java.util.List; +/** + * Interface to implement a cluster state change listener + * + * @opensearch.internal + */ public interface ClusterStateTaskListener { /** @@ -43,11 +48,22 @@ public interface ClusterStateTaskListener { void onFailure(String source, Exception e); /** - * called when the task was rejected because the local node is no longer master. - * Used only for tasks submitted to {@link MasterService}. + * called when the task was rejected because the local node is no longer cluster-manager. + * Used only for tasks submitted to {@link ClusterManagerService}. + */ + default void onNoLongerClusterManager(String source) { + onFailure(source, new NotClusterManagerException("no longer cluster-manager. source: [" + source + "]")); + } + + /** + * called when the task was rejected because the local node is no longer cluster-manager. + * Used only for tasks submitted to {@link ClusterManagerService}. + * + * @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #onNoLongerClusterManager(String)} */ + @Deprecated default void onNoLongerMaster(String source) { - onFailure(source, new NotMasterException("no longer master. source: [" + source + "]")); + onNoLongerClusterManager(source); } /** diff --git a/server/src/main/java/org/opensearch/cluster/ClusterStateUpdateTask.java b/server/src/main/java/org/opensearch/cluster/ClusterStateUpdateTask.java index 72d72158a5f0b..9225914a931b2 100644 --- a/server/src/main/java/org/opensearch/cluster/ClusterStateUpdateTask.java +++ b/server/src/main/java/org/opensearch/cluster/ClusterStateUpdateTask.java @@ -40,6 +40,8 @@ /** * A task that can update the cluster state. + * + * @opensearch.internal */ public abstract class ClusterStateUpdateTask implements @@ -101,11 +103,11 @@ public Priority priority() { } /** - * Marked as final as cluster state update tasks should only run on master. + * Marked as final as cluster state update tasks should only run on cluster-manager. * For local requests, use {@link LocalClusterUpdateTask} instead. */ @Override - public final boolean runOnlyOnMaster() { + public final boolean runOnlyOnClusterManager() { return true; } } diff --git a/server/src/main/java/org/opensearch/cluster/Diff.java b/server/src/main/java/org/opensearch/cluster/Diff.java index 165fb750f3f53..c0e8e7038d9b4 100644 --- a/server/src/main/java/org/opensearch/cluster/Diff.java +++ b/server/src/main/java/org/opensearch/cluster/Diff.java @@ -32,10 +32,12 @@ package org.opensearch.cluster; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.Writeable; /** * Represents difference between states of cluster state parts + * + * @opensearch.internal */ public interface Diff extends Writeable { diff --git a/server/src/main/java/org/opensearch/cluster/Diffable.java b/server/src/main/java/org/opensearch/cluster/Diffable.java index 3dcac5459d27d..a3fedf4903089 100644 --- a/server/src/main/java/org/opensearch/cluster/Diffable.java +++ b/server/src/main/java/org/opensearch/cluster/Diffable.java @@ -32,10 +32,12 @@ package org.opensearch.cluster; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.Writeable; /** * Cluster state part, changes in which can be serialized + * + * @opensearch.internal */ public interface Diffable extends Writeable { diff --git a/server/src/main/java/org/opensearch/cluster/DiffableUtils.java b/server/src/main/java/org/opensearch/cluster/DiffableUtils.java index 76da490eca1f3..dd2232968114e 100644 --- a/server/src/main/java/org/opensearch/cluster/DiffableUtils.java +++ b/server/src/main/java/org/opensearch/cluster/DiffableUtils.java @@ -32,16 +32,10 @@ package org.opensearch.cluster; -import com.carrotsearch.hppc.cursors.IntCursor; -import com.carrotsearch.hppc.cursors.IntObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.Version; -import org.opensearch.common.collect.ImmutableOpenIntMap; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable.Reader; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable.Reader; import java.io.IOException; import java.util.ArrayList; @@ -53,6 +47,11 @@ import java.util.Map; import java.util.Set; +/** + * Utility class for a diffable + * + * @opensearch.internal + */ public final class DiffableUtils { private DiffableUtils() {} @@ -77,56 +76,6 @@ public static KeySerializer getVIntKeySerializer() { return VIntKeySerializer.INSTANCE; } - /** - * Calculates diff between two ImmutableOpenMaps of Diffable objects - */ - public static > MapDiff> diff( - ImmutableOpenMap before, - ImmutableOpenMap after, - KeySerializer keySerializer - ) { - assert after != null && before != null; - return new ImmutableOpenMapDiff<>(before, after, keySerializer, DiffableValueSerializer.getWriteOnlyInstance()); - } - - /** - * Calculates diff between two ImmutableOpenMaps of non-diffable objects - */ - public static MapDiff> diff( - ImmutableOpenMap before, - ImmutableOpenMap after, - KeySerializer keySerializer, - ValueSerializer valueSerializer - ) { - assert after != null && before != null; - return new ImmutableOpenMapDiff<>(before, after, keySerializer, valueSerializer); - } - - /** - * Calculates diff between two ImmutableOpenIntMaps of Diffable objects - */ - public static > MapDiff> diff( - ImmutableOpenIntMap before, - ImmutableOpenIntMap after, - KeySerializer keySerializer - ) { - assert after != null && before != null; - return new ImmutableOpenIntMapDiff<>(before, after, keySerializer, DiffableValueSerializer.getWriteOnlyInstance()); - } - - /** - * Calculates diff between two ImmutableOpenIntMaps of non-diffable objects - */ - public static MapDiff> diff( - ImmutableOpenIntMap before, - ImmutableOpenIntMap after, - KeySerializer keySerializer, - ValueSerializer valueSerializer - ) { - assert after != null && before != null; - return new ImmutableOpenIntMapDiff<>(before, after, keySerializer, valueSerializer); - } - /** * Calculates diff between two Maps of Diffable objects. */ @@ -152,28 +101,6 @@ public static MapDiff> diff( return new JdkMapDiff<>(before, after, keySerializer, valueSerializer); } - /** - * Loads an object that represents difference between two ImmutableOpenMaps - */ - public static MapDiff> readImmutableOpenMapDiff( - StreamInput in, - KeySerializer keySerializer, - ValueSerializer valueSerializer - ) throws IOException { - return new ImmutableOpenMapDiff<>(in, keySerializer, valueSerializer); - } - - /** - * Loads an object that represents difference between two ImmutableOpenMaps - */ - public static MapDiff> readImmutableOpenIntMapDiff( - StreamInput in, - KeySerializer keySerializer, - ValueSerializer valueSerializer - ) throws IOException { - return new ImmutableOpenIntMapDiff<>(in, keySerializer, valueSerializer); - } - /** * Loads an object that represents difference between two Maps of Diffable objects */ @@ -185,29 +112,6 @@ public static MapDiff> readJdkMapDiff( return new JdkMapDiff<>(in, keySerializer, valueSerializer); } - /** - * Loads an object that represents difference between two ImmutableOpenMaps of Diffable objects using Diffable proto object - */ - public static > MapDiff> readImmutableOpenMapDiff( - StreamInput in, - KeySerializer keySerializer, - DiffableValueReader diffableValueReader - ) throws IOException { - return new ImmutableOpenMapDiff<>(in, keySerializer, diffableValueReader); - } - - /** - * Loads an object that represents difference between two ImmutableOpenIntMaps of Diffable objects using Diffable proto object - */ - public static > MapDiff> readImmutableOpenIntMapDiff( - StreamInput in, - KeySerializer keySerializer, - Reader reader, - Reader> diffReader - ) throws IOException { - return new ImmutableOpenIntMapDiff<>(in, keySerializer, new DiffableValueReader<>(reader, diffReader)); - } - /** * Loads an object that represents difference between two Maps of Diffable objects using Diffable proto object */ @@ -224,6 +128,8 @@ public static > MapDiff> readJdkMapDiff * Represents differences between two Maps of (possibly diffable) objects. * * @param the diffable object + * + * @opensearch.internal */ private static class JdkMapDiff extends MapDiff> { @@ -274,153 +180,6 @@ public Map apply(Map map) { } } - /** - * Represents differences between two ImmutableOpenMap of (possibly diffable) objects - * - * @param the object type - */ - public static class ImmutableOpenMapDiff extends MapDiff> { - - protected ImmutableOpenMapDiff(StreamInput in, KeySerializer keySerializer, ValueSerializer valueSerializer) - throws IOException { - super(in, keySerializer, valueSerializer); - } - - private ImmutableOpenMapDiff( - KeySerializer keySerializer, - ValueSerializer valueSerializer, - List deletes, - Map> diffs, - Map upserts - ) { - super(keySerializer, valueSerializer, deletes, diffs, upserts); - } - - public ImmutableOpenMapDiff( - ImmutableOpenMap before, - ImmutableOpenMap after, - KeySerializer keySerializer, - ValueSerializer valueSerializer - ) { - super(keySerializer, valueSerializer); - assert after != null && before != null; - - for (ObjectCursor key : before.keys()) { - if (!after.containsKey(key.value)) { - deletes.add(key.value); - } - } - - for (ObjectObjectCursor partIter : after) { - T beforePart = before.get(partIter.key); - if (beforePart == null) { - upserts.put(partIter.key, partIter.value); - } else if (partIter.value.equals(beforePart) == false) { - if (valueSerializer.supportsDiffableValues()) { - diffs.put(partIter.key, valueSerializer.diff(partIter.value, beforePart)); - } else { - upserts.put(partIter.key, partIter.value); - } - } - } - } - - /** - * Returns a new diff map with the given key removed, does not modify the invoking instance. - * If the key does not exist in the diff map, the same instance is returned. - */ - public ImmutableOpenMapDiff withKeyRemoved(K key) { - if (this.diffs.containsKey(key) == false && this.upserts.containsKey(key) == false) { - return this; - } - Map> newDiffs = new HashMap<>(this.diffs); - newDiffs.remove(key); - Map newUpserts = new HashMap<>(this.upserts); - newUpserts.remove(key); - return new ImmutableOpenMapDiff<>(this.keySerializer, this.valueSerializer, this.deletes, newDiffs, newUpserts); - } - - @Override - public ImmutableOpenMap apply(ImmutableOpenMap map) { - ImmutableOpenMap.Builder builder = ImmutableOpenMap.builder(); - builder.putAll(map); - - for (K part : deletes) { - builder.remove(part); - } - - for (Map.Entry> diff : diffs.entrySet()) { - builder.put(diff.getKey(), diff.getValue().apply(builder.get(diff.getKey()))); - } - - for (Map.Entry upsert : upserts.entrySet()) { - builder.put(upsert.getKey(), upsert.getValue()); - } - return builder.build(); - } - } - - /** - * Represents differences between two ImmutableOpenIntMap of (possibly diffable) objects - * - * @param the object type - */ - private static class ImmutableOpenIntMapDiff extends MapDiff> { - - protected ImmutableOpenIntMapDiff(StreamInput in, KeySerializer keySerializer, ValueSerializer valueSerializer) - throws IOException { - super(in, keySerializer, valueSerializer); - } - - ImmutableOpenIntMapDiff( - ImmutableOpenIntMap before, - ImmutableOpenIntMap after, - KeySerializer keySerializer, - ValueSerializer valueSerializer - ) { - super(keySerializer, valueSerializer); - assert after != null && before != null; - - for (IntCursor key : before.keys()) { - if (!after.containsKey(key.value)) { - deletes.add(key.value); - } - } - - for (IntObjectCursor partIter : after) { - T beforePart = before.get(partIter.key); - if (beforePart == null) { - upserts.put(partIter.key, partIter.value); - } else if (partIter.value.equals(beforePart) == false) { - if (valueSerializer.supportsDiffableValues()) { - diffs.put(partIter.key, valueSerializer.diff(partIter.value, beforePart)); - } else { - upserts.put(partIter.key, partIter.value); - } - } - } - } - - @Override - public ImmutableOpenIntMap apply(ImmutableOpenIntMap map) { - ImmutableOpenIntMap.Builder builder = ImmutableOpenIntMap.builder(); - builder.putAll(map); - - for (Integer part : deletes) { - builder.remove(part); - } - - for (Map.Entry> diff : diffs.entrySet()) { - builder.put(diff.getKey(), diff.getValue().apply(builder.get(diff.getKey()))); - } - - for (Map.Entry upsert : upserts.entrySet()) { - builder.put(upsert.getKey(), upsert.getValue()); - } - return builder.build(); - } - } - /** * Represents differences between two maps of objects and is used as base class for different map implementations. * @@ -429,6 +188,8 @@ public ImmutableOpenIntMap apply(ImmutableOpenIntMap map) { * @param the type of map keys * @param the type of map values * @param the map implementation type + * + * @opensearch.internal */ public abstract static class MapDiff implements Diff { @@ -548,6 +309,8 @@ public void writeTo(StreamOutput out) throws IOException { /** * Provides read and write operations to serialize keys of map * @param type of key + * + * @opensearch.internal */ public interface KeySerializer { void writeKey(K key, StreamOutput out) throws IOException; @@ -557,6 +320,8 @@ public interface KeySerializer { /** * Serializes String keys of a map + * + * @opensearch.internal */ private static final class StringKeySerializer implements KeySerializer { private static final StringKeySerializer INSTANCE = new StringKeySerializer(); @@ -574,6 +339,8 @@ public String readKey(StreamInput in) throws IOException { /** * Serializes Integer keys of a map as an Int + * + * @opensearch.internal */ private static final class IntKeySerializer implements KeySerializer { public static final IntKeySerializer INSTANCE = new IntKeySerializer(); @@ -591,6 +358,8 @@ public Integer readKey(StreamInput in) throws IOException { /** * Serializes Integer keys of a map as a VInt. Requires keys to be positive. + * + * @opensearch.internal */ private static final class VIntKeySerializer implements KeySerializer { public static final IntKeySerializer INSTANCE = new IntKeySerializer(); @@ -620,6 +389,8 @@ public Integer readKey(StreamInput in) throws IOException { * * @param key type of map * @param value type of map + * + * @opensearch.internal */ public interface ValueSerializer { @@ -674,6 +445,8 @@ default boolean supportsVersion(V value, Version version) { * * @param type of map keys * @param type of map values + * + * @opensearch.internal */ public abstract static class DiffableValueSerializer> implements ValueSerializer { private static final DiffableValueSerializer WRITE_ONLY_INSTANCE = new DiffableValueSerializer() { @@ -717,6 +490,8 @@ public void writeDiff(Diff value, StreamOutput out) throws IOException { * * @param type of map keys * @param type of map values + * + * @opensearch.internal */ public abstract static class NonDiffableValueSerializer implements ValueSerializer { @Override @@ -744,6 +519,8 @@ public Diff readDiff(StreamInput in, K key) throws IOException { * Implementation of the ValueSerializer that wraps value and diff readers. * * Note: this implementation is ignoring the key. + * + * @opensearch.internal */ public static class DiffableValueReader> extends DiffableValueSerializer { private final Reader reader; @@ -769,6 +546,8 @@ public Diff readDiff(StreamInput in, K key) throws IOException { * Implementation of ValueSerializer that serializes immutable sets * * @param type of map key + * + * @opensearch.internal */ public static class StringSetValueSerializer extends NonDiffableValueSerializer> { private static final StringSetValueSerializer INSTANCE = new StringSetValueSerializer(); diff --git a/server/src/main/java/org/opensearch/cluster/DiskUsage.java b/server/src/main/java/org/opensearch/cluster/DiskUsage.java index e7f04ffb749b9..c472522baee51 100644 --- a/server/src/main/java/org/opensearch/cluster/DiskUsage.java +++ b/server/src/main/java/org/opensearch/cluster/DiskUsage.java @@ -32,19 +32,21 @@ package org.opensearch.cluster; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.unit.ByteSizeValue; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.common.unit.ByteSizeValue; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.Objects; /** * Encapsulation class used to represent the amount of disk used on a node. + * + * @opensearch.internal */ public class DiskUsage implements ToXContentFragment, Writeable { final String nodeId; diff --git a/server/src/main/java/org/opensearch/cluster/EmptyClusterInfoService.java b/server/src/main/java/org/opensearch/cluster/EmptyClusterInfoService.java index cf2ce4c3f9ae0..27d1c706eb012 100644 --- a/server/src/main/java/org/opensearch/cluster/EmptyClusterInfoService.java +++ b/server/src/main/java/org/opensearch/cluster/EmptyClusterInfoService.java @@ -36,6 +36,8 @@ /** * {@link ClusterInfoService} that provides empty maps for disk usage and shard sizes + * + * @opensearch.internal */ public class EmptyClusterInfoService implements ClusterInfoService { public static final EmptyClusterInfoService INSTANCE = new EmptyClusterInfoService(); diff --git a/server/src/main/java/org/opensearch/cluster/IncompatibleClusterStateVersionException.java b/server/src/main/java/org/opensearch/cluster/IncompatibleClusterStateVersionException.java index 5ce71c651ae61..671748e52c544 100644 --- a/server/src/main/java/org/opensearch/cluster/IncompatibleClusterStateVersionException.java +++ b/server/src/main/java/org/opensearch/cluster/IncompatibleClusterStateVersionException.java @@ -33,12 +33,14 @@ package org.opensearch.cluster; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** * Thrown by {@link Diff#apply} method + * + * @opensearch.internal */ public class IncompatibleClusterStateVersionException extends OpenSearchException { public IncompatibleClusterStateVersionException(String msg) { diff --git a/server/src/main/java/org/opensearch/cluster/InternalClusterInfoService.java b/server/src/main/java/org/opensearch/cluster/InternalClusterInfoService.java index 05d91fdfd9ebb..35490d2f37a49 100644 --- a/server/src/main/java/org/opensearch/cluster/InternalClusterInfoService.java +++ b/server/src/main/java/org/opensearch/cluster/InternalClusterInfoService.java @@ -36,7 +36,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.LatchedActionListener; import org.opensearch.action.admin.cluster.node.stats.NodeStats; import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; @@ -51,19 +50,21 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.allocation.DiskThresholdSettings; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AbstractRunnable; -import org.opensearch.common.util.concurrent.OpenSearchRejectedExecutionException; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.concurrency.OpenSearchRejectedExecutionException; import org.opensearch.index.store.StoreStats; +import org.opensearch.index.store.remote.filecache.FileCacheStats; import org.opensearch.monitor.fs.FsInfo; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.ReceiveTimeoutTransportException; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -72,17 +73,20 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.stream.Collectors; /** * InternalClusterInfoService provides the ClusterInfoService interface, * routinely updated on a timer. The timer can be dynamically changed by * setting the cluster.info.update.interval setting (defaulting - * to 30 seconds). The InternalClusterInfoService only runs on the master node. + * to 30 seconds). The InternalClusterInfoService only runs on the cluster-manager node. * Listens for changes in the number of data nodes and immediately submits a * ClusterInfoUpdateJob if a node has been added. * * Every time the timer runs, gathers information about the disk usage and * shard sizes across the cluster. + * + * @opensearch.internal */ public class InternalClusterInfoService implements ClusterInfoService, ClusterStateListener { @@ -106,10 +110,11 @@ public class InternalClusterInfoService implements ClusterInfoService, ClusterSt private volatile TimeValue updateFrequency; - private volatile ImmutableOpenMap leastAvailableSpaceUsages; - private volatile ImmutableOpenMap mostAvailableSpaceUsages; + private volatile Map leastAvailableSpaceUsages; + private volatile Map mostAvailableSpaceUsages; + private volatile Map nodeFileCacheStats; private volatile IndicesStatsSummary indicesStatsSummary; - // null if this node is not currently the master + // null if this node is not currently the cluster-manager private final AtomicReference refreshAndRescheduleRunnable = new AtomicReference<>(); private volatile boolean enabled; private volatile TimeValue fetchTimeout; @@ -118,8 +123,9 @@ public class InternalClusterInfoService implements ClusterInfoService, ClusterSt private final List> listeners = new CopyOnWriteArrayList<>(); public InternalClusterInfoService(Settings settings, ClusterService clusterService, ThreadPool threadPool, Client client) { - this.leastAvailableSpaceUsages = ImmutableOpenMap.of(); - this.mostAvailableSpaceUsages = ImmutableOpenMap.of(); + this.leastAvailableSpaceUsages = Map.of(); + this.mostAvailableSpaceUsages = Map.of(); + this.nodeFileCacheStats = Map.of(); this.indicesStatsSummary = IndicesStatsSummary.EMPTY; this.threadPool = threadPool; this.client = client; @@ -149,14 +155,14 @@ void setUpdateFrequency(TimeValue updateFrequency) { @Override public void clusterChanged(ClusterChangedEvent event) { - if (event.localNodeMaster() && refreshAndRescheduleRunnable.get() == null) { - logger.trace("elected as master, scheduling cluster info update tasks"); - executeRefresh(event.state(), "became master"); + if (event.localNodeClusterManager() && refreshAndRescheduleRunnable.get() == null) { + logger.trace("elected as cluster-manager, scheduling cluster info update tasks"); + executeRefresh(event.state(), "became cluster-manager"); final RefreshAndRescheduleRunnable newRunnable = new RefreshAndRescheduleRunnable(); refreshAndRescheduleRunnable.set(newRunnable); threadPool.scheduleUnlessShuttingDown(updateFrequency, REFRESH_EXECUTOR, newRunnable); - } else if (event.localNodeMaster() == false) { + } else if (event.localNodeClusterManager() == false) { refreshAndRescheduleRunnable.set(null); return; } @@ -178,14 +184,14 @@ public void clusterChanged(ClusterChangedEvent event) { if (removedNode.isDataNode()) { logger.trace("Removing node from cluster info: {}", removedNode.getId()); if (leastAvailableSpaceUsages.containsKey(removedNode.getId())) { - ImmutableOpenMap.Builder newMaxUsages = ImmutableOpenMap.builder(leastAvailableSpaceUsages); + Map newMaxUsages = new HashMap<>(leastAvailableSpaceUsages); newMaxUsages.remove(removedNode.getId()); - leastAvailableSpaceUsages = newMaxUsages.build(); + leastAvailableSpaceUsages = Collections.unmodifiableMap(newMaxUsages); } if (mostAvailableSpaceUsages.containsKey(removedNode.getId())) { - ImmutableOpenMap.Builder newMinUsages = ImmutableOpenMap.builder(mostAvailableSpaceUsages); + Map newMinUsages = new HashMap<>(mostAvailableSpaceUsages); newMinUsages.remove(removedNode.getId()); - mostAvailableSpaceUsages = newMinUsages.build(); + mostAvailableSpaceUsages = Collections.unmodifiableMap(newMinUsages); } } } @@ -206,7 +212,8 @@ public ClusterInfo getClusterInfo() { mostAvailableSpaceUsages, indicesStatsSummary.shardSizes, indicesStatsSummary.shardRoutingToDataPath, - indicesStatsSummary.reservedSpace + indicesStatsSummary.reservedSpace, + nodeFileCacheStats ); } @@ -219,6 +226,7 @@ protected CountDownLatch updateNodeStats(final ActionListener(listener, latch)); return latch; @@ -252,16 +260,23 @@ public final ClusterInfo refresh() { final CountDownLatch nodeLatch = updateNodeStats(new ActionListener() { @Override public void onResponse(NodesStatsResponse nodesStatsResponse) { - ImmutableOpenMap.Builder leastAvailableUsagesBuilder = ImmutableOpenMap.builder(); - ImmutableOpenMap.Builder mostAvailableUsagesBuilder = ImmutableOpenMap.builder(); + final Map leastAvailableUsagesBuilder = new HashMap<>(); + final Map mostAvailableUsagesBuilder = new HashMap<>(); fillDiskUsagePerNode( logger, adjustNodesStats(nodesStatsResponse.getNodes()), leastAvailableUsagesBuilder, mostAvailableUsagesBuilder ); - leastAvailableSpaceUsages = leastAvailableUsagesBuilder.build(); - mostAvailableSpaceUsages = mostAvailableUsagesBuilder.build(); + leastAvailableSpaceUsages = Collections.unmodifiableMap(leastAvailableUsagesBuilder); + mostAvailableSpaceUsages = Collections.unmodifiableMap(mostAvailableUsagesBuilder); + + nodeFileCacheStats = Collections.unmodifiableMap( + nodesStatsResponse.getNodes() + .stream() + .filter(nodeStats -> nodeStats.getNode().isSearchNode()) + .collect(Collectors.toMap(nodeStats -> nodeStats.getNode().getId(), NodeStats::getFileCacheStats)) + ); } @Override @@ -277,29 +292,25 @@ public void onFailure(Exception e) { logger.warn("Failed to execute NodeStatsAction for ClusterInfoUpdateJob", e); } // we empty the usages list, to be safe - we don't know what's going on. - leastAvailableSpaceUsages = ImmutableOpenMap.of(); - mostAvailableSpaceUsages = ImmutableOpenMap.of(); + leastAvailableSpaceUsages = Map.of(); + mostAvailableSpaceUsages = Map.of(); } } }); - final CountDownLatch indicesLatch = updateIndicesStats(new ActionListener() { + final CountDownLatch indicesLatch = updateIndicesStats(new ActionListener<>() { @Override public void onResponse(IndicesStatsResponse indicesStatsResponse) { final ShardStats[] stats = indicesStatsResponse.getShards(); - final ImmutableOpenMap.Builder shardSizeByIdentifierBuilder = ImmutableOpenMap.builder(); - final ImmutableOpenMap.Builder dataPathByShardRoutingBuilder = ImmutableOpenMap.builder(); + final Map shardSizeByIdentifierBuilder = new HashMap<>(); + final Map dataPathByShardRoutingBuilder = new HashMap<>(); final Map reservedSpaceBuilders = new HashMap<>(); buildShardLevelInfo(logger, stats, shardSizeByIdentifierBuilder, dataPathByShardRoutingBuilder, reservedSpaceBuilders); - final ImmutableOpenMap.Builder rsrvdSpace = ImmutableOpenMap.builder(); + final Map rsrvdSpace = new HashMap<>(); reservedSpaceBuilders.forEach((nodeAndPath, builder) -> rsrvdSpace.put(nodeAndPath, builder.build())); - indicesStatsSummary = new IndicesStatsSummary( - shardSizeByIdentifierBuilder.build(), - dataPathByShardRoutingBuilder.build(), - rsrvdSpace.build() - ); + indicesStatsSummary = new IndicesStatsSummary(shardSizeByIdentifierBuilder, dataPathByShardRoutingBuilder, rsrvdSpace); } @Override @@ -358,9 +369,9 @@ public void addListener(Consumer clusterInfoConsumer) { static void buildShardLevelInfo( Logger logger, ShardStats[] stats, - ImmutableOpenMap.Builder shardSizes, - ImmutableOpenMap.Builder newShardRoutingToDataPath, - Map reservedSpaceByShard + final Map shardSizes, + final Map newShardRoutingToDataPath, + final Map reservedSpaceByShard ) { for (ShardStats s : stats) { final ShardRouting shardRouting = s.getShardRouting(); @@ -390,8 +401,8 @@ static void buildShardLevelInfo( static void fillDiskUsagePerNode( Logger logger, List nodeStatsArray, - ImmutableOpenMap.Builder newLeastAvailableUsages, - ImmutableOpenMap.Builder newMostAvailableUsages + final Map newLeastAvailableUsages, + final Map newMostAvailableUsages ) { for (NodeStats nodeStats : nodeStatsArray) { if (nodeStats.getFs() == null) { @@ -467,21 +478,22 @@ static void fillDiskUsagePerNode( } } + /** + * Indices statistics summary. + * + * @opensearch.internal + */ private static class IndicesStatsSummary { - static final IndicesStatsSummary EMPTY = new IndicesStatsSummary( - ImmutableOpenMap.of(), - ImmutableOpenMap.of(), - ImmutableOpenMap.of() - ); + static final IndicesStatsSummary EMPTY = new IndicesStatsSummary(Map.of(), Map.of(), Map.of()); - final ImmutableOpenMap shardSizes; - final ImmutableOpenMap shardRoutingToDataPath; - final ImmutableOpenMap reservedSpace; + final Map shardSizes; + final Map shardRoutingToDataPath; + final Map reservedSpace; IndicesStatsSummary( - ImmutableOpenMap shardSizes, - ImmutableOpenMap shardRoutingToDataPath, - ImmutableOpenMap reservedSpace + final Map shardSizes, + final Map shardRoutingToDataPath, + final Map reservedSpace ) { this.shardSizes = shardSizes; this.shardRoutingToDataPath = shardRoutingToDataPath; @@ -491,6 +503,8 @@ private static class IndicesStatsSummary { /** * Runs {@link InternalClusterInfoService#refresh()}, logging failures/rejections appropriately. + * + * @opensearch.internal */ private class RefreshRunnable extends AbstractRunnable { private final String reason; @@ -524,6 +538,8 @@ public void onRejection(Exception e) { /** * Runs {@link InternalClusterInfoService#refresh()}, logging failures/rejections appropriately, and reschedules itself on completion. + * + * @opensearch.internal */ private class RefreshAndRescheduleRunnable extends RefreshRunnable { RefreshAndRescheduleRunnable() { @@ -535,7 +551,7 @@ protected void doRun() { if (this == refreshAndRescheduleRunnable.get()) { super.doRun(); } else { - logger.trace("master changed, scheduled refresh job is stale"); + logger.trace("cluster-manager changed, scheduled refresh job is stale"); } } diff --git a/server/src/main/java/org/opensearch/cluster/LocalClusterUpdateTask.java b/server/src/main/java/org/opensearch/cluster/LocalClusterUpdateTask.java index 06ed0c0580e2f..d730be1f2afb6 100644 --- a/server/src/main/java/org/opensearch/cluster/LocalClusterUpdateTask.java +++ b/server/src/main/java/org/opensearch/cluster/LocalClusterUpdateTask.java @@ -38,7 +38,9 @@ import java.util.List; /** - * Used to apply state updates on nodes that are not necessarily master + * Used to apply state updates on nodes that are not necessarily cluster-manager + * + * @opensearch.internal */ public abstract class LocalClusterUpdateTask implements @@ -89,7 +91,7 @@ public Priority priority() { } @Override - public final boolean runOnlyOnMaster() { + public final boolean runOnlyOnClusterManager() { return false; } } diff --git a/server/src/main/java/org/opensearch/cluster/LocalNodeClusterManagerListener.java b/server/src/main/java/org/opensearch/cluster/LocalNodeClusterManagerListener.java new file mode 100644 index 0000000000000..c07dcc5daaee6 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/LocalNodeClusterManagerListener.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster; + +/** + * Enables listening to cluster-manager changes events of the local node (when the local node becomes the cluster-manager, and when the local + * node cease being a cluster-manager). + * + * @opensearch.internal + */ +public interface LocalNodeClusterManagerListener extends ClusterStateListener { + + /** + * Called when local node is elected to be the cluster-manager + */ + void onClusterManager(); + + /** + * Called when the local node used to be the cluster-manager, a new cluster-manager was elected and it's no longer the local node. + */ + void offClusterManager(); + + @Override + default void clusterChanged(ClusterChangedEvent event) { + final boolean wasClusterManager = event.previousState().nodes().isLocalNodeElectedClusterManager(); + final boolean isClusterManager = event.localNodeClusterManager(); + if (wasClusterManager == false && isClusterManager) { + onClusterManager(); + } else if (wasClusterManager && isClusterManager == false) { + offClusterManager(); + } + } +} diff --git a/server/src/main/java/org/opensearch/cluster/LocalNodeMasterListener.java b/server/src/main/java/org/opensearch/cluster/LocalNodeMasterListener.java index 1c35f7bbbe8a1..eb9bfa67686b6 100644 --- a/server/src/main/java/org/opensearch/cluster/LocalNodeMasterListener.java +++ b/server/src/main/java/org/opensearch/cluster/LocalNodeMasterListener.java @@ -6,55 +6,45 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.cluster; /** - * Enables listening to master changes events of the local node (when the local node becomes the master, and when the local - * node cease being a master). + * Enables listening to cluster-manager changes events of the local node (when the local node becomes the cluster-manager, and when the local + * node cease being a cluster-manager). + * + * @opensearch.internal + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link LocalNodeClusterManagerListener} */ -public interface LocalNodeMasterListener extends ClusterStateListener { +@Deprecated +public interface LocalNodeMasterListener extends LocalNodeClusterManagerListener { /** - * Called when local node is elected to be the master + * Called when local node is elected to be the cluster-manager. + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #onClusterManager()} */ + @Deprecated void onMaster(); /** - * Called when the local node used to be the master, a new master was elected and it's no longer the local node. + * Called when the local node used to be the cluster-manager, a new cluster-manager was elected and it's no longer the local node. + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #offClusterManager()} */ + @Deprecated void offMaster(); + /** + * Called when local node is elected to be the cluster-manager. + */ + @Override + default void onClusterManager() { + onMaster(); + } + + /** + * Called when the local node used to be the cluster-manager, a new cluster-manager was elected and it's no longer the local node. + */ @Override - default void clusterChanged(ClusterChangedEvent event) { - final boolean wasMaster = event.previousState().nodes().isLocalNodeElectedMaster(); - final boolean isMaster = event.localNodeMaster(); - if (wasMaster == false && isMaster) { - onMaster(); - } else if (wasMaster && isMaster == false) { - offMaster(); - } + default void offClusterManager() { + offMaster(); } } diff --git a/server/src/main/java/org/opensearch/cluster/MasterNodeChangePredicate.java b/server/src/main/java/org/opensearch/cluster/MasterNodeChangePredicate.java index 9d11fb84af801..2395f81d2d504 100644 --- a/server/src/main/java/org/opensearch/cluster/MasterNodeChangePredicate.java +++ b/server/src/main/java/org/opensearch/cluster/MasterNodeChangePredicate.java @@ -6,61 +6,24 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.cluster; -import org.opensearch.cluster.node.DiscoveryNode; - import java.util.function.Predicate; +/** + * Utility class to build a predicate that accepts cluster state changes + * + * @opensearch.internal + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link ClusterManagerNodeChangePredicate} + */ +@Deprecated public final class MasterNodeChangePredicate { private MasterNodeChangePredicate() { } - /** - * builds a predicate that will accept a cluster state only if it was generated after the current has - * (re-)joined the master - */ public static Predicate build(ClusterState currentState) { - final long currentVersion = currentState.version(); - final DiscoveryNode masterNode = currentState.nodes().getMasterNode(); - final String currentMasterId = masterNode == null ? null : masterNode.getEphemeralId(); - return newState -> { - final DiscoveryNode newMaster = newState.nodes().getMasterNode(); - final boolean accept; - if (newMaster == null) { - accept = false; - } else if (newMaster.getEphemeralId().equals(currentMasterId) == false) { - accept = true; - } else { - accept = newState.version() > currentVersion; - } - return accept; - }; + return ClusterManagerNodeChangePredicate.build(currentState); } } diff --git a/server/src/main/java/org/opensearch/cluster/MergableCustomMetadata.java b/server/src/main/java/org/opensearch/cluster/MergableCustomMetadata.java index 521fbe52848f8..b9898e79e3e6f 100644 --- a/server/src/main/java/org/opensearch/cluster/MergableCustomMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/MergableCustomMetadata.java @@ -40,6 +40,8 @@ * Custom metadata can be merged using {@link #merge(Metadata.Custom)}. * * @param type of custom meta data + * + * @opensearch.internal */ public interface MergableCustomMetadata { diff --git a/server/src/main/java/org/opensearch/cluster/NamedDiff.java b/server/src/main/java/org/opensearch/cluster/NamedDiff.java index 7ab73b965188c..ce971aa723394 100644 --- a/server/src/main/java/org/opensearch/cluster/NamedDiff.java +++ b/server/src/main/java/org/opensearch/cluster/NamedDiff.java @@ -33,10 +33,12 @@ package org.opensearch.cluster; import org.opensearch.Version; -import org.opensearch.common.io.stream.NamedWriteable; +import org.opensearch.core.common.io.stream.NamedWriteable; /** * Diff that also support NamedWriteable interface + * + * @opensearch.internal */ public interface NamedDiff> extends Diff, NamedWriteable { /** diff --git a/server/src/main/java/org/opensearch/cluster/NamedDiffable.java b/server/src/main/java/org/opensearch/cluster/NamedDiffable.java index 7073053d1d840..ee73666d8b3b2 100644 --- a/server/src/main/java/org/opensearch/cluster/NamedDiffable.java +++ b/server/src/main/java/org/opensearch/cluster/NamedDiffable.java @@ -32,9 +32,11 @@ package org.opensearch.cluster; -import org.opensearch.common.io.stream.VersionedNamedWriteable; +import org.opensearch.core.common.io.stream.VersionedNamedWriteable; /** * Diff that also support {@link VersionedNamedWriteable} interface + * + * @opensearch.internal */ public interface NamedDiffable extends Diffable, VersionedNamedWriteable {} diff --git a/server/src/main/java/org/opensearch/cluster/NamedDiffableValueSerializer.java b/server/src/main/java/org/opensearch/cluster/NamedDiffableValueSerializer.java index d7bd39de42884..f5ee364078361 100644 --- a/server/src/main/java/org/opensearch/cluster/NamedDiffableValueSerializer.java +++ b/server/src/main/java/org/opensearch/cluster/NamedDiffableValueSerializer.java @@ -33,12 +33,14 @@ package org.opensearch.cluster; import org.opensearch.Version; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** * Value Serializer for named diffables + * + * @opensearch.internal */ public class NamedDiffableValueSerializer> extends DiffableUtils.DiffableValueSerializer { diff --git a/server/src/main/java/org/opensearch/cluster/NodeConnectionsService.java b/server/src/main/java/org/opensearch/cluster/NodeConnectionsService.java index 02139ea21b483..5b7bb19a935b1 100644 --- a/server/src/main/java/org/opensearch/cluster/NodeConnectionsService.java +++ b/server/src/main/java/org/opensearch/cluster/NodeConnectionsService.java @@ -36,7 +36,6 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.support.GroupedActionListener; import org.opensearch.action.support.PlainListenableActionFuture; import org.opensearch.cluster.coordination.FollowersChecker; @@ -44,12 +43,13 @@ import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.service.ClusterApplier; import org.opensearch.common.Nullable; -import org.opensearch.common.component.AbstractLifecycleComponent; import org.opensearch.common.inject.Inject; +import org.opensearch.common.lifecycle.AbstractLifecycleComponent; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.action.ActionListener; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.TransportService; @@ -69,7 +69,7 @@ * This component is responsible for maintaining connections from this node to all the nodes listed in the cluster state, and for * disconnecting from nodes once they are removed from the cluster state. It periodically checks that all connections are still open and * restores them if needed. Note that this component is *not* responsible for removing nodes from the cluster state if they disconnect or - * are unresponsive: this is the job of the master's fault detection components, particularly {@link FollowersChecker}. + * are unresponsive: this is the job of the cluster-manager's fault detection components, particularly {@link FollowersChecker}. *

    * The {@link NodeConnectionsService#connectToNodes(DiscoveryNodes, Runnable)} and {@link * NodeConnectionsService#disconnectFromNodesExcept(DiscoveryNodes)} methods are called on the {@link ClusterApplier} thread. This component @@ -80,6 +80,8 @@ *

    * This component does not block on disconnections at all, because a disconnection might need to wait for an ongoing (background) connection * attempt to complete first. + * + * @opensearch.internal */ public class NodeConnectionsService extends AbstractLifecycleComponent { private static final Logger logger = LogManager.getLogger(NodeConnectionsService.class); @@ -227,6 +229,11 @@ private void connectDisconnectedTargets(Runnable onCompletion) { runnables.forEach(Runnable::run); } + /** + * A connection checker. + * + * @opensearch.internal + */ class ConnectionChecker extends AbstractRunnable { protected void doRun() { if (connectionChecker == this) { @@ -309,6 +316,8 @@ private enum ActivityType { * Similarly if we are currently disconnecting and then {@link ConnectionTarget#connect(ActionListener)} is called then all * disconnection listeners are immediately removed for failure notification and a connection is started once the disconnection is * complete. + * + * @opensearch.internal */ private class ConnectionTarget { private final DiscoveryNode discoveryNode; diff --git a/server/src/main/java/org/opensearch/cluster/NotClusterManagerException.java b/server/src/main/java/org/opensearch/cluster/NotClusterManagerException.java new file mode 100644 index 0000000000000..d96bb521f399e --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/NotClusterManagerException.java @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster; + +import org.opensearch.OpenSearchException; +import org.opensearch.core.common.io.stream.StreamInput; + +import java.io.IOException; + +/** + * Thrown when a node join request or a cluster-manager ping reaches a node which is not + * currently acting as a cluster-manager or when a cluster state update task is to be executed + * on a node that is no longer cluster-manager. + * + * @opensearch.internal + */ +public class NotClusterManagerException extends OpenSearchException { + + public NotClusterManagerException(String msg) { + super(msg); + } + + public NotClusterManagerException(StreamInput in) throws IOException { + super(in); + } + + @Override + public Throwable fillInStackTrace() { + return this; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/NotMasterException.java b/server/src/main/java/org/opensearch/cluster/NotMasterException.java index 61772aa13233b..823e65a0d52ff 100644 --- a/server/src/main/java/org/opensearch/cluster/NotMasterException.java +++ b/server/src/main/java/org/opensearch/cluster/NotMasterException.java @@ -6,42 +6,22 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.cluster; -import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** - * Thrown when a node join request or a master ping reaches a node which is not - * currently acting as a master or when a cluster state update task is to be executed - * on a node that is no longer master. + * Thrown when a node join request or a cluster-manager ping reaches a node which is not + * currently acting as a cluster-manager or when a cluster state update task is to be executed + * on a node that is no longer cluster-manager. + * + * @opensearch.internal + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link NotClusterManagerException} */ -public class NotMasterException extends OpenSearchException { +@Deprecated +public class NotMasterException extends NotClusterManagerException { public NotMasterException(String msg) { super(msg); @@ -51,8 +31,4 @@ public NotMasterException(StreamInput in) throws IOException { super(in); } - @Override - public Throwable fillInStackTrace() { - return this; - } } diff --git a/server/src/main/java/org/opensearch/cluster/RepositoryCleanupInProgress.java b/server/src/main/java/org/opensearch/cluster/RepositoryCleanupInProgress.java index bae464606c0f2..debfe43f1d2b1 100644 --- a/server/src/main/java/org/opensearch/cluster/RepositoryCleanupInProgress.java +++ b/server/src/main/java/org/opensearch/cluster/RepositoryCleanupInProgress.java @@ -33,11 +33,12 @@ import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.repositories.RepositoryOperation; import java.io.IOException; @@ -45,6 +46,11 @@ import java.util.Collections; import java.util.List; +/** + * Information passed during repository cleanup + * + * @opensearch.internal + */ public final class RepositoryCleanupInProgress extends AbstractNamedDiffable implements ClusterState.Custom { public static final RepositoryCleanupInProgress EMPTY = new RepositoryCleanupInProgress(Collections.emptyList()); @@ -104,7 +110,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override @@ -112,6 +118,11 @@ public Version getMinimalSupportedVersion() { return LegacyESVersion.V_7_4_0; } + /** + * Entry in the collection. + * + * @opensearch.internal + */ public static final class Entry implements Writeable, RepositoryOperation { private final String repository; diff --git a/server/src/main/java/org/opensearch/cluster/RestoreInProgress.java b/server/src/main/java/org/opensearch/cluster/RestoreInProgress.java index 205084261f2a9..042a4743ca25d 100644 --- a/server/src/main/java/org/opensearch/cluster/RestoreInProgress.java +++ b/server/src/main/java/org/opensearch/cluster/RestoreInProgress.java @@ -32,28 +32,29 @@ package org.opensearch.cluster; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.Version; import org.opensearch.cluster.ClusterState.Custom; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.snapshots.Snapshot; import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.UUID; /** * Meta data about restore processes that are currently executing + * + * @opensearch.internal */ public class RestoreInProgress extends AbstractNamedDiffable implements Custom, Iterable { @@ -64,17 +65,17 @@ public class RestoreInProgress extends AbstractNamedDiffable implements public static final String TYPE = "restore"; - public static final RestoreInProgress EMPTY = new RestoreInProgress(ImmutableOpenMap.of()); + public static final RestoreInProgress EMPTY = new RestoreInProgress(Map.of()); - private final ImmutableOpenMap entries; + private final Map entries; /** * Constructs new restore metadata * * @param entries map of currently running restore processes keyed by their restore uuid */ - private RestoreInProgress(ImmutableOpenMap entries) { - this.entries = entries; + private RestoreInProgress(final Map entries) { + this.entries = Collections.unmodifiableMap(entries); } @Override @@ -92,7 +93,7 @@ public int hashCode() { @Override public String toString() { StringBuilder builder = new StringBuilder("RestoreInProgress["); - entries.forEach(entry -> builder.append("{").append(entry.key).append("}{").append(entry.value.snapshot).append("},")); + entries.forEach((s, entry) -> builder.append("{").append(s).append("}{").append(entry.snapshot).append("},")); builder.setCharAt(builder.length() - 1, ']'); return builder.toString(); } @@ -107,12 +108,17 @@ public boolean isEmpty() { @Override public Iterator iterator() { - return entries.valuesIt(); + return entries.values().iterator(); } + /** + * Builder of the restore. + * + * @opensearch.internal + */ public static final class Builder { - private final ImmutableOpenMap.Builder entries = ImmutableOpenMap.builder(); + private final Map entries = new HashMap<>(); public Builder() {} @@ -126,18 +132,20 @@ public Builder add(Entry entry) { } public RestoreInProgress build() { - return entries.isEmpty() ? EMPTY : new RestoreInProgress(entries.build()); + return entries.isEmpty() ? EMPTY : new RestoreInProgress(entries); } } /** * Restore metadata + * + * @opensearch.internal */ public static class Entry { private final String uuid; private final State state; private final Snapshot snapshot; - private final ImmutableOpenMap shards; + private final Map shards; private final List indices; /** @@ -149,20 +157,14 @@ public static class Entry { * @param indices list of indices being restored * @param shards map of shards being restored to their current restore status */ - public Entry( - String uuid, - Snapshot snapshot, - State state, - List indices, - ImmutableOpenMap shards - ) { + public Entry(String uuid, Snapshot snapshot, State state, List indices, final Map shards) { this.snapshot = Objects.requireNonNull(snapshot); this.state = Objects.requireNonNull(state); this.indices = Objects.requireNonNull(indices); if (shards == null) { - this.shards = ImmutableOpenMap.of(); + this.shards = Map.of(); } else { - this.shards = shards; + this.shards = Collections.unmodifiableMap(shards); } this.uuid = Objects.requireNonNull(uuid); } @@ -189,7 +191,7 @@ public Snapshot snapshot() { * * @return list of shards */ - public ImmutableOpenMap shards() { + public Map shards() { return this.shards; } @@ -235,6 +237,8 @@ public int hashCode() { /** * Represents status of a restored shard + * + * @opensearch.internal */ public static class ShardRestoreStatus implements Writeable { private State state; @@ -358,6 +362,8 @@ public int hashCode() { /** * Shard restore process state + * + * @opensearch.internal */ public enum State { /** @@ -447,7 +453,7 @@ public static NamedDiff readDiffFrom(StreamInput in) throws IOException public RestoreInProgress(StreamInput in) throws IOException { int count = in.readVInt(); - final ImmutableOpenMap.Builder entriesBuilder = ImmutableOpenMap.builder(count); + final Map entriesBuilder = new HashMap<>(count); for (int i = 0; i < count; i++) { final String uuid; uuid = in.readString(); @@ -461,31 +467,30 @@ public RestoreInProgress(StreamInput in) throws IOException { snapshot, state, Collections.unmodifiableList(indexBuilder), - in.readImmutableMap(ShardId::new, ShardRestoreStatus::readShardRestoreStatus) + in.readMap(ShardId::new, ShardRestoreStatus::readShardRestoreStatus) ) ); } - this.entries = entriesBuilder.build(); + this.entries = Collections.unmodifiableMap(entriesBuilder); } @Override public void writeTo(StreamOutput out) throws IOException { out.writeVInt(entries.size()); - for (ObjectCursor v : entries.values()) { - Entry entry = v.value; + for (final Entry entry : entries.values()) { out.writeString(entry.uuid); entry.snapshot().writeTo(out); out.writeByte(entry.state().value()); out.writeStringCollection(entry.indices); - out.writeMap(entry.shards); + out.writeMap(entry.shards, (o, shardId) -> shardId.writeTo(o), (o, status) -> status.writeTo(o)); } } @Override public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { builder.startArray("snapshots"); - for (ObjectCursor entry : entries.values()) { - toXContent(entry.value, builder); + for (final Entry entry : entries.values()) { + toXContent(entry, builder); } builder.endArray(); return builder; @@ -511,9 +516,9 @@ public void toXContent(Entry entry, XContentBuilder builder) throws IOException builder.endArray(); builder.startArray("shards"); { - for (ObjectObjectCursor shardEntry : entry.shards) { - ShardId shardId = shardEntry.key; - ShardRestoreStatus status = shardEntry.value; + for (final Map.Entry shardEntry : entry.shards.entrySet()) { + ShardId shardId = shardEntry.getKey(); + ShardRestoreStatus status = shardEntry.getValue(); builder.startObject(); { builder.field("index", shardId.getIndex()); diff --git a/server/src/main/java/org/opensearch/cluster/SnapshotDeletionsInProgress.java b/server/src/main/java/org/opensearch/cluster/SnapshotDeletionsInProgress.java index 67ba285f45130..247eb7a195ca7 100644 --- a/server/src/main/java/org/opensearch/cluster/SnapshotDeletionsInProgress.java +++ b/server/src/main/java/org/opensearch/cluster/SnapshotDeletionsInProgress.java @@ -36,11 +36,11 @@ import org.opensearch.cluster.ClusterState.Custom; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.common.UUIDs; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.repositories.RepositoryOperation; import org.opensearch.snapshots.Snapshot; import org.opensearch.snapshots.SnapshotId; @@ -57,6 +57,8 @@ /** * A class that represents the snapshot deletions that are in progress in the cluster. + * + * @opensearch.internal */ public class SnapshotDeletionsInProgress extends AbstractNamedDiffable implements Custom { @@ -217,6 +219,8 @@ public String toString() { /** * A class representing a snapshot deletion request entry in the cluster state. + * + * @opensearch.internal */ public static final class Entry implements Writeable, RepositoryOperation { private final List snapshots; @@ -373,6 +377,11 @@ public String toString() { } } + /** + * State of the deletions. + * + * @opensearch.internal + */ public enum State implements Writeable { /** diff --git a/server/src/main/java/org/opensearch/cluster/SnapshotsInProgress.java b/server/src/main/java/org/opensearch/cluster/SnapshotsInProgress.java index 2c001833f46ce..10e714b08d138 100644 --- a/server/src/main/java/org/opensearch/cluster/SnapshotsInProgress.java +++ b/server/src/main/java/org/opensearch/cluster/SnapshotsInProgress.java @@ -32,22 +32,19 @@ package org.opensearch.cluster; -import com.carrotsearch.hppc.ObjectContainer; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.LegacyESVersion; import org.opensearch.Version; import org.opensearch.cluster.ClusterState.Custom; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.repositories.IndexId; import org.opensearch.repositories.RepositoryOperation; import org.opensearch.repositories.RepositoryShardId; @@ -57,6 +54,7 @@ import org.opensearch.snapshots.SnapshotsService; import java.io.IOException; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -71,6 +69,8 @@ /** * Meta data about snapshots that are currently executing + * + * @opensearch.internal */ public class SnapshotsInProgress extends AbstractNamedDiffable implements Custom { @@ -118,9 +118,10 @@ public static Entry startedEntry( List dataStreams, long startTime, long repositoryStateId, - ImmutableOpenMap shards, + final Map shards, Map userMetadata, - Version version + Version version, + boolean remoteStoreIndexShallowCopy ) { return new SnapshotsInProgress.Entry( snapshot, @@ -134,7 +135,8 @@ public static Entry startedEntry( shards, null, userMetadata, - version + version, + remoteStoreIndexShallowCopy ); } @@ -166,24 +168,32 @@ public static Entry startClone( Collections.emptyList(), startTime, repositoryStateId, - ImmutableOpenMap.of(), + Map.of(), null, Collections.emptyMap(), version, source, - ImmutableOpenMap.of() + Map.of(), + false // initialising to false, will be updated in startCloning method of SnapshotsService while updating entry with + // clone jobs ); } + /** + * Entry in the collection. + * + * @opensearch.internal + */ public static class Entry implements Writeable, ToXContent, RepositoryOperation { private final State state; private final Snapshot snapshot; private final boolean includeGlobalState; + private final boolean remoteStoreIndexShallowCopy; private final boolean partial; /** * Map of {@link ShardId} to {@link ShardSnapshotStatus} tracking the state of each shard snapshot operation. */ - private final ImmutableOpenMap shards; + private final Map shards; private final List indices; private final List dataStreams; private final long startTime; @@ -201,7 +211,7 @@ public static class Entry implements Writeable, ToXContent, RepositoryOperation * Map of {@link RepositoryShardId} to {@link ShardSnapshotStatus} tracking the state of each shard clone operation in this entry * the same way {@link #shards} tracks the status of each shard snapshot operation in non-clone entries. */ - private final ImmutableOpenMap clones; + private final Map clones; @Nullable private final Map userMetadata; @@ -218,10 +228,11 @@ public Entry( List dataStreams, long startTime, long repositoryStateId, - ImmutableOpenMap shards, + final Map shards, String failure, Map userMetadata, - Version version + Version version, + boolean remoteStoreIndexShallowCopy ) { this( snapshot, @@ -237,7 +248,8 @@ public Entry( userMetadata, version, null, - ImmutableOpenMap.of() + Map.of(), + remoteStoreIndexShallowCopy ); } @@ -250,12 +262,13 @@ private Entry( List dataStreams, long startTime, long repositoryStateId, - ImmutableOpenMap shards, + final Map shards, String failure, - Map userMetadata, + final Map userMetadata, Version version, @Nullable SnapshotId source, - @Nullable ImmutableOpenMap clones + @Nullable final Map clones, + boolean remoteStoreIndexShallowCopy ) { this.state = state; this.snapshot = snapshot; @@ -264,7 +277,7 @@ private Entry( this.indices = indices; this.dataStreams = dataStreams; this.startTime = startTime; - this.shards = shards; + this.shards = Collections.unmodifiableMap(shards); this.repositoryStateId = repositoryStateId; this.failure = failure; this.userMetadata = userMetadata; @@ -272,10 +285,11 @@ private Entry( this.source = source; if (source == null) { assert clones == null || clones.isEmpty() : "Provided [" + clones + "] but no source"; - this.clones = ImmutableOpenMap.of(); + this.clones = Map.of(); } else { - this.clones = clones; + this.clones = Collections.unmodifiableMap(clones); } + this.remoteStoreIndexShallowCopy = remoteStoreIndexShallowCopy; assert assertShardsConsistent(this.source, this.state, this.indices, this.shards, this.clones); } @@ -286,7 +300,7 @@ private Entry(StreamInput in) throws IOException { state = State.fromValue(in.readByte()); indices = in.readList(IndexId::new); startTime = in.readLong(); - shards = in.readImmutableMap(ShardId::new, ShardSnapshotStatus::readFrom); + shards = in.readMap(ShardId::new, ShardSnapshotStatus::readFrom); repositoryStateId = in.readLong(); failure = in.readOptionalString(); if (in.getVersion().onOrAfter(METADATA_FIELD_INTRODUCED)) { @@ -295,11 +309,11 @@ private Entry(StreamInput in) throws IOException { userMetadata = null; } if (in.getVersion().onOrAfter(VERSION_IN_SNAPSHOT_VERSION)) { - version = Version.readVersion(in); + version = in.readVersion(); } else if (in.getVersion().onOrAfter(SnapshotsService.SHARD_GEN_IN_REPO_DATA_VERSION)) { - // If an older master informs us that shard generations are supported we use the minimum shard generation compatible - // version. If shard generations are not supported yet we use a placeholder for a version that does not use shard - // generations. + // If an older cluster-manager informs us that shard generations are supported + // we use the minimum shard generation compatible version. + // If shard generations are not supported yet we use a placeholder for a version that does not use shard generations. version = in.readBoolean() ? SnapshotsService.SHARD_GEN_IN_REPO_DATA_VERSION : SnapshotsService.OLD_SNAPSHOT_FORMAT; } else { version = SnapshotsService.OLD_SNAPSHOT_FORMAT; @@ -311,10 +325,15 @@ private Entry(StreamInput in) throws IOException { } if (in.getVersion().onOrAfter(SnapshotsService.CLONE_SNAPSHOT_VERSION)) { source = in.readOptionalWriteable(SnapshotId::new); - clones = in.readImmutableMap(RepositoryShardId::new, ShardSnapshotStatus::readFrom); + clones = in.readMap(RepositoryShardId::new, ShardSnapshotStatus::readFrom); } else { source = null; - clones = ImmutableOpenMap.of(); + clones = Map.of(); + } + if (in.getVersion().onOrAfter(Version.V_2_9_0)) { + remoteStoreIndexShallowCopy = in.readBoolean(); + } else { + remoteStoreIndexShallowCopy = false; } } @@ -322,17 +341,17 @@ private static boolean assertShardsConsistent( SnapshotId source, State state, List indices, - ImmutableOpenMap shards, - ImmutableOpenMap clones + final Map shards, + final Map clones ) { if ((state == State.INIT || state == State.ABORTED) && shards.isEmpty()) { return true; } final Set indexNames = indices.stream().map(IndexId::getName).collect(Collectors.toSet()); final Set indexNamesInShards = new HashSet<>(); - shards.iterator().forEachRemaining(s -> { - indexNamesInShards.add(s.key.getIndexName()); - assert source == null || s.value.nodeId == null + shards.entrySet().forEach(s -> { + indexNamesInShards.add(s.getKey().getIndexName()); + assert source == null || s.getValue().nodeId == null : "Shard snapshot must not be assigned to data node when copying from snapshot [" + source + "]"; }); assert source == null || indexNames.isEmpty() == false : "No empty snapshot clones allowed"; @@ -368,9 +387,10 @@ public Entry( List dataStreams, long startTime, long repositoryStateId, - ImmutableOpenMap shards, + final Map shards, Map userMetadata, - Version version + Version version, + boolean remoteStoreIndexShallowCopy ) { this( snapshot, @@ -384,7 +404,8 @@ public Entry( shards, null, userMetadata, - version + version, + remoteStoreIndexShallowCopy ); } @@ -393,7 +414,7 @@ public Entry( State state, List indices, long repositoryStateId, - ImmutableOpenMap shards, + final Map shards, Version version, String failure ) { @@ -409,7 +430,8 @@ public Entry( shards, failure, entry.userMetadata, - version + version, + entry.remoteStoreIndexShallowCopy ); } @@ -433,11 +455,12 @@ public Entry withRepoGen(long newRepoGen) { userMetadata, version, source, - clones + clones, + remoteStoreIndexShallowCopy ); } - public Entry withClones(ImmutableOpenMap updatedClones) { + public Entry withClones(final Map updatedClones) { if (updatedClones.equals(clones)) { return this; } @@ -455,7 +478,28 @@ public Entry withClones(ImmutableOpenMap userMetadata, version, source, - updatedClones + updatedClones, + remoteStoreIndexShallowCopy + ); + } + + public Entry withRemoteStoreIndexShallowCopy(final boolean remoteStoreIndexShallowCopy) { + return new Entry( + snapshot, + includeGlobalState, + partial, + state, + indices, + dataStreams, + startTime, + repositoryStateId, + shards, + failure, + userMetadata, + version, + source, + clones, + remoteStoreIndexShallowCopy ); } @@ -471,11 +515,11 @@ public Entry withClones(ImmutableOpenMap */ @Nullable public Entry abort() { - final ImmutableOpenMap.Builder shardsBuilder = ImmutableOpenMap.builder(); + final Map shardsBuilder = new HashMap<>(); boolean completed = true; boolean allQueued = true; - for (ObjectObjectCursor shardEntry : shards) { - ShardSnapshotStatus status = shardEntry.value; + for (final Map.Entry shardEntry : shards.entrySet()) { + ShardSnapshotStatus status = shardEntry.getValue(); allQueued &= status.state() == ShardState.QUEUED; if (status.state().completed() == false) { final String nodeId = status.nodeId(); @@ -487,15 +531,15 @@ public Entry abort() { ); } completed &= status.state().completed(); - shardsBuilder.put(shardEntry.key, status); + shardsBuilder.put(shardEntry.getKey(), status); } if (allQueued) { return null; } - return fail(shardsBuilder.build(), completed ? State.SUCCESS : State.ABORTED, ABORTED_FAILURE_TEXT); + return fail(shardsBuilder, completed ? State.SUCCESS : State.ABORTED, ABORTED_FAILURE_TEXT); } - public Entry fail(ImmutableOpenMap shards, State state, String failure) { + public Entry fail(final Map shards, State state, String failure) { return new Entry( snapshot, includeGlobalState, @@ -510,7 +554,8 @@ public Entry fail(ImmutableOpenMap shards, State s userMetadata, version, source, - clones + clones, + remoteStoreIndexShallowCopy ); } @@ -522,7 +567,7 @@ public Entry fail(ImmutableOpenMap shards, State s * @param shards new shard snapshot states * @return new snapshot entry */ - public Entry withShardStates(ImmutableOpenMap shards) { + public Entry withShardStates(final Map shards) { if (completed(shards.values())) { return new Entry( snapshot, @@ -536,7 +581,8 @@ public Entry withShardStates(ImmutableOpenMap shar shards, failure, userMetadata, - version + version, + remoteStoreIndexShallowCopy ); } return withStartedShards(shards); @@ -546,7 +592,7 @@ public Entry withShardStates(ImmutableOpenMap shar * Same as {@link #withShardStates} but does not check if the snapshot completed and thus is only to be used when starting new * shard snapshots on data nodes for a running snapshot. */ - public Entry withStartedShards(ImmutableOpenMap shards) { + public Entry withStartedShards(final Map shards) { final SnapshotsInProgress.Entry updated = new Entry( snapshot, includeGlobalState, @@ -559,7 +605,8 @@ public Entry withStartedShards(ImmutableOpenMap sh shards, failure, userMetadata, - version + version, + remoteStoreIndexShallowCopy ); assert updated.state().completed() == false && completed(updated.shards().values()) == false : "Only running snapshots allowed but saw [" + updated + "]"; @@ -575,7 +622,7 @@ public Snapshot snapshot() { return this.snapshot; } - public ImmutableOpenMap shards() { + public Map shards() { return this.shards; } @@ -591,6 +638,10 @@ public boolean includeGlobalState() { return includeGlobalState; } + public boolean remoteStoreIndexShallowCopy() { + return remoteStoreIndexShallowCopy; + } + public Map userMetadata() { return userMetadata; } @@ -632,7 +683,7 @@ public boolean isClone() { return source != null; } - public ImmutableOpenMap clones() { + public Map clones() { return clones; } @@ -654,7 +705,7 @@ public boolean equals(Object o) { if (version.equals(entry.version) == false) return false; if (Objects.equals(source, ((Entry) o).source) == false) return false; if (clones.equals(((Entry) o).clones) == false) return false; - + if (remoteStoreIndexShallowCopy != entry.remoteStoreIndexShallowCopy) return false; return true; } @@ -671,12 +722,13 @@ public int hashCode() { result = 31 * result + version.hashCode(); result = 31 * result + (source == null ? 0 : source.hashCode()); result = 31 * result + clones.hashCode(); + result = 31 * result + (remoteStoreIndexShallowCopy ? 1 : 0); return result; } @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override @@ -699,9 +751,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.field(REPOSITORY_STATE_ID, repositoryStateId); builder.startArray(SHARDS); { - for (ObjectObjectCursor shardEntry : shards) { - ShardId shardId = shardEntry.key; - ShardSnapshotStatus status = shardEntry.value; + for (final Map.Entry shardEntry : shards.entrySet()) { + ShardId shardId = shardEntry.getKey(); + ShardSnapshotStatus status = shardEntry.getValue(); builder.startObject(); { builder.field(INDEX, shardId.getIndex()); @@ -726,14 +778,14 @@ public void writeTo(StreamOutput out) throws IOException { out.writeByte(state.value()); out.writeList(indices); out.writeLong(startTime); - out.writeMap(shards); + out.writeMap(shards, (o, v) -> v.writeTo(o), (o, v) -> v.writeTo(o)); out.writeLong(repositoryStateId); out.writeOptionalString(failure); if (out.getVersion().onOrAfter(METADATA_FIELD_INTRODUCED)) { out.writeMap(userMetadata); } if (out.getVersion().onOrAfter(VERSION_IN_SNAPSHOT_VERSION)) { - Version.writeVersion(version, out); + out.writeVersion(version); } else if (out.getVersion().onOrAfter(SnapshotsService.SHARD_GEN_IN_REPO_DATA_VERSION)) { out.writeBoolean(SnapshotsService.useShardGenerations(version)); } @@ -742,7 +794,10 @@ public void writeTo(StreamOutput out) throws IOException { } if (out.getVersion().onOrAfter(SnapshotsService.CLONE_SNAPSHOT_VERSION)) { out.writeOptionalWriteable(source); - out.writeMap(clones); + out.writeMap(clones, (o, v) -> v.writeTo(o), (o, v) -> v.writeTo(o)); + } + if (out.getVersion().onOrAfter(Version.V_2_9_0)) { + out.writeBoolean(remoteStoreIndexShallowCopy); } } @@ -758,24 +813,29 @@ public boolean isFragment() { * @param shards list of shard statuses * @return true if all shards have completed (either successfully or failed), false otherwise */ - public static boolean completed(ObjectContainer shards) { - for (ObjectCursor status : shards) { - if (status.value.state().completed == false) { + public static boolean completed(final Collection shards) { + for (final ShardSnapshotStatus status : shards) { + if (status.state().completed == false) { return false; } } return true; } - private static boolean hasFailures(ImmutableOpenMap clones) { - for (ObjectCursor value : clones.values()) { - if (value.value.state().failed()) { + private static boolean hasFailures(final Map clones) { + for (final ShardSnapshotStatus value : clones.values()) { + if (value.state().failed()) { return true; } } return false; } + /** + * Status of shard snapshots. + * + * @opensearch.internal + */ public static class ShardSnapshotStatus implements Writeable { /** @@ -911,6 +971,11 @@ public String toString() { } } + /** + * State of the snapshots. + * + * @opensearch.internal + */ public enum State { INIT((byte) 0, false), STARTED((byte) 1, false), @@ -958,9 +1023,9 @@ public static State fromValue(byte value) { private static boolean assertConsistentEntries(List entries) { final Map> assignedShardsByRepo = new HashMap<>(); for (Entry entry : entries) { - for (ObjectObjectCursor shard : entry.shards()) { - if (shard.value.isActive()) { - assert assignedShardsByRepo.computeIfAbsent(entry.repository(), k -> new HashSet<>()).add(shard.key) + for (final Map.Entry shard : entry.shards().entrySet()) { + if (shard.getValue().isActive()) { + assert assignedShardsByRepo.computeIfAbsent(entry.repository(), k -> new HashSet<>()).add(shard.getKey()) : "Found duplicate shard assignments in " + entries; } } @@ -1048,6 +1113,11 @@ public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params par return builder; } + /** + * The shard state. + * + * @opensearch.internal + */ public enum ShardState { INIT((byte) 0, false, false), SUCCESS((byte) 2, true, false), diff --git a/server/src/main/java/org/opensearch/cluster/TimeoutClusterStateListener.java b/server/src/main/java/org/opensearch/cluster/TimeoutClusterStateListener.java index d7465fa00a50c..eb31fa2b7e69d 100644 --- a/server/src/main/java/org/opensearch/cluster/TimeoutClusterStateListener.java +++ b/server/src/main/java/org/opensearch/cluster/TimeoutClusterStateListener.java @@ -37,7 +37,7 @@ /** * An exception to cluster state listener that allows for timeouts and for post added notifications. * - * + * @opensearch.internal */ public interface TimeoutClusterStateListener extends ClusterStateListener { diff --git a/server/src/main/java/org/opensearch/cluster/ack/AckedRequest.java b/server/src/main/java/org/opensearch/cluster/ack/AckedRequest.java index 23a9ed16e35d2..750f4b177cb86 100644 --- a/server/src/main/java/org/opensearch/cluster/ack/AckedRequest.java +++ b/server/src/main/java/org/opensearch/cluster/ack/AckedRequest.java @@ -36,6 +36,8 @@ /** * Identifies a cluster state update request with acknowledgement support + * + * @opensearch.internal */ public interface AckedRequest { @@ -45,7 +47,19 @@ public interface AckedRequest { TimeValue ackTimeout(); /** - * Returns the timeout for the request to be completed on the master node + * Returns the timeout for the request to be completed on the cluster-manager node + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #clusterManagerNodeTimeout()} + */ + @Deprecated + default TimeValue masterNodeTimeout() { + throw new UnsupportedOperationException("Must be overridden"); + } + + /** + * Returns the timeout for the request to be completed on the cluster-manager node */ - TimeValue masterNodeTimeout(); + // TODO: Remove default implementation after removing the deprecated masterNodeTimeout() + default TimeValue clusterManagerNodeTimeout() { + return masterNodeTimeout(); + } } diff --git a/server/src/main/java/org/opensearch/cluster/ack/ClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/cluster/ack/ClusterStateUpdateRequest.java index d142c28086f70..dd5769d7c7f89 100644 --- a/server/src/main/java/org/opensearch/cluster/ack/ClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/cluster/ack/ClusterStateUpdateRequest.java @@ -37,6 +37,8 @@ /** * Base class to be used when needing to update the cluster state * Contains the basic fields that are always needed + * + * @opensearch.internal */ public abstract class ClusterStateUpdateRequest> implements AckedRequest { @@ -62,7 +64,7 @@ public T ackTimeout(TimeValue ackTimeout) { /** * Returns the maximum time interval to wait for the request to - * be completed on the master node + * be completed on the cluster-manager node */ @Override public TimeValue masterNodeTimeout() { @@ -70,7 +72,7 @@ public TimeValue masterNodeTimeout() { } /** - * Sets the master node timeout + * Sets the cluster-manager node timeout */ @SuppressWarnings("unchecked") public T masterNodeTimeout(TimeValue masterNodeTimeout) { diff --git a/server/src/main/java/org/opensearch/cluster/ack/ClusterStateUpdateResponse.java b/server/src/main/java/org/opensearch/cluster/ack/ClusterStateUpdateResponse.java index 3a79e15380165..db26496c6f263 100644 --- a/server/src/main/java/org/opensearch/cluster/ack/ClusterStateUpdateResponse.java +++ b/server/src/main/java/org/opensearch/cluster/ack/ClusterStateUpdateResponse.java @@ -34,6 +34,8 @@ /** * Base response returned after a cluster state update + * + * @opensearch.internal */ public class ClusterStateUpdateResponse { diff --git a/server/src/main/java/org/opensearch/cluster/ack/CreateIndexClusterStateUpdateResponse.java b/server/src/main/java/org/opensearch/cluster/ack/CreateIndexClusterStateUpdateResponse.java index 9c9beb1cd986e..52b04b9961ff8 100644 --- a/server/src/main/java/org/opensearch/cluster/ack/CreateIndexClusterStateUpdateResponse.java +++ b/server/src/main/java/org/opensearch/cluster/ack/CreateIndexClusterStateUpdateResponse.java @@ -34,6 +34,8 @@ /** * A cluster state update response with specific fields for index creation. + * + * @opensearch.internal */ public class CreateIndexClusterStateUpdateResponse extends ClusterStateUpdateResponse { diff --git a/server/src/main/java/org/opensearch/cluster/ack/IndicesClusterStateUpdateRequest.java b/server/src/main/java/org/opensearch/cluster/ack/IndicesClusterStateUpdateRequest.java index 7410378760a3a..d9753027ffc67 100644 --- a/server/src/main/java/org/opensearch/cluster/ack/IndicesClusterStateUpdateRequest.java +++ b/server/src/main/java/org/opensearch/cluster/ack/IndicesClusterStateUpdateRequest.java @@ -31,10 +31,12 @@ package org.opensearch.cluster.ack; -import org.opensearch.index.Index; +import org.opensearch.core.index.Index; /** * Base cluster state update request that allows to execute update against multiple indices + * + * @opensearch.internal */ public abstract class IndicesClusterStateUpdateRequest> extends ClusterStateUpdateRequest { diff --git a/server/src/main/java/org/opensearch/cluster/ack/OpenIndexClusterStateUpdateResponse.java b/server/src/main/java/org/opensearch/cluster/ack/OpenIndexClusterStateUpdateResponse.java index 70915ea162a8a..faf9e3e9bebbc 100644 --- a/server/src/main/java/org/opensearch/cluster/ack/OpenIndexClusterStateUpdateResponse.java +++ b/server/src/main/java/org/opensearch/cluster/ack/OpenIndexClusterStateUpdateResponse.java @@ -33,6 +33,8 @@ /** * A cluster state update response with specific fields for index opening. + * + * @opensearch.internal */ public class OpenIndexClusterStateUpdateResponse extends ClusterStateUpdateResponse { diff --git a/server/src/main/java/org/opensearch/cluster/ack/package-info.java b/server/src/main/java/org/opensearch/cluster/ack/package-info.java new file mode 100644 index 0000000000000..e8206eeb37b26 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/ack/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Acknowledgment transport handlers. */ +package org.opensearch.cluster.ack; diff --git a/server/src/main/java/org/opensearch/cluster/action/index/MappingUpdatedAction.java b/server/src/main/java/org/opensearch/cluster/action/index/MappingUpdatedAction.java index f22d489ec6fd7..02f313d59d310 100644 --- a/server/src/main/java/org/opensearch/cluster/action/index/MappingUpdatedAction.java +++ b/server/src/main/java/org/opensearch/cluster/action/index/MappingUpdatedAction.java @@ -34,10 +34,9 @@ import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.mapping.put.AutoPutMappingAction; import org.opensearch.action.admin.indices.mapping.put.PutMappingRequest; -import org.opensearch.action.support.master.MasterNodeRequest; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.client.Client; import org.opensearch.client.IndicesAdminClient; import org.opensearch.cluster.service.ClusterService; @@ -49,8 +48,9 @@ import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.RunOnce; import org.opensearch.common.util.concurrent.UncategorizedExecutionException; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.index.mapper.Mapping; import java.util.concurrent.Semaphore; @@ -58,6 +58,8 @@ /** * Called by shards in the cluster when their mapping was dynamically updated and it needs to be updated * in the cluster state meta data (and broadcast to all members). + * + * @opensearch.internal */ public class MappingUpdatedAction { @@ -104,12 +106,12 @@ public void setClient(Client client) { } /** - * Update mappings on the master node, waiting for the change to be committed, + * Update mappings on the cluster-manager node, waiting for the change to be committed, * but not for the mapping update to be applied on all nodes. The timeout specified by - * {@code timeout} is the master node timeout ({@link MasterNodeRequest#masterNodeTimeout()}), - * potentially waiting for a master node to be available. + * {@code timeout} is the cluster-manager node timeout ({@link ClusterManagerNodeRequest#clusterManagerNodeTimeout()}), + * potentially waiting for a cluster-manager node to be available. */ - public void updateMappingOnMaster(Index index, Mapping mappingUpdate, ActionListener listener) { + public void updateMappingOnClusterManager(Index index, Mapping mappingUpdate, ActionListener listener) { final RunOnce release = new RunOnce(() -> semaphore.release()); try { @@ -130,6 +132,19 @@ public void updateMappingOnMaster(Index index, Mapping mappingUpdate, ActionList } } + /** + * Update mappings on the cluster-manager node, waiting for the change to be committed, + * but not for the mapping update to be applied on all nodes. The timeout specified by + * {@code timeout} is the cluster-manager node timeout ({@link ClusterManagerNodeRequest#clusterManagerNodeTimeout()}), + * potentially waiting for a cluster-manager node to be available. + * + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #updateMappingOnClusterManager(Index, Mapping, ActionListener)} + */ + @Deprecated + public void updateMappingOnMaster(Index index, Mapping mappingUpdate, ActionListener listener) { + updateMappingOnClusterManager(index, mappingUpdate, listener); + } + // used by tests int blockedThreads() { return semaphore.getQueueLength(); @@ -139,8 +154,8 @@ int blockedThreads() { protected void sendUpdateMapping(Index index, Mapping mappingUpdate, ActionListener listener) { PutMappingRequest putMappingRequest = new PutMappingRequest(); putMappingRequest.setConcreteIndex(index); - putMappingRequest.source(mappingUpdate.toString(), XContentType.JSON); - putMappingRequest.masterNodeTimeout(dynamicMappingUpdateTimeout); + putMappingRequest.source(mappingUpdate.toString(), MediaTypeRegistry.JSON); + putMappingRequest.clusterManagerNodeTimeout(dynamicMappingUpdateTimeout); putMappingRequest.timeout(TimeValue.ZERO); if (clusterService.state().nodes().getMinNodeVersion().onOrAfter(LegacyESVersion.V_7_9_0)) { client.execute( @@ -169,6 +184,11 @@ private static RuntimeException unwrapEsException(OpenSearchException esEx) { return new UncategorizedExecutionException("Failed execution", root); } + /** + * An adjustable semaphore + * + * @opensearch.internal + */ static class AdjustableSemaphore extends Semaphore { private final Object maxPermitsMutex = new Object(); diff --git a/server/src/main/java/org/opensearch/cluster/action/index/NodeMappingRefreshAction.java b/server/src/main/java/org/opensearch/cluster/action/index/NodeMappingRefreshAction.java index 23ce218904d21..fe4793171b428 100644 --- a/server/src/main/java/org/opensearch/cluster/action/index/NodeMappingRefreshAction.java +++ b/server/src/main/java/org/opensearch/cluster/action/index/NodeMappingRefreshAction.java @@ -40,19 +40,24 @@ import org.opensearch.cluster.metadata.MetadataMappingService; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.EmptyTransportResponseHandler; import org.opensearch.transport.TransportChannel; import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestHandler; -import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportService; import java.io.IOException; +/** + * Transport action for refreshing the Node Mapping + * + * @opensearch.internal + */ public class NodeMappingRefreshAction { private static final Logger logger = LogManager.getLogger(NodeMappingRefreshAction.class); @@ -74,14 +79,19 @@ public NodeMappingRefreshAction(TransportService transportService, MetadataMappi ); } - public void nodeMappingRefresh(final DiscoveryNode masterNode, final NodeMappingRefreshRequest request) { - if (masterNode == null) { - logger.warn("can't send mapping refresh for [{}], no master known.", request.index()); + public void nodeMappingRefresh(final DiscoveryNode clusterManagerNode, final NodeMappingRefreshRequest request) { + if (clusterManagerNode == null) { + logger.warn("can't send mapping refresh for [{}], no cluster-manager known.", request.index()); return; } - transportService.sendRequest(masterNode, ACTION_NAME, request, EmptyTransportResponseHandler.INSTANCE_SAME); + transportService.sendRequest(clusterManagerNode, ACTION_NAME, request, EmptyTransportResponseHandler.INSTANCE_SAME); } + /** + * A handler for a node mapping refresh transport request. + * + * @opensearch.internal + */ private class NodeMappingRefreshTransportHandler implements TransportRequestHandler { @Override @@ -91,6 +101,11 @@ public void messageReceived(NodeMappingRefreshRequest request, TransportChannel } } + /** + * Request to refresh node mapping. + * + * @opensearch.internal + */ public static class NodeMappingRefreshRequest extends TransportRequest implements IndicesRequest { private String index; diff --git a/server/src/main/java/org/opensearch/cluster/action/index/package-info.java b/server/src/main/java/org/opensearch/cluster/action/index/package-info.java new file mode 100644 index 0000000000000..3594d858b2970 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/action/index/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Index Action transport handlers. */ +package org.opensearch.cluster.action.index; diff --git a/server/src/main/java/org/opensearch/cluster/action/package-info.java b/server/src/main/java/org/opensearch/cluster/action/package-info.java new file mode 100644 index 0000000000000..acd8cf0f63592 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/action/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Action transport handlers. */ +package org.opensearch.cluster.action; diff --git a/server/src/main/java/org/opensearch/cluster/action/shard/ShardStateAction.java b/server/src/main/java/org/opensearch/cluster/action/shard/ShardStateAction.java index 300067587b78b..cb5749a91d448 100644 --- a/server/src/main/java/org/opensearch/cluster/action/shard/ShardStateAction.java +++ b/server/src/main/java/org/opensearch/cluster/action/shard/ShardStateAction.java @@ -35,17 +35,16 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.OpenSearchException; import org.opensearch.ExceptionsHelper; -import org.opensearch.action.ActionListener; +import org.opensearch.OpenSearchException; import org.opensearch.cluster.ClusterChangedEvent; +import org.opensearch.cluster.ClusterManagerNodeChangePredicate; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateObserver; import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskExecutor; import org.opensearch.cluster.ClusterStateTaskListener; -import org.opensearch.cluster.MasterNodeChangePredicate; -import org.opensearch.cluster.NotMasterException; +import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.coordination.FailedToCommitClusterStateException; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.node.DiscoveryNode; @@ -58,11 +57,13 @@ import org.opensearch.common.Nullable; import org.opensearch.common.Priority; import org.opensearch.common.inject.Inject; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Setting; import org.opensearch.common.unit.TimeValue; -import org.opensearch.index.shard.ShardId; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.node.NodeClosedException; import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; @@ -74,7 +75,6 @@ import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestDeduplicator; import org.opensearch.transport.TransportRequestHandler; -import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportService; import java.io.IOException; @@ -87,6 +87,11 @@ import java.util.function.Predicate; import java.util.function.Supplier; +/** + * Transport action for retrieving the shard state + * + * @opensearch.internal + */ public class ShardStateAction { private static final Logger logger = LogManager.getLogger(ShardStateAction.class); @@ -177,14 +182,14 @@ private void sendShardAction( final ActionListener listener ) { ClusterStateObserver observer = new ClusterStateObserver(currentState, clusterService, null, logger, threadPool.getThreadContext()); - DiscoveryNode masterNode = currentState.nodes().getMasterNode(); - Predicate changePredicate = MasterNodeChangePredicate.build(currentState); - if (masterNode == null) { - logger.warn("no master known for action [{}] for shard entry [{}]", actionName, request); - waitForNewMasterAndRetry(actionName, observer, request, listener, changePredicate); + DiscoveryNode clusterManagerNode = currentState.nodes().getClusterManagerNode(); + Predicate changePredicate = ClusterManagerNodeChangePredicate.build(currentState); + if (clusterManagerNode == null) { + logger.warn("no cluster-manager known for action [{}] for shard entry [{}]", actionName, request); + waitForNewClusterManagerAndRetry(actionName, observer, request, listener, changePredicate); } else { - logger.debug("sending [{}] to [{}] for shard entry [{}]", actionName, masterNode.getId(), request); - transportService.sendRequest(masterNode, actionName, request, new EmptyTransportResponseHandler(ThreadPool.Names.SAME) { + logger.debug("sending [{}] to [{}] for shard entry [{}]", actionName, clusterManagerNode.getId(), request); + transportService.sendRequest(clusterManagerNode, actionName, request, new EmptyTransportResponseHandler(ThreadPool.Names.SAME) { @Override public void handleResponse(TransportResponse.Empty response) { listener.onResponse(null); @@ -192,14 +197,14 @@ public void handleResponse(TransportResponse.Empty response) { @Override public void handleException(TransportException exp) { - if (isMasterChannelException(exp)) { - waitForNewMasterAndRetry(actionName, observer, request, listener, changePredicate); + if (isClusterManagerChannelException(exp)) { + waitForNewClusterManagerAndRetry(actionName, observer, request, listener, changePredicate); } else { logger.warn( new ParameterizedMessage( "unexpected failure while sending request [{}]" + " to [{}] for shard entry [{}]", actionName, - masterNode, + clusterManagerNode, request ), exp @@ -217,17 +222,17 @@ public void handleException(TransportException exp) { } } - private static Class[] MASTER_CHANNEL_EXCEPTIONS = new Class[] { - NotMasterException.class, + private static Class[] CLUSTER_MANAGER_CHANNEL_EXCEPTIONS = new Class[] { + NotClusterManagerException.class, ConnectTransportException.class, FailedToCommitClusterStateException.class }; - private static boolean isMasterChannelException(TransportException exp) { - return ExceptionsHelper.unwrap(exp, MASTER_CHANNEL_EXCEPTIONS) != null; + private static boolean isClusterManagerChannelException(TransportException exp) { + return ExceptionsHelper.unwrap(exp, CLUSTER_MANAGER_CHANNEL_EXCEPTIONS) != null; } /** - * Send a shard failed request to the master node to update the cluster state with the failure of a shard on another node. This means + * Send a shard failed request to the cluster-manager node to update the cluster state with the failure of a shard on another node. This means * that the shard should be failed because a write made it into the primary but was not replicated to this shard copy. If the shard * does not exist anymore but still has an entry in the in-sync set, remove its allocation id from the in-sync set. * @@ -261,7 +266,7 @@ int remoteShardFailedCacheSize() { } /** - * Send a shard failed request to the master node to update the cluster state when a shard on the local node failed. + * Send a shard failed request to the cluster-manager node to update the cluster state when a shard on the local node failed. */ public void localShardFailed( final ShardRouting shardRouting, @@ -273,7 +278,7 @@ public void localShardFailed( } /** - * Send a shard failed request to the master node to update the cluster state when a shard on the local node failed. + * Send a shard failed request to the cluster-manager node to update the cluster state when a shard on the local node failed. */ public void localShardFailed( final ShardRouting shardRouting, @@ -294,7 +299,7 @@ public void localShardFailed( } // visible for testing - protected void waitForNewMasterAndRetry( + protected void waitForNewClusterManagerAndRetry( String actionName, ClusterStateObserver observer, TransportRequest request, @@ -305,7 +310,7 @@ protected void waitForNewMasterAndRetry( @Override public void onNewClusterState(ClusterState state) { if (logger.isTraceEnabled()) { - logger.trace("new cluster state [{}] after waiting for master election for shard entry [{}]", state, request); + logger.trace("new cluster state [{}] after waiting for cluster-manager election for shard entry [{}]", state, request); } sendShardAction(actionName, state, request, listener); } @@ -318,7 +323,7 @@ public void onClusterServiceClose() { @Override public void onTimeout(TimeValue timeout) { - // we wait indefinitely for a new master + // we wait indefinitely for a new cluster-manager assert false; } }, changePredicate); @@ -328,6 +333,11 @@ private void setFollowUpRerouteTaskPriority(Priority followUpRerouteTaskPriority this.followUpRerouteTaskPriority = followUpRerouteTaskPriority; } + /** + * A transport handler for a shard failed action. + * + * @opensearch.internal + */ private static class ShardFailedTransportHandler implements TransportRequestHandler { private final ClusterService clusterService; private final ShardFailedClusterStateTaskExecutor shardFailedClusterStateTaskExecutor; @@ -375,14 +385,14 @@ public void onFailure(String source, Exception e) { } @Override - public void onNoLongerMaster(String source) { - logger.error("{} no longer master while failing shard [{}]", request.shardId, request); + public void onNoLongerClusterManager(String source) { + logger.error("{} no longer cluster-manager while failing shard [{}]", request.shardId, request); try { - channel.sendResponse(new NotMasterException(source)); + channel.sendResponse(new NotClusterManagerException(source)); } catch (Exception channelException) { logger.warn( () -> new ParameterizedMessage( - "{} failed to send no longer master while failing shard [{}]", + "{} failed to send no longer cluster-manager while failing shard [{}]", request.shardId, request ), @@ -411,6 +421,11 @@ public void clusterStateProcessed(String source, ClusterState oldState, ClusterS } } + /** + * Executor if shard fails cluster state task. + * + * @opensearch.internal + */ public static class ShardFailedClusterStateTaskExecutor implements ClusterStateTaskExecutor { private final AllocationService allocationService; private final RerouteService rerouteService; @@ -547,6 +562,11 @@ public void clusterStatePublished(ClusterChangedEvent clusterChangedEvent) { } } + /** + * Entry for a failed shard. + * + * @opensearch.internal + */ public static class FailedShardEntry extends TransportRequest { final ShardId shardId; final String allocationId; @@ -653,6 +673,11 @@ public void shardStarted( sendShardAction(SHARD_STARTED_ACTION_NAME, currentState, entry, listener); } + /** + * Handler for a shard started action. + * + * @opensearch.internal + */ private static class ShardStartedTransportHandler implements TransportRequestHandler { private final ClusterService clusterService; private final ShardStartedClusterStateTaskExecutor shardStartedClusterStateTaskExecutor; @@ -682,6 +707,11 @@ public void messageReceived(StartedShardEntry request, TransportChannel channel, } } + /** + * Executor for when shard starts cluster state. + * + * @opensearch.internal + */ public static class ShardStartedClusterStateTaskExecutor implements ClusterStateTaskExecutor, @@ -714,7 +744,8 @@ public ClusterTasksResult execute(ClusterState currentState, if (matched == null) { // tasks that correspond to non-existent shards are marked as successful. The reason is that we resend shard started // events on every cluster state publishing that does not contain the shard as started yet. This means that old stale - // requests might still be in flight even after the shard has already been started or failed on the master. We just + // requests might still be in flight even after the shard has already been started or failed on the cluster-manager. We + // just // ignore these requests for now. logger.debug("{} ignoring shard started task [{}] (shard does not exist anymore)", task.shardId, task); builder.success(task); @@ -786,7 +817,7 @@ public ClusterTasksResult execute(ClusterState currentState, @Override public void onFailure(String source, Exception e) { - if (e instanceof FailedToCommitClusterStateException || e instanceof NotMasterException) { + if (e instanceof FailedToCommitClusterStateException || e instanceof NotClusterManagerException) { logger.debug(() -> new ParameterizedMessage("failure during [{}]", source), e); } else { logger.error(() -> new ParameterizedMessage("unexpected failure during [{}]", source), e); @@ -806,6 +837,11 @@ public void clusterStatePublished(ClusterChangedEvent clusterChangedEvent) { } } + /** + * try for started shard. + * + * @opensearch.internal + */ public static class StartedShardEntry extends TransportRequest { final ShardId shardId; final String allocationId; @@ -849,6 +885,11 @@ public String toString() { } } + /** + * Error thrown when a shard is no longer primary. + * + * @opensearch.internal + */ public static class NoLongerPrimaryShardException extends OpenSearchException { public NoLongerPrimaryShardException(ShardId shardId, String msg) { diff --git a/server/src/main/java/org/opensearch/cluster/action/shard/package-info.java b/server/src/main/java/org/opensearch/cluster/action/shard/package-info.java new file mode 100644 index 0000000000000..5666176386d46 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/action/shard/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Shard State transport handlers. */ +package org.opensearch.cluster.action.shard; diff --git a/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessAttributeValueHealth.java b/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessAttributeValueHealth.java new file mode 100644 index 0000000000000..eae79e2ac0986 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessAttributeValueHealth.java @@ -0,0 +1,317 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.awarenesshealth; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.WeightedRoutingMetadata; +import org.opensearch.cluster.routing.RoutingNode; +import org.opensearch.cluster.routing.ShardRoutingState; +import org.opensearch.cluster.routing.WeightedRouting; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * Cluster Awareness AttributeValue Health information + */ +public class ClusterAwarenessAttributeValueHealth implements Writeable, ToXContentFragment { + + private static final String ACTIVE_SHARDS = "active_shards"; + private static final String INITIALIZING_SHARDS = "initializing_shards"; + private static final String RELOCATING_SHARDS = "relocating_shards"; + private static final String UNASSIGNED_SHARDS = "unassigned_shards"; + private static final String NODES = "data_nodes"; + private static final String WEIGHTS = "weight"; + private final String name; + private int activeShards; + private int unassignedShards; + private int initializingShards; + private int relocatingShards; + private int nodes; + private double weight; + private List nodeList; + + /** + * Creates Awareness AttributeValue Health information + * + * @param name name of awareness attribute + */ + public ClusterAwarenessAttributeValueHealth(String name, List nodeList) { + this.name = name; + this.nodeList = nodeList; + } + + // Constructor use by Unit test case. + ClusterAwarenessAttributeValueHealth( + String name, + int activeShards, + int initializingShards, + int relocatingShards, + int unassignedShards, + int nodes, + double weights + ) { + this.name = name; + this.activeShards = activeShards; + this.initializingShards = initializingShards; + this.relocatingShards = relocatingShards; + this.unassignedShards = unassignedShards; + this.nodes = nodes; + this.weight = weights; + } + + public ClusterAwarenessAttributeValueHealth(final StreamInput in) throws IOException { + name = in.readString(); + activeShards = in.readVInt(); + initializingShards = in.readVInt(); + relocatingShards = in.readVInt(); + unassignedShards = in.readVInt(); + nodes = in.readVInt(); + weight = in.readDouble(); + } + + public static ClusterAwarenessAttributeValueHealth fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + XContentParser.Token token = parser.nextToken(); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser); + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + String attributeName = parser.currentName(); + int active_shards = 0; + int initializing_shards = 0; + int relocating_shards = 0; + int unassigned_shards = 0; + int nodes = 0; + double weight = 0.0; + String currentFieldName; + + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + switch (currentFieldName) { + case ACTIVE_SHARDS: + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse active shards field, expected number but found unknown type" + ); + } + active_shards = parser.intValue(); + break; + case INITIALIZING_SHARDS: + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse initializing shards field, expected number but found unknown type" + ); + } + initializing_shards = parser.intValue(); + break; + case RELOCATING_SHARDS: + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse relocating shards field, expected number but found unknown type" + ); + } + relocating_shards = parser.intValue(); + break; + case UNASSIGNED_SHARDS: + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException("failed to parse unassigned field, expected number but found unknown type"); + } + unassigned_shards = parser.intValue(); + break; + case NODES: + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException("failed to parse node field, expected number but found unknown type"); + } + nodes = parser.intValue(); + break; + case WEIGHTS: + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException("failed to parse weight field, expected number but found unknown type"); + } + weight = parser.doubleValue(); + break; + } + } else { + throw new OpenSearchParseException( + "failed to parse awareness attribute health, expected [{}] but found [{}]", + XContentParser.Token.FIELD_NAME, + token + ); + } + } + return new ClusterAwarenessAttributeValueHealth( + attributeName, + active_shards, + initializing_shards, + relocating_shards, + unassigned_shards, + nodes, + weight + ); + } + + public int getActiveShards() { + return activeShards; + } + + public void setActiveShards(int activeShards) { + this.activeShards = activeShards; + } + + public int getUnassignedShards() { + return unassignedShards; + } + + public void setUnassignedShards(int unassignedShards) { + this.unassignedShards = unassignedShards; + } + + public int getNodes() { + return nodes; + } + + public void setNodes(int nodes) { + this.nodes = nodes; + } + + public double getWeight() { + return weight; + } + + public void setWeight(double weight) { + this.weight = weight; + } + + public String getName() { + return name; + } + + public int getInitializingShards() { + return initializingShards; + } + + public void setInitializingShards(int initializingShards) { + this.initializingShards = initializingShards; + } + + public int getRelocatingShards() { + return relocatingShards; + } + + public void setRelocatingShards(int relocatingShards) { + this.relocatingShards = relocatingShards; + } + + public List getNodeList() { + return nodeList; + } + + public void setNodeList(List nodeList) { + this.nodeList = nodeList; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(name); + out.writeVInt(activeShards); + out.writeVInt(initializingShards); + out.writeVInt(relocatingShards); + out.writeVInt(unassignedShards); + out.writeVInt(nodes); + out.writeDouble(weight); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(getName()); + builder.field(ACTIVE_SHARDS, getActiveShards()); + builder.field(INITIALIZING_SHARDS, getInitializingShards()); + builder.field(RELOCATING_SHARDS, getRelocatingShards()); + builder.field(UNASSIGNED_SHARDS, getUnassignedShards()); + builder.field(NODES, getNodes()); + builder.field(WEIGHTS, getWeight()); + builder.endObject(); + return builder; + } + + void computeAttributeValueLevelInfo(ClusterState clusterState, boolean displayUnassignedShardLevelInfo, int shardsPerAttributeValue) { + // computing nodes info + nodes = nodeList.size(); + + // computing shards into + setShardLevelInfo(clusterState, displayUnassignedShardLevelInfo, shardsPerAttributeValue); + + // compute weight info + setWeightInfo(clusterState); + } + + private void setShardLevelInfo(ClusterState clusterState, boolean displayUnassignedShardLevelInfo, int shardsPerAttributeValue) { + + for (String nodeId : nodeList) { + RoutingNode node = clusterState.getRoutingNodes().node(nodeId); + activeShards += node.numberOfShardsWithState(ShardRoutingState.STARTED); + relocatingShards += node.numberOfShardsWithState(ShardRoutingState.RELOCATING); + initializingShards += node.numberOfShardsWithState(ShardRoutingState.INITIALIZING); + } + + // computing unassigned shards info + if (displayUnassignedShardLevelInfo) { + int unassignedShardsPerAttribute = shardsPerAttributeValue - getActiveShards() - getInitializingShards(); + setUnassignedShards(unassignedShardsPerAttribute); + } else { + setUnassignedShards(-1); + } + } + + private void setWeightInfo(ClusterState clusterState) { + WeightedRoutingMetadata weightedRoutingMetadata = clusterState.getMetadata().weightedRoutingMetadata(); + double attributeWeight = 1.0; + if (weightedRoutingMetadata != null) { + WeightedRouting weightedRouting = weightedRoutingMetadata.getWeightedRouting(); + attributeWeight = weightedRouting.weights().getOrDefault(name, 1.0); + } + setWeight(attributeWeight); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ClusterAwarenessAttributeValueHealth)) return false; + ClusterAwarenessAttributeValueHealth that = (ClusterAwarenessAttributeValueHealth) o; + return name.equals(that.name) + && activeShards == that.activeShards + && relocatingShards == that.relocatingShards + && initializingShards == that.initializingShards + && unassignedShards == that.unassignedShards + && nodes == that.nodes + && weight == that.weight; + } + + @Override + public int hashCode() { + return Objects.hash(name, activeShards, relocatingShards, initializingShards, unassignedShards, nodes, weight); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessAttributesHealth.java b/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessAttributesHealth.java new file mode 100644 index 0000000000000..01d9cfc4438e3 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessAttributesHealth.java @@ -0,0 +1,298 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.awarenesshealth; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * Cluster Awareness health information + * + */ +public class ClusterAwarenessAttributesHealth implements Iterable, Writeable, ToXContentFragment { + + private final String awarenessAttributeName; + private Map awarenessAttributeValueHealthMap; + + /** + * Creates Awareness AttributeValue Health information + * + * @param awarenessAttributeValue Awareness Attribute value ie zone, rack etc + * @param displayUnassignedShardLevelInfo Governs if unassigned info should be visible or not + * @param clusterState cluster state + */ + public ClusterAwarenessAttributesHealth( + String awarenessAttributeValue, + boolean displayUnassignedShardLevelInfo, + ClusterState clusterState + ) { + awarenessAttributeName = awarenessAttributeValue; + + // This is the Map which is storing the per attribute node list. + Map> attributesNodeList = new HashMap<>(); + + // Getting the node map for cluster + final Map nodeMap = clusterState.nodes().getDataNodes(); + + // This is the map that would store all the stats per attribute ie + // health stats for rack-1, rack-2 etc. + awarenessAttributeValueHealthMap = new HashMap<>(); + String attributeValue; + + if (!nodeMap.isEmpty()) { + Iterator iter = nodeMap.keySet().iterator(); + while (iter.hasNext()) { + List clusterAwarenessAttributeNodeList; + String node = iter.next(); + DiscoveryNode nodeDiscovery = nodeMap.get(node); + Map nodeAttributes = nodeDiscovery.getAttributes(); + if (!nodeAttributes.isEmpty()) { + if (nodeAttributes.containsKey(awarenessAttributeName)) { + attributeValue = nodeAttributes.get(awarenessAttributeName); + + if (!attributesNodeList.containsKey(attributeValue)) { + clusterAwarenessAttributeNodeList = new ArrayList<>(); + attributesNodeList.put(attributeValue, clusterAwarenessAttributeNodeList); + } else { + clusterAwarenessAttributeNodeList = attributesNodeList.get(attributeValue); + } + clusterAwarenessAttributeNodeList.add(node); + } + } + } + } + + setClusterAwarenessAttributeValue(attributesNodeList, displayUnassignedShardLevelInfo, clusterState); + } + + private void setClusterAwarenessAttributeValue( + Map> perAttributeValueNodeList, + boolean displayUnassignedShardLevelInfo, + ClusterState clusterState + ) { + int numAttributes = perAttributeValueNodeList.size(); + int shardsPerAttributeValue = 0; + + // Can happen customer has defined weights as well as awareness attribute but no node level attribute was there + // So to avoid divide-by-zero error checking this + if (numAttributes != 0) { + shardsPerAttributeValue = clusterState.getMetadata().getTotalNumberOfShards() / numAttributes; + } + + Map clusterAwarenessAttributeValueHealthMap = new HashMap<>(); + + for (String attributeValueKey : perAttributeValueNodeList.keySet()) { + ClusterAwarenessAttributeValueHealth clusterAwarenessAttributeValueHealth = new ClusterAwarenessAttributeValueHealth( + attributeValueKey, + perAttributeValueNodeList.get(attributeValueKey) + ); + // computing attribute info + clusterAwarenessAttributeValueHealth.computeAttributeValueLevelInfo( + clusterState, + displayUnassignedShardLevelInfo, + shardsPerAttributeValue + ); + clusterAwarenessAttributeValueHealthMap.put(attributeValueKey, clusterAwarenessAttributeValueHealth); + } + awarenessAttributeValueHealthMap = clusterAwarenessAttributeValueHealthMap; + } + + public ClusterAwarenessAttributesHealth(final StreamInput in) throws IOException { + awarenessAttributeName = in.readString(); + int size = in.readVInt(); + if (size > 0) { + awarenessAttributeValueHealthMap = new HashMap<>(size); + for (int i = 0; i < size; i++) { + ClusterAwarenessAttributeValueHealth clusterAwarenessAttributeValueHealth = new ClusterAwarenessAttributeValueHealth(in); + awarenessAttributeValueHealthMap.put(clusterAwarenessAttributeValueHealth.getName(), clusterAwarenessAttributeValueHealth); + } + } else { + awarenessAttributeValueHealthMap = Collections.emptyMap(); + } + } + + ClusterAwarenessAttributesHealth( + String awarenessAttributeName, + Map awarenessAttributeValueHealthMap + ) { + this.awarenessAttributeName = awarenessAttributeName; + this.awarenessAttributeValueHealthMap = awarenessAttributeValueHealthMap; + } + + public String getAwarenessAttributeName() { + return awarenessAttributeName; + } + + public Map getAwarenessAttributeHealthMap() { + return awarenessAttributeValueHealthMap; + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(awarenessAttributeName); + int size = awarenessAttributeValueHealthMap.size(); + out.writeVInt(size); + if (size > 0) { + for (ClusterAwarenessAttributeValueHealth attributeHealthMapPerValue : this) { + attributeHealthMapPerValue.writeTo(out); + } + } + } + + @Override + public Iterator iterator() { + return awarenessAttributeValueHealthMap.values().iterator(); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(getAwarenessAttributeName()); + for (ClusterAwarenessAttributeValueHealth clusterAwarenessAttributeValueHealth : this) { + clusterAwarenessAttributeValueHealth.toXContent(builder, params); + } + builder.endObject(); + return null; + } + + public static ClusterAwarenessAttributesHealth fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + XContentParser.Token token = parser.nextToken(); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser); + String attributeName = parser.currentName(); + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); + Map clusterAwarenessAttributeValueHealthMap = new HashMap<>(); + String currentFieldName; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser); + String attributeValue = parser.currentName(); + token = parser.nextToken(); + if (token == XContentParser.Token.START_OBJECT) { + int active_shards = 0; + int initializing_shards = 0; + int relocating_shards = 0; + int unassigned_shards = 0; + int nodes = 0; + double weight = 0.0; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + switch (currentFieldName) { + case "active_shards": + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse active shards field, expected number but found unknown type" + ); + } + active_shards = parser.intValue(); + break; + case "initializing_shards": + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse initializing shards field, expected number but found unknown type" + ); + } + initializing_shards = parser.intValue(); + break; + case "relocating_shards": + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse relocating shards field, expected number but found unknown type" + ); + } + relocating_shards = parser.intValue(); + break; + case "unassigned_shards": + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse unassigned field, expected number but found unknown type" + ); + } + unassigned_shards = parser.intValue(); + break; + case "data_nodes": + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse node field, expected number but found unknown type" + ); + } + nodes = parser.intValue(); + break; + case "weight": + if (parser.nextToken() != XContentParser.Token.VALUE_NUMBER) { + throw new OpenSearchParseException( + "failed to parse weight field, expected number but found unknown type" + ); + } + weight = parser.doubleValue(); + break; + } + } else { + throw new OpenSearchParseException("failed to parse attribute health map"); + } + } + ClusterAwarenessAttributeValueHealth clusterAwarenessAttributeValueHealth = new ClusterAwarenessAttributeValueHealth( + attributeValue, + active_shards, + initializing_shards, + relocating_shards, + unassigned_shards, + nodes, + weight + ); + clusterAwarenessAttributeValueHealthMap.put( + clusterAwarenessAttributeValueHealth.getName(), + clusterAwarenessAttributeValueHealth + ); + } else { + throw new OpenSearchParseException("failed to parse awareness attribute value health"); + } + } + ensureExpectedToken(XContentParser.Token.END_OBJECT, parser.nextToken(), parser); + return new ClusterAwarenessAttributesHealth(attributeName, clusterAwarenessAttributeValueHealthMap); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ClusterAwarenessAttributesHealth)) return false; + ClusterAwarenessAttributesHealth that = (ClusterAwarenessAttributesHealth) o; + return awarenessAttributeName.equals(that.awarenessAttributeName) + && awarenessAttributeValueHealthMap.size() == that.awarenessAttributeValueHealthMap.size(); + } + + @Override + public int hashCode() { + return Objects.hash(awarenessAttributeName, awarenessAttributeValueHealthMap); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessHealth.java b/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessHealth.java new file mode 100644 index 0000000000000..1643db5a1f460 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/awarenesshealth/ClusterAwarenessHealth.java @@ -0,0 +1,161 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.awarenesshealth; + +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.routing.allocation.AwarenessReplicaBalance; +import org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Cluster state Awareness health information + */ +public class ClusterAwarenessHealth implements Writeable, ToXContentFragment, Iterable { + + private static final String AWARENESS_ATTRIBUTE = "awareness_attributes"; + private final Map clusterAwarenessAttributesHealthMap; + + /** + * Creates cluster awareness health from cluster state. + * + * @param clusterState The current cluster state. Must not be null. + * @param clusterSettings the current cluster settings. + * @param awarenessAttributeName Name of awareness attribute for which we need to see the health + */ + public ClusterAwarenessHealth(ClusterState clusterState, ClusterSettings clusterSettings, String awarenessAttributeName) { + // This property will govern if we need to show unassigned shard info or not + boolean displayUnassignedShardLevelInfo; + ClusterAwarenessAttributesHealth clusterAwarenessAttributesHealth; + clusterAwarenessAttributesHealthMap = new HashMap<>(); + List awarenessAttributeList = getAwarenessAttributeList(awarenessAttributeName, clusterSettings); + for (String awarenessAttribute : awarenessAttributeList) { + displayUnassignedShardLevelInfo = canCalcUnassignedShards(clusterSettings, awarenessAttribute); + clusterAwarenessAttributesHealth = new ClusterAwarenessAttributesHealth( + awarenessAttribute, + displayUnassignedShardLevelInfo, + clusterState + ); + clusterAwarenessAttributesHealthMap.put(awarenessAttribute, clusterAwarenessAttributesHealth); + } + } + + public ClusterAwarenessHealth(final StreamInput in) throws IOException { + int size = in.readVInt(); + if (size > 0) { + clusterAwarenessAttributesHealthMap = new HashMap<>(size); + for (int i = 0; i < size; i++) { + ClusterAwarenessAttributesHealth clusterAwarenessAttributesHealth = new ClusterAwarenessAttributesHealth(in); + clusterAwarenessAttributesHealthMap.put( + clusterAwarenessAttributesHealth.getAwarenessAttributeName(), + clusterAwarenessAttributesHealth + ); + } + } else { + clusterAwarenessAttributesHealthMap = Collections.emptyMap(); + } + } + + private List getAwarenessAttributeList(String awarenessAttributeName, ClusterSettings clusterSettings) { + // Helper function to check if we need health for all or for one awareness attribute. + boolean displayAllAwarenessAttribute = awarenessAttributeName == null || awarenessAttributeName.isBlank(); + List awarenessAttributeList = new ArrayList<>(); + if (!displayAllAwarenessAttribute) { + awarenessAttributeList.add(awarenessAttributeName); + } else { + awarenessAttributeList = clusterSettings.get(AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING); + } + return awarenessAttributeList; + } + + private boolean canCalcUnassignedShards(ClusterSettings clusterSettings, String awarenessAttributeName) { + // Getting the replicaEnforcement settings as both are necessary for replica enforcement. + boolean allocationAwarenessBalance = clusterSettings.get( + AwarenessReplicaBalance.CLUSTER_ROUTING_ALLOCATION_AWARENESS_BALANCE_SETTING + ); + Settings forcedAwarenessSettings = clusterSettings.get( + AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING + ); + + boolean forcedZoneSettingsExists = false; + + if (!forcedAwarenessSettings.isEmpty()) { + // We will only mark true if particular awareness attribute exists + if (forcedAwarenessSettings.hasValue(awarenessAttributeName + ".values")) { + forcedZoneSettingsExists = true; + } + } + return allocationAwarenessBalance && forcedZoneSettingsExists; + } + + public Map getClusterAwarenessAttributesHealthMap() { + return clusterAwarenessAttributesHealthMap; + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + int size = clusterAwarenessAttributesHealthMap.size(); + out.writeVInt(size); + if (size > 0) { + for (ClusterAwarenessAttributesHealth awarenessAttributeValueHealth : this) { + awarenessAttributeValueHealth.writeTo(out); + } + } + } + + @Override + public String toString() { + return "ClusterAwarenessHealth{" + + "clusterAwarenessHealth.clusterAwarenessAttributesHealthMap.size=" + + (clusterAwarenessAttributesHealthMap == null ? "null" : clusterAwarenessAttributesHealthMap.size()) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClusterAwarenessHealth that = (ClusterAwarenessHealth) o; + return clusterAwarenessAttributesHealthMap.size() == that.clusterAwarenessAttributesHealthMap.size(); + } + + @Override + public int hashCode() { + return Objects.hash(clusterAwarenessAttributesHealthMap); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(AWARENESS_ATTRIBUTE); + for (ClusterAwarenessAttributesHealth awarenessAttributeValueHealth : this) { + awarenessAttributeValueHealth.toXContent(builder, params); + } + builder.endObject(); + return builder; + } + + @Override + public Iterator iterator() { + return clusterAwarenessAttributesHealthMap.values().iterator(); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/awarenesshealth/package-info.java b/server/src/main/java/org/opensearch/cluster/awarenesshealth/package-info.java new file mode 100644 index 0000000000000..6448d484eeac5 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/awarenesshealth/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster HA Health Foundation classes. */ +package org.opensearch.cluster.awarenesshealth; diff --git a/server/src/main/java/org/opensearch/cluster/block/ClusterBlock.java b/server/src/main/java/org/opensearch/cluster/block/ClusterBlock.java index 9da2bdeaabd8e..40a5080ba74e7 100644 --- a/server/src/main/java/org/opensearch/cluster/block/ClusterBlock.java +++ b/server/src/main/java/org/opensearch/cluster/block/ClusterBlock.java @@ -33,18 +33,23 @@ package org.opensearch.cluster.block; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; import java.util.EnumSet; import java.util.Locale; import java.util.Objects; +/** + * Blocks the cluster for concurrency + * + * @opensearch.internal + */ public class ClusterBlock implements Writeable, ToXContentFragment { private final int id; diff --git a/server/src/main/java/org/opensearch/cluster/block/ClusterBlockException.java b/server/src/main/java/org/opensearch/cluster/block/ClusterBlockException.java index 8a6401b985b90..4673f075e8439 100644 --- a/server/src/main/java/org/opensearch/cluster/block/ClusterBlockException.java +++ b/server/src/main/java/org/opensearch/cluster/block/ClusterBlockException.java @@ -33,9 +33,9 @@ package org.opensearch.cluster.block; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; import java.util.Collection; @@ -46,6 +46,11 @@ import static java.util.Collections.unmodifiableSet; +/** + * Internal exception on obtaining a cluster block + * + * @opensearch.internal + */ public class ClusterBlockException extends OpenSearchException { private final Set blocks; diff --git a/server/src/main/java/org/opensearch/cluster/block/ClusterBlockLevel.java b/server/src/main/java/org/opensearch/cluster/block/ClusterBlockLevel.java index fa8f7f19b752a..5ec847e100c86 100644 --- a/server/src/main/java/org/opensearch/cluster/block/ClusterBlockLevel.java +++ b/server/src/main/java/org/opensearch/cluster/block/ClusterBlockLevel.java @@ -34,11 +34,17 @@ import java.util.EnumSet; +/** + * What level to block the cluster + * + * @opensearch.internal + */ public enum ClusterBlockLevel { READ, WRITE, METADATA_READ, - METADATA_WRITE; + METADATA_WRITE, + CREATE_INDEX; public static final EnumSet ALL = EnumSet.allOf(ClusterBlockLevel.class); public static final EnumSet READ_WRITE = EnumSet.of(READ, WRITE); diff --git a/server/src/main/java/org/opensearch/cluster/block/ClusterBlocks.java b/server/src/main/java/org/opensearch/cluster/block/ClusterBlocks.java index a9bde418e962c..bcb4f7f71268e 100644 --- a/server/src/main/java/org/opensearch/cluster/block/ClusterBlocks.java +++ b/server/src/main/java/org/opensearch/cluster/block/ClusterBlocks.java @@ -32,17 +32,16 @@ package org.opensearch.cluster.block; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.cluster.AbstractDiffable; import org.opensearch.cluster.Diff; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.MetadataIndexStateService; import org.opensearch.common.Nullable; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.util.set.Sets; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.index.IndexModule; import java.io.IOException; import java.util.Collections; @@ -59,19 +58,21 @@ /** * Represents current cluster level blocks to block dirty operations done against the cluster. + * + * @opensearch.internal */ public class ClusterBlocks extends AbstractDiffable { - public static final ClusterBlocks EMPTY_CLUSTER_BLOCK = new ClusterBlocks(emptySet(), ImmutableOpenMap.of()); + public static final ClusterBlocks EMPTY_CLUSTER_BLOCK = new ClusterBlocks(emptySet(), Map.of()); private final Set global; - private final ImmutableOpenMap> indicesBlocks; + private final Map> indicesBlocks; private final EnumMap levelHolders; - ClusterBlocks(Set global, ImmutableOpenMap> indicesBlocks) { + ClusterBlocks(Set global, final Map> indicesBlocks) { this.global = global; - this.indicesBlocks = indicesBlocks; + this.indicesBlocks = Collections.unmodifiableMap(indicesBlocks); levelHolders = generateLevelHolders(global, indicesBlocks); } @@ -79,7 +80,7 @@ public Set global() { return global; } - public ImmutableOpenMap> indices() { + public Map> indices() { return indicesBlocks; } @@ -87,7 +88,7 @@ public Set global(ClusterBlockLevel level) { return levelHolders.get(level).global(); } - public ImmutableOpenMap> indices(ClusterBlockLevel level) { + public Map> indices(ClusterBlockLevel level) { return levelHolders.get(level).indices(); } @@ -97,7 +98,7 @@ private Set blocksForIndex(ClusterBlockLevel level, String index) private static EnumMap generateLevelHolders( Set global, - ImmutableOpenMap> indicesBlocks + final Map> indicesBlocks ) { EnumMap levelHolders = new EnumMap<>(ClusterBlockLevel.class); @@ -105,11 +106,11 @@ private static EnumMap generateLevelHol Predicate containsLevel = block -> block.contains(level); Set newGlobal = unmodifiableSet(global.stream().filter(containsLevel).collect(toSet())); - ImmutableOpenMap.Builder> indicesBuilder = ImmutableOpenMap.builder(); - for (ObjectObjectCursor> entry : indicesBlocks) { - indicesBuilder.put(entry.key, unmodifiableSet(entry.value.stream().filter(containsLevel).collect(toSet()))); + final Map> indicesBuilder = new HashMap<>(); + for (final Map.Entry> entry : indicesBlocks.entrySet()) { + indicesBuilder.put(entry.getKey(), unmodifiableSet(entry.getValue().stream().filter(containsLevel).collect(toSet()))); } - levelHolders.put(level, new ImmutableLevelHolder(newGlobal, indicesBuilder.build())); + levelHolders.put(level, new ImmutableLevelHolder(newGlobal, indicesBuilder)); } return levelHolders; } @@ -202,6 +203,13 @@ public ClusterBlockException globalBlockedException(ClusterBlockLevel level) { return new ClusterBlockException(global(level)); } + public IndexCreateBlockException createIndexBlockedException(ClusterBlockLevel level) { + if (!globalBlocked(level)) { + return null; + } + return new IndexCreateBlockException(global(level)); + } + public void indexBlockedRaiseException(ClusterBlockLevel level, String index) throws ClusterBlockException { ClusterBlockException blockException = indexBlockedException(level, index); if (blockException != null) { @@ -278,9 +286,9 @@ public String toString() { sb.append(" ").append(block); } } - for (ObjectObjectCursor> entry : indices()) { - sb.append(" ").append(entry.key).append(":\n"); - for (ClusterBlock block : entry.value) { + for (final Map.Entry> entry : indices().entrySet()) { + sb.append(" ").append(entry.getKey()).append(":\n"); + for (ClusterBlock block : entry.getValue()) { sb.append(" ").append(block); } } @@ -300,10 +308,7 @@ private static void writeBlockSet(Set blocks, StreamOutput out) th public static ClusterBlocks readFrom(StreamInput in) throws IOException { final Set global = readBlockSet(in); - ImmutableOpenMap> indicesBlocks = in.readImmutableMap( - i -> i.readString().intern(), - ClusterBlocks::readBlockSet - ); + final Map> indicesBlocks = in.readMap(i -> i.readString().intern(), ClusterBlocks::readBlockSet); if (global.isEmpty() && indicesBlocks.isEmpty()) { return EMPTY_CLUSTER_BLOCK; } @@ -319,21 +324,26 @@ public static Diff readDiffFrom(StreamInput in) throws IOExceptio return AbstractDiffable.readDiffFrom(ClusterBlocks::readFrom, in); } + /** + * An immutable level holder. + * + * @opensearch.internal + */ static class ImmutableLevelHolder { private final Set global; - private final ImmutableOpenMap> indices; + private final Map> indices; - ImmutableLevelHolder(Set global, ImmutableOpenMap> indices) { + ImmutableLevelHolder(Set global, final Map> indices) { this.global = global; - this.indices = indices; + this.indices = Collections.unmodifiableMap(indices); } public Set global() { return global; } - public ImmutableOpenMap> indices() { + public Map> indices() { return indices; } } @@ -342,6 +352,11 @@ public static Builder builder() { return new Builder(); } + /** + * Builder for cluster blocks. + * + * @opensearch.internal + */ public static class Builder { private final Set global = new HashSet<>(); @@ -352,11 +367,11 @@ public Builder() {} public Builder blocks(ClusterBlocks blocks) { global.addAll(blocks.global()); - for (ObjectObjectCursor> entry : blocks.indices()) { - if (!indices.containsKey(entry.key)) { - indices.put(entry.key, new HashSet<>()); + for (final Map.Entry> entry : blocks.indices().entrySet()) { + if (!indices.containsKey(entry.getKey())) { + indices.put(entry.getKey(), new HashSet<>()); } - indices.get(entry.key).addAll(entry.value); + indices.get(entry.getKey()).addAll(entry.getValue()); } return this; } @@ -381,6 +396,9 @@ public Builder addBlocks(IndexMetadata indexMetadata) { if (IndexMetadata.INDEX_BLOCKS_READ_ONLY_ALLOW_DELETE_SETTING.get(indexMetadata.getSettings())) { addIndexBlock(indexName, IndexMetadata.INDEX_READ_ONLY_ALLOW_DELETE_BLOCK); } + if (IndexModule.Type.REMOTE_SNAPSHOT.match(indexMetadata.getSettings().get(IndexModule.INDEX_STORE_TYPE_SETTING.getKey()))) { + addIndexBlock(indexName, IndexMetadata.REMOTE_READ_ONLY_ALLOW_DELETE); + } return this; } @@ -453,11 +471,11 @@ public ClusterBlocks build() { return EMPTY_CLUSTER_BLOCK; } // We copy the block sets here in case of the builder is modified after build is called - ImmutableOpenMap.Builder> indicesBuilder = ImmutableOpenMap.builder(indices.size()); + final HashMap> indicesBuilder = new HashMap<>(indices.size()); for (Map.Entry> entry : indices.entrySet()) { indicesBuilder.put(entry.getKey(), unmodifiableSet(new HashSet<>(entry.getValue()))); } - return new ClusterBlocks(unmodifiableSet(new HashSet<>(global)), indicesBuilder.build()); + return new ClusterBlocks(unmodifiableSet(new HashSet<>(global)), indicesBuilder); } } } diff --git a/server/src/main/java/org/opensearch/cluster/block/IndexCreateBlockException.java b/server/src/main/java/org/opensearch/cluster/block/IndexCreateBlockException.java new file mode 100644 index 0000000000000..729d76c72e99e --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/block/IndexCreateBlockException.java @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.block; + +import org.opensearch.core.common.io.stream.StreamInput; + +import java.io.IOException; +import java.util.Set; + +/** + * Internal exception on obtaining an index create block + * + * @opensearch.internal + */ +public class IndexCreateBlockException extends ClusterBlockException { + + public IndexCreateBlockException(Set globalLevelBlocks) { + super(globalLevelBlocks); + } + + public IndexCreateBlockException(StreamInput in) throws IOException { + super(in); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/block/package-info.java b/server/src/main/java/org/opensearch/cluster/block/package-info.java new file mode 100644 index 0000000000000..a51b4623d34af --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/block/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Block level classes. */ +package org.opensearch.cluster.block; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/ApplyCommitRequest.java b/server/src/main/java/org/opensearch/cluster/coordination/ApplyCommitRequest.java index 40d6375d8d916..a722ccd5c6aff 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/ApplyCommitRequest.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/ApplyCommitRequest.java @@ -32,15 +32,17 @@ package org.opensearch.cluster.coordination; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; /** - * A master node sends this request to its peers to inform them that it could commit the + * A cluster-manager node sends this request to its peers to inform them that it could commit the * cluster state with the given term and version. Peers that have accepted the given cluster * state will then consider it as committed and proceed to apply the state locally. + * + * @opensearch.internal */ public class ApplyCommitRequest extends TermVersionRequest { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/ClusterBootstrapService.java b/server/src/main/java/org/opensearch/cluster/coordination/ClusterBootstrapService.java index ce34a21e4adb6..addd2b3e46597 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/ClusterBootstrapService.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/ClusterBootstrapService.java @@ -69,6 +69,11 @@ import static org.opensearch.discovery.SettingsBasedSeedHostsProvider.DISCOVERY_SEED_HOSTS_SETTING; import static org.opensearch.discovery.SettingsBasedSeedHostsProvider.LEGACY_DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING; +/** + * Service for bootstrapping the OpenSearch cluster + * + * @opensearch.internal + */ public class ClusterBootstrapService { public static final Setting> INITIAL_MASTER_NODES_SETTING = Setting.listSetting( @@ -113,12 +118,12 @@ public ClusterBootstrapService( BooleanSupplier isBootstrappedSupplier, Consumer votingConfigurationConsumer ) { + // TODO: Remove variable 'initialClusterManagerSettingName' after removing MASTER_ROLE. + String initialClusterManagerSettingName = INITIAL_CLUSTER_MANAGER_NODES_SETTING.exists(settings) + ? INITIAL_CLUSTER_MANAGER_NODES_SETTING.getKey() + : INITIAL_MASTER_NODES_SETTING.getKey(); if (DiscoveryModule.isSingleNodeDiscovery(settings)) { if (INITIAL_CLUSTER_MANAGER_NODES_SETTING.existsOrFallbackExists(settings)) { - // TODO: Remove variable 'initialClusterManagerSettingName' after removing MASTER_ROLE. - String initialClusterManagerSettingName = INITIAL_CLUSTER_MANAGER_NODES_SETTING.exists(settings) - ? INITIAL_CLUSTER_MANAGER_NODES_SETTING.getKey() - : INITIAL_MASTER_NODES_SETTING.getKey(); throw new IllegalArgumentException( "setting [" + initialClusterManagerSettingName @@ -129,23 +134,23 @@ public ClusterBootstrapService( + "]" ); } - if (DiscoveryNode.isMasterNode(settings) == false) { + if (DiscoveryNode.isClusterManagerNode(settings) == false) { throw new IllegalArgumentException( "node with [" + DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey() + "] set to [" + DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE - + "] must be master-eligible" + + "] must be cluster-manager-eligible" ); } bootstrapRequirements = Collections.singleton(Node.NODE_NAME_SETTING.get(settings)); unconfiguredBootstrapTimeout = null; } else { - final List initialMasterNodes = INITIAL_CLUSTER_MANAGER_NODES_SETTING.get(settings); - bootstrapRequirements = unmodifiableSet(new LinkedHashSet<>(initialMasterNodes)); - if (bootstrapRequirements.size() != initialMasterNodes.size()) { + final List initialClusterManagerNodes = INITIAL_CLUSTER_MANAGER_NODES_SETTING.get(settings); + bootstrapRequirements = unmodifiableSet(new LinkedHashSet<>(initialClusterManagerNodes)); + if (bootstrapRequirements.size() != initialClusterManagerNodes.size()) { throw new IllegalArgumentException( - "setting [" + INITIAL_CLUSTER_MANAGER_NODES_SETTING.getKey() + "] contains duplicates: " + initialMasterNodes + "setting [" + initialClusterManagerSettingName + "] contains duplicates: " + initialClusterManagerNodes ); } unconfiguredBootstrapTimeout = discoveryIsConfigured(settings) ? null : UNCONFIGURED_BOOTSTRAP_TIMEOUT_SETTING.get(settings); @@ -163,14 +168,15 @@ public static boolean discoveryIsConfigured(Settings settings) { LEGACY_DISCOVERY_HOSTS_PROVIDER_SETTING, DISCOVERY_SEED_HOSTS_SETTING, LEGACY_DISCOVERY_ZEN_PING_UNICAST_HOSTS_SETTING, - INITIAL_CLUSTER_MANAGER_NODES_SETTING + INITIAL_CLUSTER_MANAGER_NODES_SETTING, + INITIAL_MASTER_NODES_SETTING ).anyMatch(s -> s.exists(settings)); } void onFoundPeersUpdated() { final Set nodes = getDiscoveredNodes(); if (bootstrappingPermitted.get() - && transportService.getLocalNode().isMasterNode() + && transportService.getLocalNode().isClusterManagerNode() && bootstrapRequirements.isEmpty() == false && isBootstrappedSupplier.getAsBoolean() == false && nodes.stream().noneMatch(Coordinator::isZen1Node)) { @@ -213,13 +219,13 @@ void scheduleUnconfiguredBootstrap() { return; } - if (transportService.getLocalNode().isMasterNode() == false) { + if (transportService.getLocalNode().isClusterManagerNode() == false) { return; } logger.info( "no discovery configuration found, will perform best-effort cluster bootstrapping after [{}] " - + "unless existing master is discovered", + + "unless existing cluster-manager is discovered", unconfiguredBootstrapTimeout ); @@ -251,7 +257,7 @@ private Set getDiscoveredNodes() { } private void startBootstrap(Set discoveryNodes, List unsatisfiedRequirements) { - assert discoveryNodes.stream().allMatch(DiscoveryNode::isMasterNode) : discoveryNodes; + assert discoveryNodes.stream().allMatch(DiscoveryNode::isClusterManagerNode) : discoveryNodes; assert discoveryNodes.stream().noneMatch(Coordinator::isZen1Node) : discoveryNodes; assert unsatisfiedRequirements.size() < discoveryNodes.size() : discoveryNodes + " smaller than " + unsatisfiedRequirements; if (bootstrappingPermitted.compareAndSet(true, false)) { @@ -271,7 +277,7 @@ public static boolean isBootstrapPlaceholder(String nodeId) { } private void doBootstrap(VotingConfiguration votingConfiguration) { - assert transportService.getLocalNode().isMasterNode(); + assert transportService.getLocalNode().isClusterManagerNode(); try { votingConfigurationConsumer.accept(votingConfiguration); diff --git a/server/src/main/java/org/opensearch/cluster/coordination/ClusterFormationFailureHelper.java b/server/src/main/java/org/opensearch/cluster/coordination/ClusterFormationFailureHelper.java index c36a2983a011a..4a301e4395140 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/ClusterFormationFailureHelper.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/ClusterFormationFailureHelper.java @@ -40,9 +40,9 @@ import org.opensearch.common.Nullable; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.gateway.GatewayMetaState; import org.opensearch.monitor.StatusInfo; import org.opensearch.threadpool.ThreadPool; @@ -59,6 +59,11 @@ import static org.opensearch.cluster.coordination.ClusterBootstrapService.INITIAL_CLUSTER_MANAGER_NODES_SETTING; import static org.opensearch.monitor.StatusInfo.Status.UNHEALTHY; +/** + * Helper for cluster failure events + * + * @opensearch.internal + */ public class ClusterFormationFailureHelper { private static final Logger logger = LogManager.getLogger(ClusterFormationFailureHelper.class); @@ -102,6 +107,11 @@ public void stop() { warningScheduler = null; } + /** + * A warning scheduler. + * + * @opensearch.internal + */ private class WarningScheduler { private boolean isActive() { @@ -138,6 +148,11 @@ public String toString() { } } + /** + * State of the cluster formation. + * + * @opensearch.internal + */ static class ClusterFormationState { private final Settings settings; private final ClusterState clusterState; @@ -169,9 +184,10 @@ String getDescription() { if (statusInfo.getStatus() == UNHEALTHY) { return String.format(Locale.ROOT, "this node is unhealthy: %s", statusInfo.getInfo()); } - final List clusterStateNodes = StreamSupport.stream(clusterState.nodes().getMasterNodes().values().spliterator(), false) - .map(n -> n.value.toString()) - .collect(Collectors.toList()); + final List clusterStateNodes = StreamSupport.stream( + clusterState.nodes().getClusterManagerNodes().values().spliterator(), + false + ).map(n -> n.toString()).collect(Collectors.toList()); final String discoveryWillContinueDescription = String.format( Locale.ROOT, @@ -191,8 +207,8 @@ String getDescription() { discoveryWillContinueDescription ); - if (clusterState.nodes().getLocalNode().isMasterNode() == false) { - return String.format(Locale.ROOT, "master not discovered yet: %s", discoveryStateIgnoringQuorum); + if (clusterState.nodes().getLocalNode().isClusterManagerNode() == false) { + return String.format(Locale.ROOT, "cluster-manager not discovered yet: %s", discoveryStateIgnoringQuorum); } if (clusterState.getLastAcceptedConfiguration().isEmpty()) { @@ -203,14 +219,14 @@ String getDescription() { } else { bootstrappingDescription = String.format( Locale.ROOT, - "this node must discover master-eligible nodes %s to bootstrap a cluster", + "this node must discover cluster-manager-eligible nodes %s to bootstrap a cluster", INITIAL_CLUSTER_MANAGER_NODES_SETTING.get(settings) ); } return String.format( Locale.ROOT, - "master not discovered yet, this node has not previously joined a bootstrapped cluster, and %s: %s", + "cluster-manager not discovered yet, this node has not previously joined a bootstrapped cluster, and %s: %s", bootstrappingDescription, discoveryStateIgnoringQuorum ); @@ -218,10 +234,11 @@ String getDescription() { assert clusterState.getLastCommittedConfiguration().isEmpty() == false; - if (clusterState.getLastCommittedConfiguration().equals(VotingConfiguration.MUST_JOIN_ELECTED_MASTER)) { + if (clusterState.getLastCommittedConfiguration().equals(VotingConfiguration.MUST_JOIN_ELECTED_MASTER) + || clusterState.getLastCommittedConfiguration().equals(VotingConfiguration.MUST_JOIN_ELECTED_CLUSTER_MANAGER)) { return String.format( Locale.ROOT, - "master not discovered yet and this node was detached from its previous cluster, have discovered %s; %s", + "cluster-manager not discovered yet and this node was detached from its previous cluster, have discovered %s; %s", foundPeers, discoveryWillContinueDescription ); @@ -250,7 +267,7 @@ String getDescription() { return String.format( Locale.ROOT, - "master not discovered or elected yet, an election requires %s, have discovered %s which %s; %s", + "cluster-manager not discovered or elected yet, an election requires %s, have discovered %s which %s; %s", quorumDescription, foundPeers, isQuorumOrNot, @@ -269,8 +286,8 @@ private String describeQuorum(VotingConfiguration votingConfiguration) { if (nodeIds.size() == 1) { if (nodeIds.contains(GatewayMetaState.STALE_STATE_CONFIG_NODE_ID)) { - return "one or more nodes that have already participated as master-eligible nodes in the cluster but this node was " - + "not master-eligible the last time it joined the cluster"; + return "one or more nodes that have already participated as cluster-manager-eligible nodes in the cluster but this node was " + + "not cluster-manager-eligible the last time it joined the cluster"; } else { return "a node with id " + realNodeIds; } diff --git a/server/src/main/java/org/opensearch/cluster/coordination/ClusterStatePublisher.java b/server/src/main/java/org/opensearch/cluster/coordination/ClusterStatePublisher.java index 47a18d5be1ec4..39d05e672f977 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/ClusterStatePublisher.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/ClusterStatePublisher.java @@ -31,16 +31,21 @@ package org.opensearch.cluster.coordination; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.Nullable; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +/** + * Publishes the cluster state + * + * @opensearch.internal + */ public interface ClusterStatePublisher { /** - * Publish all the changes to the cluster from the master (can be called just by the master). The publish - * process should apply this state to the master as well! + * Publish all the changes to the cluster from the cluster-manager (can be called just by the cluster-manager). The publish + * process should apply this state to the cluster-manager as well! * * The publishListener allows to wait for the publication to complete, which can be either successful completion, timing out or failing. * The method is guaranteed to pass back a {@link FailedToCommitClusterStateException} to the publishListener if the change is not @@ -51,6 +56,11 @@ public interface ClusterStatePublisher { */ void publish(ClusterChangedEvent clusterChangedEvent, ActionListener publishListener, AckListener ackListener); + /** + * An acknowledgement listener. + * + * @opensearch.internal + */ interface AckListener { /** * Should be called when the cluster coordination layer has committed the cluster state (i.e. even if this publication fails, diff --git a/server/src/main/java/org/opensearch/cluster/coordination/CompressedStreamUtils.java b/server/src/main/java/org/opensearch/cluster/coordination/CompressedStreamUtils.java new file mode 100644 index 0000000000000..dc7b203eb7c4b --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/coordination/CompressedStreamUtils.java @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.coordination; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.Version; +import org.opensearch.common.CheckedConsumer; +import org.opensearch.common.io.stream.BytesStreamOutput; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.InputStreamStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableAwareStreamInput; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.OutputStreamStreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.compress.Compressor; +import org.opensearch.core.compress.CompressorRegistry; +import org.opensearch.transport.BytesTransportRequest; + +import java.io.IOException; + +/** + * A helper class to utilize the compressed stream. + * + * @opensearch.internal + */ +public final class CompressedStreamUtils { + private static final Logger logger = LogManager.getLogger(CompressedStreamUtils.class); + + public static BytesReference createCompressedStream(Version version, CheckedConsumer outputConsumer) + throws IOException { + final BytesStreamOutput bStream = new BytesStreamOutput(); + try (StreamOutput stream = new OutputStreamStreamOutput(CompressorRegistry.defaultCompressor().threadLocalOutputStream(bStream))) { + stream.setVersion(version); + outputConsumer.accept(stream); + } + final BytesReference serializedByteRef = bStream.bytes(); + logger.trace("serialized writable object for node version [{}] with size [{}]", version, serializedByteRef.length()); + return serializedByteRef; + } + + public static StreamInput decompressBytes(BytesTransportRequest request, NamedWriteableRegistry namedWriteableRegistry) + throws IOException { + final Compressor compressor = CompressorRegistry.compressor(request.bytes()); + final StreamInput in; + if (compressor != null) { + in = new InputStreamStreamInput(compressor.threadLocalInputStream(request.bytes().streamInput())); + } else { + in = request.bytes().streamInput(); + } + in.setVersion(request.version()); + return new NamedWriteableAwareStreamInput(in, namedWriteableRegistry); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/coordination/CoordinationMetadata.java b/server/src/main/java/org/opensearch/cluster/coordination/CoordinationMetadata.java index c7671cb9e9b2b..682ebfd474e7a 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/CoordinationMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/CoordinationMetadata.java @@ -32,15 +32,15 @@ package org.opensearch.cluster.coordination; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Arrays; @@ -52,6 +52,11 @@ import java.util.Set; import java.util.stream.Collectors; +/** + * Metadata for cluster coordination + * + * @opensearch.internal + */ public class CoordinationMetadata implements Writeable, ToXContentFragment { public static final CoordinationMetadata EMPTY_METADATA = builder().build(); @@ -206,6 +211,11 @@ public String toString() { + '}'; } + /** + * Builder for coordination metadata. + * + * @opensearch.internal + */ public static class Builder { private long term = 0; private VotingConfiguration lastCommittedConfiguration = VotingConfiguration.EMPTY_CONFIG; @@ -253,6 +263,11 @@ public CoordinationMetadata build() { } } + /** + * Excluded nodes from voting config. + * + * @opensearch.internal + */ public static class VotingConfigExclusion implements Writeable, ToXContentFragment { public static final String MISSING_VALUE_MARKER = "_absent_"; private final String nodeId; @@ -346,13 +361,22 @@ public String toString() { /** * A collection of persistent node ids, denoting the voting configuration for cluster state changes. + * + * @opensearch.internal */ public static class VotingConfiguration implements Writeable, ToXContentFragment { public static final VotingConfiguration EMPTY_CONFIG = new VotingConfiguration(Collections.emptySet()); + /** + * @deprecated As of 2.0, because supporting inclusive language, replaced by {@link #MUST_JOIN_ELECTED_CLUSTER_MANAGER} + */ + @Deprecated public static final VotingConfiguration MUST_JOIN_ELECTED_MASTER = new VotingConfiguration( Collections.singleton("_must_join_elected_master_") ); + public static final VotingConfiguration MUST_JOIN_ELECTED_CLUSTER_MANAGER = new VotingConfiguration( + Collections.singleton("_must_join_elected_cluster_manager_") + ); private final Set nodeIds; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/CoordinationState.java b/server/src/main/java/org/opensearch/cluster/coordination/CoordinationState.java index b28fde5d9cc16..a339852e6ed8d 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/CoordinationState.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/CoordinationState.java @@ -35,8 +35,11 @@ import org.apache.logging.log4j.Logger; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfiguration; +import org.opensearch.cluster.coordination.PersistedStateRegistry.PersistedStateType; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.io.IOUtils; import java.io.Closeable; import java.io.IOException; @@ -49,10 +52,13 @@ import java.util.Set; import static org.opensearch.cluster.coordination.Coordinator.ZEN1_BWC_TERM; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.isRemoteStoreClusterStateEnabled; /** * The core class of the cluster state coordination algorithm, directly implementing the * formal model + * + * @opensearch.internal */ public class CoordinationState { @@ -62,8 +68,8 @@ public class CoordinationState { private final ElectionStrategy electionStrategy; - // persisted state - private final PersistedState persistedState; + // persisted state registry + private final PersistedStateRegistry persistedStateRegistry; // transient state private VoteCollection joinVotes; @@ -72,12 +78,18 @@ public class CoordinationState { private long lastPublishedVersion; private VotingConfiguration lastPublishedConfiguration; private VoteCollection publishVotes; - - public CoordinationState(DiscoveryNode localNode, PersistedState persistedState, ElectionStrategy electionStrategy) { + private final boolean isRemoteStateEnabled; + + public CoordinationState( + DiscoveryNode localNode, + PersistedStateRegistry persistedStateRegistry, + ElectionStrategy electionStrategy, + Settings settings + ) { this.localNode = localNode; - // persisted state - this.persistedState = persistedState; + // persisted state registry + this.persistedStateRegistry = persistedStateRegistry; this.electionStrategy = electionStrategy; // transient state @@ -85,16 +97,19 @@ public CoordinationState(DiscoveryNode localNode, PersistedState persistedState, this.startedJoinSinceLastReboot = false; this.electionWon = false; this.lastPublishedVersion = 0L; - this.lastPublishedConfiguration = persistedState.getLastAcceptedState().getLastAcceptedConfiguration(); + this.lastPublishedConfiguration = persistedStateRegistry.getPersistedState(PersistedStateType.LOCAL) + .getLastAcceptedState() + .getLastAcceptedConfiguration(); this.publishVotes = new VoteCollection(); + this.isRemoteStateEnabled = isRemoteStoreClusterStateEnabled(settings); } public long getCurrentTerm() { - return persistedState.getCurrentTerm(); + return persistedStateRegistry.getPersistedState(PersistedStateType.LOCAL).getCurrentTerm(); } public ClusterState getLastAcceptedState() { - return persistedState.getLastAcceptedState(); + return persistedStateRegistry.getPersistedState(PersistedStateType.LOCAL).getLastAcceptedState(); } public long getLastAcceptedTerm() { @@ -184,7 +199,7 @@ public void setInitialState(ClusterState initialState) { assert initialState.getLastAcceptedConfiguration().isEmpty() == false; assert initialState.getLastCommittedConfiguration().isEmpty() == false; - persistedState.setLastAcceptedState(initialState); + persistedStateRegistry.getPersistedState(PersistedStateType.LOCAL).setLastAcceptedState(initialState); } /** @@ -220,7 +235,7 @@ public Join handleStartJoin(StartJoinRequest startJoinRequest) { logger.debug("handleStartJoin: discarding {}: {}", joinVotes, reason); } - persistedState.setCurrentTerm(startJoinRequest.getTerm()); + persistedStateRegistry.getPersistedState(PersistedStateType.LOCAL).setCurrentTerm(startJoinRequest.getTerm()); assert getCurrentTerm() == startJoinRequest.getTerm(); lastPublishedVersion = 0; lastPublishedConfiguration = getLastAcceptedConfiguration(); @@ -411,7 +426,7 @@ public PublishResponse handlePublishRequest(PublishRequest publishRequest) { } if (clusterState.term() == getLastAcceptedTerm() && clusterState.version() <= getLastAcceptedVersion()) { if (clusterState.term() == ZEN1_BWC_TERM - && clusterState.nodes().getMasterNode().equals(getLastAcceptedState().nodes().getMasterNode()) == false) { + && clusterState.nodes().getClusterManagerNode().equals(getLastAcceptedState().nodes().getClusterManagerNode()) == false) { logger.debug( "handling publish request in compatibility mode despite version mismatch (expected: >[{}], actual: [{}])", getLastAcceptedVersion(), @@ -434,7 +449,7 @@ public PublishResponse handlePublishRequest(PublishRequest publishRequest) { clusterState.version(), clusterState.term() ); - persistedState.setLastAcceptedState(clusterState); + persistedStateRegistry.getPersistedState(PersistedStateType.LOCAL).setLastAcceptedState(clusterState); assert getLastAcceptedState() == clusterState; return new PublishResponse(clusterState.term(), clusterState.version()); @@ -488,6 +503,7 @@ public Optional handlePublishResponse(DiscoveryNode sourceNo publishResponse.getVersion(), publishResponse.getTerm() ); + handlePreCommit(); return Optional.of(new ApplyCommitRequest(localNode, publishResponse.getTerm(), publishResponse.getVersion())); } @@ -545,10 +561,36 @@ public void handleCommit(ApplyCommitRequest applyCommit) { applyCommit.getVersion() ); - persistedState.markLastAcceptedStateAsCommitted(); + persistedStateRegistry.getPersistedState(PersistedStateType.LOCAL).markLastAcceptedStateAsCommitted(); assert getLastCommittedConfiguration().equals(getLastAcceptedConfiguration()); } + /** + * This method should be called just before sending the PublishRequest to all cluster nodes. + * @param clusterState The cluster state for which pre publish activities should happen. + */ + public void handlePrePublish(ClusterState clusterState) { + // Publishing the current state to remote store before sending the cluster state to other nodes. + // This is to ensure the remote store is the single source of truth for current state. Even if the current node + // goes down after sending the cluster state to other nodes, we should be able to read the remote state and + // recover the cluster. + if (isRemoteStateEnabled) { + assert persistedStateRegistry.getPersistedState(PersistedStateType.REMOTE) != null : "Remote state has not been initialized"; + persistedStateRegistry.getPersistedState(PersistedStateType.REMOTE).setLastAcceptedState(clusterState); + } + } + + /** + * This method should be called just before sending the ApplyCommitRequest to all cluster nodes. + */ + public void handlePreCommit() { + // Publishing the committed state to remote store before sending apply commit to other nodes. + if (isRemoteStateEnabled) { + assert persistedStateRegistry.getPersistedState(PersistedStateType.REMOTE) != null : "Remote state has not been initialized"; + persistedStateRegistry.getPersistedState(PersistedStateType.REMOTE).markLastAcceptedStateAsCommitted(); + } + } + public void invariant() { assert getLastAcceptedTerm() <= getCurrentTerm(); assert electionWon() == isElectionQuorum(joinVotes); @@ -562,11 +604,13 @@ public void invariant() { } public void close() throws IOException { - persistedState.close(); + IOUtils.close(persistedStateRegistry); } /** * Pluggable persistence layer for {@link CoordinationState}. + * + * @opensearch.internal */ public interface PersistedState extends Closeable { @@ -610,7 +654,8 @@ default void markLastAcceptedStateAsCommitted() { metadataBuilder = Metadata.builder(lastAcceptedState.metadata()); metadataBuilder.coordinationMetadata(coordinationMetadata); } - // if we receive a commit from a Zen1 master that has not recovered its state yet, the cluster uuid might not been known yet. + // if we receive a commit from a Zen1 cluster-manager that has not recovered its state yet, + // the cluster uuid might not been known yet. assert lastAcceptedState.metadata().clusterUUID().equals(Metadata.UNKNOWN_CLUSTER_UUID) == false || lastAcceptedState.term() == ZEN1_BWC_TERM : "received cluster state with empty cluster uuid but not Zen1 BWC term: " + lastAcceptedState; @@ -622,7 +667,8 @@ default void markLastAcceptedStateAsCommitted() { metadataBuilder.clusterUUIDCommitted(true); if (lastAcceptedState.term() != ZEN1_BWC_TERM) { - // Zen1 masters never publish a committed cluster UUID so if we logged this it'd happen on on every update. Let's just + // Zen1 cluster-managers never publish a committed cluster UUID so if we logged this it'd happen on on every update. + // Let's just // not log it at all in a 6.8/7.x rolling upgrade. logger.info("cluster UUID set to [{}]", lastAcceptedState.metadata().clusterUUID()); } @@ -637,6 +683,8 @@ default void close() throws IOException {} /** * A collection of votes, used to calculate quorums. Optionally records the Joins as well. + * + * @opensearch.internal */ public static class VoteCollection { @@ -644,7 +692,7 @@ public static class VoteCollection { private final Set joins; public boolean addVote(DiscoveryNode sourceNode) { - return sourceNode.isMasterNode() && nodes.put(sourceNode.getId(), sourceNode) == null; + return sourceNode.isClusterManagerNode() && nodes.put(sourceNode.getId(), sourceNode) == null; } public boolean addJoinVote(Join join) { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/CoordinationStateRejectedException.java b/server/src/main/java/org/opensearch/cluster/coordination/CoordinationStateRejectedException.java index 078424a079f02..8dc8d4295072a 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/CoordinationStateRejectedException.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/CoordinationStateRejectedException.java @@ -33,7 +33,7 @@ package org.opensearch.cluster.coordination; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; @@ -43,6 +43,8 @@ * Occurrences of this exception don't always signal failures, but can often be just caused by the * asynchronous, distributed nature of the system. They will, for example, naturally happen during * leader election, if multiple nodes are trying to become leader at the same time. + * + * @opensearch.internal */ public class CoordinationStateRejectedException extends OpenSearchException { public CoordinationStateRejectedException(String msg, Object... args) { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java b/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java index 557f11f75d969..f587a73137c0a 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/Coordinator.java @@ -35,9 +35,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.lucene.util.SetOnce; import org.opensearch.LegacyESVersion; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterName; import org.opensearch.cluster.ClusterState; @@ -58,23 +56,27 @@ import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterApplier; import org.opensearch.cluster.service.ClusterApplier.ClusterApplyListener; -import org.opensearch.cluster.service.MasterService; +import org.opensearch.cluster.service.ClusterManagerService; import org.opensearch.common.Booleans; import org.opensearch.common.Nullable; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; -import org.opensearch.common.component.AbstractLifecycleComponent; -import org.opensearch.common.io.stream.NamedWriteableRegistry; +import org.opensearch.common.SetOnce; import org.opensearch.common.lease.Releasable; +import org.opensearch.common.lifecycle.AbstractLifecycleComponent; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.util.concurrent.OpenSearchExecutors; import org.opensearch.common.util.concurrent.ListenableFuture; +import org.opensearch.common.util.concurrent.OpenSearchExecutors; import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.transport.TransportResponse.Empty; +import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.discovery.Discovery; import org.opensearch.discovery.DiscoveryModule; import org.opensearch.discovery.DiscoveryStats; @@ -84,9 +86,9 @@ import org.opensearch.discovery.SeedHostsResolver; import org.opensearch.monitor.NodeHealthService; import org.opensearch.monitor.StatusInfo; +import org.opensearch.node.remotestore.RemoteStoreNodeService; import org.opensearch.threadpool.Scheduler; import org.opensearch.threadpool.ThreadPool.Names; -import org.opensearch.transport.TransportResponse.Empty; import org.opensearch.transport.TransportService; import java.io.IOException; @@ -105,11 +107,17 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static org.opensearch.cluster.coordination.NoMasterBlockService.NO_MASTER_BLOCK_ID; +import static org.opensearch.cluster.coordination.NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID; +import static org.opensearch.cluster.decommission.DecommissionHelper.nodeCommissioned; import static org.opensearch.gateway.ClusterStateUpdaters.hideStateIfNotRecovered; import static org.opensearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK; import static org.opensearch.monitor.StatusInfo.Status.UNHEALTHY; +/** + * The main lifecycle resource coordinator + * + * @opensearch.internal + */ public class Coordinator extends AbstractLifecycleComponent implements Discovery { public static final long ZEN1_BWC_TERM = 0; @@ -134,14 +142,15 @@ public class Coordinator extends AbstractLifecycleComponent implements Discovery private final Settings settings; private final boolean singleNodeDiscovery; + private volatile boolean localNodeCommissioned; private final ElectionStrategy electionStrategy; private final TransportService transportService; - private final MasterService masterService; + private final ClusterManagerService clusterManagerService; private final AllocationService allocationService; private final JoinHelper joinHelper; private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; private final Supplier persistedStateSupplier; - private final NoMasterBlockService noMasterBlockService; + private final NoClusterManagerBlockService noClusterManagerBlockService; final Object mutex = new Object(); // package-private to allow tests to call methods that assert that the mutex is held private final SetOnce coordinationState = new SetOnce<>(); // initialized on start-up (see doStart) private volatile ClusterState applierState; // the state that should be exposed to the cluster state applier @@ -174,6 +183,8 @@ public class Coordinator extends AbstractLifecycleComponent implements Discovery private JoinHelper.JoinAccumulator joinAccumulator; private Optional currentPublication = Optional.empty(); private final NodeHealthService nodeHealthService; + private final PersistedStateRegistry persistedStateRegistry; + private final RemoteStoreNodeService remoteStoreNodeService; /** * @param nodeName The name of the node, used to name the {@link java.util.concurrent.ExecutorService} of the {@link SeedHostsResolver}. @@ -186,7 +197,7 @@ public Coordinator( TransportService transportService, NamedWriteableRegistry namedWriteableRegistry, AllocationService allocationService, - MasterService masterService, + ClusterManagerService clusterManagerService, Supplier persistedStateSupplier, SeedHostsProvider seedHostsProvider, ClusterApplier clusterApplier, @@ -194,11 +205,13 @@ public Coordinator( Random random, RerouteService rerouteService, ElectionStrategy electionStrategy, - NodeHealthService nodeHealthService + NodeHealthService nodeHealthService, + PersistedStateRegistry persistedStateRegistry, + RemoteStoreNodeService remoteStoreNodeService ) { this.settings = settings; this.transportService = transportService; - this.masterService = masterService; + this.clusterManagerService = clusterManagerService; this.allocationService = allocationService; this.onJoinValidators = JoinTaskExecutor.addBuiltInJoinValidators(onJoinValidators); this.singleNodeDiscovery = DiscoveryModule.isSingleNodeDiscovery(settings); @@ -206,18 +219,21 @@ public Coordinator( this.joinHelper = new JoinHelper( settings, allocationService, - masterService, + clusterManagerService, transportService, + remoteStoreNodeService, this::getCurrentTerm, - this::getStateForMasterService, + this::getStateForClusterManagerService, this::handleJoinRequest, this::joinLeaderInTerm, this.onJoinValidators, rerouteService, - nodeHealthService + nodeHealthService, + this::onNodeCommissionStatusChange, + namedWriteableRegistry ); this.persistedStateSupplier = persistedStateSupplier; - this.noMasterBlockService = new NoMasterBlockService(settings, clusterSettings); + this.noClusterManagerBlockService = new NoClusterManagerBlockService(settings, clusterSettings); this.lastKnownLeader = Optional.empty(); this.lastJoin = Optional.empty(); this.joinAccumulator = new InitialJoinAccumulator(); @@ -255,7 +271,7 @@ public Coordinator( ); this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); this.clusterApplier = clusterApplier; - masterService.setClusterStateSupplier(this::getStateForMasterService); + clusterManagerService.setClusterStateSupplier(this::getStateForClusterManagerService); this.reconfigurator = new Reconfigurator(settings, clusterSettings); this.clusterBootstrapService = new ClusterBootstrapService( settings, @@ -277,12 +293,15 @@ public Coordinator( joinHelper::logLastFailedJoinAttempt ); this.nodeHealthService = nodeHealthService; + this.persistedStateRegistry = persistedStateRegistry; + this.localNodeCommissioned = true; + this.remoteStoreNodeService = remoteStoreNodeService; } private ClusterFormationState getClusterFormationState() { return new ClusterFormationState( settings, - getStateForMasterService(), + getStateForClusterManagerService(), peerFinder.getLastResolvedAddresses(), Stream.concat(Stream.of(getLocalNode()), StreamSupport.stream(peerFinder.getFoundPeers().spliterator(), false)) .collect(Collectors.toList()), @@ -296,7 +315,7 @@ private void onLeaderFailure(Exception e) { synchronized (mutex) { if (mode != Mode.CANDIDATE) { assert lastKnownLeader.isPresent(); - logger.info(new ParameterizedMessage("master node [{}] failed, restarting discovery", lastKnownLeader.get()), e); + logger.info(new ParameterizedMessage("cluster-manager node [{}] failed, restarting discovery", lastKnownLeader.get()), e); } becomeCandidate("onLeaderFailure"); } @@ -305,7 +324,7 @@ private void onLeaderFailure(Exception e) { private void removeNode(DiscoveryNode discoveryNode, String reason) { synchronized (mutex) { if (mode == Mode.LEADER) { - masterService.submitStateUpdateTask( + clusterManagerService.submitStateUpdateTask( "node-left", new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason), ClusterStateTaskConfig.build(Priority.IMMEDIATE), @@ -336,11 +355,11 @@ void onFollowerCheckRequest(FollowerCheckRequest followerCheckRequest) { } else if (mode == Mode.FOLLOWER) { logger.trace("onFollowerCheckRequest: responding successfully to {}", followerCheckRequest); } else if (joinHelper.isJoinPending()) { - logger.trace("onFollowerCheckRequest: rejoining master, responding successfully to {}", followerCheckRequest); + logger.trace("onFollowerCheckRequest: rejoining cluster-manager, responding successfully to {}", followerCheckRequest); } else { - logger.trace("onFollowerCheckRequest: received check from faulty master, rejecting {}", followerCheckRequest); + logger.trace("onFollowerCheckRequest: received check from faulty cluster-manager, rejecting {}", followerCheckRequest); throw new CoordinationStateRejectedException( - "onFollowerCheckRequest: received check from faulty master, rejecting " + followerCheckRequest + "onFollowerCheckRequest: received check from faulty cluster-manager, rejecting " + followerCheckRequest ); } } @@ -352,9 +371,9 @@ private void handleApplyCommit(ApplyCommitRequest applyCommitRequest, ActionList coordinationState.get().handleCommit(applyCommitRequest); final ClusterState committedState = hideStateIfNotRecovered(coordinationState.get().getLastAcceptedState()); - applierState = mode == Mode.CANDIDATE ? clusterStateWithNoMasterBlock(committedState) : committedState; + applierState = mode == Mode.CANDIDATE ? clusterStateWithNoClusterManagerBlock(committedState) : committedState; if (applyCommitRequest.getSourceNode().equals(getLocalNode())) { - // master node applies the committed state at the end of the publication process, not here. + // cluster-manager node applies the committed state at the end of the publication process, not here. applyListener.onResponse(null); } else { clusterApplier.onNewClusterState(applyCommitRequest.toString(), () -> applierState, new ClusterApplyListener() { @@ -381,7 +400,7 @@ PublishWithJoinResponse handlePublishRequest(PublishRequest publishRequest) { + getLocalNode(); synchronized (mutex) { - final DiscoveryNode sourceNode = publishRequest.getAcceptedState().nodes().getMasterNode(); + final DiscoveryNode sourceNode = publishRequest.getAcceptedState().nodes().getClusterManagerNode(); logger.trace("handlePublishRequest: handling [{}] from [{}]", publishRequest, sourceNode); if (sourceNode.equals(getLocalNode()) && mode != Mode.LEADER) { @@ -423,7 +442,7 @@ && getCurrentTerm() == ZEN1_BWC_TERM } if (publishRequest.getAcceptedState().term() > localState.term()) { - // only do join validation if we have not accepted state from this master yet + // only do join validation if we have not accepted state from this cluster manager yet onJoinValidators.forEach(a -> a.accept(getLocalNode(), publishRequest.getAcceptedState())); } @@ -507,13 +526,14 @@ private void startElection() { } } - private void abdicateTo(DiscoveryNode newMaster) { + private void abdicateTo(DiscoveryNode newClusterManager) { assert Thread.holdsLock(mutex); assert mode == Mode.LEADER : "expected to be leader on abdication but was " + mode; - assert newMaster.isMasterNode() : "should only abdicate to master-eligible node but was " + newMaster; - final StartJoinRequest startJoinRequest = new StartJoinRequest(newMaster, Math.max(getCurrentTerm(), maxTermSeen) + 1); - logger.info("abdicating to {} with term {}", newMaster, startJoinRequest.getTerm()); - getLastAcceptedState().nodes().mastersFirstStream().forEach(node -> { + assert newClusterManager.isClusterManagerNode() : "should only abdicate to cluster-manager-eligible node but was " + + newClusterManager; + final StartJoinRequest startJoinRequest = new StartJoinRequest(newClusterManager, Math.max(getCurrentTerm(), maxTermSeen) + 1); + logger.info("abdicating to {} with term {}", newClusterManager, startJoinRequest.getTerm()); + getLastAcceptedState().nodes().clusterManagersFirstStream().forEach(node -> { if (isZen1Node(node) == false) { joinHelper.sendStartJoinRequest(startJoinRequest, node); } @@ -521,7 +541,7 @@ private void abdicateTo(DiscoveryNode newMaster) { // handling of start join messages on the local node will be dispatched to the generic thread-pool assert mode == Mode.LEADER : "should still be leader after sending abdication messages " + mode; // explicitly move node to candidate state so that the next cluster state update task yields an onNoLongerMaster event - becomeCandidate("after abdicating to " + newMaster); + becomeCandidate("after abdicating to " + newClusterManager); } private static boolean localNodeMayWinElection(ClusterState lastAcceptedState) { @@ -563,7 +583,7 @@ private Join joinLeaderInTerm(StartJoinRequest startJoinRequest) { private void handleJoinRequest(JoinRequest joinRequest, JoinHelper.JoinCallback joinCallback) { assert Thread.holdsLock(mutex) == false; - assert getLocalNode().isMasterNode() : getLocalNode() + " received a join but is not master-eligible"; + assert getLocalNode().isClusterManagerNode() : getLocalNode() + " received a join but is not cluster-manager-eligible"; logger.trace("handleJoinRequest: as {}, handling {}", mode, joinRequest); if (singleNodeDiscovery && joinRequest.getSourceNode().equals(getLocalNode()) == false) { @@ -580,9 +600,9 @@ private void handleJoinRequest(JoinRequest joinRequest, JoinHelper.JoinCallback } transportService.connectToNode(joinRequest.getSourceNode(), ActionListener.wrap(ignore -> { - final ClusterState stateForJoinValidation = getStateForMasterService(); + final ClusterState stateForJoinValidation = getStateForClusterManagerService(); - if (stateForJoinValidation.nodes().isLocalNodeElectedMaster()) { + if (stateForJoinValidation.nodes().isLocalNodeElectedClusterManager()) { onJoinValidators.forEach(a -> a.accept(joinRequest.getSourceNode(), stateForJoinValidation)); if (stateForJoinValidation.getBlocks().hasGlobalBlock(STATE_NOT_RECOVERED_BLOCK) == false) { // we do this in a couple of places including the cluster update thread. This one here is really just best effort @@ -591,6 +611,9 @@ private void handleJoinRequest(JoinRequest joinRequest, JoinHelper.JoinCallback joinRequest.getSourceNode().getVersion(), stateForJoinValidation.getNodes().getMinNodeVersion() ); + // we are checking source node commission status here to reject any join request coming from a decommissioned node + // even before executing the join task to fail fast + JoinTaskExecutor.ensureNodeCommissioned(joinRequest.getSourceNode(), stateForJoinValidation.metadata()); } sendValidateJoinRequest(stateForJoinValidation, joinRequest, joinCallback); } else { @@ -668,11 +691,11 @@ void becomeCandidate(String method) { lagDetector.clearTrackedNodes(); if (prevMode == Mode.LEADER) { - cleanMasterService(); + cleanClusterManagerService(); } - if (applierState.nodes().getMasterNodeId() != null) { - applierState = clusterStateWithNoMasterBlock(applierState); + if (applierState.nodes().getClusterManagerNodeId() != null) { + applierState = clusterStateWithNoClusterManagerBlock(applierState); clusterApplier.onNewClusterState("becoming candidate: " + method, () -> applierState, (source, e) -> {}); } } @@ -683,7 +706,7 @@ void becomeCandidate(String method) { void becomeLeader(String method) { assert Thread.holdsLock(mutex) : "Coordinator mutex not held"; assert mode == Mode.CANDIDATE : "expected candidate but was " + mode; - assert getLocalNode().isMasterNode() : getLocalNode() + " became a leader but is not master-eligible"; + assert getLocalNode().isClusterManagerNode() : getLocalNode() + " became a leader but is not cluster-manager-eligible"; logger.debug( "{}: coordinator becoming LEADER in term {} (was {}, lastKnownLeader was [{}])", @@ -709,7 +732,7 @@ void becomeLeader(String method) { void becomeFollower(String method, DiscoveryNode leaderNode) { assert Thread.holdsLock(mutex) : "Coordinator mutex not held"; - assert leaderNode.isMasterNode() : leaderNode + " became a leader but is not master-eligible"; + assert leaderNode.isClusterManagerNode() : leaderNode + " became a leader but is not cluster-manager-eligible"; assert mode != Mode.LEADER : "do not switch to follower from leader (should be candidate first)"; if (mode == Mode.FOLLOWER && Optional.of(leaderNode).equals(lastKnownLeader)) { @@ -750,17 +773,17 @@ void becomeFollower(String method, DiscoveryNode leaderNode) { lagDetector.clearTrackedNodes(); } - private void cleanMasterService() { - masterService.submitStateUpdateTask("clean-up after stepping down as master", new LocalClusterUpdateTask() { + private void cleanClusterManagerService() { + clusterManagerService.submitStateUpdateTask("clean-up after stepping down as cluster-manager", new LocalClusterUpdateTask() { @Override public void onFailure(String source, Exception e) { // ignore - logger.trace("failed to clean-up after stepping down as master", e); + logger.trace("failed to clean-up after stepping down as cluster-manager", e); } @Override public ClusterTasksResult execute(ClusterState currentState) { - if (currentState.nodes().isLocalNodeElectedMaster() == false) { + if (currentState.nodes().isLocalNodeElectedClusterManager() == false) { allocationService.cleanCaches(); } return unchanged(); @@ -806,8 +829,7 @@ boolean publicationInProgress() { @Override protected void doStart() { synchronized (mutex) { - CoordinationState.PersistedState persistedState = persistedStateSupplier.get(); - coordinationState.set(new CoordinationState(getLocalNode(), persistedState, electionStrategy)); + coordinationState.set(new CoordinationState(getLocalNode(), persistedStateRegistry, electionStrategy, settings)); peerFinder.setCurrentTerm(getCurrentTerm()); configuredHostsResolver.start(); final ClusterState lastAcceptedState = coordinationState.get().getLastAcceptedState(); @@ -833,7 +855,7 @@ protected void doStart() { .blocks( ClusterBlocks.builder() .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK) - .addGlobalBlock(noMasterBlockService.getNoMasterBlock()) + .addGlobalBlock(noClusterManagerBlockService.getNoClusterManagerBlock()) ) .nodes(DiscoveryNodes.builder().add(getLocalNode()).localNodeId(getLocalNode().getId())) .build(); @@ -879,7 +901,8 @@ public void invariant() { assert peerFinder.getCurrentTerm() == getCurrentTerm(); assert followersChecker.getFastResponseState().term == getCurrentTerm() : followersChecker.getFastResponseState(); assert followersChecker.getFastResponseState().mode == getMode() : followersChecker.getFastResponseState(); - assert (applierState.nodes().getMasterNodeId() == null) == applierState.blocks().hasGlobalBlockWithId(NO_MASTER_BLOCK_ID); + assert (applierState.nodes().getClusterManagerNodeId() == null) == applierState.blocks() + .hasGlobalBlockWithId(NO_CLUSTER_MANAGER_BLOCK_ID); assert preVoteCollector.getPreVoteResponse().equals(getPreVoteResponse()) : preVoteCollector + " vs " + getPreVoteResponse(); assert lagDetector.getTrackedNodes().contains(getLocalNode()) == false : lagDetector.getTrackedNodes(); @@ -888,7 +911,7 @@ public void invariant() { + lagDetector.getTrackedNodes(); if (mode == Mode.LEADER) { - final boolean becomingMaster = getStateForMasterService().term() != getCurrentTerm(); + final boolean becomingClusterManager = getStateForClusterManagerService().term() != getCurrentTerm(); assert coordinationState.get().electionWon(); assert lastKnownLeader.isPresent() && lastKnownLeader.get().equals(getLocalNode()); @@ -896,16 +919,18 @@ public void invariant() { assert peerFinderLeader.equals(lastKnownLeader) : peerFinderLeader; assert electionScheduler == null : electionScheduler; assert prevotingRound == null : prevotingRound; - assert becomingMaster || getStateForMasterService().nodes().getMasterNodeId() != null : getStateForMasterService(); + assert becomingClusterManager || getStateForClusterManagerService().nodes().getClusterManagerNodeId() != null + : getStateForClusterManagerService(); assert leaderChecker.leader() == null : leaderChecker.leader(); - assert getLocalNode().equals(applierState.nodes().getMasterNode()) - || (applierState.nodes().getMasterNodeId() == null && applierState.term() < getCurrentTerm()); + assert getLocalNode().equals(applierState.nodes().getClusterManagerNode()) + || (applierState.nodes().getClusterManagerNodeId() == null && applierState.term() < getCurrentTerm()); assert preVoteCollector.getLeader() == getLocalNode() : preVoteCollector; assert clusterFormationFailureHelper.isRunning() == false; final boolean activePublication = currentPublication.map(CoordinatorPublication::isActiveForCurrentLeader).orElse(false); - if (becomingMaster && activePublication == false) { - // cluster state update task to become master is submitted to MasterService, but publication has not started yet + if (becomingClusterManager && activePublication == false) { + // cluster state update task to become cluster-manager is submitted to MasterService, + // but publication has not started yet assert followersChecker.getKnownFollowers().isEmpty() : followersChecker.getKnownFollowers(); } else { final ClusterState lastPublishedState; @@ -924,7 +949,7 @@ assert getLocalNode().equals(applierState.nodes().getMasterNode()) + followersChecker.getKnownFollowers(); } - assert becomingMaster + assert becomingClusterManager || activePublication || coordinationState.get() .getLastAcceptedConfiguration() @@ -939,12 +964,12 @@ assert getLocalNode().equals(applierState.nodes().getMasterNode()) assert peerFinderLeader.equals(lastKnownLeader) : peerFinderLeader; assert electionScheduler == null : electionScheduler; assert prevotingRound == null : prevotingRound; - assert getStateForMasterService().nodes().getMasterNodeId() == null : getStateForMasterService(); - assert leaderChecker.currentNodeIsMaster() == false; + assert getStateForClusterManagerService().nodes().getClusterManagerNodeId() == null : getStateForClusterManagerService(); + assert leaderChecker.currentNodeIsClusterManager() == false; assert lastKnownLeader.equals(Optional.of(leaderChecker.leader())); assert followersChecker.getKnownFollowers().isEmpty(); - assert lastKnownLeader.get().equals(applierState.nodes().getMasterNode()) - || (applierState.nodes().getMasterNodeId() == null + assert lastKnownLeader.get().equals(applierState.nodes().getClusterManagerNode()) + || (applierState.nodes().getClusterManagerNodeId() == null && (applierState.term() < getCurrentTerm() || applierState.version() < getLastAcceptedState().version())); assert currentPublication.map(Publication::isCommitted).orElse(true); assert preVoteCollector.getLeader().equals(lastKnownLeader.get()) : preVoteCollector; @@ -954,11 +979,11 @@ assert getLocalNode().equals(applierState.nodes().getMasterNode()) assert joinAccumulator instanceof JoinHelper.CandidateJoinAccumulator; assert peerFinderLeader.isPresent() == false : peerFinderLeader; assert prevotingRound == null || electionScheduler != null; - assert getStateForMasterService().nodes().getMasterNodeId() == null : getStateForMasterService(); - assert leaderChecker.currentNodeIsMaster() == false; + assert getStateForClusterManagerService().nodes().getClusterManagerNodeId() == null : getStateForClusterManagerService(); + assert leaderChecker.currentNodeIsClusterManager() == false; assert leaderChecker.leader() == null : leaderChecker.leader(); assert followersChecker.getKnownFollowers().isEmpty(); - assert applierState.nodes().getMasterNodeId() == null; + assert applierState.nodes().getClusterManagerNodeId() == null; assert currentPublication.map(Publication::isCommitted).orElse(true); assert preVoteCollector.getLeader() == null : preVoteCollector; assert clusterFormationFailureHelper.isRunning(); @@ -967,7 +992,7 @@ assert getLocalNode().equals(applierState.nodes().getMasterNode()) } public boolean isInitialConfigurationSet() { - return getStateForMasterService().getLastAcceptedConfiguration().isEmpty() == false; + return getStateForClusterManagerService().getLastAcceptedConfiguration().isEmpty() == false; } /** @@ -979,17 +1004,17 @@ public boolean isInitialConfigurationSet() { */ public boolean setInitialConfiguration(final VotingConfiguration votingConfiguration) { synchronized (mutex) { - final ClusterState currentState = getStateForMasterService(); + final ClusterState currentState = getStateForClusterManagerService(); if (isInitialConfigurationSet()) { logger.debug("initial configuration already set, ignoring {}", votingConfiguration); return false; } - if (getLocalNode().isMasterNode() == false) { - logger.debug("skip setting initial configuration as local node is not a master-eligible node"); + if (getLocalNode().isClusterManagerNode() == false) { + logger.debug("skip setting initial configuration as local node is not a cluster-manager-eligible node"); throw new CoordinationStateRejectedException( - "this node is not master-eligible, but cluster bootstrapping can only happen on a master-eligible node" + "this node is not cluster-manager-eligible, but cluster bootstrapping can only happen on a cluster-manager-eligible node" ); } @@ -1046,25 +1071,27 @@ ClusterState improveConfiguration(ClusterState clusterState) { // exclude any nodes whose ID is in the voting config exclusions list ... final Stream excludedNodeIds = clusterState.getVotingConfigExclusions().stream().map(VotingConfigExclusion::getNodeId); - // ... and also automatically exclude the node IDs of master-ineligible nodes that were previously master-eligible and are still in - // the voting config. We could exclude all the master-ineligible nodes here, but there could be quite a few of them and that makes + // ... and also automatically exclude the node IDs of cluster-manager-ineligible nodes that were previously cluster-manager-eligible + // and are still in + // the voting config. We could exclude all the cluster-manager-ineligible nodes here, but there could be quite a few of them and + // that makes // the logging much harder to follow. - final Stream masterIneligibleNodeIdsInVotingConfig = StreamSupport.stream(clusterState.nodes().spliterator(), false) + final Stream clusterManagerIneligibleNodeIdsInVotingConfig = StreamSupport.stream(clusterState.nodes().spliterator(), false) .filter( - n -> n.isMasterNode() == false + n -> n.isClusterManagerNode() == false && (clusterState.getLastAcceptedConfiguration().getNodeIds().contains(n.getId()) || clusterState.getLastCommittedConfiguration().getNodeIds().contains(n.getId())) ) .map(DiscoveryNode::getId); final Set liveNodes = StreamSupport.stream(clusterState.nodes().spliterator(), false) - .filter(DiscoveryNode::isMasterNode) + .filter(DiscoveryNode::isClusterManagerNode) .filter(coordinationState.get()::containsJoinVoteFor) .filter(discoveryNode -> isZen1Node(discoveryNode) == false) .collect(Collectors.toSet()); final VotingConfiguration newConfig = reconfigurator.reconfigure( liveNodes, - Stream.concat(masterIneligibleNodeIdsInVotingConfig, excludedNodeIds).collect(Collectors.toSet()), + Stream.concat(clusterManagerIneligibleNodeIdsInVotingConfig, excludedNodeIds).collect(Collectors.toSet()), getLocalNode(), clusterState.getLastAcceptedConfiguration() ); @@ -1099,7 +1126,8 @@ static boolean validVotingConfigExclusionState(ClusterState clusterState) { .map(VotingConfigExclusion::getNodeId) .collect(Collectors.toSet()); for (DiscoveryNode node : clusterState.getNodes()) { - if (node.isMasterNode() && (nodeIdsWithAbsentName.contains(node.getId()) || nodeNamesWithAbsentId.contains(node.getName()))) { + if (node.isClusterManagerNode() + && (nodeIdsWithAbsentName.contains(node.getId()) || nodeNamesWithAbsentId.contains(node.getName()))) { return false; } } @@ -1117,7 +1145,7 @@ private void scheduleReconfigurationIfNeeded() { final ClusterState state = getLastAcceptedState(); if (improveConfiguration(state) != state && reconfigurationTaskScheduled.compareAndSet(false, true)) { logger.trace("scheduling reconfiguration"); - masterService.submitStateUpdateTask("reconfigure", new ClusterStateUpdateTask(Priority.URGENT) { + clusterManagerService.submitStateUpdateTask("reconfigure", new ClusterStateUpdateTask(Priority.URGENT) { @Override public ClusterState execute(ClusterState currentState) { reconfigurationTaskScheduled.set(false); @@ -1137,7 +1165,7 @@ public void onFailure(String source, Exception e) { // exposed for tests boolean missingJoinVoteFrom(DiscoveryNode node) { - return node.isMasterNode() && coordinationState.get().containsJoinVoteFor(node) == false; + return node.isClusterManagerNode() && coordinationState.get().containsJoinVoteFor(node) == false; } private void handleJoin(Join join) { @@ -1146,13 +1174,14 @@ private void handleJoin(Join join) { if (coordinationState.get().electionWon()) { // If we have already won the election then the actual join does not matter for election purposes, so swallow any exception - final boolean isNewJoinFromMasterEligibleNode = handleJoinIgnoringExceptions(join); + final boolean isNewJoinFromClusterManagerEligibleNode = handleJoinIgnoringExceptions(join); - // If we haven't completely finished becoming master then there's already a publication scheduled which will, in turn, + // If we haven't completely finished becoming cluster-manager then there's already a publication scheduled which will, in + // turn, // schedule a reconfiguration if needed. It's benign to schedule a reconfiguration anyway, but it might fail if it wins the // race against the election-winning publication and log a big error message, which we can prevent by checking this here: - final boolean establishedAsMaster = mode == Mode.LEADER && getLastAcceptedState().term() == getCurrentTerm(); - if (isNewJoinFromMasterEligibleNode && establishedAsMaster && publicationInProgress() == false) { + final boolean establishedAsClusterManager = mode == Mode.LEADER && getLastAcceptedState().term() == getCurrentTerm(); + if (isNewJoinFromClusterManagerEligibleNode && establishedAsClusterManager && publicationInProgress() == false) { scheduleReconfigurationIfNeeded(); } } else { @@ -1191,29 +1220,30 @@ private List getDiscoveredNodes() { return nodes; } - ClusterState getStateForMasterService() { + ClusterState getStateForClusterManagerService() { synchronized (mutex) { - // expose last accepted cluster state as base state upon which the master service + // expose last accepted cluster state as base state upon which the cluster_manager service // speculatively calculates the next cluster state update final ClusterState clusterState = coordinationState.get().getLastAcceptedState(); if (mode != Mode.LEADER || clusterState.term() != getCurrentTerm()) { - // the master service checks if the local node is the master node in order to fail execution of the state update early - return clusterStateWithNoMasterBlock(clusterState); + // the cluster-manager service checks if the local node is the cluster-manager node in order to fail execution of the state + // update early + return clusterStateWithNoClusterManagerBlock(clusterState); } return clusterState; } } - private ClusterState clusterStateWithNoMasterBlock(ClusterState clusterState) { - if (clusterState.nodes().getMasterNodeId() != null) { + private ClusterState clusterStateWithNoClusterManagerBlock(ClusterState clusterState) { + if (clusterState.nodes().getClusterManagerNodeId() != null) { // remove block if it already exists before adding new one - assert clusterState.blocks().hasGlobalBlockWithId(NO_MASTER_BLOCK_ID) == false + assert clusterState.blocks().hasGlobalBlockWithId(NO_CLUSTER_MANAGER_BLOCK_ID) == false : "NO_MASTER_BLOCK should only be added by Coordinator"; final ClusterBlocks clusterBlocks = ClusterBlocks.builder() .blocks(clusterState.blocks()) - .addGlobalBlock(noMasterBlockService.getNoMasterBlock()) + .addGlobalBlock(noClusterManagerBlockService.getNoClusterManagerBlock()) .build(); - final DiscoveryNodes discoveryNodes = new DiscoveryNodes.Builder(clusterState.nodes()).masterNodeId(null).build(); + final DiscoveryNodes discoveryNodes = new DiscoveryNodes.Builder(clusterState.nodes()).clusterManagerNodeId(null).build(); return ClusterState.builder(clusterState).blocks(clusterBlocks).nodes(discoveryNodes).build(); } else { return clusterState; @@ -1231,14 +1261,16 @@ public void publish( if (mode != Mode.LEADER || getCurrentTerm() != clusterChangedEvent.state().term()) { logger.debug( () -> new ParameterizedMessage( - "[{}] failed publication as node is no longer master for term {}", + "[{}] failed publication as node is no longer cluster-manager for term {}", clusterChangedEvent.source(), clusterChangedEvent.state().term() ) ); publishListener.onFailure( new FailedToCommitClusterStateException( - "node is no longer master for term " + clusterChangedEvent.state().term() + " while handling publication" + "node is no longer cluster-manager for term " + + clusterChangedEvent.state().term() + + " while handling publication" ) ); return; @@ -1284,6 +1316,7 @@ assert getLocalNode().equals(clusterState.getNodes().get(getLocalNode().getId()) leaderChecker.setCurrentNodes(publishNodes); followersChecker.setCurrentNodes(publishNodes); lagDetector.setTrackedNodes(publishNodes); + coordinationState.get().handlePrePublish(clusterState); publication.start(followersChecker.getFaultyNodes()); } } catch (Exception e) { @@ -1296,16 +1329,26 @@ assert getLocalNode().equals(clusterState.getNodes().get(getLocalNode().getId()) // deserialized from the resulting JSON private boolean assertPreviousStateConsistency(ClusterChangedEvent event) { assert event.previousState() == coordinationState.get().getLastAcceptedState() - || XContentHelper.convertToMap(JsonXContent.jsonXContent, Strings.toString(event.previousState()), false) + || XContentHelper.convertToMap( + JsonXContent.jsonXContent, + Strings.toString(MediaTypeRegistry.JSON, event.previousState()), + false + ) .equals( XContentHelper.convertToMap( JsonXContent.jsonXContent, - Strings.toString(clusterStateWithNoMasterBlock(coordinationState.get().getLastAcceptedState())), + Strings.toString( + MediaTypeRegistry.JSON, + clusterStateWithNoClusterManagerBlock(coordinationState.get().getLastAcceptedState()) + ), false ) - ) : Strings.toString(event.previousState()) + ) : Strings.toString(MediaTypeRegistry.JSON, event.previousState()) + " vs " - + Strings.toString(clusterStateWithNoMasterBlock(coordinationState.get().getLastAcceptedState())); + + Strings.toString( + MediaTypeRegistry.JSON, + clusterStateWithNoClusterManagerBlock(coordinationState.get().getLastAcceptedState()) + ); return true; } @@ -1338,12 +1381,22 @@ public Collection> getOnJoinValidators() return onJoinValidators; } + /** + * The mode of the coordinator. + * + * @opensearch.internal + */ public enum Mode { CANDIDATE, LEADER, FOLLOWER } + /** + * The coordinator peer finder. + * + * @opensearch.internal + */ private class CoordinatorPeerFinder extends PeerFinder { CoordinatorPeerFinder( @@ -1361,10 +1414,10 @@ private class CoordinatorPeerFinder extends PeerFinder { } @Override - protected void onActiveMasterFound(DiscoveryNode masterNode, long term) { + protected void onActiveClusterManagerFound(DiscoveryNode clusterManagerNode, long term) { synchronized (mutex) { - ensureTermAtLeast(masterNode, term); - joinHelper.sendJoinRequest(masterNode, getCurrentTerm(), joinWithDestination(lastJoin, masterNode, term)); + ensureTermAtLeast(clusterManagerNode, term); + joinHelper.sendJoinRequest(clusterManagerNode, getCurrentTerm(), joinWithDestination(lastJoin, clusterManagerNode, term)); } } @@ -1399,10 +1452,20 @@ protected void onFoundPeersUpdated() { } } + // package-visible for testing + synchronized void onNodeCommissionStatusChange(boolean localNodeCommissioned) { + this.localNodeCommissioned = localNodeCommissioned; + peerFinder.onNodeCommissionStatusChange(localNodeCommissioned); + } + + public boolean localNodeCommissioned() { + return localNodeCommissioned; + } + private void startElectionScheduler() { assert electionScheduler == null : electionScheduler; - if (getLocalNode().isMasterNode() == false) { + if (getLocalNode().isClusterManagerNode() == false) { return; } @@ -1425,6 +1488,14 @@ public void run() { return; } + // if either the localNodeCommissioned flag or the last accepted state thinks it should skip pre voting, we will + // acknowledge it + if (nodeCommissioned(lastAcceptedState.nodes().getLocalNode(), lastAcceptedState.metadata()) == false + || localNodeCommissioned == false) { + logger.debug("skip prevoting as local node is decommissioned"); + return; + } + if (prevotingRound != null) { prevotingRound.close(); } @@ -1467,6 +1538,11 @@ boolean cancelCommittedPublication() { } } + /** + * The coordinator publication. + * + * @opensearch.internal + */ class CoordinatorPublication extends Publication { private final PublishRequest publishRequest; @@ -1611,12 +1687,12 @@ public void onSuccess(String source) { boolean attemptReconfiguration = true; final ClusterState state = getLastAcceptedState(); // committed state if (localNodeMayWinElection(state) == false) { - final List masterCandidates = completedNodes().stream() - .filter(DiscoveryNode::isMasterNode) + final List clusterManagerCandidates = completedNodes().stream() + .filter(DiscoveryNode::isClusterManagerNode) .filter(node -> nodeMayWinElection(state, node)) .filter(node -> { - // check if master candidate would be able to get an election quorum if we were to - // abdicate to it. Assume that every node that completed the publication can provide + // check if cluster_manager candidate would be able to get an election quorum if we were + // to abdicate to it. Assume that every node that completed the publication can provide // a vote in that next election and has the latest state. final long futureElectionTerm = state.term() + 1; final VoteCollection futureVoteCollection = new VoteCollection(); @@ -1636,8 +1712,8 @@ public void onSuccess(String source) { ); }) .collect(Collectors.toList()); - if (masterCandidates.isEmpty() == false) { - abdicateTo(masterCandidates.get(random.nextInt(masterCandidates.size()))); + if (clusterManagerCandidates.isEmpty() == false) { + abdicateTo(clusterManagerCandidates.get(random.nextInt(clusterManagerCandidates.size()))); attemptReconfiguration = false; } } @@ -1663,7 +1739,7 @@ public void onFailure(Exception e) { cancelTimeoutHandlers(); final FailedToCommitClusterStateException exception = new FailedToCommitClusterStateException("publication failed", e); - ackListener.onNodeAck(getLocalNode(), exception); // other nodes have acked, but not the master. + ackListener.onNodeAck(getLocalNode(), exception); // other nodes have acked, but not the cluster manager. publishListener.onFailure(exception); } }, OpenSearchExecutors.newDirectExecutorService(), transportService.getThreadPool().getThreadContext()); diff --git a/server/src/main/java/org/opensearch/cluster/coordination/DetachClusterCommand.java b/server/src/main/java/org/opensearch/cluster/coordination/DetachClusterCommand.java index 49d88fd33c724..d17ef31f1b818 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/DetachClusterCommand.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/DetachClusterCommand.java @@ -41,15 +41,20 @@ import java.io.IOException; import java.nio.file.Path; +/** + * Command to detach a node from the cluster + * + * @opensearch.internal + */ public class DetachClusterCommand extends OpenSearchNodeCommand { static final String NODE_DETACHED_MSG = "Node was successfully detached from the cluster"; static final String CONFIRMATION_MSG = DELIMITER + "\n" + "You should only run this tool if you have permanently lost all of the\n" - + "master-eligible nodes in this cluster and you cannot restore the cluster\n" + + "cluster-manager-eligible nodes in this cluster and you cannot restore the cluster\n" + "from a snapshot, or you have already unsafely bootstrapped a new cluster\n" - + "by running `opensearch-node unsafe-bootstrap` on a master-eligible\n" + + "by running `opensearch-node unsafe-bootstrap` on a cluster-manager-eligible\n" + "node that belonged to the same cluster as this node. This tool can cause\n" + "arbitrary data loss and its use should be your last resort.\n" + "\n" @@ -86,8 +91,8 @@ protected void processNodePaths(Terminal terminal, Path[] dataPaths, int nodeLoc // package-private for tests static Metadata updateMetadata(Metadata oldMetadata) { final CoordinationMetadata coordinationMetadata = CoordinationMetadata.builder() - .lastAcceptedConfiguration(CoordinationMetadata.VotingConfiguration.MUST_JOIN_ELECTED_MASTER) - .lastCommittedConfiguration(CoordinationMetadata.VotingConfiguration.MUST_JOIN_ELECTED_MASTER) + .lastAcceptedConfiguration(CoordinationMetadata.VotingConfiguration.MUST_JOIN_ELECTED_CLUSTER_MANAGER) + .lastCommittedConfiguration(CoordinationMetadata.VotingConfiguration.MUST_JOIN_ELECTED_CLUSTER_MANAGER) .term(0) .build(); return Metadata.builder(oldMetadata).coordinationMetadata(coordinationMetadata).clusterUUIDCommitted(false).build(); diff --git a/server/src/main/java/org/opensearch/cluster/coordination/ElectionSchedulerFactory.java b/server/src/main/java/org/opensearch/cluster/coordination/ElectionSchedulerFactory.java index f0ec9681d76cc..828db5864d28b 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/ElectionSchedulerFactory.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/ElectionSchedulerFactory.java @@ -56,6 +56,8 @@ * randomly at reasonably high frequency and backing off (linearly) until one of them succeeds. We also place an upper bound on the backoff * so that if elections are failing due to a network partition that lasts for a long time then when the partition heals there is an election * attempt reasonably quickly. + * + * @opensearch.internal */ public class ElectionSchedulerFactory { @@ -181,6 +183,11 @@ public String toString() { + '}'; } + /** + * The Election scheduler. + * + * @opensearch.internal + */ private class ElectionScheduler implements Releasable { private final AtomicBoolean isClosed = new AtomicBoolean(); private final AtomicLong attempt = new AtomicLong(); diff --git a/server/src/main/java/org/opensearch/cluster/coordination/ElectionStrategy.java b/server/src/main/java/org/opensearch/cluster/coordination/ElectionStrategy.java index f3f7fd0c1b072..c6ea4649faaf0 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/ElectionStrategy.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/ElectionStrategy.java @@ -38,6 +38,8 @@ /** * Allows plugging in a custom election strategy, restricting the notion of an election quorum. * Custom additional quorum restrictions can be defined by implementing the {@link #satisfiesAdditionalQuorumConstraints} method. + * + * @opensearch.internal */ public abstract class ElectionStrategy { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/FailedToCommitClusterStateException.java b/server/src/main/java/org/opensearch/cluster/coordination/FailedToCommitClusterStateException.java index 59cd94aecdd57..8379bcaa0f0d8 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/FailedToCommitClusterStateException.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/FailedToCommitClusterStateException.java @@ -32,12 +32,14 @@ package org.opensearch.cluster.coordination; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** * Thrown when failing to publish a cluster state. See {@link ClusterStatePublisher} for more details. + * + * @opensearch.internal */ public class FailedToCommitClusterStateException extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/FollowersChecker.java b/server/src/main/java/org/opensearch/cluster/coordination/FollowersChecker.java index c5fcee712b683..f69a4f771cf21 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/FollowersChecker.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/FollowersChecker.java @@ -38,12 +38,13 @@ import org.opensearch.cluster.coordination.Coordinator.Mode; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.concurrent.AbstractRunnable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse.Empty; import org.opensearch.monitor.NodeHealthService; import org.opensearch.monitor.StatusInfo; import org.opensearch.threadpool.ThreadPool.Names; @@ -55,7 +56,6 @@ import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestOptions; import org.opensearch.transport.TransportRequestOptions.Type; -import org.opensearch.transport.TransportResponse.Empty; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -76,6 +76,8 @@ * follower has failed the leader will remove it from the cluster. We are fairly lenient, possibly allowing multiple checks to fail before * considering a follower to be faulty, to allow for a brief network partition or a long GC cycle to occur without triggering the removal of * a node and the consequent shard reallocation. + * + * @opensearch.internal */ public class FollowersChecker { @@ -166,7 +168,7 @@ public void setCurrentNodes(DiscoveryNodes discoveryNodes) { followerCheckers.keySet().removeIf(isUnknownNode); faultyNodes.removeIf(isUnknownNode); - discoveryNodes.mastersFirstStream().forEach(discoveryNode -> { + discoveryNodes.clusterManagersFirstStream().forEach(discoveryNode -> { if (discoveryNode.equals(discoveryNodes.getLocalNode()) == false && followerCheckers.containsKey(discoveryNode) == false && faultyNodes.contains(discoveryNode) == false) { @@ -292,6 +294,11 @@ private void handleDisconnectedNode(DiscoveryNode discoveryNode) { } } + /** + * A fast response state. + * + * @opensearch.internal + */ static class FastResponseState { final long term; final Mode mode; @@ -309,6 +316,8 @@ public String toString() { /** * A checker for an individual follower. + * + * @opensearch.internal */ private class FollowerChecker { private final DiscoveryNode discoveryNode; @@ -447,6 +456,11 @@ public String toString() { } } + /** + * Request to check follower. + * + * @opensearch.internal + */ public static class FollowerCheckRequest extends TransportRequest { private final long term; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/InMemoryPersistedState.java b/server/src/main/java/org/opensearch/cluster/coordination/InMemoryPersistedState.java index d9c10f7fe0af9..67ef82ee7b2e9 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/InMemoryPersistedState.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/InMemoryPersistedState.java @@ -33,6 +33,11 @@ import org.opensearch.cluster.ClusterState; +/** + * Persist state in memory + * + * @opensearch.internal + */ public class InMemoryPersistedState implements CoordinationState.PersistedState { private long currentTerm; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/Join.java b/server/src/main/java/org/opensearch/cluster/coordination/Join.java index 50225c70620c5..58fa85992ebc8 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/Join.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/Join.java @@ -32,9 +32,9 @@ package org.opensearch.cluster.coordination; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; @@ -46,6 +46,8 @@ * information about the current state of the node that provided the vote, so that * the receiver of the vote can determine if it has a more up-to-date state than the * source node. + * + * @opensearch.internal */ public class Join implements Writeable { private final DiscoveryNode sourceNode; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java index 6d2fb99e04f86..0976d15c2a96b 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinHelper.java @@ -35,34 +35,41 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; +import org.opensearch.Version; import org.opensearch.action.ActionListenerResponseHandler; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateTaskConfig; import org.opensearch.cluster.ClusterStateTaskListener; -import org.opensearch.cluster.NotMasterException; +import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.coordination.Coordinator.Mode; +import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.RerouteService; import org.opensearch.cluster.routing.allocation.AllocationService; -import org.opensearch.cluster.service.MasterService; +import org.opensearch.cluster.service.ClusterManagerService; import org.opensearch.common.Priority; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.transport.TransportResponse; +import org.opensearch.core.transport.TransportResponse.Empty; import org.opensearch.monitor.NodeHealthService; import org.opensearch.monitor.StatusInfo; +import org.opensearch.node.remotestore.RemoteStoreNodeService; import org.opensearch.threadpool.ThreadPool; import org.opensearch.threadpool.ThreadPool.Names; +import org.opensearch.transport.BytesTransportRequest; +import org.opensearch.transport.RemoteTransportException; import org.opensearch.transport.TransportChannel; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestOptions; -import org.opensearch.transport.TransportResponse; -import org.opensearch.transport.TransportResponse.Empty; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -78,18 +85,25 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.LongSupplier; import java.util.function.Supplier; import static org.opensearch.monitor.StatusInfo.Status.UNHEALTHY; +/** + * Helper utility class for joining the cluster + * + * @opensearch.internal + */ public class JoinHelper { private static final Logger logger = LogManager.getLogger(JoinHelper.class); public static final String JOIN_ACTION_NAME = "internal:cluster/coordination/join"; public static final String VALIDATE_JOIN_ACTION_NAME = "internal:cluster/coordination/join/validate"; + public static final String VALIDATE_COMPRESSED_JOIN_ACTION_NAME = JOIN_ACTION_NAME + "/validate_compressed"; public static final String START_JOIN_ACTION_NAME = "internal:cluster/coordination/start_join"; // the timeout for Zen1 join attempts @@ -101,7 +115,7 @@ public class JoinHelper { Setting.Property.Deprecated ); - private final MasterService masterService; + private final ClusterManagerService clusterManagerService; private final TransportService transportService; private volatile JoinTaskExecutor joinTaskExecutor; @@ -113,49 +127,74 @@ public class JoinHelper { private final AtomicReference lastFailedJoinAttempt = new AtomicReference<>(); private final Supplier joinTaskExecutorGenerator; + private final Consumer nodeCommissioned; + private final NamedWriteableRegistry namedWriteableRegistry; + private final AtomicReference> serializedState = new AtomicReference<>(); JoinHelper( Settings settings, AllocationService allocationService, - MasterService masterService, + ClusterManagerService clusterManagerService, TransportService transportService, + RemoteStoreNodeService remoteStoreNodeService, LongSupplier currentTermSupplier, Supplier currentStateSupplier, BiConsumer joinHandler, Function joinLeaderInTerm, Collection> joinValidators, RerouteService rerouteService, - NodeHealthService nodeHealthService + NodeHealthService nodeHealthService, + Consumer nodeCommissioned, + NamedWriteableRegistry namedWriteableRegistry ) { - this.masterService = masterService; + this.clusterManagerService = clusterManagerService; this.transportService = transportService; this.nodeHealthService = nodeHealthService; this.joinTimeout = JOIN_TIMEOUT_SETTING.get(settings); - this.joinTaskExecutorGenerator = () -> new JoinTaskExecutor(settings, allocationService, logger, rerouteService, transportService) { + this.nodeCommissioned = nodeCommissioned; + this.namedWriteableRegistry = namedWriteableRegistry; + + this.joinTaskExecutorGenerator = () -> new JoinTaskExecutor( + settings, + allocationService, + logger, + rerouteService, + transportService, + remoteStoreNodeService + ) { private final long term = currentTermSupplier.getAsLong(); @Override public ClusterTasksResult execute(ClusterState currentState, List joiningTasks) throws Exception { - // The current state that MasterService uses might have been updated by a (different) master in a higher term already + // The current state that ClusterManagerService uses might have been updated by a (different) cluster-manager + // in a higher term already // Stop processing the current cluster state update, as there's no point in continuing to compute it as // it will later be rejected by Coordinator.publish(...) anyhow if (currentState.term() > term) { - logger.trace("encountered higher term {} than current {}, there is a newer master", currentState.term(), term); - throw new NotMasterException( - "Higher term encountered (current: " + currentState.term() + " > used: " + term + "), there is a newer master" + logger.trace("encountered higher term {} than current {}, there is a newer cluster-manager", currentState.term(), term); + throw new NotClusterManagerException( + "Higher term encountered (current: " + + currentState.term() + + " > used: " + + term + + "), there is a newer cluster-manager" ); - } else if (currentState.nodes().getMasterNodeId() == null && joiningTasks.stream().anyMatch(Task::isBecomeMasterTask)) { - assert currentState.term() < term : "there should be at most one become master task per election (= by term)"; - final CoordinationMetadata coordinationMetadata = CoordinationMetadata.builder(currentState.coordinationMetadata()) - .term(term) - .build(); - final Metadata metadata = Metadata.builder(currentState.metadata()).coordinationMetadata(coordinationMetadata).build(); - currentState = ClusterState.builder(currentState).metadata(metadata).build(); - } else if (currentState.nodes().isLocalNodeElectedMaster()) { - assert currentState.term() == term : "term should be stable for the same master"; - } + } else if (currentState.nodes().getClusterManagerNodeId() == null + && joiningTasks.stream().anyMatch(Task::isBecomeClusterManagerTask)) { + assert currentState.term() < term + : "there should be at most one become cluster-manager task per election (= by term)"; + final CoordinationMetadata coordinationMetadata = CoordinationMetadata.builder(currentState.coordinationMetadata()) + .term(term) + .build(); + final Metadata metadata = Metadata.builder(currentState.metadata()) + .coordinationMetadata(coordinationMetadata) + .build(); + currentState = ClusterState.builder(currentState).metadata(metadata).build(); + } else if (currentState.nodes().isLocalNodeElectedClusterManager()) { + assert currentState.term() == term : "term should be stable for the same cluster-manager"; + } return super.execute(currentState, joiningTasks); } @@ -188,22 +227,52 @@ public ClusterTasksResult execute(ClusterState currentSta ThreadPool.Names.GENERIC, ValidateJoinRequest::new, (request, channel, task) -> { - final ClusterState localState = currentStateSupplier.get(); - if (localState.metadata().clusterUUIDCommitted() - && localState.metadata().clusterUUID().equals(request.getState().metadata().clusterUUID()) == false) { - throw new CoordinationStateRejectedException( - "join validation on cluster state" - + " with a different cluster uuid " - + request.getState().metadata().clusterUUID() - + " than local cluster uuid " - + localState.metadata().clusterUUID() - + ", rejecting" - ); - } - joinValidators.forEach(action -> action.accept(transportService.getLocalNode(), request.getState())); + runJoinValidators(currentStateSupplier, request.getState(), joinValidators); channel.sendResponse(Empty.INSTANCE); } ); + + transportService.registerRequestHandler( + VALIDATE_COMPRESSED_JOIN_ACTION_NAME, + ThreadPool.Names.GENERIC, + BytesTransportRequest::new, + (request, channel, task) -> { + handleCompressedValidateJoinRequest(currentStateSupplier, joinValidators, request); + channel.sendResponse(Empty.INSTANCE); + } + ); + + } + + private void runJoinValidators( + Supplier currentStateSupplier, + ClusterState incomingState, + Collection> joinValidators + ) { + final ClusterState localState = currentStateSupplier.get(); + if (localState.metadata().clusterUUIDCommitted() + && localState.metadata().clusterUUID().equals(incomingState.metadata().clusterUUID()) == false) { + throw new CoordinationStateRejectedException( + "join validation on cluster state" + + " with a different cluster uuid " + + incomingState.metadata().clusterUUID() + + " than local cluster uuid " + + localState.metadata().clusterUUID() + + ", rejecting" + ); + } + joinValidators.forEach(action -> action.accept(transportService.getLocalNode(), incomingState)); + } + + private void handleCompressedValidateJoinRequest( + Supplier currentStateSupplier, + Collection> joinValidators, + BytesTransportRequest request + ) throws IOException { + try (StreamInput input = CompressedStreamUtils.decompressBytes(request, namedWriteableRegistry)) { + ClusterState incomingState = ClusterState.readFrom(input, transportService.getLocalNode()); + runJoinValidators(currentStateSupplier, incomingState, joinValidators); + } } private JoinCallback transportJoinCallback(TransportRequest request, TransportChannel channel) { @@ -243,6 +312,11 @@ public void sendJoinRequest(DiscoveryNode destination, long term, Optional sendJoinRequest(destination, term, optionalJoin, () -> {}); } + /** + * A failed join attempt. + * + * @opensearch.internal + */ // package-private for testing static class FailedJoinAttempt { private final DiscoveryNode destination; @@ -269,7 +343,7 @@ static Level getLogLevel(TransportException e) { Throwable cause = e.unwrapCause(); if (cause instanceof CoordinationStateRejectedException || cause instanceof FailedToCommitClusterStateException - || cause instanceof NotMasterException) { + || cause instanceof NotClusterManagerException) { return Level.DEBUG; } return Level.INFO; @@ -297,7 +371,7 @@ void logLastFailedJoinAttempt() { } public void sendJoinRequest(DiscoveryNode destination, long term, Optional optionalJoin, Runnable onCompletion) { - assert destination.isMasterNode() : "trying to join master-ineligible " + destination; + assert destination.isClusterManagerNode() : "trying to join cluster-manager-ineligible " + destination; final StatusInfo statusInfo = nodeHealthService.getHealth(); if (statusInfo.getStatus() == UNHEALTHY) { logger.debug("dropping join request to [{}]: [{}]", destination, statusInfo.getInfo()); @@ -323,6 +397,7 @@ public void handleResponse(Empty response) { pendingOutgoingJoins.remove(dedupKey); logger.debug("successfully joined {} with {}", destination, joinRequest); lastFailedJoinAttempt.set(null); + nodeCommissioned.accept(true); onCompletion.run(); } @@ -333,6 +408,13 @@ public void handleException(TransportException exp) { FailedJoinAttempt attempt = new FailedJoinAttempt(destination, joinRequest, exp); attempt.logNow(); lastFailedJoinAttempt.set(attempt); + if (exp instanceof RemoteTransportException && (exp.getCause() instanceof NodeDecommissionedException)) { + logger.info( + "local node is decommissioned [{}]. Will not be able to join the cluster", + exp.getCause().getMessage() + ); + nodeCommissioned.accept(false); + } onCompletion.run(); } @@ -348,7 +430,7 @@ public String executor() { } public void sendStartJoinRequest(final StartJoinRequest startJoinRequest, final DiscoveryNode destination) { - assert startJoinRequest.getSourceNode().isMasterNode() : "sending start-join request for master-ineligible " + assert startJoinRequest.getSourceNode().isClusterManagerNode() : "sending start-join request for cluster-manager-ineligible " + startJoinRequest.getSourceNode(); transportService.sendRequest(destination, START_JOIN_ACTION_NAME, startJoinRequest, new TransportResponseHandler() { @Override @@ -374,20 +456,60 @@ public String executor() { } public void sendValidateJoinRequest(DiscoveryNode node, ClusterState state, ActionListener listener) { - transportService.sendRequest( - node, - VALIDATE_JOIN_ACTION_NAME, - new ValidateJoinRequest(state), - new ActionListenerResponseHandler<>(listener, i -> Empty.INSTANCE, ThreadPool.Names.GENERIC) - ); + if (node.getVersion().before(Version.V_2_9_0)) { + transportService.sendRequest( + node, + VALIDATE_JOIN_ACTION_NAME, + new ValidateJoinRequest(state), + new ActionListenerResponseHandler<>(listener, i -> Empty.INSTANCE, ThreadPool.Names.GENERIC) + ); + } else { + try { + final BytesReference bytes = serializedState.updateAndGet(cachedState -> { + if (cachedState == null || cachedState.v1() != state.version()) { + try { + return new Tuple<>( + state.version(), + CompressedStreamUtils.createCompressedStream(node.getVersion(), state::writeTo) + ); + } catch (IOException e) { + // mandatory as AtomicReference doesn't rethrow IOException. + throw new RuntimeException(e); + } + } else { + return cachedState; + } + }).v2(); + final BytesTransportRequest request = new BytesTransportRequest(bytes, node.getVersion()); + transportService.sendRequest( + node, + VALIDATE_COMPRESSED_JOIN_ACTION_NAME, + request, + new ActionListenerResponseHandler<>(listener, i -> Empty.INSTANCE, ThreadPool.Names.GENERIC) + ); + } catch (Exception e) { + logger.warn("error sending cluster state to {}", node); + listener.onFailure(e); + } + } } + /** + * The callback interface. + * + * @opensearch.internal + */ public interface JoinCallback { void onSuccess(); void onFailure(Exception e); } + /** + * Listener for the join task + * + * @opensearch.internal + */ static class JoinTaskListener implements ClusterStateTaskListener { private final JoinTaskExecutor.Task task; private final JoinCallback joinCallback; @@ -419,12 +541,17 @@ interface JoinAccumulator { default void close(Mode newMode) {} } + /** + * A leader join accumulator. + * + * @opensearch.internal + */ class LeaderJoinAccumulator implements JoinAccumulator { @Override public void handleJoinRequest(DiscoveryNode sender, JoinCallback joinCallback) { final JoinTaskExecutor.Task task = new JoinTaskExecutor.Task(sender, "join existing leader"); assert joinTaskExecutor != null; - masterService.submitStateUpdateTask( + clusterManagerService.submitStateUpdateTask( "node-join", task, ClusterStateTaskConfig.build(Priority.URGENT), @@ -439,6 +566,11 @@ public String toString() { } } + /** + * An initial join accumulator. + * + * @opensearch.internal + */ static class InitialJoinAccumulator implements JoinAccumulator { @Override public void handleJoinRequest(DiscoveryNode sender, JoinCallback joinCallback) { @@ -452,6 +584,11 @@ public String toString() { } } + /** + * A follower join accumulator. + * + * @opensearch.internal + */ static class FollowerJoinAccumulator implements JoinAccumulator { @Override public void handleJoinRequest(DiscoveryNode sender, JoinCallback joinCallback) { @@ -464,6 +601,11 @@ public String toString() { } } + /** + * A candidate join accumulator. + * + * @opensearch.internal + */ class CandidateJoinAccumulator implements JoinAccumulator { private final Map joinRequestAccumulator = new HashMap<>(); @@ -489,12 +631,12 @@ public void close(Mode newMode) { pendingAsTasks.put(task, new JoinTaskListener(task, value)); }); - final String stateUpdateSource = "elected-as-master ([" + pendingAsTasks.size() + "] nodes joined)"; + final String stateUpdateSource = "elected-as-cluster-manager ([" + pendingAsTasks.size() + "] nodes joined)"; - pendingAsTasks.put(JoinTaskExecutor.newBecomeMasterTask(), (source, e) -> {}); + pendingAsTasks.put(JoinTaskExecutor.newBecomeClusterManagerTask(), (source, e) -> {}); pendingAsTasks.put(JoinTaskExecutor.newFinishElectionTask(), (source, e) -> {}); joinTaskExecutor = joinTaskExecutorGenerator.get(); - masterService.submitStateUpdateTasks( + clusterManagerService.submitStateUpdateTasks( stateUpdateSource, pendingAsTasks, ClusterStateTaskConfig.build(Priority.URGENT), diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinRequest.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinRequest.java index f18396e78fbf9..6eb1514ae848c 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinRequest.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinRequest.java @@ -33,14 +33,19 @@ import org.opensearch.LegacyESVersion; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.transport.TransportRequest; import java.io.IOException; import java.util.Objects; import java.util.Optional; +/** + * Request for a node to join the cluster + * + * @opensearch.internal + */ public class JoinRequest extends TransportRequest { /** @@ -50,15 +55,15 @@ public class JoinRequest extends TransportRequest { /** * The minimum term for which the joining node will accept any cluster state publications. If the joining node is in a strictly greater - * term than the master it wants to join then the master must enter a new term and hold another election. Doesn't necessarily match + * term than the cluster-manager it wants to join then the cluster-manager must enter a new term and hold another election. Doesn't necessarily match * {@link JoinRequest#optionalJoin} and may be zero in join requests sent prior to {@link LegacyESVersion#V_7_7_0}. */ private final long minimumTerm; /** - * A vote for the receiving node. This vote is optional since the sending node may have voted for a different master in this term. - * That's ok, the sender likely discovered that the master we voted for lost the election and now we're trying to join the winner. Once - * the sender has successfully joined the master, the lack of a vote in its term causes another election (see + * A vote for the receiving node. This vote is optional since the sending node may have voted for a different cluster-manager in this term. + * That's ok, the sender likely discovered that the cluster-manager we voted for lost the election and now we're trying to join the winner. Once + * the sender has successfully joined the cluster-manager, the lack of a vote in its term causes another election (see * {@link Publication#onMissingJoin(DiscoveryNode)}). */ private final Optional optionalJoin; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java index ea5c33b4300a5..a7b96d6a3e0f8 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/JoinTaskExecutor.java @@ -34,19 +34,23 @@ import org.apache.logging.log4j.Logger; import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateTaskExecutor; -import org.opensearch.cluster.NotMasterException; +import org.opensearch.cluster.NotClusterManagerException; import org.opensearch.cluster.block.ClusterBlocks; +import org.opensearch.cluster.decommission.NodeDecommissionedException; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.metadata.RepositoriesMetadata; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.cluster.routing.RerouteService; import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.common.Priority; import org.opensearch.common.settings.Settings; +import org.opensearch.core.action.ActionListener; +import org.opensearch.node.remotestore.RemoteStoreNodeAttribute; +import org.opensearch.node.remotestore.RemoteStoreNodeService; import org.opensearch.persistent.PersistentTasksCustomMetadata; import org.opensearch.transport.TransportService; @@ -60,8 +64,17 @@ import java.util.function.BiConsumer; import java.util.stream.Collectors; +import static org.opensearch.cluster.decommission.DecommissionHelper.nodeCommissioned; import static org.opensearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK; +import static org.opensearch.node.remotestore.RemoteStoreNodeService.CompatibilityMode; +import static org.opensearch.node.remotestore.RemoteStoreNodeService.CompatibilityMode.STRICT; +import static org.opensearch.node.remotestore.RemoteStoreNodeService.REMOTE_STORE_COMPATIBILITY_MODE_SETTING; +/** + * Main executor for Nodes joining the OpenSearch cluster + * + * @opensearch.internal + */ public class JoinTaskExecutor implements ClusterStateTaskExecutor { private final AllocationService allocationService; @@ -70,6 +83,13 @@ public class JoinTaskExecutor implements ClusterStateTaskExecutor execute(ClusterState currentState, List jo if (joiningNodes.size() == 1 && joiningNodes.get(0).isFinishElectionTask()) { return results.successes(joiningNodes).build(currentState); - } else if (currentNodes.getMasterNode() == null && joiningNodes.stream().anyMatch(Task::isBecomeMasterTask)) { - assert joiningNodes.stream().anyMatch(Task::isFinishElectionTask) : "becoming a master but election is not finished " + } else if (currentNodes.getClusterManagerNode() == null && joiningNodes.stream().anyMatch(Task::isBecomeClusterManagerTask)) { + assert joiningNodes.stream().anyMatch(Task::isFinishElectionTask) : "becoming a cluster-manager but election is not finished " + joiningNodes; - // use these joins to try and become the master. + // use these joins to try and become the cluster-manager. // Note that we don't have to do any validation of the amount of joining nodes - the commit // during the cluster state publishing guarantees that we have enough - newState = becomeMasterAndTrimConflictingNodes(currentState, joiningNodes); + newState = becomeClusterManagerAndTrimConflictingNodes(currentState, joiningNodes); nodesChanged = true; - } else if (currentNodes.isLocalNodeElectedMaster() == false) { - logger.trace("processing node joins, but we are not the master. current master: {}", currentNodes.getMasterNode()); - throw new NotMasterException("Node [" + currentNodes.getLocalNode() + "] not master for join request"); + } else if (currentNodes.isLocalNodeElectedClusterManager() == false) { + logger.trace( + "processing node joins, but we are not the cluster-manager. current cluster-manager: {}", + currentNodes.getClusterManagerNode() + ); + throw new NotClusterManagerException("Node [" + currentNodes.getLocalNode() + "] not cluster-manager for join request"); } else { newState = ClusterState.builder(currentState); } DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(newState.nodes()); - assert nodesBuilder.isLocalNodeElectedMaster(); + // TODO: We are using one of the existing node to build the repository metadata, this will need to be updated + // once we start supporting mixed compatibility mode. An optimization can be done as this will get invoked + // for every set of node join task which we can optimize to not compute if cluster state already has + // repository information. + RepositoriesMetadata repositoriesMetadata = remoteStoreNodeService.updateRepositoriesMetadata( + (currentNodes.getNodes().values()).stream().findFirst().get(), + currentState.getMetadata().custom(RepositoriesMetadata.TYPE) + ); + + assert nodesBuilder.isLocalNodeElectedClusterManager(); Version minClusterNodeVersion = newState.nodes().getMinNodeVersion(); Version maxClusterNodeVersion = newState.nodes().getMaxNodeVersion(); @@ -154,7 +199,7 @@ public ClusterTasksResult execute(ClusterState currentState, List jo // processing any joins Map joiniedNodeNameIds = new HashMap<>(); for (final Task joinTask : joiningNodes) { - if (joinTask.isBecomeMasterTask() || joinTask.isFinishElectionTask()) { + if (joinTask.isBecomeClusterManagerTask() || joinTask.isFinishElectionTask()) { // noop } else if (currentNodes.nodeExistsWithSameRoles(joinTask.node()) && !currentNodes.nodeExistsWithBWCVersion(joinTask.node())) { logger.debug("received a join request for an existing node [{}]", joinTask.node()); @@ -164,18 +209,21 @@ public ClusterTasksResult execute(ClusterState currentState, List jo if (enforceMajorVersion) { ensureMajorVersionBarrier(node.getVersion(), minClusterNodeVersion); } - ensureNodesCompatibility(node.getVersion(), minClusterNodeVersion, maxClusterNodeVersion); + ensureNodesCompatibility(node, currentNodes, currentState.metadata(), minClusterNodeVersion, maxClusterNodeVersion); // we do this validation quite late to prevent race conditions between nodes joining and importing dangling indices // we have to reject nodes that don't support all indices we have in this cluster ensureIndexCompatibility(node.getVersion(), currentState.getMetadata()); + // we have added the same check in handleJoinRequest method and adding it here as this method + // would guarantee that a decommissioned node would never be able to join the cluster and ensures correctness + ensureNodeCommissioned(node, currentState.metadata()); nodesBuilder.add(node); nodesChanged = true; minClusterNodeVersion = Version.min(minClusterNodeVersion, node.getVersion()); maxClusterNodeVersion = Version.max(maxClusterNodeVersion, node.getVersion()); - if (node.isMasterNode()) { + if (node.isClusterManagerNode()) { joiniedNodeNameIds.put(node.getName(), node.getId()); } - } catch (IllegalArgumentException | IllegalStateException e) { + } catch (IllegalArgumentException | IllegalStateException | NodeDecommissionedException e) { results.failure(joinTask, e); continue; } @@ -213,27 +261,47 @@ public ClusterTasksResult execute(ClusterState currentState, List jo .coordinationMetadata(coordMetadataBuilder.build()) .build(); return results.build( - allocationService.adaptAutoExpandReplicas(newState.nodes(nodesBuilder).metadata(newMetadata).build()) + allocationService.adaptAutoExpandReplicas( + newState.nodes(nodesBuilder) + .metadata(updateMetadataWithRepositoriesMetadata(newMetadata, repositoriesMetadata)) + .build() + ) ); } } - return results.build(allocationService.adaptAutoExpandReplicas(newState.nodes(nodesBuilder).build())); + return results.build( + allocationService.adaptAutoExpandReplicas( + newState.nodes(nodesBuilder) + .metadata(updateMetadataWithRepositoriesMetadata(currentState.metadata(), repositoriesMetadata)) + .build() + ) + ); } else { // we must return a new cluster state instance to force publishing. This is important - // for the joining node to finalize its join and set us as a master - return results.build(newState.build()); + // for the joining node to finalize its join and set us as a cluster-manager + return results.build( + newState.metadata(updateMetadataWithRepositoriesMetadata(currentState.metadata(), repositoriesMetadata)).build() + ); + } + } + + private Metadata updateMetadataWithRepositoriesMetadata(Metadata currentMetadata, RepositoriesMetadata repositoriesMetadata) { + if (repositoriesMetadata == null || repositoriesMetadata.repositories() == null || repositoriesMetadata.repositories().isEmpty()) { + return currentMetadata; + } else { + return Metadata.builder(currentMetadata).putCustom(RepositoriesMetadata.TYPE, repositoriesMetadata.get()).build(); } } - protected ClusterState.Builder becomeMasterAndTrimConflictingNodes(ClusterState currentState, List joiningNodes) { - assert currentState.nodes().getMasterNodeId() == null : currentState; + protected ClusterState.Builder becomeClusterManagerAndTrimConflictingNodes(ClusterState currentState, List joiningNodes) { + assert currentState.nodes().getClusterManagerNodeId() == null : currentState; DiscoveryNodes currentNodes = currentState.nodes(); DiscoveryNodes.Builder nodesBuilder = DiscoveryNodes.builder(currentNodes); - nodesBuilder.masterNodeId(currentState.nodes().getLocalNodeId()); + nodesBuilder.clusterManagerNodeId(currentState.nodes().getLocalNodeId()); for (final Task joinTask : joiningNodes) { - if (joinTask.isBecomeMasterTask()) { + if (joinTask.isBecomeClusterManagerTask()) { refreshDiscoveryNodeVersionAfterUpgrade(currentNodes, nodesBuilder); } else if (joinTask.isFinishElectionTask()) { // no-op @@ -256,13 +324,17 @@ protected ClusterState.Builder becomeMasterAndTrimConflictingNodes(ClusterState } } - // now trim any left over dead nodes - either left there when the previous master stepped down + // now trim any left over dead nodes - either left there when the previous cluster-manager stepped down // or removed by us above ClusterState tmpState = ClusterState.builder(currentState) .nodes(nodesBuilder) - .blocks(ClusterBlocks.builder().blocks(currentState.blocks()).removeGlobalBlock(NoMasterBlockService.NO_MASTER_BLOCK_ID)) + .blocks( + ClusterBlocks.builder() + .blocks(currentState.blocks()) + .removeGlobalBlock(NoClusterManagerBlockService.NO_CLUSTER_MANAGER_BLOCK_ID) + ) .build(); - logger.trace("becomeMasterAndTrimConflictingNodes: {}", tmpState.nodes()); + logger.trace("becomeClusterManagerAndTrimConflictingNodes: {}", tmpState.nodes()); allocationService.cleanCaches(); tmpState = PersistentTasksCustomMetadata.disassociateDeadNodes(tmpState); return ClusterState.builder(allocationService.disassociateDeadNodes(tmpState, false, "removed dead nodes on election")); @@ -277,7 +349,7 @@ private void refreshDiscoveryNodeVersionAfterUpgrade(DiscoveryNodes currentNodes // updating the version of those node which have connection with the new master. // Note: This should get deprecated with BWC mode logic if (null == transportService) { - // this logic is only applicable when OpenSearch node is master and is noop for zen discovery node + // this logic is only applicable when OpenSearch node is cluster-manager and is noop for zen discovery node return; } if (currentNodes.getMinNodeVersion().before(Version.V_1_0_0)) { @@ -310,7 +382,7 @@ private void refreshDiscoveryNodeVersionAfterUpgrade(DiscoveryNodes currentNodes } } else { // in case existing OpenSearch node is present in the cluster and but there is no connection to that node yet, - // either that node will send new JoinRequest to the master with version >=1.0, then no issue or + // either that node will send new JoinRequest to the cluster-manager/master with version >=1.0, then no issue or // there is an edge case if doesn't send JoinRequest and connection is established, // then it can continue to report version as 7.10.2 instead of actual OpenSearch version. So, // removing the node from cluster state to prevent stale version reporting and let it reconnect. @@ -323,18 +395,31 @@ private void refreshDiscoveryNodeVersionAfterUpgrade(DiscoveryNodes currentNodes } @Override - public boolean runOnlyOnMaster() { + public boolean runOnlyOnClusterManager() { // we validate that we are allowed to change the cluster state during cluster state processing return false; } + /** + * a task indicates that the current node should become master + * + * @deprecated As of 2.0, because supporting inclusive language, replaced by {@link #newBecomeClusterManagerTask()} + */ + @Deprecated public static Task newBecomeMasterTask() { return new Task(null, Task.BECOME_MASTER_TASK_REASON); } + /** + * a task indicates that the current node should become cluster-manager + */ + public static Task newBecomeClusterManagerTask() { + return new Task(null, Task.BECOME_CLUSTER_MANAGER_TASK_REASON); + } + /** * a task that is used to signal the election is stopped and we should process pending joins. - * it may be used in combination with {@link JoinTaskExecutor#newBecomeMasterTask()} + * it may be used in combination with {@link JoinTaskExecutor#newBecomeClusterManagerTask()} */ public static Task newFinishElectionTask() { return new Task(null, Task.FINISH_ELECTION_TASK_REASON); @@ -344,8 +429,9 @@ public static Task newFinishElectionTask() { * Ensures that all indices are compatible with the given node version. This will ensure that all indices in the given metadata * will not be created with a newer version of opensearch as well as that all indices are newer or equal to the minimum index * compatibility version. - * @see Version#minimumIndexCompatibilityVersion() + * * @throws IllegalStateException if any index is incompatible with the given version + * @see Version#minimumIndexCompatibilityVersion() */ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata metadata) { Version supportedIndexVersion = nodeVersion.minimumIndexCompatibilityVersion(); @@ -375,15 +461,27 @@ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata } } - /** ensures that the joining node has a version that's compatible with all current nodes*/ - public static void ensureNodesCompatibility(final Version joiningNodeVersion, DiscoveryNodes currentNodes) { + /** + * ensures that the joining node has a version that's compatible with all current nodes + */ + public static void ensureNodesCompatibility(final DiscoveryNode joiningNode, DiscoveryNodes currentNodes, Metadata metadata) { final Version minNodeVersion = currentNodes.getMinNodeVersion(); final Version maxNodeVersion = currentNodes.getMaxNodeVersion(); - ensureNodesCompatibility(joiningNodeVersion, minNodeVersion, maxNodeVersion); + ensureNodesCompatibility(joiningNode, currentNodes, metadata, minNodeVersion, maxNodeVersion); } - /** ensures that the joining node has a version that's compatible with a given version range */ - public static void ensureNodesCompatibility(Version joiningNodeVersion, Version minClusterNodeVersion, Version maxClusterNodeVersion) { + /** + * ensures that the joining node has a version that's compatible with a given version range and ensures that the + * joining node has required attributes to join a remotestore cluster. + */ + public static void ensureNodesCompatibility( + DiscoveryNode joiningNode, + DiscoveryNodes currentNodes, + Metadata metadata, + Version minClusterNodeVersion, + Version maxClusterNodeVersion + ) { + Version joiningNodeVersion = joiningNode.getVersion(); assert minClusterNodeVersion.onOrBefore(maxClusterNodeVersion) : minClusterNodeVersion + " > " + maxClusterNodeVersion; if (joiningNodeVersion.isCompatible(maxClusterNodeVersion) == false) { throw new IllegalStateException( @@ -405,11 +503,13 @@ public static void ensureNodesCompatibility(Version joiningNodeVersion, Version + "], which is incompatible." ); } + + ensureRemoteStoreNodesCompatibility(joiningNode, currentNodes, metadata); } /** * ensures that the joining node's major version is equal or higher to the minClusterNodeVersion. This is needed - * to ensure that if the master is already fully operating under the new major version, it doesn't go back to mixed + * to ensure that if the cluster-manager/master is already fully operating under the new major version, it doesn't go back to mixed * version mode **/ public static void ensureMajorVersionBarrier(Version joiningNodeVersion, Version minClusterNodeVersion) { @@ -426,13 +526,78 @@ public static void ensureMajorVersionBarrier(Version joiningNodeVersion, Version } } + public static void ensureNodeCommissioned(DiscoveryNode node, Metadata metadata) { + if (nodeCommissioned(node, metadata) == false) { + throw new NodeDecommissionedException( + "node [{}] has decommissioned attribute [{}] with current status of decommissioning [{}]", + node.toString(), + metadata.decommissionAttributeMetadata().decommissionAttribute().toString(), + metadata.decommissionAttributeMetadata().status().status() + ); + } + } + + /** + * The method ensures homogeneity - + * 1. The joining node has to be a remote store backed if it's joining a remote store backed cluster. Validates + * remote store attributes of joining node against the existing nodes of cluster. + * 2. The joining node has to be a non-remote store backed if it is joining a non-remote store backed cluster. + * Validates no remote store attributes are present in joining node as existing nodes in the cluster doesn't have + * remote store attributes. + * + * A remote store backed node is the one which holds all the remote store attributes and a remote store backed + * cluster is the one which has only homogeneous remote store backed nodes with same node attributes + * + * TODO: When we support moving from remote store cluster to non remote store and vice versa the this logic will + * needs to be modified. + */ + private static void ensureRemoteStoreNodesCompatibility(DiscoveryNode joiningNode, DiscoveryNodes currentNodes, Metadata metadata) { + List existingNodes = new ArrayList<>(currentNodes.getNodes().values()); + + assert existingNodes.isEmpty() == false; + + // TODO: The below check is valid till we don't support migration, once we start supporting migration a remote + // store node will be able to join a non remote store cluster and vice versa. #7986 + CompatibilityMode remoteStoreCompatibilityMode = REMOTE_STORE_COMPATIBILITY_MODE_SETTING.get(metadata.settings()); + if (STRICT.equals(remoteStoreCompatibilityMode)) { + DiscoveryNode existingNode = existingNodes.get(0); + if (joiningNode.isRemoteStoreNode()) { + if (existingNode.isRemoteStoreNode()) { + RemoteStoreNodeAttribute joiningRemoteStoreNodeAttribute = new RemoteStoreNodeAttribute(joiningNode); + RemoteStoreNodeAttribute existingRemoteStoreNodeAttribute = new RemoteStoreNodeAttribute(existingNode); + if (existingRemoteStoreNodeAttribute.equals(joiningRemoteStoreNodeAttribute) == false) { + throw new IllegalStateException( + "a remote store node [" + + joiningNode + + "] is trying to join a remote store cluster with incompatible node attributes in " + + "comparison with existing node [" + + existingNode + + "]" + ); + } + } else { + throw new IllegalStateException( + "a remote store node [" + joiningNode + "] is trying to join a non remote store cluster" + ); + } + } else { + if (existingNode.isRemoteStoreNode()) { + throw new IllegalStateException( + "a non remote store node [" + joiningNode + "] is trying to join a remote store cluster" + ); + } + } + } + } + public static Collection> addBuiltInJoinValidators( Collection> onJoinValidators ) { final Collection> validators = new ArrayList<>(); validators.add((node, state) -> { - ensureNodesCompatibility(node.getVersion(), state.getNodes()); + ensureNodesCompatibility(node, state.getNodes(), state.metadata()); ensureIndexCompatibility(node.getVersion(), state.getMetadata()); + ensureNodeCommissioned(node, state.getMetadata()); }); validators.addAll(onJoinValidators); return Collections.unmodifiableCollection(validators); diff --git a/server/src/main/java/org/opensearch/cluster/coordination/LagDetector.java b/server/src/main/java/org/opensearch/cluster/coordination/LagDetector.java index e599fffa68ff1..eeb0800663d0a 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/LagDetector.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/LagDetector.java @@ -56,6 +56,8 @@ * A publication can succeed and complete before all nodes have applied the published state and acknowledged it; however we need every node * eventually either to apply the published state (or a later state) or be removed from the cluster. This component achieves this by * removing any lagging nodes from the cluster after a timeout. + * + * @opensearch.internal */ public class LagDetector { @@ -102,7 +104,7 @@ public void clearTrackedNodes() { public void setAppliedVersion(final DiscoveryNode discoveryNode, final long appliedVersion) { final NodeAppliedStateTracker nodeAppliedStateTracker = appliedStateTrackersByNode.get(discoveryNode); if (nodeAppliedStateTracker == null) { - // Received an ack from a node that a later publication has removed (or we are no longer master). No big deal. + // Received an ack from a node that a later publication has removed (or we are no longer cluster-manager). No big deal. logger.trace("node {} applied version {} but this node's version is not being tracked", discoveryNode, appliedVersion); } else { nodeAppliedStateTracker.increaseAppliedVersion(appliedVersion); @@ -149,6 +151,11 @@ Set getTrackedNodes() { return Collections.unmodifiableSet(appliedStateTrackersByNode.keySet()); } + /** + * A tracker that the node applied state. + * + * @opensearch.internal + */ private class NodeAppliedStateTracker { private final DiscoveryNode discoveryNode; private final AtomicLong appliedVersion = new AtomicLong(); diff --git a/server/src/main/java/org/opensearch/cluster/coordination/LeaderChecker.java b/server/src/main/java/org/opensearch/cluster/coordination/LeaderChecker.java index b4edc9401234d..69ba1f977f326 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/LeaderChecker.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/LeaderChecker.java @@ -39,12 +39,14 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; import org.opensearch.common.Nullable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.lease.Releasable; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse; +import org.opensearch.core.transport.TransportResponse.Empty; import org.opensearch.monitor.NodeHealthService; import org.opensearch.monitor.StatusInfo; import org.opensearch.threadpool.ThreadPool.Names; @@ -56,8 +58,6 @@ import org.opensearch.transport.TransportRequest; import org.opensearch.transport.TransportRequestOptions; import org.opensearch.transport.TransportRequestOptions.Type; -import org.opensearch.transport.TransportResponse; -import org.opensearch.transport.TransportResponse.Empty; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -75,6 +75,8 @@ * fairly lenient, possibly allowing multiple checks to fail before considering the leader to be faulty, to allow for the leader to * temporarily stand down on occasion, e.g. if it needs to move to a higher term. On deciding that the leader has failed a follower will * become a candidate and attempt to become a leader itself. + * + * @opensearch.internal */ public class LeaderChecker { @@ -190,8 +192,8 @@ void setCurrentNodes(DiscoveryNodes discoveryNodes) { } // For assertions - boolean currentNodeIsMaster() { - return discoveryNodes.isLocalNodeElectedMaster(); + boolean currentNodeIsClusterManager() { + return discoveryNodes.isLocalNodeElectedClusterManager(); } private void handleLeaderCheck(LeaderCheckRequest request) { @@ -207,10 +209,10 @@ private void handleLeaderCheck(LeaderCheckRequest request) { + "]"; logger.debug(message); throw new NodeHealthCheckFailureException(message); - } else if (discoveryNodes.isLocalNodeElectedMaster() == false) { - logger.debug("rejecting leader check on non-master {}", request); + } else if (discoveryNodes.isLocalNodeElectedClusterManager() == false) { + logger.debug("rejecting leader check on non-cluster-manager {}", request); throw new CoordinationStateRejectedException( - "rejecting leader check from [" + request.getSender() + "] sent to a node that is no longer the master" + "rejecting leader check from [" + request.getSender() + "] sent to a node that is no longer the cluster-manager" ); } else if (discoveryNodes.nodeExists(request.getSender()) == false) { logger.debug("rejecting leader check from removed node: {}", request); @@ -231,6 +233,11 @@ private void handleDisconnectedNode(DiscoveryNode discoveryNode) { } } + /** + * A check scheduler. + * + * @opensearch.internal + */ private class CheckScheduler implements Releasable { private final AtomicBoolean isClosed = new AtomicBoolean(); @@ -378,6 +385,11 @@ public String toString() { } } + /** + * A leader check request. + * + * @opensearch.internal + */ static class LeaderCheckRequest extends TransportRequest { private final DiscoveryNode sender; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/NoClusterManagerBlockService.java b/server/src/main/java/org/opensearch/cluster/coordination/NoClusterManagerBlockService.java new file mode 100644 index 0000000000000..b377fe592b0f4 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/coordination/NoClusterManagerBlockService.java @@ -0,0 +1,145 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster.coordination; + +import org.opensearch.cluster.block.ClusterBlock; +import org.opensearch.cluster.block.ClusterBlockLevel; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Setting.Property; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.rest.RestStatus; + +import java.util.EnumSet; + +/** + * Service to block the master node + * + * @opensearch.internal + */ +public class NoClusterManagerBlockService { + public static final int NO_CLUSTER_MANAGER_BLOCK_ID = 2; + public static final ClusterBlock NO_CLUSTER_MANAGER_BLOCK_WRITES = new ClusterBlock( + NO_CLUSTER_MANAGER_BLOCK_ID, + "no cluster-manager", + true, + false, + false, + RestStatus.SERVICE_UNAVAILABLE, + EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE) + ); + public static final ClusterBlock NO_CLUSTER_MANAGER_BLOCK_ALL = new ClusterBlock( + NO_CLUSTER_MANAGER_BLOCK_ID, + "no cluster-manager", + true, + true, + false, + RestStatus.SERVICE_UNAVAILABLE, + ClusterBlockLevel.ALL + ); + public static final ClusterBlock NO_CLUSTER_MANAGER_BLOCK_METADATA_WRITES = new ClusterBlock( + NO_CLUSTER_MANAGER_BLOCK_ID, + "no cluster-manager", + true, + false, + false, + RestStatus.SERVICE_UNAVAILABLE, + EnumSet.of(ClusterBlockLevel.METADATA_WRITE) + ); + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #NO_CLUSTER_MANAGER_BLOCK_ID} */ + @Deprecated + public static final int NO_MASTER_BLOCK_ID = NO_CLUSTER_MANAGER_BLOCK_ID; + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #NO_CLUSTER_MANAGER_BLOCK_WRITES} */ + @Deprecated + public static final ClusterBlock NO_MASTER_BLOCK_WRITES = NO_CLUSTER_MANAGER_BLOCK_WRITES; + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #NO_CLUSTER_MANAGER_BLOCK_ALL} */ + @Deprecated + public static final ClusterBlock NO_MASTER_BLOCK_ALL = NO_CLUSTER_MANAGER_BLOCK_ALL; + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #NO_CLUSTER_MANAGER_BLOCK_METADATA_WRITES} */ + @Deprecated + public static final ClusterBlock NO_MASTER_BLOCK_METADATA_WRITES = NO_CLUSTER_MANAGER_BLOCK_METADATA_WRITES; + + public static final Setting NO_MASTER_BLOCK_SETTING = new Setting<>( + "cluster.no_master_block", + "metadata_write", + NoClusterManagerBlockService::parseNoClusterManagerBlock, + Property.Dynamic, + Property.NodeScope, + Property.Deprecated + ); + // The setting below is going to replace the above. + // To keep backwards compatibility, the old usage is remained, and it's also used as the fallback for the new usage. + public static final Setting NO_CLUSTER_MANAGER_BLOCK_SETTING = new Setting<>( + "cluster.no_cluster_manager_block", + NO_MASTER_BLOCK_SETTING, + NoClusterManagerBlockService::parseNoClusterManagerBlock, + Property.Dynamic, + Property.NodeScope + ); + + private volatile ClusterBlock noClusterManagerBlock; + + public NoClusterManagerBlockService(Settings settings, ClusterSettings clusterSettings) { + this.noClusterManagerBlock = NO_CLUSTER_MANAGER_BLOCK_SETTING.get(settings); + clusterSettings.addSettingsUpdateConsumer(NO_CLUSTER_MANAGER_BLOCK_SETTING, this::setNoClusterManagerBlock); + } + + private static ClusterBlock parseNoClusterManagerBlock(String value) { + switch (value) { + case "all": + return NO_CLUSTER_MANAGER_BLOCK_ALL; + case "write": + return NO_CLUSTER_MANAGER_BLOCK_WRITES; + case "metadata_write": + return NO_CLUSTER_MANAGER_BLOCK_METADATA_WRITES; + default: + throw new IllegalArgumentException( + "invalid no-cluster-manager block [" + value + "], must be one of [all, write, metadata_write]" + ); + } + } + + public ClusterBlock getNoClusterManagerBlock() { + return noClusterManagerBlock; + } + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #getNoClusterManagerBlock()} */ + @Deprecated + public ClusterBlock getNoMasterBlock() { + return noClusterManagerBlock; + } + + private void setNoClusterManagerBlock(ClusterBlock noClusterManagerBlock) { + this.noClusterManagerBlock = noClusterManagerBlock; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/coordination/NoMasterBlockService.java b/server/src/main/java/org/opensearch/cluster/coordination/NoMasterBlockService.java index 8cbb0446a1337..c759e36b548f9 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/NoMasterBlockService.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/NoMasterBlockService.java @@ -6,114 +6,22 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.cluster.coordination; -import org.opensearch.cluster.block.ClusterBlock; -import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.common.settings.ClusterSettings; -import org.opensearch.common.settings.Setting; -import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; -import org.opensearch.rest.RestStatus; - -import java.util.EnumSet; - -public class NoMasterBlockService { - public static final int NO_MASTER_BLOCK_ID = 2; - public static final ClusterBlock NO_MASTER_BLOCK_WRITES = new ClusterBlock( - NO_MASTER_BLOCK_ID, - "no master", - true, - false, - false, - RestStatus.SERVICE_UNAVAILABLE, - EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE) - ); - public static final ClusterBlock NO_MASTER_BLOCK_ALL = new ClusterBlock( - NO_MASTER_BLOCK_ID, - "no master", - true, - true, - false, - RestStatus.SERVICE_UNAVAILABLE, - ClusterBlockLevel.ALL - ); - public static final ClusterBlock NO_MASTER_BLOCK_METADATA_WRITES = new ClusterBlock( - NO_MASTER_BLOCK_ID, - "no master", - true, - false, - false, - RestStatus.SERVICE_UNAVAILABLE, - EnumSet.of(ClusterBlockLevel.METADATA_WRITE) - ); - public static final Setting NO_MASTER_BLOCK_SETTING = new Setting<>( - "cluster.no_master_block", - "write", - NoMasterBlockService::parseNoMasterBlock, - Property.Dynamic, - Property.NodeScope, - Property.Deprecated - ); - // The setting below is going to replace the above. - // To keep backwards compatibility, the old usage is remained, and it's also used as the fallback for the new usage. - public static final Setting NO_CLUSTER_MANAGER_BLOCK_SETTING = new Setting<>( - "cluster.no_cluster_manager_block", - NO_MASTER_BLOCK_SETTING, - NoMasterBlockService::parseNoMasterBlock, - Property.Dynamic, - Property.NodeScope - ); - - private volatile ClusterBlock noMasterBlock; +/** + * Service to block the master node + * + * @opensearch.internal + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link NoClusterManagerBlockService} + */ +@Deprecated +public class NoMasterBlockService extends NoClusterManagerBlockService { public NoMasterBlockService(Settings settings, ClusterSettings clusterSettings) { - this.noMasterBlock = NO_CLUSTER_MANAGER_BLOCK_SETTING.get(settings); - clusterSettings.addSettingsUpdateConsumer(NO_CLUSTER_MANAGER_BLOCK_SETTING, this::setNoMasterBlock); - } - - private static ClusterBlock parseNoMasterBlock(String value) { - switch (value) { - case "all": - return NO_MASTER_BLOCK_ALL; - case "write": - return NO_MASTER_BLOCK_WRITES; - case "metadata_write": - return NO_MASTER_BLOCK_METADATA_WRITES; - default: - throw new IllegalArgumentException("invalid no-master block [" + value + "], must be one of [all, write, metadata_write]"); - } - } - - public ClusterBlock getNoMasterBlock() { - return noMasterBlock; + super(settings, clusterSettings); } - private void setNoMasterBlock(ClusterBlock noMasterBlock) { - this.noMasterBlock = noMasterBlock; - } } diff --git a/server/src/main/java/org/opensearch/cluster/coordination/NodeHealthCheckFailureException.java b/server/src/main/java/org/opensearch/cluster/coordination/NodeHealthCheckFailureException.java index 273a220d1eadb..43cc975573072 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/NodeHealthCheckFailureException.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/NodeHealthCheckFailureException.java @@ -33,13 +33,15 @@ package org.opensearch.cluster.coordination; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamInput; import java.io.IOException; /** * This exception is thrown if the File system is reported unhealthy by @{@link org.opensearch.monitor.fs.FsHealthService} * and this nodes needs to be removed from the cluster + * + * @opensearch.internal */ public class NodeHealthCheckFailureException extends OpenSearchException { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/NodeRemovalClusterStateTaskExecutor.java b/server/src/main/java/org/opensearch/cluster/coordination/NodeRemovalClusterStateTaskExecutor.java index 02bdb65c7edf2..2cf0fb3bffeee 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/NodeRemovalClusterStateTaskExecutor.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/NodeRemovalClusterStateTaskExecutor.java @@ -43,6 +43,11 @@ import java.util.List; +/** + * Update cluster state when node is removed from the cluster + * + * @opensearch.internal + */ public class NodeRemovalClusterStateTaskExecutor implements ClusterStateTaskExecutor, @@ -51,6 +56,11 @@ public class NodeRemovalClusterStateTaskExecutor private final AllocationService allocationService; private final Logger logger; + /** + * Task for the executor. + * + * @opensearch.internal + */ public static class Task { private final DiscoveryNode node; @@ -126,8 +136,8 @@ public void onFailure(final String source, final Exception e) { } @Override - public void onNoLongerMaster(String source) { - logger.debug("no longer master while processing node removal [{}]", source); + public void onNoLongerClusterManager(String source) { + logger.debug("no longer cluster-manager while processing node removal [{}]", source); } } diff --git a/server/src/main/java/org/opensearch/cluster/coordination/NodeToolCli.java b/server/src/main/java/org/opensearch/cluster/coordination/NodeToolCli.java index 72a3b3969e2e2..7d51002150fb9 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/NodeToolCli.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/NodeToolCli.java @@ -39,18 +39,24 @@ // NodeToolCli does not extend LoggingAwareCommand, because LoggingAwareCommand performs logging initialization // after LoggingAwareCommand instance is constructed. -// It's too late for us, because before UnsafeBootstrapMasterCommand is added to the list of subcommands +// It's too late for us, because before UnsafeBootstrapClusterManagerCommand is added to the list of subcommands // log4j2 initialization will happen, because it has static reference to Logger class. // Even if we avoid making a static reference to Logger class, there is no nice way to avoid declaring // UNSAFE_BOOTSTRAP, which depends on ClusterService, which in turn has static Logger. // TODO execute CommandLoggingConfigurator.configureLoggingWithoutConfig() in the constructor of commands, not in beforeMain + +/** + * Command Line Interface tool for Nodes + * + * @opensearch.internal + */ public class NodeToolCli extends MultiCommand { public NodeToolCli() { super("A CLI tool to do unsafe cluster and index manipulations on current node", () -> {}); CommandLoggingConfigurator.configureLoggingWithoutConfig(); subcommands.put("repurpose", new NodeRepurposeCommand()); - subcommands.put("unsafe-bootstrap", new UnsafeBootstrapMasterCommand()); + subcommands.put("unsafe-bootstrap", new UnsafeBootstrapClusterManagerCommand()); subcommands.put("detach-cluster", new DetachClusterCommand()); subcommands.put("override-version", new OverrideNodeVersionCommand()); subcommands.put("remove-settings", new RemoveSettingsCommand()); diff --git a/server/src/main/java/org/opensearch/cluster/coordination/OpenSearchNodeCommand.java b/server/src/main/java/org/opensearch/cluster/coordination/OpenSearchNodeCommand.java index bc7bd3abbca86..259d8961a3e78 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/OpenSearchNodeCommand.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/OpenSearchNodeCommand.java @@ -50,13 +50,13 @@ import org.opensearch.cluster.metadata.DataStreamMetadata; import org.opensearch.cluster.metadata.Metadata; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.BigArrays; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.env.Environment; import org.opensearch.env.NodeEnvironment; import org.opensearch.env.NodeMetadata; @@ -70,6 +70,11 @@ import java.util.Map; import java.util.Objects; +/** + * Main set of node commands + * + * @opensearch.internal + */ public abstract class OpenSearchNodeCommand extends EnvironmentAwareCommand { private static final Logger logger = LogManager.getLogger(OpenSearchNodeCommand.class); protected static final String DELIMITER = "------------------------------------------------------------------------\n"; @@ -225,6 +230,11 @@ OptionParser getParser() { return parser; } + /** + * Custom unknown metadata. + * + * @opensearch.internal + */ public static class UnknownMetadataCustom implements Metadata.Custom { private final String name; @@ -269,6 +279,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } } + /** + * An unknown condition. + * + * @opensearch.internal + */ public static class UnknownCondition extends Condition { public UnknownCondition(String name, Object value) { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PeersResponse.java b/server/src/main/java/org/opensearch/cluster/coordination/PeersResponse.java index 76be3ebd3a374..8a70c71d53fdd 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PeersResponse.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PeersResponse.java @@ -33,37 +33,42 @@ package org.opensearch.cluster.coordination; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.transport.TransportResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse; import java.io.IOException; import java.util.List; import java.util.Objects; import java.util.Optional; +/** + * Response from peer nodes + * + * @opensearch.internal + */ public class PeersResponse extends TransportResponse { - private final Optional masterNode; + private final Optional clusterManagerNode; private final List knownPeers; private final long term; - public PeersResponse(Optional masterNode, List knownPeers, long term) { - assert masterNode.isPresent() == false || knownPeers.isEmpty(); - this.masterNode = masterNode; + public PeersResponse(Optional clusterManagerNode, List knownPeers, long term) { + assert clusterManagerNode.isPresent() == false || knownPeers.isEmpty(); + this.clusterManagerNode = clusterManagerNode; this.knownPeers = knownPeers; this.term = term; } public PeersResponse(StreamInput in) throws IOException { - masterNode = Optional.ofNullable(in.readOptionalWriteable(DiscoveryNode::new)); + clusterManagerNode = Optional.ofNullable(in.readOptionalWriteable(DiscoveryNode::new)); knownPeers = in.readList(DiscoveryNode::new); term = in.readLong(); - assert masterNode.isPresent() == false || knownPeers.isEmpty(); + assert clusterManagerNode.isPresent() == false || knownPeers.isEmpty(); } @Override public void writeTo(StreamOutput out) throws IOException { - out.writeOptionalWriteable(masterNode.orElse(null)); + out.writeOptionalWriteable(clusterManagerNode.orElse(null)); out.writeList(knownPeers); out.writeLong(term); } @@ -71,8 +76,17 @@ public void writeTo(StreamOutput out) throws IOException { /** * @return the node that is currently leading, according to the responding node. */ + public Optional getClusterManagerNode() { + return clusterManagerNode; + } + + /** + * @return the node that is currently leading, according to the responding node. + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #getClusterManagerNode()} + */ + @Deprecated public Optional getMasterNode() { - return masterNode; + return getClusterManagerNode(); } /** @@ -93,7 +107,7 @@ public long getTerm() { @Override public String toString() { - return "PeersResponse{" + "masterNode=" + masterNode + ", knownPeers=" + knownPeers + ", term=" + term + '}'; + return "PeersResponse{" + "clusterManagerNode=" + clusterManagerNode + ", knownPeers=" + knownPeers + ", term=" + term + '}'; } @Override @@ -101,11 +115,13 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PeersResponse that = (PeersResponse) o; - return term == that.term && Objects.equals(masterNode, that.masterNode) && Objects.equals(knownPeers, that.knownPeers); + return term == that.term + && Objects.equals(clusterManagerNode, that.clusterManagerNode) + && Objects.equals(knownPeers, that.knownPeers); } @Override public int hashCode() { - return Objects.hash(masterNode, knownPeers, term); + return Objects.hash(clusterManagerNode, knownPeers, term); } } diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PendingClusterStateStats.java b/server/src/main/java/org/opensearch/cluster/coordination/PendingClusterStateStats.java index 92dd926b2fa0a..9cab59c5fa492 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PendingClusterStateStats.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PendingClusterStateStats.java @@ -32,16 +32,18 @@ package org.opensearch.cluster.coordination; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; /** * Class encapsulating stats about the PendingClusterStatsQueue + * + * @opensearch.internal */ public class PendingClusterStateStats implements Writeable, ToXContentFragment { @@ -90,6 +92,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Fields for parsing and toXContent + * + * @opensearch.internal + */ static final class Fields { static final String QUEUE = "cluster_state_queue"; static final String TOTAL = "total"; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PersistedStateRegistry.java b/server/src/main/java/org/opensearch/cluster/coordination/PersistedStateRegistry.java new file mode 100644 index 0000000000000..470ab02a682a8 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/coordination/PersistedStateRegistry.java @@ -0,0 +1,52 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.coordination; + +import org.opensearch.cluster.coordination.CoordinationState.PersistedState; +import org.opensearch.common.util.io.IOUtils; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A class which encapsulates the PersistedStates + * + * @opensearch.internal + */ +public class PersistedStateRegistry implements Closeable { + + public PersistedStateRegistry() {} + + /** + * Distinct Types PersistedState which can be present on a node + */ + public enum PersistedStateType { + LOCAL, + REMOTE; + } + + private final Map persistedStates = new ConcurrentHashMap<>(); + + public void addPersistedState(PersistedStateType persistedStateType, PersistedState persistedState) { + PersistedState existingState = this.persistedStates.putIfAbsent(persistedStateType, persistedState); + assert existingState == null : "should only be set once, but already have " + existingState; + } + + public PersistedState getPersistedState(PersistedStateType persistedStateType) { + return this.persistedStates.get(persistedStateType); + } + + @Override + public void close() throws IOException { + IOUtils.close(persistedStates.values()); + } + +} diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PreVoteCollector.java b/server/src/main/java/org/opensearch/cluster/coordination/PreVoteCollector.java index 1b934b5f000cd..cc4d1ac156c53 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PreVoteCollector.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PreVoteCollector.java @@ -40,8 +40,8 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.Nullable; import org.opensearch.common.collect.Tuple; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.lease.Releasable; +import org.opensearch.core.common.io.stream.StreamInput; import org.opensearch.monitor.NodeHealthService; import org.opensearch.monitor.StatusInfo; import org.opensearch.threadpool.ThreadPool.Names; @@ -58,6 +58,11 @@ import static org.opensearch.common.util.concurrent.ConcurrentCollections.newConcurrentMap; import static org.opensearch.monitor.StatusInfo.Status.UNHEALTHY; +/** + * Collects information prior to a promotion vote + * + * @opensearch.internal + */ public class PreVoteCollector { private static final Logger logger = LogManager.getLogger(PreVoteCollector.class); @@ -162,6 +167,11 @@ public String toString() { return "PreVoteCollector{" + "state=" + state + '}'; } + /** + * The pre vote round. + * + * @opensearch.internal + */ private class PreVotingRound implements Releasable { private final Map preVotesReceived = newConcurrentMap(); private final AtomicBoolean electionStarted = new AtomicBoolean(); diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PreVoteRequest.java b/server/src/main/java/org/opensearch/cluster/coordination/PreVoteRequest.java index e99b2716a263f..85632bdc06e75 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PreVoteRequest.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PreVoteRequest.java @@ -33,13 +33,18 @@ package org.opensearch.cluster.coordination; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.transport.TransportRequest; import java.io.IOException; import java.util.Objects; +/** + * Requests pre vote information collection + * + * @opensearch.internal + */ public class PreVoteRequest extends TransportRequest { private final DiscoveryNode sourceNode; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PreVoteResponse.java b/server/src/main/java/org/opensearch/cluster/coordination/PreVoteResponse.java index 09259867ebf53..9c683f7de0878 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PreVoteResponse.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PreVoteResponse.java @@ -32,13 +32,18 @@ package org.opensearch.cluster.coordination; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.transport.TransportResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse; import java.io.IOException; import java.util.Objects; +/** + * Response for a PreVoteRequest + * + * @opensearch.internal + */ public class PreVoteResponse extends TransportResponse { private final long currentTerm; private final long lastAcceptedTerm; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/Publication.java b/server/src/main/java/org/opensearch/cluster/coordination/Publication.java index 5529c3248345c..43801a05dbc24 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/Publication.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/Publication.java @@ -37,13 +37,13 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.coordination.ClusterStatePublisher.AckListener; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.transport.TransportException; -import org.opensearch.transport.TransportResponse; import java.util.ArrayList; import java.util.List; @@ -52,6 +52,11 @@ import java.util.function.LongSupplier; import java.util.stream.Collectors; +/** + * Publication task + * + * @opensearch.internal + */ public abstract class Publication { protected final Logger logger = LogManager.getLogger(getClass()); @@ -73,7 +78,10 @@ public Publication(PublishRequest publishRequest, AckListener ackListener, LongS startTime = currentTimeSupplier.getAsLong(); applyCommitRequest = Optional.empty(); publicationTargets = new ArrayList<>(publishRequest.getAcceptedState().getNodes().getNodes().size()); - publishRequest.getAcceptedState().getNodes().mastersFirstStream().forEach(n -> publicationTargets.add(new PublicationTarget(n))); + publishRequest.getAcceptedState() + .getNodes() + .clusterManagersFirstStream() + .forEach(n -> publicationTargets.add(new PublicationTarget(n))); } public void start(Set faultyNodes) { @@ -247,6 +255,11 @@ enum PublicationTargetState { APPLIED_COMMIT, } + /** + * A publication target. + * + * @opensearch.internal + */ class PublicationTarget { private final DiscoveryNode discoveryNode; private boolean ackIsPending = true; @@ -358,6 +371,11 @@ boolean isFailed() { return state == PublicationTargetState.FAILED; } + /** + * A handler for a publish response. + * + * @opensearch.internal + */ private class PublishResponseHandler implements ActionListener { @Override @@ -399,6 +417,11 @@ public void onFailure(Exception e) { } + /** + * An apply commit response handler. + * + * @opensearch.internal + */ private class ApplyCommitResponseHandler implements ActionListener { @Override diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PublicationTransportHandler.java b/server/src/main/java/org/opensearch/cluster/coordination/PublicationTransportHandler.java index ee97c0e07eb48..1fdaeead0d28d 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PublicationTransportHandler.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PublicationTransportHandler.java @@ -36,30 +36,22 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.cluster.ClusterChangedEvent; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.Diff; import org.opensearch.cluster.IncompatibleClusterStateVersionException; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.node.DiscoveryNodes; -import org.opensearch.common.bytes.BytesReference; -import org.opensearch.common.compress.Compressor; -import org.opensearch.common.compress.CompressorFactory; -import org.opensearch.common.io.stream.BytesStreamOutput; -import org.opensearch.common.io.stream.InputStreamStreamInput; -import org.opensearch.common.io.stream.NamedWriteableAwareStreamInput; -import org.opensearch.common.io.stream.NamedWriteableRegistry; -import org.opensearch.common.io.stream.OutputStreamStreamOutput; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.core.internal.io.IOUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.NamedWriteableRegistry; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.transport.TransportResponse; import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.BytesTransportRequest; import org.opensearch.transport.TransportChannel; import org.opensearch.transport.TransportException; import org.opensearch.transport.TransportRequestOptions; -import org.opensearch.transport.TransportResponse; import org.opensearch.transport.TransportResponseHandler; import org.opensearch.transport.TransportService; @@ -72,6 +64,11 @@ import java.util.function.Consumer; import java.util.function.Function; +/** + * Transport handler for publication + * + * @opensearch.internal + */ public class PublicationTransportHandler { private static final Logger logger = LogManager.getLogger(PublicationTransportHandler.class); @@ -85,7 +82,7 @@ public class PublicationTransportHandler { private final AtomicReference lastSeenClusterState = new AtomicReference<>(); - // the master needs the original non-serialized state as the cluster state contains some volatile information that we + // the cluster-manager needs the original non-serialized state as the cluster state contains some volatile information that we // don't want to be replicated because it's not usable on another node (e.g. UnassignedInfo.unassignedTimeNanos) or // because it's mostly just debugging info that would unnecessarily blow up CS updates (I think there was one in // snapshot code). @@ -163,17 +160,9 @@ public PublishClusterStateStats stats() { } private PublishWithJoinResponse handleIncomingPublishRequest(BytesTransportRequest request) throws IOException { - final Compressor compressor = CompressorFactory.compressor(request.bytes()); - StreamInput in = request.bytes().streamInput(); - try { - if (compressor != null) { - in = new InputStreamStreamInput(compressor.threadLocalInputStream(in)); - } - in = new NamedWriteableAwareStreamInput(in, namedWriteableRegistry); - in.setVersion(request.version()); - // If true we received full cluster state - otherwise diffs + try (StreamInput in = CompressedStreamUtils.decompressBytes(request, namedWriteableRegistry)) { + ClusterState incomingState; if (in.readBoolean()) { - final ClusterState incomingState; // Close early to release resources used by the de-compression as early as possible try (StreamInput input = in) { incomingState = ClusterState.readFrom(input, transportService.getLocalNode()); @@ -193,7 +182,6 @@ private PublishWithJoinResponse handleIncomingPublishRequest(BytesTransportReque incompatibleClusterStateDiffReceivedCount.incrementAndGet(); throw new IncompatibleClusterStateVersionException("have no local cluster state"); } else { - ClusterState incomingState; try { final Diff diff; // Close stream early to release resources used by the de-compression as early as possible @@ -220,14 +208,12 @@ private PublishWithJoinResponse handleIncomingPublishRequest(BytesTransportReque return response; } } - } finally { - IOUtils.close(in); } } private PublishWithJoinResponse acceptState(ClusterState incomingState) { // if the state is coming from the current node, use original request instead (see currentPublishRequestToSelf for explanation) - if (transportService.getLocalNode().equals(incomingState.nodes().getMasterNode())) { + if (transportService.getLocalNode().equals(incomingState.nodes().getClusterManagerNode())) { final PublishRequest publishRequest = currentPublishRequestToSelf.get(); if (publishRequest == null || publishRequest.getAcceptedState().stateUUID().equals(incomingState.stateUUID()) == false) { throw new IllegalStateException("publication to self failed for " + publishRequest); @@ -249,13 +235,10 @@ public PublicationContext newPublicationContext(ClusterChangedEvent clusterChang } private static BytesReference serializeFullClusterState(ClusterState clusterState, Version nodeVersion) throws IOException { - final BytesStreamOutput bStream = new BytesStreamOutput(); - try (StreamOutput stream = new OutputStreamStreamOutput(CompressorFactory.COMPRESSOR.threadLocalOutputStream(bStream))) { - stream.setVersion(nodeVersion); + final BytesReference serializedState = CompressedStreamUtils.createCompressedStream(nodeVersion, stream -> { stream.writeBoolean(true); clusterState.writeTo(stream); - } - final BytesReference serializedState = bStream.bytes(); + }); logger.trace( "serialized full cluster state version [{}] for node version [{}] with size [{}]", clusterState.version(), @@ -266,19 +249,18 @@ private static BytesReference serializeFullClusterState(ClusterState clusterStat } private static BytesReference serializeDiffClusterState(Diff diff, Version nodeVersion) throws IOException { - final BytesStreamOutput bStream = new BytesStreamOutput(); - try (StreamOutput stream = new OutputStreamStreamOutput(CompressorFactory.COMPRESSOR.threadLocalOutputStream(bStream))) { - stream.setVersion(nodeVersion); + return CompressedStreamUtils.createCompressedStream(nodeVersion, stream -> { stream.writeBoolean(false); diff.writeTo(stream); - } - return bStream.bytes(); + }); } /** * Publishing a cluster state typically involves sending the same cluster state (or diff) to every node, so the work of diffing, * serializing, and compressing the state can be done once and the results shared across publish requests. The * {@code PublicationContext} implements this sharing. + * + * @opensearch.internal */ public class PublicationContext { @@ -337,8 +319,9 @@ public void sendPublishRequest( if (destination.equals(discoveryNodes.getLocalNode())) { // if publishing to self, use original request instead (see currentPublishRequestToSelf for explanation) final PublishRequest previousRequest = currentPublishRequestToSelf.getAndSet(publishRequest); - // we might override an in-flight publication to self in case where we failed as master and became master again, - // and the new publication started before the previous one completed (which fails anyhow because of higher current term) + // we might override an in-flight publication to self in case where we failed as cluster-manager and + // became cluster-manager again, and the new publication started before the previous one completed + // (which fails anyhow because of higher current term) assert previousRequest == null || previousRequest.getAcceptedState().term() < publishRequest.getAcceptedState().term(); responseActionListener = new ActionListener() { @Override diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PublishClusterStateStats.java b/server/src/main/java/org/opensearch/cluster/coordination/PublishClusterStateStats.java index 77320810eba4c..6a1dca0d27208 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PublishClusterStateStats.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PublishClusterStateStats.java @@ -32,16 +32,18 @@ package org.opensearch.cluster.coordination; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; import java.io.IOException; /** * Class encapsulating stats about the PublishClusterStateAction + * + * @opensearch.internal */ public class PublishClusterStateStats implements Writeable, ToXContentObject { @@ -50,8 +52,8 @@ public class PublishClusterStateStats implements Writeable, ToXContentObject { private final long compatibleClusterStateDiffReceivedCount; /** - * @param fullClusterStateReceivedCount the number of times this node has received a full copy of the cluster state from the master. - * @param incompatibleClusterStateDiffReceivedCount the number of times this node has received a cluster-state diff from the master. + * @param fullClusterStateReceivedCount the number of times this node has received a full copy of the cluster state from the cluster-manager. + * @param incompatibleClusterStateDiffReceivedCount the number of times this node has received a cluster-state diff from the cluster-manager. * @param compatibleClusterStateDiffReceivedCount the number of times that received cluster-state diffs were compatible with */ public PublishClusterStateStats( diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PublishRequest.java b/server/src/main/java/org/opensearch/cluster/coordination/PublishRequest.java index 76517573115fd..e7c3e2d2c965b 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PublishRequest.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PublishRequest.java @@ -36,8 +36,10 @@ import java.util.Objects; /** - * Request which is used by the master node to publish cluster state changes. + * Request which is used by the cluster-manager node to publish cluster state changes. * Actual serialization of this request is done by {@link PublicationTransportHandler} + * + * @opensearch.internal */ public class PublishRequest { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PublishResponse.java b/server/src/main/java/org/opensearch/cluster/coordination/PublishResponse.java index 9d219331c0d0e..23a60cb88283e 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PublishResponse.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PublishResponse.java @@ -31,15 +31,17 @@ package org.opensearch.cluster.coordination; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; /** * Response to a {@link PublishRequest}, carrying the term and version of the request. * Typically wrapped in a {@link PublishWithJoinResponse}. + * + * @opensearch.internal */ public class PublishResponse implements Writeable { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/PublishWithJoinResponse.java b/server/src/main/java/org/opensearch/cluster/coordination/PublishWithJoinResponse.java index 430e539fd3255..f6350c5558a82 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/PublishWithJoinResponse.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/PublishWithJoinResponse.java @@ -31,9 +31,9 @@ package org.opensearch.cluster.coordination; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.transport.TransportResponse; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.transport.TransportResponse; import java.io.IOException; import java.util.Optional; @@ -41,6 +41,8 @@ /** * Response to a {@link PublishRequest}. Encapsulates both a {@link PublishResponse} * and an optional {@link Join}. + * + * @opensearch.internal */ public class PublishWithJoinResponse extends TransportResponse { private final PublishResponse publishResponse; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/Reconfigurator.java b/server/src/main/java/org/opensearch/cluster/coordination/Reconfigurator.java index 26f289f5547d6..1570a84ab871f 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/Reconfigurator.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/Reconfigurator.java @@ -47,24 +47,26 @@ /** * Computes the optimal configuration of voting nodes in the cluster. + * + * @opensearch.internal */ public class Reconfigurator { private static final Logger logger = LogManager.getLogger(Reconfigurator.class); /** - * The cluster usually requires a vote from at least half of the master nodes in order to commit a cluster state update, and to achieve - * the best resilience it makes automatic adjustments to the voting configuration as master nodes join or leave the cluster. Adjustments + * The cluster usually requires a vote from at least half of the cluster-manager nodes in order to commit a cluster state update, and to achieve + * the best resilience it makes automatic adjustments to the voting configuration as cluster-manager nodes join or leave the cluster. Adjustments * that fix or increase the size of the voting configuration are always a good idea, but the wisdom of reducing the voting configuration * size is less clear. For instance, automatically reducing the voting configuration down to a single node means the cluster requires - * this node to operate, which is not resilient: if it broke we could restore every other master-eligible node in the cluster to health + * this node to operate, which is not resilient: if it broke we could restore every other cluster-manager-eligible node in the cluster to health * and still the cluster would be unavailable. However not reducing the voting configuration size can also hamper resilience: in a * five-node cluster we could lose two nodes and by reducing the voting configuration to the remaining three nodes we could tolerate the * loss of a further node before failing. * * We offer two options: either we auto-shrink the voting configuration as long as it contains more than three nodes, or we don't and we * require the user to control the voting configuration manually using the retirement API. The former, default, option, guarantees that - * as long as there have been at least three master-eligible nodes in the cluster and no more than one of them is currently unavailable, + * as long as there have been at least three cluster-manager-eligible nodes in the cluster and no more than one of them is currently unavailable, * then the cluster will still operate, which is what almost everyone wants. Manual control is for users who want different guarantees. */ public static final Setting CLUSTER_AUTO_SHRINK_VOTING_CONFIGURATION = Setting.boolSetting( @@ -102,39 +104,44 @@ public String toString() { * @param retiredNodeIds Nodes that are leaving the cluster and which should not appear in the configuration if possible. Nodes that are * retired and not in the current configuration will never appear in the resulting configuration; this is useful * for shifting the vote in a 2-node cluster so one of the nodes can be restarted without harming availability. - * @param currentMaster The current master. Unless retired, we prefer to keep the current master in the config. + * @param currentClusterManager The current cluster-manager. Unless retired, we prefer to keep the current cluster-manager in the config. * @param currentConfig The current configuration. As far as possible, we prefer to keep the current config as-is. * @return An optimal configuration, or leave the current configuration unchanged if the optimal configuration has no live quorum. */ public VotingConfiguration reconfigure( Set liveNodes, Set retiredNodeIds, - DiscoveryNode currentMaster, + DiscoveryNode currentClusterManager, VotingConfiguration currentConfig ) { - assert liveNodes.contains(currentMaster) : "liveNodes = " + liveNodes + " master = " + currentMaster; + assert liveNodes.contains(currentClusterManager) : "liveNodes = " + liveNodes + " cluster-manager = " + currentClusterManager; logger.trace( - "{} reconfiguring {} based on liveNodes={}, retiredNodeIds={}, currentMaster={}", + "{} reconfiguring {} based on liveNodes={}, retiredNodeIds={}, currentClusterManager={}", this, currentConfig, liveNodes, retiredNodeIds, - currentMaster + currentClusterManager ); final Set liveNodeIds = liveNodes.stream() - .filter(DiscoveryNode::isMasterNode) + .filter(DiscoveryNode::isClusterManagerNode) .map(DiscoveryNode::getId) .collect(Collectors.toSet()); final Set currentConfigNodeIds = currentConfig.getNodeIds(); final Set orderedCandidateNodes = new TreeSet<>(); liveNodes.stream() - .filter(DiscoveryNode::isMasterNode) + .filter(DiscoveryNode::isClusterManagerNode) .filter(n -> retiredNodeIds.contains(n.getId()) == false) .forEach( n -> orderedCandidateNodes.add( - new VotingConfigNode(n.getId(), true, n.getId().equals(currentMaster.getId()), currentConfigNodeIds.contains(n.getId())) + new VotingConfigNode( + n.getId(), + true, + n.getId().equals(currentClusterManager.getId()), + currentConfigNodeIds.contains(n.getId()) + ) ) ); currentConfigNodeIds.stream() @@ -163,25 +170,30 @@ public VotingConfiguration reconfigure( } } + /** + * A node to handle voting configs. + * + * @opensearch.internal + */ static class VotingConfigNode implements Comparable { final String id; final boolean live; - final boolean currentMaster; + final boolean currentClusterManager; final boolean inCurrentConfig; - VotingConfigNode(String id, boolean live, boolean currentMaster, boolean inCurrentConfig) { + VotingConfigNode(String id, boolean live, boolean currentClusterManager, boolean inCurrentConfig) { this.id = id; this.live = live; - this.currentMaster = currentMaster; + this.currentClusterManager = currentClusterManager; this.inCurrentConfig = inCurrentConfig; } @Override public int compareTo(VotingConfigNode other) { - // prefer current master - final int currentMasterComp = Boolean.compare(other.currentMaster, currentMaster); - if (currentMasterComp != 0) { - return currentMasterComp; + // prefer current cluster-manager + final int currentClusterManagerComp = Boolean.compare(other.currentClusterManager, currentClusterManager); + if (currentClusterManagerComp != 0) { + return currentClusterManagerComp; } // prefer nodes that are live final int liveComp = Boolean.compare(other.live, live); @@ -205,8 +217,8 @@ public String toString() { + '\'' + ", live=" + live - + ", currentMaster=" - + currentMaster + + ", currentClusterManager=" + + currentClusterManager + ", inCurrentConfig=" + inCurrentConfig + '}'; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/RemoveCustomsCommand.java b/server/src/main/java/org/opensearch/cluster/coordination/RemoveCustomsCommand.java index 83f3298862776..8dad8e3836635 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/RemoveCustomsCommand.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/RemoveCustomsCommand.java @@ -31,7 +31,6 @@ package org.opensearch.cluster.coordination; -import com.carrotsearch.hppc.cursors.ObjectCursor; import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.opensearch.cli.ExitCodes; @@ -48,6 +47,11 @@ import java.nio.file.Path; import java.util.List; +/** + * Removes custom metadata + * + * @opensearch.internal + */ public class RemoveCustomsCommand extends OpenSearchNodeCommand { static final String CUSTOMS_REMOVED_MSG = "Customs were successfully removed from the cluster state"; @@ -79,12 +83,11 @@ protected void processNodePaths(Terminal terminal, Path[] dataPaths, int nodeLoc terminal.println(Terminal.Verbosity.VERBOSE, "Loading cluster state"); final Tuple termAndClusterState = loadTermAndClusterState(persistedClusterStateService, env); final ClusterState oldClusterState = termAndClusterState.v2(); - terminal.println(Terminal.Verbosity.VERBOSE, "custom metadata names: " + oldClusterState.metadata().customs().keys()); + terminal.println(Terminal.Verbosity.VERBOSE, "custom metadata names: " + oldClusterState.metadata().customs().keySet()); final Metadata.Builder metadataBuilder = Metadata.builder(oldClusterState.metadata()); for (String customToRemove : customsToRemove) { boolean matched = false; - for (ObjectCursor customKeyCur : oldClusterState.metadata().customs().keys()) { - final String customKey = customKeyCur.value; + for (final String customKey : oldClusterState.metadata().customs().keySet()) { if (Regex.simpleMatch(customToRemove, customKey)) { metadataBuilder.removeCustom(customKey); if (matched == false) { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/RemoveSettingsCommand.java b/server/src/main/java/org/opensearch/cluster/coordination/RemoveSettingsCommand.java index 298dfde7858cd..da7adec6c4f11 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/RemoveSettingsCommand.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/RemoveSettingsCommand.java @@ -48,6 +48,11 @@ import java.nio.file.Path; import java.util.List; +/** + * Removes custom settings + * + * @opensearch.internal + */ public class RemoveSettingsCommand extends OpenSearchNodeCommand { static final String SETTINGS_REMOVED_MSG = "Settings were successfully removed from the cluster state"; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/StartJoinRequest.java b/server/src/main/java/org/opensearch/cluster/coordination/StartJoinRequest.java index efdbd37a9c0b6..de58eb721b28f 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/StartJoinRequest.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/StartJoinRequest.java @@ -32,8 +32,8 @@ package org.opensearch.cluster.coordination; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.transport.TransportRequest; import java.io.IOException; @@ -41,6 +41,8 @@ /** * Represents the action of requesting a join vote (see {@link Join}) from a node. * The source node represents the node that is asking for join votes. + * + * @opensearch.internal */ public class StartJoinRequest extends TransportRequest { diff --git a/server/src/main/java/org/opensearch/cluster/coordination/TermVersionRequest.java b/server/src/main/java/org/opensearch/cluster/coordination/TermVersionRequest.java index cea8c43302a23..81923f4211b7e 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/TermVersionRequest.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/TermVersionRequest.java @@ -32,13 +32,18 @@ package org.opensearch.cluster.coordination; import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.transport.TransportRequest; import java.io.IOException; +/** + * Get's the version of a term + * + * @opensearch.internal + */ abstract class TermVersionRequest extends TransportRequest implements Writeable { protected final DiscoveryNode sourceNode; protected final long term; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/UnsafeBootstrapClusterManagerCommand.java b/server/src/main/java/org/opensearch/cluster/coordination/UnsafeBootstrapClusterManagerCommand.java new file mode 100644 index 0000000000000..168ae5212888f --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/coordination/UnsafeBootstrapClusterManagerCommand.java @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.cluster.coordination; + +import joptsimple.OptionSet; +import joptsimple.OptionSpec; +import org.opensearch.OpenSearchException; +import org.opensearch.cli.Terminal; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.UUIDs; +import org.opensearch.common.collect.Tuple; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; +import org.opensearch.env.Environment; +import org.opensearch.gateway.PersistedClusterStateService; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Locale; +import java.util.Objects; + +import static org.opensearch.gateway.remote.RemoteClusterStateService.REMOTE_CLUSTER_STATE_ENABLED_SETTING; + +/** + * Tool to run an unsafe bootstrap + * + * @opensearch.internal + */ +public class UnsafeBootstrapClusterManagerCommand extends OpenSearchNodeCommand { + + static final String CLUSTER_STATE_TERM_VERSION_MSG_FORMAT = "Current node cluster state (term, version) pair is (%s, %s)"; + static final String CONFIRMATION_MSG = DELIMITER + + "\n" + + "You should only run this tool if you have permanently lost half or more\n" + + "of the cluster-manager-eligible nodes in this cluster, and you cannot restore the\n" + + "cluster from a snapshot. This tool can cause arbitrary data loss and its\n" + + "use should be your last resort. If you have multiple surviving cluster-manager\n" + + "eligible nodes, you should run this tool on the node with the highest\n" + + "cluster state (term, version) pair.\n" + + "\n" + + "Do you want to proceed?\n"; + + static final String NOT_CLUSTER_MANAGER_NODE_MSG = "unsafe-bootstrap tool can only be run on cluster-manager eligible node"; + + static final String EMPTY_LAST_COMMITTED_VOTING_CONFIG_MSG = + "last committed voting voting configuration is empty, cluster has never been bootstrapped?"; + + static final String CLUSTER_MANAGER_NODE_BOOTSTRAPPED_MSG = "Cluster-manager node was successfully bootstrapped"; + static final Setting UNSAFE_BOOTSTRAP = ClusterService.USER_DEFINED_METADATA.getConcreteSetting( + "cluster.metadata.unsafe-bootstrap" + ); + static final String REMOTE_CLUSTER_STATE_ENABLED_NODE = + "Unsafe bootstrap cannot be performed when remote cluster state is enabled. The cluster state in the remote store is considered the source of truth. " + + "In case, you still wish to do best effort recovery with unsafe-bootstrap, then please disable the " + + REMOTE_CLUSTER_STATE_ENABLED_SETTING.getKey() + + ". For more details, please check the OpenSearch documentation."; + private OptionSpec applyClusterReadOnlyBlockOption; + + UnsafeBootstrapClusterManagerCommand() { + super( + "Forces the successful election of the current node after the permanent loss of the half or more cluster-manager-eligible nodes" + ); + applyClusterReadOnlyBlockOption = parser.accepts("apply-cluster-read-only-block", "Optional cluster.blocks.read_only setting") + .withOptionalArg() + .ofType(Boolean.class); + } + + @Override + protected boolean validateBeforeLock(Terminal terminal, Environment env) { + Settings settings = env.settings(); + terminal.println(Terminal.Verbosity.VERBOSE, "Checking node.roles setting"); + Boolean clusterManager = DiscoveryNode.isClusterManagerNode(settings); + if (clusterManager == false) { + throw new OpenSearchException(NOT_CLUSTER_MANAGER_NODE_MSG); + } + // During unsafe bootstrap, node will form a cluster with a new cluster UUID but with the existing metadata. + // This new state will not know about the previous cluster UUIDs and so we will not able to construct + // the cluster UUID chain to get the last known cluster UUID to restore from. + // Blocking unsafe-bootstrap below for this reason. + if (REMOTE_CLUSTER_STATE_ENABLED_SETTING.get(settings) == true) { + throw new OpenSearchException(REMOTE_CLUSTER_STATE_ENABLED_NODE); + } + + return true; + } + + protected void processNodePaths(Terminal terminal, Path[] dataPaths, int nodeLockId, OptionSet options, Environment env) + throws IOException { + final PersistedClusterStateService persistedClusterStateService = createPersistedClusterStateService(env.settings(), dataPaths); + + final Tuple state = loadTermAndClusterState(persistedClusterStateService, env); + final ClusterState oldClusterState = state.v2(); + + final Metadata metadata = oldClusterState.metadata(); + + final CoordinationMetadata coordinationMetadata = metadata.coordinationMetadata(); + if (coordinationMetadata == null + || coordinationMetadata.getLastCommittedConfiguration() == null + || coordinationMetadata.getLastCommittedConfiguration().isEmpty()) { + throw new OpenSearchException(EMPTY_LAST_COMMITTED_VOTING_CONFIG_MSG); + } + terminal.println( + String.format(Locale.ROOT, CLUSTER_STATE_TERM_VERSION_MSG_FORMAT, coordinationMetadata.term(), metadata.version()) + ); + + CoordinationMetadata newCoordinationMetadata = CoordinationMetadata.builder(coordinationMetadata) + .clearVotingConfigExclusions() + .lastAcceptedConfiguration( + new CoordinationMetadata.VotingConfiguration(Collections.singleton(persistedClusterStateService.getNodeId())) + ) + .lastCommittedConfiguration( + new CoordinationMetadata.VotingConfiguration(Collections.singleton(persistedClusterStateService.getNodeId())) + ) + .build(); + + Settings persistentSettings = Settings.builder().put(metadata.persistentSettings()).put(UNSAFE_BOOTSTRAP.getKey(), true).build(); + + Boolean applyClusterReadOnlyBlock = applyClusterReadOnlyBlockOption.value(options); + if (Objects.nonNull(applyClusterReadOnlyBlock)) { + persistentSettings = Settings.builder() + .put(persistentSettings) + .put(Metadata.SETTING_READ_ONLY_SETTING.getKey(), applyClusterReadOnlyBlock) + .build(); + } + + Metadata.Builder newMetadata = Metadata.builder(metadata) + .clusterUUID(Metadata.UNKNOWN_CLUSTER_UUID) + .generateClusterUuidIfNeeded() + .clusterUUIDCommitted(true) + .persistentSettings(persistentSettings) + .coordinationMetadata(newCoordinationMetadata); + for (final IndexMetadata indexMetadata : metadata.indices().values()) { + newMetadata.put( + IndexMetadata.builder(indexMetadata) + .settings( + Settings.builder() + .put(indexMetadata.getSettings()) + .put(IndexMetadata.SETTING_HISTORY_UUID, UUIDs.randomBase64UUID()) + ) + ); + } + + final ClusterState newClusterState = ClusterState.builder(oldClusterState).metadata(newMetadata).build(); + + terminal.println( + Terminal.Verbosity.VERBOSE, + "[old cluster state = " + oldClusterState + ", new cluster state = " + newClusterState + "]" + ); + + confirm(terminal, CONFIRMATION_MSG); + + try (PersistedClusterStateService.Writer writer = persistedClusterStateService.createWriter()) { + writer.writeFullStateAndCommit(state.v1(), newClusterState); + } + + terminal.println(CLUSTER_MANAGER_NODE_BOOTSTRAPPED_MSG); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/coordination/UnsafeBootstrapMasterCommand.java b/server/src/main/java/org/opensearch/cluster/coordination/UnsafeBootstrapMasterCommand.java index e61b6448f6ac9..9a8bfd5ed1e35 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/UnsafeBootstrapMasterCommand.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/UnsafeBootstrapMasterCommand.java @@ -6,169 +6,19 @@ * compatible open source license. */ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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. - */ -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - package org.opensearch.cluster.coordination; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import joptsimple.OptionSet; -import joptsimple.OptionSpec; -import org.opensearch.OpenSearchException; -import org.opensearch.cli.Terminal; -import org.opensearch.cluster.ClusterState; -import org.opensearch.cluster.metadata.IndexMetadata; -import org.opensearch.cluster.metadata.Metadata; -import org.opensearch.cluster.node.DiscoveryNode; -import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.UUIDs; -import org.opensearch.common.collect.Tuple; -import org.opensearch.common.settings.Setting; -import org.opensearch.common.settings.Settings; -import org.opensearch.env.Environment; -import org.opensearch.gateway.PersistedClusterStateService; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Collections; -import java.util.Locale; -import java.util.Objects; - -public class UnsafeBootstrapMasterCommand extends OpenSearchNodeCommand { - - static final String CLUSTER_STATE_TERM_VERSION_MSG_FORMAT = "Current node cluster state (term, version) pair is (%s, %s)"; - static final String CONFIRMATION_MSG = DELIMITER - + "\n" - + "You should only run this tool if you have permanently lost half or more\n" - + "of the master-eligible nodes in this cluster, and you cannot restore the\n" - + "cluster from a snapshot. This tool can cause arbitrary data loss and its\n" - + "use should be your last resort. If you have multiple surviving master\n" - + "eligible nodes, you should run this tool on the node with the highest\n" - + "cluster state (term, version) pair.\n" - + "\n" - + "Do you want to proceed?\n"; - - static final String NOT_MASTER_NODE_MSG = "unsafe-bootstrap tool can only be run on master eligible node"; - - static final String EMPTY_LAST_COMMITTED_VOTING_CONFIG_MSG = - "last committed voting voting configuration is empty, cluster has never been bootstrapped?"; - - static final String MASTER_NODE_BOOTSTRAPPED_MSG = "Master node was successfully bootstrapped"; - static final Setting UNSAFE_BOOTSTRAP = ClusterService.USER_DEFINED_METADATA.getConcreteSetting( - "cluster.metadata.unsafe-bootstrap" - ); - - private OptionSpec applyClusterReadOnlyBlockOption; +/** + * Tool to run an unsafe bootstrap + * + * @opensearch.internal + * @deprecated As of 2.2, because supporting inclusive language, replaced by {@link UnsafeBootstrapClusterManagerCommand} + */ +@Deprecated +public class UnsafeBootstrapMasterCommand extends UnsafeBootstrapClusterManagerCommand { UnsafeBootstrapMasterCommand() { - super("Forces the successful election of the current node after the permanent loss of the half or more master-eligible nodes"); - applyClusterReadOnlyBlockOption = parser.accepts("apply-cluster-read-only-block", "Optional cluster.blocks.read_only setting") - .withOptionalArg() - .ofType(Boolean.class); + super(); } - @Override - protected boolean validateBeforeLock(Terminal terminal, Environment env) { - Settings settings = env.settings(); - terminal.println(Terminal.Verbosity.VERBOSE, "Checking node.master setting"); - Boolean master = DiscoveryNode.isMasterNode(settings); - if (master == false) { - throw new OpenSearchException(NOT_MASTER_NODE_MSG); - } - - return true; - } - - protected void processNodePaths(Terminal terminal, Path[] dataPaths, int nodeLockId, OptionSet options, Environment env) - throws IOException { - final PersistedClusterStateService persistedClusterStateService = createPersistedClusterStateService(env.settings(), dataPaths); - - final Tuple state = loadTermAndClusterState(persistedClusterStateService, env); - final ClusterState oldClusterState = state.v2(); - - final Metadata metadata = oldClusterState.metadata(); - - final CoordinationMetadata coordinationMetadata = metadata.coordinationMetadata(); - if (coordinationMetadata == null - || coordinationMetadata.getLastCommittedConfiguration() == null - || coordinationMetadata.getLastCommittedConfiguration().isEmpty()) { - throw new OpenSearchException(EMPTY_LAST_COMMITTED_VOTING_CONFIG_MSG); - } - terminal.println( - String.format(Locale.ROOT, CLUSTER_STATE_TERM_VERSION_MSG_FORMAT, coordinationMetadata.term(), metadata.version()) - ); - - CoordinationMetadata newCoordinationMetadata = CoordinationMetadata.builder(coordinationMetadata) - .clearVotingConfigExclusions() - .lastAcceptedConfiguration( - new CoordinationMetadata.VotingConfiguration(Collections.singleton(persistedClusterStateService.getNodeId())) - ) - .lastCommittedConfiguration( - new CoordinationMetadata.VotingConfiguration(Collections.singleton(persistedClusterStateService.getNodeId())) - ) - .build(); - - Settings persistentSettings = Settings.builder().put(metadata.persistentSettings()).put(UNSAFE_BOOTSTRAP.getKey(), true).build(); - - Boolean applyClusterReadOnlyBlock = applyClusterReadOnlyBlockOption.value(options); - if (Objects.nonNull(applyClusterReadOnlyBlock)) { - persistentSettings = Settings.builder() - .put(persistentSettings) - .put(Metadata.SETTING_READ_ONLY_SETTING.getKey(), applyClusterReadOnlyBlock) - .build(); - } - - Metadata.Builder newMetadata = Metadata.builder(metadata) - .clusterUUID(Metadata.UNKNOWN_CLUSTER_UUID) - .generateClusterUuidIfNeeded() - .clusterUUIDCommitted(true) - .persistentSettings(persistentSettings) - .coordinationMetadata(newCoordinationMetadata); - for (ObjectCursor idx : metadata.indices().values()) { - IndexMetadata indexMetadata = idx.value; - newMetadata.put( - IndexMetadata.builder(indexMetadata) - .settings( - Settings.builder() - .put(indexMetadata.getSettings()) - .put(IndexMetadata.SETTING_HISTORY_UUID, UUIDs.randomBase64UUID()) - ) - ); - } - - final ClusterState newClusterState = ClusterState.builder(oldClusterState).metadata(newMetadata).build(); - - terminal.println( - Terminal.Verbosity.VERBOSE, - "[old cluster state = " + oldClusterState + ", new cluster state = " + newClusterState + "]" - ); - - confirm(terminal, CONFIRMATION_MSG); - - try (PersistedClusterStateService.Writer writer = persistedClusterStateService.createWriter()) { - writer.writeFullStateAndCommit(state.v1(), newClusterState); - } - - terminal.println(MASTER_NODE_BOOTSTRAPPED_MSG); - } } diff --git a/server/src/main/java/org/opensearch/cluster/coordination/ValidateJoinRequest.java b/server/src/main/java/org/opensearch/cluster/coordination/ValidateJoinRequest.java index afdd124bddef2..34bbfc09d92dd 100644 --- a/server/src/main/java/org/opensearch/cluster/coordination/ValidateJoinRequest.java +++ b/server/src/main/java/org/opensearch/cluster/coordination/ValidateJoinRequest.java @@ -32,12 +32,17 @@ package org.opensearch.cluster.coordination; import org.opensearch.cluster.ClusterState; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import org.opensearch.transport.TransportRequest; import java.io.IOException; +/** + * Transport request to validate node join + * + * @opensearch.internal + */ public class ValidateJoinRequest extends TransportRequest { private ClusterState state; diff --git a/server/src/main/java/org/opensearch/cluster/coordination/package-info.java b/server/src/main/java/org/opensearch/cluster/coordination/package-info.java new file mode 100644 index 0000000000000..2f0514f25f6d6 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/coordination/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Coordination foundation classes. */ +package org.opensearch.cluster.coordination; diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java new file mode 100644 index 0000000000000..3f67870781580 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttribute.java @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; + +import java.io.IOException; +import java.util.Objects; + +/** + * {@link DecommissionAttribute} encapsulates information about decommissioned node attribute like attribute name, attribute value. + * + * @opensearch.internal + */ +public final class DecommissionAttribute implements Writeable { + private final String attributeName; + private final String attributeValue; + + /** + * Constructs new decommission attribute name value pair + * + * @param attributeName attribute name + * @param attributeValue attribute value + */ + public DecommissionAttribute(String attributeName, String attributeValue) { + this.attributeName = attributeName; + this.attributeValue = attributeValue; + } + + /** + * Returns attribute name + * + * @return attributeName + */ + public String attributeName() { + return this.attributeName; + } + + /** + * Returns attribute value + * + * @return attributeValue + */ + public String attributeValue() { + return this.attributeValue; + } + + public DecommissionAttribute(StreamInput in) throws IOException { + attributeName = in.readString(); + attributeValue = in.readString(); + } + + /** + * Writes decommission attribute name value to stream output + * + * @param out stream output + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(attributeName); + out.writeString(attributeValue); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DecommissionAttribute that = (DecommissionAttribute) o; + + if (!attributeName.equals(that.attributeName)) return false; + return attributeValue.equals(that.attributeValue); + } + + @Override + public int hashCode() { + return Objects.hash(attributeName, attributeValue); + } + + @Override + public String toString() { + return "DecommissionAttribute{" + "attributeName='" + attributeName + '\'' + ", attributeValue='" + attributeValue + '\'' + '}'; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java new file mode 100644 index 0000000000000..3df807a4f94d3 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionAttributeMetadata.java @@ -0,0 +1,287 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.Version; +import org.opensearch.cluster.AbstractNamedDiffable; +import org.opensearch.cluster.NamedDiff; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.metadata.Metadata.Custom; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.Objects; +import java.util.Set; + +/** + * Contains metadata about decommission attribute + * + * @opensearch.internal + */ +public class DecommissionAttributeMetadata extends AbstractNamedDiffable implements Custom { + + public static final String TYPE = "decommissionedAttribute"; + + private final DecommissionAttribute decommissionAttribute; + private DecommissionStatus status; + private String requestID; + public static final String attributeType = "awareness"; + + /** + * Constructs new decommission attribute metadata with given status + * + * @param decommissionAttribute attribute details + * @param status current status of the attribute decommission + */ + public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute, DecommissionStatus status, String requestId) { + this.decommissionAttribute = decommissionAttribute; + this.status = status; + this.requestID = requestId; + } + + /** + * Constructs new decommission attribute metadata with status as {@link DecommissionStatus#INIT} and request id + * + * @param decommissionAttribute attribute details + */ + public DecommissionAttributeMetadata(DecommissionAttribute decommissionAttribute, String requestID) { + this(decommissionAttribute, DecommissionStatus.INIT, requestID); + } + + /** + * Returns the current decommissioned attribute + * + * @return decommissioned attributes + */ + public DecommissionAttribute decommissionAttribute() { + return this.decommissionAttribute; + } + + /** + * Returns the current status of the attribute decommission + * + * @return attribute type + */ + public DecommissionStatus status() { + return this.status; + } + + /** + * Returns the request id of the decommission + * + * @return request id + */ + public String requestID() { + return this.requestID; + } + + /** + * Returns instance of the metadata with updated status + * @param newStatus status to be updated with + */ + // synchronized is strictly speaking not needed (this is called by a single thread), but just to be safe + public synchronized void validateNewStatus(DecommissionStatus newStatus) { + // if the current status is the expected status already or new status is FAILED, we let the check pass + if (newStatus.equals(status) || newStatus.equals(DecommissionStatus.FAILED)) { + return; + } + // We don't expect that INIT will be new status, as it is registered only when starting the decommission action + switch (newStatus) { + case DRAINING: + validateStatus(Set.of(DecommissionStatus.INIT), newStatus); + break; + case IN_PROGRESS: + validateStatus(Set.of(DecommissionStatus.DRAINING, DecommissionStatus.INIT), newStatus); + break; + case SUCCESSFUL: + validateStatus(Set.of(DecommissionStatus.IN_PROGRESS), newStatus); + break; + default: + throw new IllegalArgumentException( + "illegal decommission status [" + newStatus.status() + "] requested for updating metadata" + ); + } + } + + private void validateStatus(Set expectedStatuses, DecommissionStatus next) { + if (expectedStatuses.contains(status) == false) { + assert false : "can't move decommission status to [" + + next + + "]. current status: [" + + status + + "] (allowed statuses [" + + expectedStatuses + + "])"; + throw new IllegalStateException( + "can't move decommission status to [" + next + "]. current status: [" + status + "] (expected [" + expectedStatuses + "])" + ); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DecommissionAttributeMetadata that = (DecommissionAttributeMetadata) o; + + if (!status.equals(that.status)) return false; + if (!requestID.equals(that.requestID)) return false; + return decommissionAttribute.equals(that.decommissionAttribute); + } + + @Override + public int hashCode() { + return Objects.hash(attributeType, decommissionAttribute, status, requestID); + } + + /** + * {@inheritDoc} + */ + @Override + public String getWriteableName() { + return TYPE; + } + + @Override + public Version getMinimalSupportedVersion() { + return Version.V_2_4_0; + } + + public DecommissionAttributeMetadata(StreamInput in) throws IOException { + this.decommissionAttribute = new DecommissionAttribute(in); + this.status = DecommissionStatus.fromString(in.readString()); + this.requestID = in.readString(); + } + + public static NamedDiff readDiffFrom(StreamInput in) throws IOException { + return readDiffFrom(Custom.class, TYPE, in); + } + + /** + * {@inheritDoc} + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + decommissionAttribute.writeTo(out); + out.writeString(status.status()); + out.writeString(requestID); + } + + public static DecommissionAttributeMetadata fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token; + DecommissionAttribute decommissionAttribute = null; + DecommissionStatus status = null; + String requestID = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String currentFieldName = parser.currentName(); + if (attributeType.equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new OpenSearchParseException( + "failed to parse decommission attribute type [{}], expected object", + attributeType + ); + } + token = parser.nextToken(); + if (token != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String fieldName = parser.currentName(); + String value; + token = parser.nextToken(); + if (token == XContentParser.Token.VALUE_STRING) { + value = parser.text(); + } else { + throw new OpenSearchParseException( + "failed to parse attribute [{}], expected string for attribute value", + fieldName + ); + } + decommissionAttribute = new DecommissionAttribute(fieldName, value); + parser.nextToken(); + } else { + throw new OpenSearchParseException("failed to parse attribute type [{}], unexpected type", attributeType); + } + } else { + throw new OpenSearchParseException("failed to parse attribute type [{}]", attributeType); + } + } else if ("status".equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new OpenSearchParseException( + "failed to parse status of decommissioning, expected string but found unknown type" + ); + } + status = DecommissionStatus.fromString(parser.text()); + } else if ("requestID".equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new OpenSearchParseException( + "failed to parse status of decommissioning, expected string but found unknown type" + ); + } + requestID = parser.text(); + } else { + throw new OpenSearchParseException( + "unknown field found [{}], failed to parse the decommission attribute", + currentFieldName + ); + } + } + } + return new DecommissionAttributeMetadata(decommissionAttribute, status, requestID); + } + + /** + * {@inheritDoc} + */ + @Override + public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { + toXContent(decommissionAttribute, status, requestID, attributeType, builder, params); + return builder; + } + + @Override + public EnumSet context() { + return Metadata.API_AND_GATEWAY; + } + + /** + * @param decommissionAttribute decommission attribute + * @param status decommission status + * @param attributeType attribute type + * @param builder XContent builder + * @param params serialization parameters + */ + public static void toXContent( + DecommissionAttribute decommissionAttribute, + DecommissionStatus status, + String requestID, + String attributeType, + XContentBuilder builder, + ToXContent.Params params + ) throws IOException { + builder.startObject(attributeType); + builder.field(decommissionAttribute.attributeName(), decommissionAttribute.attributeValue()); + builder.endObject(); + builder.field("status", status.status()); + builder.field("requestID", requestID); + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java new file mode 100644 index 0000000000000..fec313b4b0b73 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionController.java @@ -0,0 +1,263 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.OpenSearchTimeoutException; +import org.opensearch.action.admin.cluster.node.stats.NodeStats; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsAction; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; +import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.ClusterStateTaskConfig; +import org.opensearch.cluster.ClusterStateTaskListener; +import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.coordination.NodeRemovalClusterStateTaskExecutor; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.Priority; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.http.HttpStats; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportResponseHandler; +import org.opensearch.transport.TransportService; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.opensearch.action.admin.cluster.configuration.VotingConfigExclusionsHelper.clearExclusionsAndGetState; + +/** + * Helper controller class to remove list of nodes from the cluster and update status + * + * @opensearch.internal + */ + +public class DecommissionController { + + private static final Logger logger = LogManager.getLogger(DecommissionController.class); + + private final NodeRemovalClusterStateTaskExecutor nodeRemovalExecutor; + private final ClusterService clusterService; + private final TransportService transportService; + private final ThreadPool threadPool; + + DecommissionController( + ClusterService clusterService, + TransportService transportService, + AllocationService allocationService, + ThreadPool threadPool + ) { + this.clusterService = clusterService; + this.transportService = transportService; + this.nodeRemovalExecutor = new NodeRemovalClusterStateTaskExecutor(allocationService, logger); + this.threadPool = threadPool; + } + + /** + * This method triggers batch of tasks for nodes to be decommissioned using executor {@link NodeRemovalClusterStateTaskExecutor} + * Once the tasks are submitted, it waits for an expected cluster state to guarantee + * that the expected decommissioned nodes are removed from the cluster + * + * @param nodesToBeDecommissioned set of the node to be decommissioned + * @param reason reason of removal + * @param timeout timeout for the request + * @param nodesRemovedListener callback for the success or failure + */ + public synchronized void removeDecommissionedNodes( + Set nodesToBeDecommissioned, + String reason, + TimeValue timeout, + ActionListener nodesRemovedListener + ) { + final Map nodesDecommissionTasks = new LinkedHashMap<>( + nodesToBeDecommissioned.size() + ); + nodesToBeDecommissioned.forEach(discoveryNode -> { + final NodeRemovalClusterStateTaskExecutor.Task task = new NodeRemovalClusterStateTaskExecutor.Task(discoveryNode, reason); + nodesDecommissionTasks.put(task, nodeRemovalExecutor); + }); + + logger.info("submitting state update task to remove [{}] nodes due to decommissioning", nodesToBeDecommissioned.toString()); + clusterService.submitStateUpdateTasks( + "node-decommissioned", + nodesDecommissionTasks, + ClusterStateTaskConfig.build(Priority.URGENT), + nodeRemovalExecutor + ); + + Predicate allDecommissionedNodesRemovedPredicate = clusterState -> { + Set intersection = Arrays.stream(clusterState.nodes().getNodes().values().toArray(new DiscoveryNode[0])) + .collect(Collectors.toSet()); + intersection.retainAll(nodesToBeDecommissioned); + return intersection.size() == 0; + }; + + final ClusterStateObserver observer = new ClusterStateObserver(clusterService, timeout, logger, threadPool.getThreadContext()); + + final ClusterStateObserver.Listener removalListener = new ClusterStateObserver.Listener() { + @Override + public void onNewClusterState(ClusterState state) { + logger.info("successfully removed all decommissioned nodes [{}] from the cluster", nodesToBeDecommissioned.toString()); + nodesRemovedListener.onResponse(null); + } + + @Override + public void onClusterServiceClose() { + logger.warn( + "cluster service closed while waiting for removal of decommissioned nodes [{}]", + nodesToBeDecommissioned.toString() + ); + } + + @Override + public void onTimeout(TimeValue timeout) { + logger.info( + "timed out [{}] while waiting for removal of decommissioned nodes [{}]", + timeout.toString(), + nodesToBeDecommissioned.toString() + ); + nodesRemovedListener.onFailure( + new OpenSearchTimeoutException( + "timed out [{}] while waiting for removal of decommissioned nodes [{}]", + timeout.toString(), + nodesToBeDecommissioned.toString() + ) + ); + } + }; + + if (allDecommissionedNodesRemovedPredicate.test(clusterService.getClusterApplierService().state())) { + removalListener.onNewClusterState(clusterService.getClusterApplierService().state()); + } else { + observer.waitForNextChange(removalListener, allDecommissionedNodesRemovedPredicate); + } + } + + /** + * This method updates the status in the currently registered metadata. + * + * @param decommissionStatus status to update decommission metadata with + * @param listener listener for response and failure + */ + public void updateMetadataWithDecommissionStatus(DecommissionStatus decommissionStatus, ActionListener listener) { + clusterService.submitStateUpdateTask("update-decommission-status", new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) { + DecommissionAttributeMetadata decommissionAttributeMetadata = currentState.metadata().decommissionAttributeMetadata(); + assert decommissionAttributeMetadata != null && decommissionAttributeMetadata.decommissionAttribute() != null; + logger.info( + "attempting to update current decommission status [{}] with expected status [{}]", + decommissionAttributeMetadata.status(), + decommissionStatus + ); + // validateNewStatus can throw IllegalStateException if the sequence of update is not valid + decommissionAttributeMetadata.validateNewStatus(decommissionStatus); + decommissionAttributeMetadata = new DecommissionAttributeMetadata( + decommissionAttributeMetadata.decommissionAttribute(), + decommissionStatus, + decommissionAttributeMetadata.requestID() + ); + ClusterState newState = ClusterState.builder(currentState) + .metadata(Metadata.builder(currentState.metadata()).decommissionAttributeMetadata(decommissionAttributeMetadata)) + .build(); + + // For terminal status we will go ahead and clear any exclusion that was added as part of decommission action + if (decommissionStatus.equals(DecommissionStatus.SUCCESSFUL) || decommissionStatus.equals(DecommissionStatus.FAILED)) { + newState = clearExclusionsAndGetState(newState); + } + return newState; + } + + @Override + public void onFailure(String source, Exception e) { + listener.onFailure(e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().decommissionAttributeMetadata(); + assert decommissionAttributeMetadata != null; + assert decommissionAttributeMetadata.status().equals(decommissionStatus); + listener.onResponse(decommissionAttributeMetadata.status()); + } + }); + } + + private void logActiveConnections(NodesStatsResponse nodesStatsResponse) { + if (nodesStatsResponse == null || nodesStatsResponse.getNodes() == null) { + logger.info("Node stats response received is null/empty."); + return; + } + + Map nodeActiveConnectionMap = new HashMap<>(); + List responseNodes = nodesStatsResponse.getNodes(); + for (int i = 0; i < responseNodes.size(); i++) { + HttpStats httpStats = responseNodes.get(i).getHttp(); + DiscoveryNode node = responseNodes.get(i).getNode(); + nodeActiveConnectionMap.put(node.getId(), httpStats.getServerOpen()); + } + logger.info("Decommissioning node with connections : [{}]", nodeActiveConnectionMap); + } + + void getActiveRequestCountOnDecommissionedNodes(Set decommissionedNodes) { + if (decommissionedNodes == null || decommissionedNodes.isEmpty()) { + return; + } + String[] nodes = decommissionedNodes.stream().map(DiscoveryNode::getId).toArray(String[]::new); + if (nodes.length == 0) { + return; + } + + final NodesStatsRequest nodesStatsRequest = new NodesStatsRequest(nodes); + nodesStatsRequest.clear(); + nodesStatsRequest.addMetric(NodesStatsRequest.Metric.HTTP.metricName()); + + transportService.sendRequest( + transportService.getLocalNode(), + NodesStatsAction.NAME, + nodesStatsRequest, + new TransportResponseHandler() { + @Override + public void handleResponse(NodesStatsResponse response) { + logActiveConnections(response); + } + + @Override + public void handleException(TransportException exp) { + logger.error("Failure occurred while dumping connection for decommission nodes - ", exp.unwrapCause()); + } + + @Override + public String executor() { + return ThreadPool.Names.SAME; + } + + @Override + public NodesStatsResponse read(StreamInput in) throws IOException { + return new NodesStatsResponse(in); + } + } + ); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java new file mode 100644 index 0000000000000..4cd65e4f32453 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionHelper.java @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.action.admin.cluster.configuration.AddVotingConfigExclusionsRequest; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.coordination.CoordinationMetadata.VotingConfigExclusion; +import org.opensearch.cluster.metadata.Metadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.common.Strings; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import static org.opensearch.action.admin.cluster.configuration.VotingConfigExclusionsHelper.addExclusionAndGetState; +import static org.opensearch.action.admin.cluster.configuration.VotingConfigExclusionsHelper.resolveVotingConfigExclusionsAndCheckMaximum; + +/** + * Static helper utilities to execute decommission + * + * @opensearch.internal + */ +public class DecommissionHelper { + + static ClusterState registerDecommissionAttributeInClusterState( + ClusterState currentState, + DecommissionAttribute decommissionAttribute, + String requestID + ) { + DecommissionAttributeMetadata decommissionAttributeMetadata = new DecommissionAttributeMetadata(decommissionAttribute, requestID); + return ClusterState.builder(currentState) + .metadata(Metadata.builder(currentState.metadata()).decommissionAttributeMetadata(decommissionAttributeMetadata)) + .build(); + } + + static ClusterState deleteDecommissionAttributeInClusterState(ClusterState currentState) { + Metadata metadata = currentState.metadata(); + Metadata.Builder mdBuilder = Metadata.builder(metadata); + mdBuilder.removeCustom(DecommissionAttributeMetadata.TYPE); + return ClusterState.builder(currentState).metadata(mdBuilder).build(); + } + + static ClusterState addVotingConfigExclusionsForNodesToBeDecommissioned( + ClusterState currentState, + Set nodeIdsToBeExcluded, + TimeValue decommissionActionTimeout, + final int maxVotingConfigExclusions + ) { + AddVotingConfigExclusionsRequest request = new AddVotingConfigExclusionsRequest( + Strings.EMPTY_ARRAY, + nodeIdsToBeExcluded.toArray(String[]::new), + Strings.EMPTY_ARRAY, + decommissionActionTimeout + ); + Set resolvedExclusion = resolveVotingConfigExclusionsAndCheckMaximum( + request, + currentState, + maxVotingConfigExclusions + ); + return addExclusionAndGetState(currentState, resolvedExclusion, maxVotingConfigExclusions); + } + + static Set filterNodesWithDecommissionAttribute( + ClusterState clusterState, + DecommissionAttribute decommissionAttribute, + boolean onlyClusterManagerNodes + ) { + Set nodesWithDecommissionAttribute = new HashSet<>(); + Iterator nodesIter = onlyClusterManagerNodes + ? clusterState.nodes().getClusterManagerNodes().values().iterator() + : clusterState.nodes().getNodes().values().iterator(); + + while (nodesIter.hasNext()) { + final DiscoveryNode node = nodesIter.next(); + if (nodeHasDecommissionedAttribute(node, decommissionAttribute)) { + nodesWithDecommissionAttribute.add(node); + } + } + return nodesWithDecommissionAttribute; + } + + /** + * Utility method to check if the node has decommissioned attribute + * + * @param discoveryNode node to check on + * @param decommissionAttribute attribute to be checked with + * @return true or false based on whether node has decommissioned attribute + */ + public static boolean nodeHasDecommissionedAttribute(DiscoveryNode discoveryNode, DecommissionAttribute decommissionAttribute) { + String nodeAttributeValue = discoveryNode.getAttributes().get(decommissionAttribute.attributeName()); + return nodeAttributeValue != null && nodeAttributeValue.equals(decommissionAttribute.attributeValue()); + } + + /** + * Utility method to check if the node is commissioned or not + * + * @param discoveryNode node to check on + * @param metadata metadata present current which will be used to check the commissioning status of the node + * @return if the node is commissioned or not + */ + public static boolean nodeCommissioned(DiscoveryNode discoveryNode, Metadata metadata) { + DecommissionAttributeMetadata decommissionAttributeMetadata = metadata.decommissionAttributeMetadata(); + if (decommissionAttributeMetadata != null) { + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + DecommissionStatus status = decommissionAttributeMetadata.status(); + if (decommissionAttribute != null && status != null) { + if (nodeHasDecommissionedAttribute(discoveryNode, decommissionAttribute) + && (status.equals(DecommissionStatus.IN_PROGRESS) + || status.equals(DecommissionStatus.SUCCESSFUL) + || status.equals(DecommissionStatus.DRAINING))) { + return false; + } + } + } + return true; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java new file mode 100644 index 0000000000000..7ff894afa21a7 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionService.java @@ -0,0 +1,572 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.opensearch.OpenSearchTimeoutException; +import org.opensearch.action.admin.cluster.decommission.awareness.delete.DeleteDecommissionStateResponse; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionRequest; +import org.opensearch.action.admin.cluster.decommission.awareness.put.DecommissionResponse; +import org.opensearch.cluster.ClusterState; +import org.opensearch.cluster.ClusterStateObserver; +import org.opensearch.cluster.ClusterStateObserver.Listener; +import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.NotClusterManagerException; +import org.opensearch.cluster.coordination.CoordinationMetadata; +import org.opensearch.cluster.metadata.WeightedRoutingMetadata; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.routing.WeightedRouting; +import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.Priority; +import org.opensearch.common.UUIDs; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.core.action.ActionListener; +import org.opensearch.threadpool.ThreadPool; +import org.opensearch.transport.TransportService; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.opensearch.action.admin.cluster.configuration.TransportAddVotingConfigExclusionsAction.MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING; +import static org.opensearch.action.admin.cluster.configuration.VotingConfigExclusionsHelper.clearExclusionsAndGetState; +import static org.opensearch.cluster.decommission.DecommissionHelper.addVotingConfigExclusionsForNodesToBeDecommissioned; +import static org.opensearch.cluster.decommission.DecommissionHelper.deleteDecommissionAttributeInClusterState; +import static org.opensearch.cluster.decommission.DecommissionHelper.filterNodesWithDecommissionAttribute; +import static org.opensearch.cluster.decommission.DecommissionHelper.nodeHasDecommissionedAttribute; +import static org.opensearch.cluster.decommission.DecommissionHelper.registerDecommissionAttributeInClusterState; +import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING; +import static org.opensearch.cluster.routing.allocation.decider.AwarenessAllocationDecider.CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING; + +/** + * Service responsible for entire lifecycle of decommissioning and recommissioning an awareness attribute. + *

    + * Whenever a cluster manager initiates operation to decommission an awareness attribute, + * the service makes the best attempt to perform the following task - + *

      + *
    • Initiates nodes decommissioning by adding custom metadata with the attribute and state as {@link DecommissionStatus#INIT}
    • + *
    • Remove to-be-decommissioned cluster-manager eligible nodes from voting config and wait for its abdication if it is active leader
    • + *
    • After the draining timeout, the service triggers nodes decommission. This marks the decommission status as {@link DecommissionStatus#IN_PROGRESS}
    • + *
    • Once the decommission is successful, the service clears the voting config and marks the status as {@link DecommissionStatus#SUCCESSFUL}
    • + *
    • If service fails at any step, it makes best attempt to mark the status as {@link DecommissionStatus#FAILED} and to clear voting config exclusion
    • + *
    + * + * @opensearch.internal + */ +public class DecommissionService { + + private static final Logger logger = LogManager.getLogger(DecommissionService.class); + + private final ClusterService clusterService; + private final TransportService transportService; + private final ThreadPool threadPool; + private final DecommissionController decommissionController; + private volatile List awarenessAttributes; + private volatile Map> forcedAwarenessAttributes; + private volatile int maxVotingConfigExclusions; + + @Inject + public DecommissionService( + Settings settings, + ClusterSettings clusterSettings, + ClusterService clusterService, + TransportService transportService, + ThreadPool threadPool, + AllocationService allocationService + ) { + this.clusterService = clusterService; + this.transportService = transportService; + this.threadPool = threadPool; + this.decommissionController = new DecommissionController(clusterService, transportService, allocationService, threadPool); + this.awarenessAttributes = CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING.get(settings); + clusterSettings.addSettingsUpdateConsumer(CLUSTER_ROUTING_ALLOCATION_AWARENESS_ATTRIBUTE_SETTING, this::setAwarenessAttributes); + + setForcedAwarenessAttributes(CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING.get(settings)); + clusterSettings.addSettingsUpdateConsumer( + CLUSTER_ROUTING_ALLOCATION_AWARENESS_FORCE_GROUP_SETTING, + this::setForcedAwarenessAttributes + ); + maxVotingConfigExclusions = MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING.get(settings); + clusterSettings.addSettingsUpdateConsumer(MAXIMUM_VOTING_CONFIG_EXCLUSIONS_SETTING, this::setMaxVotingConfigExclusions); + } + + private void setAwarenessAttributes(List awarenessAttributes) { + this.awarenessAttributes = awarenessAttributes; + } + + private void setForcedAwarenessAttributes(Settings forceSettings) { + Map> forcedAwarenessAttributes = new HashMap<>(); + Map forceGroups = forceSettings.getAsGroups(); + for (Map.Entry entry : forceGroups.entrySet()) { + List aValues = entry.getValue().getAsList("values"); + if (aValues.size() > 0) { + forcedAwarenessAttributes.put(entry.getKey(), aValues); + } + } + this.forcedAwarenessAttributes = forcedAwarenessAttributes; + } + + private void setMaxVotingConfigExclusions(int maxVotingConfigExclusions) { + this.maxVotingConfigExclusions = maxVotingConfigExclusions; + } + + /** + * Starts the new decommission request and registers the metadata with status as {@link DecommissionStatus#INIT} + * Once the status is updated, it tries to exclude to-be-decommissioned cluster manager eligible nodes from Voting Configuration + * + * @param decommissionRequest request for decommission action + * @param listener register decommission listener + */ + public void startDecommissionAction( + final DecommissionRequest decommissionRequest, + final ActionListener listener + ) { + final DecommissionAttribute decommissionAttribute = decommissionRequest.getDecommissionAttribute(); + // register the metadata with status as INIT as first step + clusterService.submitStateUpdateTask("decommission [" + decommissionAttribute + "]", new ClusterStateUpdateTask(Priority.URGENT) { + private Set nodeIdsToBeExcluded; + + @Override + public ClusterState execute(ClusterState currentState) { + // validates if correct awareness attributes and forced awareness attribute set to the cluster before starting action + validateAwarenessAttribute(decommissionAttribute, awarenessAttributes, forcedAwarenessAttributes); + if (decommissionRequest.requestID() == null) { + decommissionRequest.setRequestID(UUIDs.randomBase64UUID()); + } + DecommissionAttributeMetadata decommissionAttributeMetadata = currentState.metadata().decommissionAttributeMetadata(); + // check that request is eligible to proceed and attribute is weighed away + ensureEligibleRequest(decommissionAttributeMetadata, decommissionRequest); + ensureToBeDecommissionedAttributeWeighedAway(currentState, decommissionAttribute); + + ClusterState newState = registerDecommissionAttributeInClusterState( + currentState, + decommissionAttribute, + decommissionRequest.requestID() + ); + // add all 'to-be-decommissioned' cluster manager eligible nodes to voting config exclusion + nodeIdsToBeExcluded = filterNodesWithDecommissionAttribute(currentState, decommissionAttribute, true).stream() + .map(DiscoveryNode::getId) + .collect(Collectors.toSet()); + logger.info( + "resolved cluster manager eligible nodes [{}] that should be added to voting config exclusion", + nodeIdsToBeExcluded.toString() + ); + newState = addVotingConfigExclusionsForNodesToBeDecommissioned( + newState, + nodeIdsToBeExcluded, + TimeValue.timeValueSeconds(120), // TODO - update it with request timeout + maxVotingConfigExclusions + ); + logger.debug( + "registering decommission metadata [{}] to execute action", + newState.metadata().decommissionAttributeMetadata().toString() + ); + return newState; + } + + @Override + public void onFailure(String source, Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to start decommission action for attribute [{}]", + decommissionAttribute.toString() + ), + e + ); + listener.onFailure(e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + DecommissionAttributeMetadata decommissionAttributeMetadata = newState.metadata().decommissionAttributeMetadata(); + assert decommissionAttribute.equals(decommissionAttributeMetadata.decommissionAttribute()); + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.INIT); + assert decommissionAttributeMetadata.requestID().equals(decommissionRequest.requestID()); + assert newState.getVotingConfigExclusions() + .stream() + .map(CoordinationMetadata.VotingConfigExclusion::getNodeId) + .collect(Collectors.toSet()) + .containsAll(nodeIdsToBeExcluded); + logger.debug( + "registered decommission metadata for attribute [{}] with status [{}]", + decommissionAttributeMetadata.decommissionAttribute(), + decommissionAttributeMetadata.status() + ); + + final ClusterStateObserver observer = new ClusterStateObserver( + clusterService, + TimeValue.timeValueSeconds(120), // TODO - update it with request timeout + logger, + threadPool.getThreadContext() + ); + + final Predicate allNodesRemovedAndAbdicated = clusterState -> { + final Set votingConfigNodeIds = clusterState.getLastCommittedConfiguration().getNodeIds(); + return nodeIdsToBeExcluded.stream().noneMatch(votingConfigNodeIds::contains) + && clusterState.nodes().getClusterManagerNodeId() != null + && nodeIdsToBeExcluded.contains(clusterState.nodes().getClusterManagerNodeId()) == false; + }; + + final Listener clusterStateListener = new Listener() { + @Override + public void onNewClusterState(ClusterState state) { + logger.info( + "successfully removed decommissioned cluster manager eligible nodes [{}] from voting config ", + nodeIdsToBeExcluded.toString() + ); + if (state.nodes().isLocalNodeElectedClusterManager()) { + if (nodeHasDecommissionedAttribute(clusterService.localNode(), decommissionAttribute)) { + // this is an unexpected state, as after exclusion of nodes having decommission attribute, + // this local node shouldn't have had the decommission attribute. Will send the failure response to the user + String errorMsg = + "unexpected state encountered [local node is to-be-decommissioned leader] while executing decommission request"; + logger.error(errorMsg); + // will go ahead and clear the voting config and mark the status as failed + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.FAILED, + statusUpdateListener() + ); + listener.onFailure(new IllegalStateException(errorMsg)); + } else { + logger.info("will proceed to drain decommissioned nodes as local node is eligible to process the request"); + // we are good here to send the response now as the request is processed by an eligible active leader + // and to-be-decommissioned cluster manager is no more part of Voting Configuration + listener.onResponse(new DecommissionResponse(true)); + drainNodesWithDecommissionedAttribute(decommissionRequest); + } + } else { + // explicitly calling listener.onFailure with NotClusterManagerException as the local node is not leader + // this will ensures that request is retried until cluster manager times out + logger.info( + "local node is not eligible to process the request, " + + "throwing NotClusterManagerException to attempt a retry on an eligible node" + ); + listener.onFailure( + new NotClusterManagerException( + "node [" + + transportService.getLocalNode().toString() + + "] not eligible to execute decommission request. Will retry until timeout." + ) + ); + } + } + + @Override + public void onClusterServiceClose() { + String errorMsg = "cluster service closed while waiting for abdication of to-be-decommissioned leader"; + logger.error(errorMsg); + listener.onFailure(new DecommissioningFailedException(decommissionAttribute, errorMsg)); + } + + @Override + public void onTimeout(TimeValue timeout) { + String errorMsg = "timed out [" + + timeout.toString() + + "] while removing to-be-decommissioned cluster manager eligible nodes [" + + nodeIdsToBeExcluded.toString() + + "] from voting config"; + logger.error(errorMsg); + listener.onFailure(new OpenSearchTimeoutException(errorMsg)); + // will go ahead and clear the voting config and mark the status as failed + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); + } + }; + + // In case the cluster state is already processed even before this code is executed + // therefore testing first before attaching the listener + if (allNodesRemovedAndAbdicated.test(newState)) { + clusterStateListener.onNewClusterState(newState); + } else { + logger.debug("waiting to abdicate to-be-decommissioned leader"); + observer.waitForNextChange(clusterStateListener, allNodesRemovedAndAbdicated); // TODO add request timeout here + } + } + }); + } + + // TODO - after registering the new status check if any node which is not excluded still present in decommissioned zone. If yes, start + // the action again (retry) + void drainNodesWithDecommissionedAttribute(DecommissionRequest decommissionRequest) { + ClusterState state = clusterService.getClusterApplierService().state(); + assert state.metadata().decommissionAttributeMetadata().requestID().equals(decommissionRequest.requestID()); + Set decommissionedNodes = filterNodesWithDecommissionAttribute( + state, + decommissionRequest.getDecommissionAttribute(), + false + ); + + if (decommissionRequest.isNoDelay()) { + // Call to fail the decommission nodes + failDecommissionedNodes(decommissionedNodes, decommissionRequest.getDecommissionAttribute()); + } else { + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.DRAINING, new ActionListener<>() { + @Override + public void onResponse(DecommissionStatus status) { + logger.info("updated the decommission status to [{}]", status); + // set the weights + scheduleNodesDecommissionOnTimeout(decommissionedNodes, decommissionRequest.getDelayTimeout()); + } + + @Override + public void onFailure(Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to update decommission status for attribute [{}] to [{}]", + decommissionRequest.getDecommissionAttribute().toString(), + DecommissionStatus.DRAINING + ), + e + ); + // This decommission state update call will most likely fail as the state update call to 'DRAINING' + // failed. But attempting it anyways as FAILED update might still pass as it doesn't have dependency on + // the current state + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); + } + }); + } + } + + void scheduleNodesDecommissionOnTimeout(Set decommissionedNodes, TimeValue timeoutForNodeDraining) { + ClusterState state = clusterService.getClusterApplierService().state(); + DecommissionAttributeMetadata decommissionAttributeMetadata = state.metadata().decommissionAttributeMetadata(); + if (decommissionAttributeMetadata == null) { + return; + } + assert decommissionAttributeMetadata.status().equals(DecommissionStatus.DRAINING) + : "Unexpected status encountered while decommissioning nodes."; + + // This method ensures no matter what, we always exit from this function after clearing the voting config exclusion + DecommissionAttribute decommissionAttribute = decommissionAttributeMetadata.decommissionAttribute(); + + // Wait for timeout to happen. Log the active connection before decommissioning of nodes. + transportService.getThreadPool().schedule(() -> { + // Log active connections. + decommissionController.getActiveRequestCountOnDecommissionedNodes(decommissionedNodes); + // Call to fail the decommission nodes + failDecommissionedNodes(decommissionedNodes, decommissionAttribute); + }, timeoutForNodeDraining, ThreadPool.Names.GENERIC); + } + + private void failDecommissionedNodes(Set decommissionedNodes, DecommissionAttribute decommissionAttribute) { + + // Weighing away is complete. We have allowed the nodes to be drained. Let's move decommission status to IN_PROGRESS. + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.IN_PROGRESS, new ActionListener<>() { + @Override + public void onResponse(DecommissionStatus status) { + logger.info("updated the decommission status to [{}]", status); + // execute nodes decommissioning + decommissionController.removeDecommissionedNodes( + decommissionedNodes, + "nodes-decommissioned", + TimeValue.timeValueSeconds(120L), + new ActionListener() { + @Override + public void onResponse(Void unused) { + // will clear the voting config exclusion and mark the status as successful + decommissionController.updateMetadataWithDecommissionStatus( + DecommissionStatus.SUCCESSFUL, + statusUpdateListener() + ); + } + + @Override + public void onFailure(Exception e) { + // will go ahead and clear the voting config and mark the status as failed + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); + } + } + ); + } + + @Override + public void onFailure(Exception e) { + logger.error( + () -> new ParameterizedMessage( + "failed to update decommission status for attribute [{}] to [{}]", + decommissionAttribute.toString(), + DecommissionStatus.IN_PROGRESS + ), + e + ); + // This decommission state update call will most likely fail as the state update call to 'DRAINING' + // failed. But attempting it anyways as FAILED update might still pass as it doesn't have dependency on + // the current state + decommissionController.updateMetadataWithDecommissionStatus(DecommissionStatus.FAILED, statusUpdateListener()); + } + }); + } + + private static void validateAwarenessAttribute( + final DecommissionAttribute decommissionAttribute, + List awarenessAttributes, + Map> forcedAwarenessAttributes + ) { + String msg = null; + if (awarenessAttributes == null) { + msg = "awareness attribute not set to the cluster."; + } else if (forcedAwarenessAttributes == null) { + msg = "forced awareness attribute not set to the cluster."; + } else if (awarenessAttributes.contains(decommissionAttribute.attributeName()) == false) { + msg = "invalid awareness attribute requested for decommissioning"; + } else if (forcedAwarenessAttributes.containsKey(decommissionAttribute.attributeName()) == false) { + msg = "forced awareness attribute [" + forcedAwarenessAttributes.toString() + "] doesn't have the decommissioning attribute"; + } + // we don't need to check for attributes presence in forced awareness attribute because, weights API ensures that weights are set + // for all discovered routing attributes and forced attributes. + // So, if the weight is not present for the attribute it could mean its a non routing node (eg. cluster manager) + // And in that case, we are ok to proceed with the decommission. A routing node's attribute absence in forced awareness attribute is + // a problem elsewhere + + if (msg != null) { + throw new DecommissioningFailedException(decommissionAttribute, msg); + } + } + + private static void ensureToBeDecommissionedAttributeWeighedAway(ClusterState state, DecommissionAttribute decommissionAttribute) { + WeightedRoutingMetadata weightedRoutingMetadata = state.metadata().weightedRoutingMetadata(); + if (weightedRoutingMetadata == null) { + throw new DecommissioningFailedException( + decommissionAttribute, + "no weights are set to the attribute. Please set appropriate weights before triggering decommission action" + ); + } + WeightedRouting weightedRouting = weightedRoutingMetadata.getWeightedRouting(); + if (weightedRouting.attributeName().equals(decommissionAttribute.attributeName()) == false) { + throw new DecommissioningFailedException( + decommissionAttribute, + "no weights are specified to attribute [" + decommissionAttribute.attributeName() + "]" + ); + } + // in case the weight is not set for the attribute value, then we know that attribute values was not part of discovered routing node + // attribute or forced awareness attribute and in that case, we are ok if the attribute's value weight is not set. But if it's set, + // its weight has to be zero + Double attributeValueWeight = weightedRouting.weights().get(decommissionAttribute.attributeValue()); + if (attributeValueWeight != null && attributeValueWeight.equals(0.0) == false) { + throw new DecommissioningFailedException( + decommissionAttribute, + "weight for decommissioned attribute is expected to be [0.0] but found [" + attributeValueWeight + "]" + ); + } + } + + private static void ensureEligibleRequest( + DecommissionAttributeMetadata decommissionAttributeMetadata, + DecommissionRequest decommissionRequest + ) { + String msg; + DecommissionAttribute requestedDecommissionAttribute = decommissionRequest.getDecommissionAttribute(); + if (decommissionAttributeMetadata != null) { + // check if the same attribute is registered and handle it accordingly + if (decommissionAttributeMetadata.decommissionAttribute().equals(requestedDecommissionAttribute)) { + switch (decommissionAttributeMetadata.status()) { + // for INIT - check if it is eligible internal retry + case INIT: + if (decommissionRequest.requestID().equals(decommissionAttributeMetadata.requestID()) == false) { + throw new DecommissioningFailedException( + requestedDecommissionAttribute, + "same request is already in status [INIT]" + ); + } + break; + // for FAILED - we are good to process it again + case FAILED: + break; + case DRAINING: + case IN_PROGRESS: + case SUCCESSFUL: + msg = "same request is already in status [" + decommissionAttributeMetadata.status() + "]"; + throw new DecommissioningFailedException(requestedDecommissionAttribute, msg); + default: + throw new IllegalStateException( + "unknown status [" + decommissionAttributeMetadata.status() + "] currently registered in metadata" + ); + } + } else { + switch (decommissionAttributeMetadata.status()) { + case SUCCESSFUL: + // one awareness attribute is already decommissioned. We will reject the new request + msg = "one awareness attribute [" + + decommissionAttributeMetadata.decommissionAttribute().toString() + + "] already successfully decommissioned, recommission before triggering another decommission"; + throw new DecommissioningFailedException(requestedDecommissionAttribute, msg); + case DRAINING: + case IN_PROGRESS: + case INIT: + // it means the decommission has been initiated or is inflight. In that case, will fail new request + msg = "there's an inflight decommission request for attribute [" + + decommissionAttributeMetadata.decommissionAttribute().toString() + + "] is in progress, cannot process this request"; + throw new DecommissioningFailedException(requestedDecommissionAttribute, msg); + case FAILED: + break; + default: + throw new IllegalStateException( + "unknown status [" + decommissionAttributeMetadata.status() + "] currently registered in metadata" + ); + } + } + } + } + + private ActionListener statusUpdateListener() { + return new ActionListener<>() { + @Override + public void onResponse(DecommissionStatus status) { + logger.info("updated the decommission status to [{}]", status); + } + + @Override + public void onFailure(Exception e) { + logger.error("unexpected failure occurred during decommission status update", e); + } + }; + } + + public void startRecommissionAction(final ActionListener listener) { + /* + * For abandoned requests, we might not really know if it actually restored the exclusion list. + * And can land up in cases where even after recommission, exclusions are set(which is unexpected). + * And by definition of OpenSearch - Clusters should have no voting configuration exclusions in normal operation. + * Once the excluded nodes have stopped, clear the voting configuration exclusions with DELETE /_cluster/voting_config_exclusions. + * And hence it is safe to remove the exclusion if any. User should make conscious choice before decommissioning awareness attribute. + */ + clusterService.submitStateUpdateTask("delete-decommission-state", new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) { + ClusterState newState = clearExclusionsAndGetState(currentState); + logger.info("Deleting the decommission attribute from the cluster state"); + newState = deleteDecommissionAttributeInClusterState(newState); + return newState; + } + + @Override + public void onFailure(String source, Exception e) { + logger.error(() -> new ParameterizedMessage("failure during recommission action [{}]", source), e); + listener.onFailure(e); + } + + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + logger.info("successfully cleared voting config exclusion and decommissioned attribute"); + assert newState.metadata().decommissionAttributeMetadata() == null; + assert newState.coordinationMetadata().getVotingConfigExclusions().isEmpty(); + listener.onResponse(new DeleteDecommissionStateResponse(true)); + } + }); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java new file mode 100644 index 0000000000000..4ca8c3cc4286e --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissionStatus.java @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +/** + * An enumeration of the states during decommissioning + */ +public enum DecommissionStatus { + /** + * Decommission process is initiated, and to-be-decommissioned leader is excluded from voting config + */ + INIT("init"), + /** + * Decommission process is initiated, and the zone is being drained. + */ + DRAINING("draining"), + + /** + * Decommission process has started, decommissioned nodes should be removed + */ + IN_PROGRESS("in_progress"), + /** + * Decommission action completed + */ + SUCCESSFUL("successful"), + /** + * Decommission request failed + */ + FAILED("failed"); + + private final String status; + + DecommissionStatus(String status) { + this.status = status; + } + + /** + * Returns status that represents the decommission state + * + * @return status + */ + public String status() { + return status; + } + + /** + * Generate decommission status from given string + * + * @param status status in string + * @return status + */ + public static DecommissionStatus fromString(String status) { + if (status == null) { + throw new IllegalArgumentException("decommission status cannot be null"); + } + if (status.equals(INIT.status())) { + return INIT; + } else if (status.equals(DRAINING.status())) { + return DRAINING; + } else if (status.equals(IN_PROGRESS.status())) { + return IN_PROGRESS; + } else if (status.equals(SUCCESSFUL.status())) { + return SUCCESSFUL; + } else if (status.equals(FAILED.status())) { + return FAILED; + } + throw new IllegalStateException("Decommission status [" + status + "] not recognized."); + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java b/server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java new file mode 100644 index 0000000000000..6f3b319a543c6 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/DecommissioningFailedException.java @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.OpenSearchException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; + +import java.io.IOException; + +/** + * This exception is thrown whenever a failure occurs in decommission request @{@link DecommissionService} + * + * @opensearch.internal + */ + +public class DecommissioningFailedException extends OpenSearchException { + + private final DecommissionAttribute decommissionAttribute; + + public DecommissioningFailedException(DecommissionAttribute decommissionAttribute, String msg) { + this(decommissionAttribute, msg, null); + } + + public DecommissioningFailedException(DecommissionAttribute decommissionAttribute, String msg, Throwable cause) { + super("[" + (decommissionAttribute == null ? "_na" : decommissionAttribute.toString()) + "] " + msg, cause); + this.decommissionAttribute = decommissionAttribute; + } + + public DecommissioningFailedException(StreamInput in) throws IOException { + super(in); + decommissionAttribute = new DecommissionAttribute(in); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + super.writeTo(out); + decommissionAttribute.writeTo(out); + } + + /** + * Returns decommission attribute + * + * @return decommission attribute + */ + public DecommissionAttribute decommissionAttribute() { + return decommissionAttribute; + } + + @Override + public RestStatus status() { + return RestStatus.BAD_REQUEST; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java new file mode 100644 index 0000000000000..ef85335be9a74 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/NodeDecommissionedException.java @@ -0,0 +1,37 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.decommission; + +import org.opensearch.OpenSearchException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.rest.RestStatus; + +import java.io.IOException; + +/** + * This exception is thrown if the node is decommissioned by @{@link DecommissionService} + * and this nodes needs to be removed from the cluster + * + * @opensearch.internal + */ +public class NodeDecommissionedException extends OpenSearchException { + + public NodeDecommissionedException(String msg, Object... args) { + super(msg, args); + } + + public NodeDecommissionedException(StreamInput in) throws IOException { + super(in); + } + + @Override + public RestStatus status() { + return RestStatus.FAILED_DEPENDENCY; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/decommission/package-info.java b/server/src/main/java/org/opensearch/cluster/decommission/package-info.java new file mode 100644 index 0000000000000..256c2f22253cc --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/decommission/package-info.java @@ -0,0 +1,12 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Decommission lifecycle classes + */ +package org.opensearch.cluster.decommission; diff --git a/server/src/main/java/org/opensearch/cluster/health/ClusterHealthStatus.java b/server/src/main/java/org/opensearch/cluster/health/ClusterHealthStatus.java index 07a66a3731ff6..5ea482b8b8ffa 100644 --- a/server/src/main/java/org/opensearch/cluster/health/ClusterHealthStatus.java +++ b/server/src/main/java/org/opensearch/cluster/health/ClusterHealthStatus.java @@ -32,12 +32,17 @@ package org.opensearch.cluster.health; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import java.io.IOException; +/** + * Cluster health status + * + * @opensearch.internal + */ public enum ClusterHealthStatus implements Writeable { GREEN((byte) 0), YELLOW((byte) 1), diff --git a/server/src/main/java/org/opensearch/cluster/health/ClusterIndexHealth.java b/server/src/main/java/org/opensearch/cluster/health/ClusterIndexHealth.java index ffe54e42355f8..0bb762e3ff744 100644 --- a/server/src/main/java/org/opensearch/cluster/health/ClusterIndexHealth.java +++ b/server/src/main/java/org/opensearch/cluster/health/ClusterIndexHealth.java @@ -35,15 +35,15 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.routing.IndexRoutingTable; import org.opensearch.cluster.routing.IndexShardRoutingTable; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; @@ -54,10 +54,15 @@ import java.util.Objects; import static java.util.Collections.emptyMap; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; +/** + * Cluster Index Health Information + * + * @opensearch.internal + */ public final class ClusterIndexHealth implements Iterable, Writeable, ToXContentFragment { private static final String STATUS = "status"; private static final String NUMBER_OF_SHARDS = "number_of_shards"; diff --git a/server/src/main/java/org/opensearch/cluster/health/ClusterShardHealth.java b/server/src/main/java/org/opensearch/cluster/health/ClusterShardHealth.java index fc6c6eb4f9bdc..d06e89d9ea170 100644 --- a/server/src/main/java/org/opensearch/cluster/health/ClusterShardHealth.java +++ b/server/src/main/java/org/opensearch/cluster/health/ClusterShardHealth.java @@ -37,23 +37,29 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.UnassignedInfo; import org.opensearch.cluster.routing.UnassignedInfo.AllocationStatus; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Locale; import java.util.Objects; -import static org.opensearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.opensearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.opensearch.core.xcontent.ConstructingObjectParser.constructorArg; +import static org.opensearch.core.xcontent.XContentParserUtils.ensureExpectedToken; +/** + * Cluster shard health information + * + * @opensearch.internal + */ public final class ClusterShardHealth implements Writeable, ToXContentFragment { private static final String STATUS = "status"; private static final String ACTIVE_SHARDS = "active_shards"; @@ -269,7 +275,7 @@ public static ClusterShardHealth fromXContent(XContentParser parser) throws IOEx @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/server/src/main/java/org/opensearch/cluster/health/ClusterStateHealth.java b/server/src/main/java/org/opensearch/cluster/health/ClusterStateHealth.java index c6b214d31707e..15b8c90c5d08f 100644 --- a/server/src/main/java/org/opensearch/cluster/health/ClusterStateHealth.java +++ b/server/src/main/java/org/opensearch/cluster/health/ClusterStateHealth.java @@ -36,10 +36,10 @@ import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.cluster.routing.IndexRoutingTable; import org.opensearch.cluster.routing.ShardRouting; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; import java.util.Collections; @@ -49,11 +49,16 @@ import java.util.Map; import java.util.Objects; +/** + * Cluster state health information + * + * @opensearch.internal + */ public final class ClusterStateHealth implements Iterable, Writeable { private final int numberOfNodes; private final int numberOfDataNodes; - private final boolean hasDiscoveredMaster; + private final boolean hasDiscoveredClusterManager; private final int activeShards; private final int relocatingShards; private final int activePrimaryShards; @@ -81,7 +86,7 @@ public ClusterStateHealth(final ClusterState clusterState) { public ClusterStateHealth(final ClusterState clusterState, final String[] concreteIndices) { numberOfNodes = clusterState.nodes().getSize(); numberOfDataNodes = clusterState.nodes().getDataNodes().size(); - hasDiscoveredMaster = clusterState.nodes().getMasterNodeId() != null; + hasDiscoveredClusterManager = clusterState.nodes().getClusterManagerNodeId() != null; indices = new HashMap<>(); for (String index : concreteIndices) { IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(index); @@ -150,9 +155,9 @@ public ClusterStateHealth(final StreamInput in) throws IOException { numberOfNodes = in.readVInt(); numberOfDataNodes = in.readVInt(); if (in.getVersion().onOrAfter(Version.V_1_0_0)) { - hasDiscoveredMaster = in.readBoolean(); + hasDiscoveredClusterManager = in.readBoolean(); } else { - hasDiscoveredMaster = true; + hasDiscoveredClusterManager = true; } status = ClusterHealthStatus.fromValue(in.readByte()); int size = in.readVInt(); @@ -175,7 +180,7 @@ public ClusterStateHealth( int unassignedShards, int numberOfNodes, int numberOfDataNodes, - boolean hasDiscoveredMaster, + boolean hasDiscoveredClusterManager, double activeShardsPercent, ClusterHealthStatus status, Map indices @@ -187,7 +192,7 @@ public ClusterStateHealth( this.unassignedShards = unassignedShards; this.numberOfNodes = numberOfNodes; this.numberOfDataNodes = numberOfDataNodes; - this.hasDiscoveredMaster = hasDiscoveredMaster; + this.hasDiscoveredClusterManager = hasDiscoveredClusterManager; this.activeShardsPercent = activeShardsPercent; this.status = status; this.indices = indices; @@ -233,8 +238,14 @@ public double getActiveShardsPercent() { return activeShardsPercent; } + public boolean hasDiscoveredClusterManager() { + return hasDiscoveredClusterManager; + } + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #hasDiscoveredClusterManager()} */ + @Deprecated public boolean hasDiscoveredMaster() { - return hasDiscoveredMaster; + return hasDiscoveredClusterManager(); } @Override @@ -252,7 +263,7 @@ public void writeTo(final StreamOutput out) throws IOException { out.writeVInt(numberOfNodes); out.writeVInt(numberOfDataNodes); if (out.getVersion().onOrAfter(Version.V_1_0_0)) { - out.writeBoolean(hasDiscoveredMaster); + out.writeBoolean(hasDiscoveredClusterManager); } out.writeByte(status.value()); out.writeVInt(indices.size()); @@ -269,8 +280,8 @@ public String toString() { + numberOfNodes + ", numberOfDataNodes=" + numberOfDataNodes - + ", hasDiscoveredMaster=" - + hasDiscoveredMaster + + ", hasDiscoveredClusterManager=" + + hasDiscoveredClusterManager + ", activeShards=" + activeShards + ", relocatingShards=" @@ -297,7 +308,7 @@ public boolean equals(Object o) { ClusterStateHealth that = (ClusterStateHealth) o; return numberOfNodes == that.numberOfNodes && numberOfDataNodes == that.numberOfDataNodes - && hasDiscoveredMaster == that.hasDiscoveredMaster + && hasDiscoveredClusterManager == that.hasDiscoveredClusterManager && activeShards == that.activeShards && relocatingShards == that.relocatingShards && activePrimaryShards == that.activePrimaryShards @@ -313,7 +324,7 @@ public int hashCode() { return Objects.hash( numberOfNodes, numberOfDataNodes, - hasDiscoveredMaster, + hasDiscoveredClusterManager, activeShards, relocatingShards, activePrimaryShards, diff --git a/server/src/main/java/org/opensearch/cluster/health/package-info.java b/server/src/main/java/org/opensearch/cluster/health/package-info.java new file mode 100644 index 0000000000000..f022210548cfe --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/health/package-info.java @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Cluster Health Foundation classes. */ +package org.opensearch.cluster.health; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/AliasAction.java b/server/src/main/java/org/opensearch/cluster/metadata/AliasAction.java index eede77917a2fd..47c6cad04343e 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/AliasAction.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/AliasAction.java @@ -34,10 +34,12 @@ import org.opensearch.action.admin.indices.alias.IndicesAliasesRequest; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; +import org.opensearch.core.common.Strings; /** * Individual operation to perform on the cluster state as part of an {@link IndicesAliasesRequest}. + * + * @opensearch.internal */ public abstract class AliasAction { private final String index; @@ -74,6 +76,8 @@ public String getIndex() { /** * Validate a new alias. + * + * @opensearch.internal */ @FunctionalInterface public interface NewAliasValidator { @@ -82,6 +86,8 @@ public interface NewAliasValidator { /** * Operation to add an alias to an index. + * + * @opensearch.internal */ public static class Add extends AliasAction { private final String alias; @@ -132,6 +138,18 @@ public String getAlias() { return alias; } + public String getFilter() { + return filter; + } + + public String getSearchRouting() { + return searchRouting; + } + + public String getIndexRouting() { + return indexRouting; + } + public Boolean writeIndex() { return writeIndex; } @@ -172,6 +190,8 @@ boolean apply(NewAliasValidator aliasValidator, Metadata.Builder metadata, Index /** * Operation to remove an alias from an index. + * + * @opensearch.internal */ public static class Remove extends AliasAction { private final String alias; @@ -218,6 +238,8 @@ boolean apply(NewAliasValidator aliasValidator, Metadata.Builder metadata, Index /** * Operation to remove an index. This is an "alias action" because it allows us to remove an index at the same time as we remove add an * alias to replace it. + * + * @opensearch.internal */ public static class RemoveIndex extends AliasAction { public RemoveIndex(String index) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java index e6deb22477bae..0f97667f8fbed 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java @@ -37,18 +37,19 @@ import org.opensearch.cluster.AbstractDiffable; import org.opensearch.cluster.Diff; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Collections; @@ -58,6 +59,11 @@ import static java.util.Collections.emptySet; +/** + * Metadata for index aliases + * + * @opensearch.internal + */ public class AliasMetadata extends AbstractDiffable implements ToXContentFragment { private final String alias; @@ -262,7 +268,7 @@ public static Diff readDiffFrom(StreamInput in) throws IOExceptio @Override public String toString() { - return Strings.toString(this, true, true); + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); } @Override @@ -271,6 +277,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * Builder of alias metadata. + * + * @opensearch.internal + */ public static class Builder { private final String alias; @@ -301,11 +312,11 @@ public Builder filter(CompressedXContent filter) { } public Builder filter(String filter) { - if (!Strings.hasLength(filter)) { + if (Strings.hasLength(filter) == false) { this.filter = null; return this; } - return filter(XContentHelper.convertToMap(XContentFactory.xContent(filter), filter, true)); + return filter(XContentHelper.convertToMap(MediaTypeRegistry.xContent(filter).xContent(), filter, true)); } public Builder filter(Map filter) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/AliasValidator.java b/server/src/main/java/org/opensearch/cluster/metadata/AliasValidator.java index c024358c5d1d3..db7f38518b80d 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/AliasValidator.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/AliasValidator.java @@ -34,13 +34,13 @@ import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.xcontent.LoggingDeprecationHandler; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.query.QueryBuilder; import org.opensearch.index.query.QueryShardContext; import org.opensearch.index.query.Rewriteable; @@ -55,6 +55,8 @@ /** * Validator for an alias, to be used before adding an alias to the index metadata * and make sure the alias is valid + * + * @opensearch.internal */ public class AliasValidator { /** @@ -78,7 +80,7 @@ public void validateAliasMetadata(AliasMetadata aliasMetadata, String index, Met /** * Allows to partially validate an alias, without knowing which index it'll get applied to. * Useful with index templates containing aliases. Checks also that it is possible to parse - * the alias filter via {@link org.opensearch.common.xcontent.XContentParser}, + * the alias filter via {@link XContentParser}, * without validating it as a filter though. * @throws IllegalArgumentException if the alias is not valid */ @@ -86,7 +88,7 @@ public void validateAliasStandalone(Alias alias) { validateAliasStandalone(alias.name(), alias.indexRouting()); if (Strings.hasLength(alias.filter())) { try { - XContentHelper.convertToMap(XContentFactory.xContent(alias.filter()), alias.filter(), false); + XContentHelper.convertToMap(MediaTypeRegistry.xContent(alias.filter()).xContent(), alias.filter(), false); } catch (Exception e) { throw new IllegalArgumentException("failed to parse filter for alias [" + alias.name() + "]", e); } @@ -99,7 +101,7 @@ public void validateAliasStandalone(Alias alias) { public void validateAlias(String alias, String index, @Nullable String indexRouting, Function indexLookup) { validateAliasStandalone(alias, indexRouting); - if (!Strings.hasText(index)) { + if (Strings.hasText(index) == false) { throw new IllegalArgumentException("index name is required"); } @@ -132,7 +134,8 @@ public void validateAliasFilter( ) { assert queryShardContext != null; try ( - XContentParser parser = XContentFactory.xContent(filter) + XContentParser parser = MediaTypeRegistry.xContent(filter) + .xContent() .createParser(xContentRegistry, LoggingDeprecationHandler.INSTANCE, filter) ) { validateAliasFilter(parser, queryShardContext); @@ -156,7 +159,7 @@ public void validateAliasFilter( try ( InputStream inputStream = filter.streamInput(); - XContentParser parser = XContentFactory.xContentType(inputStream) + XContentParser parser = MediaTypeRegistry.xContentType(inputStream) .xContent() .createParser(xContentRegistry, LoggingDeprecationHandler.INSTANCE, filter.streamInput()) ) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/AutoExpandReplicas.java b/server/src/main/java/org/opensearch/cluster/metadata/AutoExpandReplicas.java index a7538d0a5b4f8..6acae8cca59fd 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/AutoExpandReplicas.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/AutoExpandReplicas.java @@ -31,7 +31,6 @@ package org.opensearch.cluster.metadata; -import com.carrotsearch.hppc.cursors.ObjectCursor; import org.opensearch.LegacyESVersion; import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.cluster.routing.allocation.RoutingAllocation; @@ -52,6 +51,8 @@ * This class acts as a functional wrapper around the {@code index.auto_expand_replicas} setting. * This setting or rather it's value is expanded into a min and max value which requires special handling * based on the number of datanodes in the cluster. This class handles all the parsing and streamlines the access to these values. + * + * @opensearch.internal */ public final class AutoExpandReplicas { // the value we recognize in the "max" position to mean all the nodes @@ -132,13 +133,21 @@ int getMaxReplicas(int numDataNodes) { return Math.min(maxReplicas, numDataNodes - 1); } + public int getMaxReplicas() { + return maxReplicas; + } + + public boolean isEnabled() { + return enabled; + } + private OptionalInt getDesiredNumberOfReplicas(IndexMetadata indexMetadata, RoutingAllocation allocation) { if (enabled) { int numMatchingDataNodes = 0; // Only start using new logic once all nodes are migrated to 7.6.0, avoiding disruption during an upgrade if (allocation.nodes().getMinNodeVersion().onOrAfter(LegacyESVersion.V_7_6_0)) { - for (ObjectCursor cursor : allocation.nodes().getDataNodes().values()) { - Decision decision = allocation.deciders().shouldAutoExpandToNode(indexMetadata, cursor.value, allocation); + for (final DiscoveryNode cursor : allocation.nodes().getDataNodes().values()) { + Decision decision = allocation.deciders().shouldAutoExpandToNode(indexMetadata, cursor, allocation); if (decision.type() != Decision.Type.NO) { numMatchingDataNodes++; } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/ClusterNameExpressionResolver.java b/server/src/main/java/org/opensearch/cluster/metadata/ClusterNameExpressionResolver.java index 72f6f142ed290..fdc6c17cb3b13 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/ClusterNameExpressionResolver.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/ClusterNameExpressionResolver.java @@ -43,6 +43,8 @@ /** * Resolves cluster names from an expression. The expression must be the exact match of a cluster * name or must be a wildcard expression. + * + * @opensearch.internal */ public final class ClusterNameExpressionResolver { @@ -66,6 +68,11 @@ public List resolveClusterNames(Set remoteClusters, String clust } } + /** + * A wildcard expression resolver. + * + * @opensearch.internal + */ private static class WildcardExpressionResolver { private List resolve(Set remoteClusters, String clusterExpression) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/ComponentTemplate.java b/server/src/main/java/org/opensearch/cluster/metadata/ComponentTemplate.java index c3ba1a2f7a45b..cab1b75afe3e1 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/ComponentTemplate.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/ComponentTemplate.java @@ -35,14 +35,15 @@ import org.opensearch.cluster.AbstractDiffable; import org.opensearch.cluster.Diff; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.Map; @@ -53,6 +54,8 @@ * component template is expected to be valid on its own. For example, if a component template * contains a field "foo", it's expected to contain all the necessary settings/mappings/etc for the * "foo" field. These component templates make up the individual pieces composing an index template. + * + * @opensearch.internal */ public class ComponentTemplate extends AbstractDiffable implements ToXContentObject { private static final ParseField TEMPLATE = new ParseField("template"); @@ -149,7 +152,7 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } @Override diff --git a/server/src/main/java/org/opensearch/cluster/metadata/ComponentTemplateMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/ComponentTemplateMetadata.java index 0d2496b5812be..8c755201fb4b7 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/ComponentTemplateMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/ComponentTemplateMetadata.java @@ -37,13 +37,14 @@ import org.opensearch.cluster.Diff; import org.opensearch.cluster.DiffableUtils; import org.opensearch.cluster.NamedDiff; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.EnumSet; @@ -54,6 +55,8 @@ /** * {@link ComponentTemplateMetadata} is a custom {@link Metadata} implementation for storing a map * of component templates and their names. + * + * @opensearch.internal */ public class ComponentTemplateMetadata implements Metadata.Custom { public static final String TYPE = "component_template"; @@ -151,9 +154,14 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * A diff between component template metadata. + * + * @opensearch.internal + */ static class ComponentTemplateMetadataDiff implements NamedDiff { final Diff> componentTemplateDiff; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/ComposableIndexTemplate.java b/server/src/main/java/org/opensearch/cluster/metadata/ComposableIndexTemplate.java index b9d71adc89c1c..2414521c3a438 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/ComposableIndexTemplate.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/ComposableIndexTemplate.java @@ -38,15 +38,16 @@ import org.opensearch.cluster.Diff; import org.opensearch.cluster.metadata.DataStream.TimestampField; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.DataStreamFieldMapper; import org.opensearch.index.mapper.MapperService; @@ -58,12 +59,13 @@ import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableMap; -import static org.opensearch.common.collect.Map.of; /** * An index template is comprised of a set of index patterns, an optional template, and a list of * ids corresponding to component templates that should be composed in order when creating a new * index. + * + * @opensearch.internal */ public class ComposableIndexTemplate extends AbstractDiffable implements ToXContentObject { private static final ParseField INDEX_PATTERNS = new ParseField("index_patterns"); @@ -284,9 +286,14 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * Template for data stream. + * + * @opensearch.internal + */ public static class DataStreamTemplate implements Writeable, ToXContentObject { private static final ParseField TIMESTAMP_FIELD_FIELD = new ParseField("timestamp_field"); @@ -329,7 +336,10 @@ public TimestampField getTimestampField() { public Map getDataStreamMappingSnippet() { return singletonMap( MapperService.SINGLE_MAPPING_NAME, - singletonMap("_data_stream_timestamp", unmodifiableMap(of("enabled", true, "timestamp_field", getTimestampField().toMap()))) + singletonMap( + "_data_stream_timestamp", + unmodifiableMap(Map.of("enabled", true, "timestamp_field", getTimestampField().toMap())) + ) ); } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/ComposableIndexTemplateMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/ComposableIndexTemplateMetadata.java index 46712a3b529b2..7056abcad8be6 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/ComposableIndexTemplateMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/ComposableIndexTemplateMetadata.java @@ -37,13 +37,14 @@ import org.opensearch.cluster.Diff; import org.opensearch.cluster.DiffableUtils; import org.opensearch.cluster.NamedDiff; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.EnumSet; @@ -54,6 +55,8 @@ /** * The {@link ComposableIndexTemplateMetadata} class is a custom {@link Metadata.Custom} implementation that * stores a map of ids to {@link ComposableIndexTemplate} templates. + * + * @opensearch.internal */ public class ComposableIndexTemplateMetadata implements Metadata.Custom { public static final String TYPE = "index_template"; @@ -152,9 +155,14 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * A diff between composable metadata templates. + * + * @opensearch.internal + */ static class ComposableIndexTemplateMetadataDiff implements NamedDiff { final Diff> indexTemplateDiff; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/CryptoMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/CryptoMetadata.java new file mode 100644 index 0000000000000..27803cb106005 --- /dev/null +++ b/server/src/main/java/org/opensearch/cluster/metadata/CryptoMetadata.java @@ -0,0 +1,167 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cluster.metadata; + +import org.opensearch.OpenSearchParseException; +import org.opensearch.action.admin.cluster.crypto.CryptoSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Objects; + +/** + * Metadata about encryption and decryption + * + * @opensearch.internal + */ +public class CryptoMetadata implements Writeable { + static final public String CRYPTO_METADATA_KEY = "crypto_metadata"; + static final public String KEY_PROVIDER_NAME_KEY = "key_provider_name"; + static final public String KEY_PROVIDER_TYPE_KEY = "key_provider_type"; + static final public String SETTINGS_KEY = "settings"; + private final String keyProviderName; + private final String keyProviderType; + private final Settings settings; + + /** + * Constructs new crypto metadata + * + * @param keyProviderName key provider name + * @param keyProviderType key provider type + * @param settings crypto settings + */ + public CryptoMetadata(String keyProviderName, String keyProviderType, Settings settings) { + this.keyProviderName = keyProviderName; + this.keyProviderType = keyProviderType; + this.settings = settings; + } + + /** + * Returns key provider name + * + * @return Key provider name + */ + public String keyProviderName() { + return this.keyProviderName; + } + + /** + * Returns key provider type + * + * @return key provider type + */ + public String keyProviderType() { + return this.keyProviderType; + } + + /** + * Returns crypto settings + * + * @return crypto settings + */ + public Settings settings() { + return this.settings; + } + + public CryptoMetadata(StreamInput in) throws IOException { + keyProviderName = in.readString(); + keyProviderType = in.readString(); + settings = Settings.readSettingsFromStream(in); + } + + public static CryptoMetadata fromRequest(CryptoSettings cryptoSettings) { + if (cryptoSettings == null) { + return null; + } + return new CryptoMetadata(cryptoSettings.getKeyProviderName(), cryptoSettings.getKeyProviderType(), cryptoSettings.getSettings()); + } + + /** + * Writes crypto metadata to stream output + * + * @param out stream output + */ + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(keyProviderName); + out.writeString(keyProviderType); + Settings.writeSettingsToStream(settings, out); + } + + public static CryptoMetadata fromXContent(XContentParser parser) throws IOException { + XContentParser.Token token; + String keyProviderType = null; + Settings settings = null; + String keyProviderName = parser.currentName(); + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + String currentFieldName = parser.currentName(); + if (KEY_PROVIDER_NAME_KEY.equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new OpenSearchParseException("failed to parse crypto metadata [{}], unknown type"); + } + keyProviderName = parser.text(); + } else if (KEY_PROVIDER_TYPE_KEY.equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.VALUE_STRING) { + throw new OpenSearchParseException("failed to parse crypto metadata [{}], unknown type"); + } + keyProviderType = parser.text(); + } else if (SETTINGS_KEY.equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new OpenSearchParseException("failed to parse crypto metadata [{}], unknown type"); + } + settings = Settings.fromXContent(parser); + } else { + throw new OpenSearchParseException("failed to parse crypto metadata, unknown field [{}]", currentFieldName); + } + } else { + throw new OpenSearchParseException("failed to parse repositories"); + } + } + return new CryptoMetadata(keyProviderName, keyProviderType, settings); + } + + public void toXContent(CryptoMetadata cryptoMetadata, XContentBuilder builder, ToXContent.Params params) throws IOException { + builder.startObject(CRYPTO_METADATA_KEY); + builder.field(KEY_PROVIDER_NAME_KEY, cryptoMetadata.keyProviderName()); + builder.field(KEY_PROVIDER_TYPE_KEY, cryptoMetadata.keyProviderType()); + builder.startObject(SETTINGS_KEY); + cryptoMetadata.settings().toXContent(builder, params); + builder.endObject(); + builder.endObject(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CryptoMetadata that = (CryptoMetadata) o; + + if (!keyProviderName.equals(that.keyProviderName)) return false; + if (!keyProviderType.equals(that.keyProviderType)) return false; + return settings.equals(that.settings); + } + + @Override + public int hashCode() { + return Objects.hash(keyProviderName, keyProviderType, settings); + } + + @Override + public String toString() { + return "CryptoMetadata{" + keyProviderName + "}{" + keyProviderType + "}{" + settings + "}"; + } +} diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DataStream.java b/server/src/main/java/org/opensearch/cluster/metadata/DataStream.java index eaf1b35ca2ce3..753f872f88b22 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DataStream.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DataStream.java @@ -31,29 +31,57 @@ package org.opensearch.cluster.metadata; +import org.apache.lucene.document.LongPoint; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.PointValues; +import org.opensearch.OpenSearchException; import org.opensearch.cluster.AbstractDiffable; import org.opensearch.cluster.Diff; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.index.Index; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; +/** + * Primary DataStream class + * + * @opensearch.internal + */ public final class DataStream extends AbstractDiffable implements ToXContentObject { public static final String BACKING_INDEX_PREFIX = ".ds-"; + public static final String TIMESERIES_FIELDNAME = "@timestamp"; + public static final Comparator TIMESERIES_LEAF_SORTER = Comparator.comparingLong((LeafReader r) -> { + try { + PointValues points = r.getPointValues(TIMESERIES_FIELDNAME); + if (points != null) { + // could be a multipoint (probably not) but get the maximum time value anyway + byte[] sortValue = points.getMaxPackedValue(); + // decode the first dimension because this should not be a multi dimension field + // it's a bug in the date field if it is + return LongPoint.decodeDimension(sortValue, 0); + } else { + // segment does not have a timestamp field, just return the minimum value + return Long.MIN_VALUE; + } + } catch (IOException e) { + throw new OpenSearchException("Not a timeseries Index! Field [{}] not found!", TIMESERIES_FIELDNAME); + } + }).reversed(); private final String name; private final TimestampField timeStampField; @@ -227,6 +255,11 @@ public int hashCode() { return Objects.hash(name, timeStampField, indices, generation); } + /** + * A timestamp field. + * + * @opensearch.internal + */ public static final class TimestampField implements Writeable, ToXContentObject { static ParseField NAME_FIELD = new ParseField("name"); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DataStreamMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/DataStreamMetadata.java index d94b6f0c8cffc..ef880754ff429 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DataStreamMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DataStreamMetadata.java @@ -37,13 +37,14 @@ import org.opensearch.cluster.Diff; import org.opensearch.cluster.DiffableUtils; import org.opensearch.cluster.NamedDiff; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.util.EnumSet; @@ -53,6 +54,8 @@ /** * Custom {@link Metadata} implementation for storing a map of {@link DataStream}s and their names. + * + * @opensearch.internal */ public class DataStreamMetadata implements Metadata.Custom { @@ -156,9 +159,14 @@ public boolean equals(Object obj) { @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } + /** + * Builder of data stream metadata. + * + * @opensearch.internal + */ public static class Builder { private final Map dataStreams = new HashMap<>(); @@ -173,6 +181,11 @@ public DataStreamMetadata build() { } } + /** + * A diff between data stream metadata. + * + * @opensearch.internal + */ static class DataStreamMetadataDiff implements NamedDiff { final Diff> dataStreamDiff; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/DiffableStringMap.java b/server/src/main/java/org/opensearch/cluster/metadata/DiffableStringMap.java index 2c35fbf4ae67e..8209c7bb56ad8 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/DiffableStringMap.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/DiffableStringMap.java @@ -34,8 +34,8 @@ import org.opensearch.cluster.Diff; import org.opensearch.cluster.Diffable; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; import java.io.IOException; import java.util.AbstractMap; @@ -49,6 +49,8 @@ /** * This is a {@code Map} that implements AbstractDiffable so it * can be used for cluster state purposes + * + * @opensearch.internal */ public class DiffableStringMap extends AbstractMap implements Diffable { @@ -88,6 +90,8 @@ public static Diff readDiffFrom(StreamInput in) throws IOExce /** * Represents differences between two DiffableStringMaps. + * + * @opensearch.internal */ public static class DiffableStringMapDiff implements Diff { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/IndexAbstraction.java b/server/src/main/java/org/opensearch/cluster/metadata/IndexAbstraction.java index 653755664cbc0..0c316373e484f 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/IndexAbstraction.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexAbstraction.java @@ -31,10 +31,10 @@ package org.opensearch.cluster.metadata; -import org.apache.lucene.util.SetOnce; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; +import org.opensearch.common.SetOnce; import org.opensearch.common.collect.Tuple; +import org.opensearch.core.common.Strings; import java.util.ArrayList; import java.util.Collections; @@ -46,12 +46,13 @@ import static org.opensearch.cluster.metadata.DataStream.getDefaultBackingIndexName; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_HIDDEN_SETTING; -import static org.opensearch.common.collect.List.copyOf; /** * An index abstraction is a reference to one or more concrete indices. * An index abstraction has a unique name and encapsulates all the {@link IndexMetadata} instances it is pointing to. * Also depending on type it may refer to a single or many concrete indices and may or may not have a write index. + * + * @opensearch.internal */ public interface IndexAbstraction { @@ -137,6 +138,8 @@ public String getDisplayName() { /** * Represents an concrete index and encapsulates its {@link IndexMetadata} + * + * @opensearch.internal */ class Index implements IndexAbstraction { @@ -190,6 +193,8 @@ public boolean isSystem() { /** * Represents an alias and groups all {@link IndexMetadata} instances sharing the same alias name together. + * + * @opensearch.internal */ class Alias implements IndexAbstraction { @@ -327,6 +332,11 @@ private boolean isNonEmpty(List idxMetas) { } } + /** + * A data stream. + * + * @opensearch.internal + */ class DataStream implements IndexAbstraction { private final org.opensearch.cluster.metadata.DataStream dataStream; @@ -335,7 +345,7 @@ class DataStream implements IndexAbstraction { public DataStream(org.opensearch.cluster.metadata.DataStream dataStream, List dataStreamIndices) { this.dataStream = dataStream; - this.dataStreamIndices = copyOf(dataStreamIndices); + this.dataStreamIndices = List.copyOf(dataStreamIndices); this.writeIndex = dataStreamIndices.get(dataStreamIndices.size() - 1); assert writeIndex.getIndex().getName().equals(getDefaultBackingIndexName(dataStream.getName(), dataStream.getGeneration())); } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/IndexAbstractionResolver.java b/server/src/main/java/org/opensearch/cluster/metadata/IndexAbstractionResolver.java index 4045ea938e373..a83c778a4b83a 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/IndexAbstractionResolver.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexAbstractionResolver.java @@ -43,6 +43,11 @@ import java.util.List; import java.util.Set; +/** + * Utility class to resolve index abstractions + * + * @opensearch.internal + */ public class IndexAbstractionResolver { private final IndexNameExpressionResolver indexNameExpressionResolver; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/IndexGraveyard.java b/server/src/main/java/org/opensearch/cluster/metadata/IndexGraveyard.java index c0b207158c9bc..0da948dc78c5d 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/IndexGraveyard.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexGraveyard.java @@ -35,19 +35,19 @@ import org.opensearch.Version; import org.opensearch.cluster.Diff; import org.opensearch.cluster.NamedDiff; -import org.opensearch.common.ParseField; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; import org.opensearch.common.time.DateFormatter; -import org.opensearch.common.xcontent.ContextParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.index.Index; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.ContextParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import java.io.IOException; import java.time.Instant; @@ -67,6 +67,8 @@ * nodes and a node could be removed from the cluster for a period of time, the * tombstones remain in the cluster state for a fixed period of time, after which * they are purged. + * + * @opensearch.internal */ public final class IndexGraveyard implements Metadata.Custom { @@ -188,6 +190,8 @@ public static IndexGraveyard.Builder builder(final IndexGraveyard graveyard) { /** * A class to build an IndexGraveyard. + * + * @opensearch.internal */ public static final class Builder { private List tombstones; @@ -273,6 +277,8 @@ public IndexGraveyard build(final Settings settings) { /** * A class representing a diff of two IndexGraveyard objects. + * + * @opensearch.internal */ public static final class IndexGraveyardDiff implements NamedDiff { @@ -360,6 +366,8 @@ public String getWriteableName() { /** * An individual tombstone entry for representing a deleted index. + * + * @opensearch.internal */ public static final class Tombstone implements ToXContentObject, Writeable { @@ -458,6 +466,8 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa /** * A builder for building tombstone entries. + * + * @opensearch.internal */ private static final class Builder { private Index index; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java index 6510c57060fe0..b2c776df130b6 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java @@ -32,11 +32,6 @@ package org.opensearch.cluster.metadata; -import com.carrotsearch.hppc.LongArrayList; -import com.carrotsearch.hppc.cursors.IntObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; -import org.opensearch.Assertions; import org.opensearch.LegacyESVersion; import org.opensearch.Version; import org.opensearch.action.admin.indices.rollover.RolloverInfo; @@ -49,33 +44,35 @@ import org.opensearch.cluster.node.DiscoveryNodeFilters; import org.opensearch.cluster.routing.allocation.IndexMetadataUpdater; import org.opensearch.common.Nullable; -import org.opensearch.common.collect.ImmutableOpenIntMap; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.Assertions; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.gateway.MetadataStateFormat; -import org.opensearch.index.Index; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.seqno.SequenceNumbers; -import org.opensearch.index.shard.ShardId; -import org.opensearch.rest.RestStatus; +import org.opensearch.indices.replication.common.ReplicationType; import java.io.IOException; import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -85,6 +82,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Function; @@ -95,6 +93,11 @@ import static org.opensearch.common.settings.Settings.readSettingsFromStream; import static org.opensearch.common.settings.Settings.writeSettingsToStream; +/** + * Index metadata information + * + * @opensearch.internal + */ public class IndexMetadata implements Diffable, ToXContentFragment { public static final ClusterBlock INDEX_READ_ONLY_BLOCK = new ClusterBlock( @@ -143,6 +146,21 @@ public class IndexMetadata implements Diffable, ToXContentFragmen EnumSet.of(ClusterBlockLevel.METADATA_WRITE, ClusterBlockLevel.WRITE) ); + public static final ClusterBlock REMOTE_READ_ONLY_ALLOW_DELETE = new ClusterBlock( + 13, + "remote index is read-only", + false, + false, + true, + RestStatus.FORBIDDEN, + EnumSet.of(ClusterBlockLevel.METADATA_WRITE, ClusterBlockLevel.WRITE) + ); + + /** + * The state of the index. + * + * @opensearch.internal + */ public enum State { OPEN((byte) 0), CLOSE((byte) 1); @@ -260,9 +278,180 @@ public Iterator> settings() { Property.IndexScope ); + /** + * Used to specify the replication type for the index. By default, document replication is used. + */ + public static final String SETTING_REPLICATION_TYPE = "index.replication.type"; + public static final Setting INDEX_REPLICATION_TYPE_SETTING = new Setting<>( + SETTING_REPLICATION_TYPE, + ReplicationType.DOCUMENT.toString(), + ReplicationType::parseString, + new Setting.Validator<>() { + + @Override + public void validate(final ReplicationType value) {} + + @Override + public void validate(final ReplicationType value, final Map, Object> settings) { + final Object remoteStoreEnabled = settings.get(INDEX_REMOTE_STORE_ENABLED_SETTING); + if (ReplicationType.SEGMENT.equals(value) == false && Objects.equals(remoteStoreEnabled, true)) { + throw new IllegalArgumentException( + "To enable " + + INDEX_REMOTE_STORE_ENABLED_SETTING.getKey() + + ", " + + INDEX_REPLICATION_TYPE_SETTING.getKey() + + " should be set to " + + ReplicationType.SEGMENT + ); + } + } + + @Override + public Iterator> settings() { + final List> settings = List.of(INDEX_REMOTE_STORE_ENABLED_SETTING); + return settings.iterator(); + } + }, + Property.IndexScope, + Property.Final + ); + + public static final String SETTING_REMOTE_STORE_ENABLED = "index.remote_store.enabled"; + + public static final String SETTING_REMOTE_SEGMENT_STORE_REPOSITORY = "index.remote_store.segment.repository"; + + public static final String SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY = "index.remote_store.translog.repository"; + + /** + * Used to specify if the index data should be persisted in the remote store. + */ + public static final Setting INDEX_REMOTE_STORE_ENABLED_SETTING = Setting.boolSetting( + SETTING_REMOTE_STORE_ENABLED, + false, + new Setting.Validator<>() { + + @Override + public void validate(final Boolean value) {} + + @Override + public void validate(final Boolean value, final Map, Object> settings) { + final Object replicationType = settings.get(INDEX_REPLICATION_TYPE_SETTING); + if (ReplicationType.SEGMENT.equals(replicationType) == false && value) { + throw new IllegalArgumentException( + "To enable " + + INDEX_REMOTE_STORE_ENABLED_SETTING.getKey() + + ", " + + INDEX_REPLICATION_TYPE_SETTING.getKey() + + " should be set to " + + ReplicationType.SEGMENT + ); + } + } + + @Override + public Iterator> settings() { + final List> settings = List.of(INDEX_REPLICATION_TYPE_SETTING); + return settings.iterator(); + } + }, + Property.IndexScope, + Property.PrivateIndex, + Property.Dynamic + ); + + /** + * Used to specify remote store repository to use for this index. + */ + public static final Setting INDEX_REMOTE_SEGMENT_STORE_REPOSITORY_SETTING = Setting.simpleString( + SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, + new Setting.Validator<>() { + + @Override + public void validate(final String value) {} + + @Override + public void validate(final String value, final Map, Object> settings) { + if (value == null || value.isEmpty()) { + throw new IllegalArgumentException( + "Setting " + + INDEX_REMOTE_SEGMENT_STORE_REPOSITORY_SETTING.getKey() + + " should be provided with non-empty repository ID" + ); + } else { + validateRemoteStoreSettingEnabled(settings, INDEX_REMOTE_SEGMENT_STORE_REPOSITORY_SETTING); + } + } + + @Override + public Iterator> settings() { + final List> settings = Collections.singletonList(INDEX_REMOTE_STORE_ENABLED_SETTING); + return settings.iterator(); + } + }, + Property.IndexScope, + Property.PrivateIndex, + Property.Dynamic + ); + + private static void validateRemoteStoreSettingEnabled(final Map, Object> settings, Setting setting) { + final Boolean isRemoteSegmentStoreEnabled = (Boolean) settings.get(INDEX_REMOTE_STORE_ENABLED_SETTING); + if (isRemoteSegmentStoreEnabled == false) { + throw new IllegalArgumentException( + "Settings " + + setting.getKey() + + " can only be set/enabled when " + + INDEX_REMOTE_STORE_ENABLED_SETTING.getKey() + + " is set to true" + ); + } + } + + public static final Setting INDEX_REMOTE_TRANSLOG_REPOSITORY_SETTING = Setting.simpleString( + SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, + new Setting.Validator<>() { + + @Override + public void validate(final String value) {} + + @Override + public void validate(final String value, final Map, Object> settings) { + if (value == null || value.isEmpty()) { + throw new IllegalArgumentException( + "Setting " + INDEX_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey() + " should be provided with non-empty repository ID" + ); + } else { + final Boolean isRemoteTranslogStoreEnabled = (Boolean) settings.get(INDEX_REMOTE_STORE_ENABLED_SETTING); + if (isRemoteTranslogStoreEnabled == null || isRemoteTranslogStoreEnabled == false) { + throw new IllegalArgumentException( + "Settings " + + INDEX_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey() + + " can only be set/enabled when " + + INDEX_REMOTE_STORE_ENABLED_SETTING.getKey() + + " is set to true" + ); + } + } + } + + @Override + public Iterator> settings() { + final List> settings = Collections.singletonList(INDEX_REMOTE_STORE_ENABLED_SETTING); + return settings.iterator(); + } + }, + Property.IndexScope, + Property.PrivateIndex, + Property.Dynamic + ); + public static final String SETTING_AUTO_EXPAND_REPLICAS = "index.auto_expand_replicas"; public static final Setting INDEX_AUTO_EXPAND_REPLICAS_SETTING = AutoExpandReplicas.SETTING; + /** + * Blocks the API. + * + * @opensearch.internal + */ public enum APIBlock implements Writeable { READ_ONLY("read_only", INDEX_READ_ONLY_BLOCK), READ("read", INDEX_READ_BLOCK), @@ -373,7 +562,7 @@ public static APIBlock readFrom(StreamInput input) throws IOException { Function.identity(), Property.IndexScope ); - public static final String INDEX_UUID_NA_VALUE = "_na_"; + public static final String INDEX_UUID_NA_VALUE = Strings.UNKNOWN_UUID_VALUE; public static final String INDEX_ROUTING_REQUIRE_GROUP_PREFIX = "index.routing.allocation.require"; public static final String INDEX_ROUTING_INCLUDE_GROUP_PREFIX = "index.routing.allocation.include"; @@ -467,15 +656,15 @@ public static APIBlock readFrom(StreamInput input) throws IOException { private final State state; - private final ImmutableOpenMap aliases; + private final Map aliases; private final Settings settings; - private final ImmutableOpenMap mappings; + private final Map mappings; - private final ImmutableOpenMap customData; + private final Map customData; - private final ImmutableOpenIntMap> inSyncAllocationIds; + private final Map> inSyncAllocationIds; private final transient int totalNumberOfShards; @@ -488,7 +677,7 @@ public static APIBlock readFrom(StreamInput input) throws IOException { private final Version indexUpgradedVersion; private final ActiveShardCount waitForActiveShards; - private final ImmutableOpenMap rolloverInfos; + private final Map rolloverInfos; private final boolean isSystem; private IndexMetadata( @@ -502,10 +691,10 @@ private IndexMetadata( final int numberOfShards, final int numberOfReplicas, final Settings settings, - final ImmutableOpenMap mappings, - final ImmutableOpenMap aliases, - final ImmutableOpenMap customData, - final ImmutableOpenIntMap> inSyncAllocationIds, + final Map mappings, + final Map aliases, + final Map customData, + final Map> inSyncAllocationIds, final DiscoveryNodeFilters requireFilters, final DiscoveryNodeFilters initialRecoveryFilters, final DiscoveryNodeFilters includeFilters, @@ -515,7 +704,7 @@ private IndexMetadata( final int routingNumShards, final int routingPartitionSize, final ActiveShardCount waitForActiveShards, - final ImmutableOpenMap rolloverInfos, + final Map rolloverInfos, final boolean isSystem ) { @@ -534,10 +723,10 @@ private IndexMetadata( this.numberOfReplicas = numberOfReplicas; this.totalNumberOfShards = numberOfShards * (numberOfReplicas + 1); this.settings = settings; - this.mappings = mappings; - this.customData = customData; - this.aliases = aliases; - this.inSyncAllocationIds = inSyncAllocationIds; + this.mappings = Collections.unmodifiableMap(mappings); + this.customData = Collections.unmodifiableMap(customData); + this.aliases = Collections.unmodifiableMap(aliases); + this.inSyncAllocationIds = Collections.unmodifiableMap(inSyncAllocationIds); this.requireFilters = requireFilters; this.includeFilters = includeFilters; this.excludeFilters = excludeFilters; @@ -548,7 +737,7 @@ private IndexMetadata( this.routingFactor = routingNumShards / numberOfShards; this.routingPartitionSize = routingPartitionSize; this.waitForActiveShards = waitForActiveShards; - this.rolloverInfos = rolloverInfos; + this.rolloverInfos = Collections.unmodifiableMap(rolloverInfos); this.isSystem = isSystem; assert numberOfShards * routingFactor == routingNumShards : routingNumShards + " must be a multiple of " + numberOfShards; } @@ -656,7 +845,7 @@ public Settings getSettings() { return settings; } - public ImmutableOpenMap getAliases() { + public Map getAliases() { return this.aliases; } @@ -665,8 +854,8 @@ public ImmutableOpenMap getAliases() { */ @Nullable public MappingMetadata mapping() { - for (ObjectObjectCursor cursor : mappings) { - return cursor.value; + for (final MappingMetadata cursor : mappings.values()) { + return cursor; } return null; } @@ -682,7 +871,7 @@ public Index getResizeSourceIndex() { : null; } - ImmutableOpenMap getCustomData() { + Map getCustomData() { return this.customData; } @@ -690,11 +879,11 @@ public Map getCustomData(final String key) { return this.customData.get(key); } - public ImmutableOpenIntMap> getInSyncAllocationIds() { + public Map> getInSyncAllocationIds() { return inSyncAllocationIds; } - public ImmutableOpenMap getRolloverInfos() { + public Map getRolloverInfos() { return rolloverInfos; } @@ -814,6 +1003,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * A diff of index metadata. + * + * @opensearch.internal + */ private static class IndexMetadataDiff implements Diff { private final String index; @@ -825,11 +1019,11 @@ private static class IndexMetadataDiff implements Diff { private final long[] primaryTerms; private final State state; private final Settings settings; - private final Diff> mappings; - private final Diff> aliases; - private final Diff> customData; - private final Diff>> inSyncAllocationIds; - private final Diff> rolloverInfos; + private final Diff> mappings; + private final Diff> aliases; + private final Diff> customData; + private final Diff>> inSyncAllocationIds; + private final Diff> rolloverInfos; private final boolean isSystem; IndexMetadataDiff(IndexMetadata before, IndexMetadata after) { @@ -878,19 +1072,15 @@ private static class IndexMetadataDiff implements Diff { state = State.fromId(in.readByte()); settings = Settings.readSettingsFromStream(in); primaryTerms = in.readVLongArray(); - mappings = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), MAPPING_DIFF_VALUE_READER); - aliases = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), ALIAS_METADATA_DIFF_VALUE_READER); - customData = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_DIFF_VALUE_READER); - inSyncAllocationIds = DiffableUtils.readImmutableOpenIntMapDiff( + mappings = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), MAPPING_DIFF_VALUE_READER); + aliases = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), ALIAS_METADATA_DIFF_VALUE_READER); + customData = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_DIFF_VALUE_READER); + inSyncAllocationIds = DiffableUtils.readJdkMapDiff( in, DiffableUtils.getVIntKeySerializer(), DiffableUtils.StringSetValueSerializer.getInstance() ); - rolloverInfos = DiffableUtils.readImmutableOpenMapDiff( - in, - DiffableUtils.getStringKeySerializer(), - ROLLOVER_INFO_DIFF_VALUE_READER - ); + rolloverInfos = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), ROLLOVER_INFO_DIFF_VALUE_READER); if (in.getVersion().onOrAfter(SYSTEM_INDEX_FLAG_ADDED)) { isSystem = in.readBoolean(); } else { @@ -1002,26 +1192,26 @@ public void writeTo(StreamOutput out) throws IOException { writeSettingsToStream(settings, out); out.writeVLongArray(primaryTerms); out.writeVInt(mappings.size()); - for (ObjectCursor cursor : mappings.values()) { - cursor.value.writeTo(out); + for (final MappingMetadata cursor : mappings.values()) { + cursor.writeTo(out); } out.writeVInt(aliases.size()); - for (ObjectCursor cursor : aliases.values()) { - cursor.value.writeTo(out); + for (final AliasMetadata cursor : aliases.values()) { + cursor.writeTo(out); } out.writeVInt(customData.size()); - for (final ObjectObjectCursor cursor : customData) { - out.writeString(cursor.key); - cursor.value.writeTo(out); + for (final Map.Entry cursor : customData.entrySet()) { + out.writeString(cursor.getKey()); + cursor.getValue().writeTo(out); } out.writeVInt(inSyncAllocationIds.size()); - for (IntObjectCursor> cursor : inSyncAllocationIds) { - out.writeVInt(cursor.key); - DiffableUtils.StringSetValueSerializer.getInstance().write(cursor.value, out); + for (final Map.Entry> cursor : inSyncAllocationIds.entrySet()) { + out.writeVInt(cursor.getKey()); + DiffableUtils.StringSetValueSerializer.getInstance().write(cursor.getValue(), out); } out.writeVInt(rolloverInfos.size()); - for (ObjectCursor cursor : rolloverInfos.values()) { - cursor.value.writeTo(out); + for (final RolloverInfo cursor : rolloverInfos.values()) { + cursor.writeTo(out); } if (out.getVersion().onOrAfter(SYSTEM_INDEX_FLAG_ADDED)) { out.writeBoolean(isSystem); @@ -1040,6 +1230,11 @@ public static Builder builder(IndexMetadata indexMetadata) { return new Builder(indexMetadata); } + /** + * Builder of index metadata. + * + * @opensearch.internal + */ public static class Builder { private String index; @@ -1050,21 +1245,21 @@ public static class Builder { private long aliasesVersion = 1; private long[] primaryTerms = null; private Settings settings = Settings.Builder.EMPTY_SETTINGS; - private final ImmutableOpenMap.Builder mappings; - private final ImmutableOpenMap.Builder aliases; - private final ImmutableOpenMap.Builder customMetadata; - private final ImmutableOpenIntMap.Builder> inSyncAllocationIds; - private final ImmutableOpenMap.Builder rolloverInfos; + private final Map mappings; + private final Map aliases; + private final Map customMetadata; + private final Map> inSyncAllocationIds; + private final Map rolloverInfos; private Integer routingNumShards; private boolean isSystem; public Builder(String index) { this.index = index; - this.mappings = ImmutableOpenMap.builder(); - this.aliases = ImmutableOpenMap.builder(); - this.customMetadata = ImmutableOpenMap.builder(); - this.inSyncAllocationIds = ImmutableOpenIntMap.builder(); - this.rolloverInfos = ImmutableOpenMap.builder(); + this.mappings = new HashMap<>(); + this.aliases = new HashMap<>(); + this.customMetadata = new HashMap<>(); + this.inSyncAllocationIds = new HashMap<>(); + this.rolloverInfos = new HashMap<>(); this.isSystem = false; } @@ -1077,12 +1272,12 @@ public Builder(IndexMetadata indexMetadata) { this.aliasesVersion = indexMetadata.aliasesVersion; this.settings = indexMetadata.getSettings(); this.primaryTerms = indexMetadata.primaryTerms.clone(); - this.mappings = ImmutableOpenMap.builder(indexMetadata.mappings); - this.aliases = ImmutableOpenMap.builder(indexMetadata.aliases); - this.customMetadata = ImmutableOpenMap.builder(indexMetadata.customData); + this.mappings = new HashMap<>(indexMetadata.mappings); + this.aliases = new HashMap<>(indexMetadata.aliases); + this.customMetadata = new HashMap<>(indexMetadata.customData); this.routingNumShards = indexMetadata.routingNumShards; - this.inSyncAllocationIds = ImmutableOpenIntMap.builder(indexMetadata.inSyncAllocationIds); - this.rolloverInfos = ImmutableOpenMap.builder(indexMetadata.rolloverInfos); + this.inSyncAllocationIds = new HashMap<>(indexMetadata.inSyncAllocationIds); + this.rolloverInfos = new HashMap<>(indexMetadata.rolloverInfos); this.isSystem = indexMetadata.isSystem; } @@ -1157,7 +1352,7 @@ public Builder putMapping(String source) throws IOException { putMapping( new MappingMetadata( MapperService.SINGLE_MAPPING_NAME, - XContentHelper.convertToMap(XContentFactory.xContent(source), source, true) + XContentHelper.convertToMap(MediaTypeRegistry.xContent(source).xContent(), source, true) ) ); return this; @@ -1301,7 +1496,7 @@ public boolean isSystem() { } public IndexMetadata build() { - ImmutableOpenMap.Builder tmpAliases = aliases; + final Map tmpAliases = aliases; Settings tmpSettings = settings; /* @@ -1333,7 +1528,7 @@ public IndexMetadata build() { } // fill missing slots in inSyncAllocationIds with empty set if needed and make all entries immutable - ImmutableOpenIntMap.Builder> filledInSyncAllocationIds = ImmutableOpenIntMap.builder(); + final Map> filledInSyncAllocationIds = new HashMap<>(); for (int i = 0; i < numberOfShards; i++) { if (inSyncAllocationIds.containsKey(i)) { filledInSyncAllocationIds.put(i, Collections.unmodifiableSet(new HashSet<>(inSyncAllocationIds.get(i)))); @@ -1369,7 +1564,7 @@ public IndexMetadata build() { } else { initialRecoveryFilters = DiscoveryNodeFilters.buildOrUpdateFromKeyValue(null, OR, initialRecoveryMap); } - Version indexCreatedVersion = Version.indexCreated(settings); + Version indexCreatedVersion = indexCreated(settings); Version indexUpgradedVersion = settings.getAsVersion(IndexMetadata.SETTING_VERSION_UPGRADED, indexCreatedVersion); if (primaryTerms == null) { @@ -1411,10 +1606,10 @@ public IndexMetadata build() { numberOfShards, numberOfReplicas, tmpSettings, - mappings.build(), - tmpAliases.build(), - customMetadata.build(), - filledInSyncAllocationIds.build(), + mappings, + tmpAliases, + customMetadata, + filledInSyncAllocationIds, requireFilters, initialRecoveryFilters, includeFilters, @@ -1424,7 +1619,7 @@ public IndexMetadata build() { getRoutingNumShards(), routingPartitionSize, waitForActiveShards, - rolloverInfos.build(), + rolloverInfos, isSystem ); } @@ -1480,15 +1675,15 @@ public static void toXContent(IndexMetadata indexMetadata, XContentBuilder build builder.endObject(); } - for (ObjectObjectCursor cursor : indexMetadata.customData) { - builder.field(cursor.key); - builder.map(cursor.value); + for (final Map.Entry cursor : indexMetadata.customData.entrySet()) { + builder.field(cursor.getKey()); + builder.map(cursor.getValue()); } if (context != Metadata.XContentContext.API) { builder.startObject(KEY_ALIASES); - for (ObjectCursor cursor : indexMetadata.getAliases().values()) { - AliasMetadata.Builder.toXContent(cursor.value, builder, params); + for (final AliasMetadata cursor : indexMetadata.getAliases().values()) { + AliasMetadata.Builder.toXContent(cursor, builder, params); } builder.endObject(); @@ -1499,8 +1694,8 @@ public static void toXContent(IndexMetadata indexMetadata, XContentBuilder build builder.endArray(); } else { builder.startArray(KEY_ALIASES); - for (ObjectCursor cursor : indexMetadata.getAliases().keys()) { - builder.value(cursor.value); + for (final String cursor : indexMetadata.getAliases().keySet()) { + builder.value(cursor); } builder.endArray(); @@ -1512,9 +1707,9 @@ public static void toXContent(IndexMetadata indexMetadata, XContentBuilder build } builder.startObject(KEY_IN_SYNC_ALLOCATIONS); - for (IntObjectCursor> cursor : indexMetadata.inSyncAllocationIds) { - builder.startArray(String.valueOf(cursor.key)); - for (String allocationId : cursor.value) { + for (final Map.Entry> cursor : indexMetadata.inSyncAllocationIds.entrySet()) { + builder.startArray(String.valueOf(cursor.getKey())); + for (String allocationId : cursor.getValue()) { builder.value(allocationId); } builder.endArray(); @@ -1522,8 +1717,8 @@ public static void toXContent(IndexMetadata indexMetadata, XContentBuilder build builder.endObject(); builder.startObject(KEY_ROLLOVER_INFOS); - for (ObjectCursor cursor : indexMetadata.getRolloverInfos().values()) { - cursor.value.toXContent(builder, params); + for (final RolloverInfo cursor : indexMetadata.getRolloverInfos().values()) { + cursor.toXContent(builder, params); } builder.endObject(); builder.field(KEY_SYSTEM, indexMetadata.isSystem); @@ -1627,7 +1822,7 @@ public static IndexMetadata fromXContent(XContentParser parser) throws IOExcepti } } } else if (KEY_PRIMARY_TERMS.equals(currentFieldName)) { - LongArrayList list = new LongArrayList(); + final List list = new ArrayList<>(); while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { if (token == XContentParser.Token.VALUE_NUMBER) { list.add(parser.longValue()); @@ -1635,7 +1830,7 @@ public static IndexMetadata fromXContent(XContentParser parser) throws IOExcepti throw new IllegalStateException("found a non-numeric value under [" + KEY_PRIMARY_TERMS + "]"); } } - builder.primaryTerms(list.toArray()); + builder.primaryTerms(list.stream().mapToLong(i -> i).toArray()); } else { throw new IllegalArgumentException("Unexpected field for an array " + currentFieldName); } @@ -1664,13 +1859,14 @@ public static IndexMetadata fromXContent(XContentParser parser) throws IOExcepti throw new IllegalArgumentException("Unexpected token " + token); } } - if (Assertions.ENABLED) { + // Reference: + // https://github.com/opensearch-project/OpenSearch/blob/4dde0f2a3b445b2fc61dab29c5a2178967f4a3e3/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java#L1620-L1628 + Version legacyVersion = LegacyESVersion.fromId(6050099); + if (Assertions.ENABLED && indexCreated(builder.settings).onOrAfter(legacyVersion)) { assert mappingVersion : "mapping version should be present for indices"; - } - if (Assertions.ENABLED) { assert settingsVersion : "settings version should be present for indices"; } - if (Assertions.ENABLED && Version.indexCreated(builder.settings).onOrAfter(LegacyESVersion.V_7_2_0)) { + if (Assertions.ENABLED && indexCreated(builder.settings).onOrAfter(LegacyESVersion.V_7_2_0)) { assert aliasesVersion : "aliases version should be present for indices created on or after 7.2.0"; } return builder.build(); @@ -1707,10 +1903,29 @@ public static Settings addHumanReadableSettings(Settings settings) { FORMAT_PARAMS = new MapParams(params); } + /** + * Return the version the index was created from the provided index settings + * + * This looks for the presence of the {@link Version} object with key {@link IndexMetadata#SETTING_VERSION_CREATED} + */ + public static Version indexCreated(final Settings indexSettings) { + final Version indexVersion = SETTING_INDEX_VERSION_CREATED.get(indexSettings); + if (indexVersion.equals(Version.V_EMPTY)) { + final String message = String.format( + Locale.ROOT, + "[%s] is not present in the index settings for index with UUID [%s]", + SETTING_INDEX_VERSION_CREATED.getKey(), + indexSettings.get(IndexMetadata.SETTING_INDEX_UUID) + ); + throw new IllegalStateException(message); + } + return indexVersion; + } + /** * State format for {@link IndexMetadata} to write to and load from disk */ - public static final MetadataStateFormat FORMAT = new MetadataStateFormat(INDEX_STATE_FILE_PREFIX) { + public static final MetadataStateFormat FORMAT = new MetadataStateFormat<>(INDEX_STATE_FILE_PREFIX) { @Override public void toXContent(XContentBuilder builder, IndexMetadata state) throws IOException { @@ -1774,8 +1989,8 @@ public static ShardId selectCloneShard(int shardId, IndexMetadata sourceIndexMet throw new IllegalArgumentException( "the number of target shards (" + numTargetShards - + ") must be the same as the number of " - + " source shards ( " + + ") must be the same as the number of" + + " source shards (" + numSourceShards + ")" ); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/IndexNameExpressionResolver.java b/server/src/main/java/org/opensearch/cluster/metadata/IndexNameExpressionResolver.java index bd8535d18c9cb..86961010c77d6 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/IndexNameExpressionResolver.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexNameExpressionResolver.java @@ -40,18 +40,18 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.common.Booleans; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenMap; +import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.collect.Tuple; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.regex.Regex; import org.opensearch.common.time.DateFormatter; import org.opensearch.common.time.DateMathParser; import org.opensearch.common.time.DateUtils; -import org.opensearch.common.util.CollectionUtils; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.common.util.set.Sets; -import org.opensearch.index.Index; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.util.CollectionUtils; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexNotFoundException; import org.opensearch.indices.IndexClosedException; import org.opensearch.indices.InvalidIndexNameException; @@ -76,6 +76,12 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; +/** + * Resolves index name from an expression + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") public class IndexNameExpressionResolver { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(IndexNameExpressionResolver.class); @@ -85,10 +91,7 @@ public class IndexNameExpressionResolver { private final DateMathExpressionResolver dateMathExpressionResolver = new DateMathExpressionResolver(); private final WildcardExpressionResolver wildcardExpressionResolver = new WildcardExpressionResolver(); - private final List expressionResolvers = org.opensearch.common.collect.List.of( - dateMathExpressionResolver, - wildcardExpressionResolver - ); + private final List expressionResolvers = List.of(dateMathExpressionResolver, wildcardExpressionResolver); private final ThreadContext threadContext; @@ -172,7 +175,7 @@ public List dataStreamNames(ClusterState state, IndicesOptions options, } List dataStreams = wildcardExpressionResolver.resolve(context, Arrays.asList(indexExpressions)); - return ((dataStreams == null) ? org.opensearch.common.collect.List.of() : dataStreams).stream() + return ((dataStreams == null) ? List.of() : dataStreams).stream() .map(x -> state.metadata().getIndicesLookup().get(x)) .filter(Objects::nonNull) .filter(ia -> ia.getType() == IndexAbstraction.Type.DATA_STREAM) @@ -567,12 +570,11 @@ public String[] indexAliases( return null; } - final ImmutableOpenMap indexAliases = indexMetadata.getAliases(); + final Map indexAliases = indexMetadata.getAliases(); final AliasMetadata[] aliasCandidates; if (iterateIndexAliases(indexAliases.size(), resolvedExpressions.size())) { // faster to iterate indexAliases aliasCandidates = StreamSupport.stream(Spliterators.spliteratorUnknownSize(indexAliases.values().iterator(), 0), false) - .map(cursor -> cursor.value) .filter(aliasMetadata -> resolvedExpressions.contains(aliasMetadata.alias())) .toArray(AliasMetadata[]::new); } else { @@ -768,6 +770,11 @@ public boolean isSystemIndexAccessAllowed() { return Booleans.parseBoolean(threadContext.getHeader(SYSTEM_INDEX_ACCESS_CONTROL_HEADER_KEY), true); } + /** + * Context for the resolver. + * + * @opensearch.internal + */ public static class Context { private final ClusterState state; @@ -907,6 +914,8 @@ private interface ExpressionResolver { /** * Resolves alias/index name expressions with wildcards into the corresponding concrete indices/aliases + * + * @opensearch.internal */ static final class WildcardExpressionResolver implements ExpressionResolver { @@ -1187,6 +1196,11 @@ private static List resolveEmptyOrTrivialWildcard(IndicesOptions options } } + /** + * A date math expression resolver. + * + * @opensearch.internal + */ public static final class DateMathExpressionResolver implements ExpressionResolver { private static final DateFormatter DEFAULT_DATE_FORMATTER = DateFormatter.forPattern("uuuu.MM.dd"); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/IndexTemplateMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/IndexTemplateMetadata.java index 810365589ae1f..272bb132197af 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/IndexTemplateMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexTemplateMetadata.java @@ -31,37 +31,42 @@ package org.opensearch.cluster.metadata; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.opensearch.OpenSearchParseException; import org.opensearch.cluster.AbstractDiffable; import org.opensearch.cluster.Diff; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.collect.ImmutableOpenMap; +import org.opensearch.common.annotation.PublicApi; import org.opensearch.common.collect.MapBuilder; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import java.io.IOException; import java.io.UncheckedIOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +/** + * Metadata for Index Templates + * + * @opensearch.api + */ +@PublicApi(since = "1.0.0") public class IndexTemplateMetadata extends AbstractDiffable { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(IndexTemplateMetadata.class); @@ -95,9 +100,9 @@ public class IndexTemplateMetadata extends AbstractDiffable mappings; + private final Map mappings; - private final ImmutableOpenMap aliases; + private final Map aliases; public IndexTemplateMetadata( String name, @@ -105,8 +110,8 @@ public IndexTemplateMetadata( Integer version, List patterns, Settings settings, - ImmutableOpenMap mappings, - ImmutableOpenMap aliases + final Map mappings, + final Map aliases ) { if (patterns == null || patterns.isEmpty()) { throw new IllegalArgumentException("Index patterns must not be null or empty; got " + patterns); @@ -116,7 +121,7 @@ public IndexTemplateMetadata( this.version = version; this.patterns = patterns; this.settings = settings; - this.mappings = mappings; + this.mappings = Collections.unmodifiableMap(mappings); if (this.mappings.size() > 1) { deprecationLogger.deprecate( "index-templates", @@ -124,7 +129,7 @@ public IndexTemplateMetadata( name ); } - this.aliases = aliases; + this.aliases = Collections.unmodifiableMap(aliases); } public String name() { @@ -165,18 +170,18 @@ public CompressedXContent mappings() { if (this.mappings.isEmpty()) { return null; } - return this.mappings.iterator().next().value; + return this.mappings.values().iterator().next(); } public CompressedXContent getMappings() { return this.mappings(); } - public ImmutableOpenMap aliases() { + public Map aliases() { return this.aliases; } - public ImmutableOpenMap getAliases() { + public Map getAliases() { return this.aliases; } @@ -241,13 +246,13 @@ public void writeTo(StreamOutput out) throws IOException { out.writeStringCollection(patterns); Settings.writeSettingsToStream(settings, out); out.writeVInt(mappings.size()); - for (ObjectObjectCursor cursor : mappings) { - out.writeString(cursor.key); - cursor.value.writeTo(out); + for (final Map.Entry cursor : mappings.entrySet()) { + out.writeString(cursor.getKey()); + cursor.getValue().writeTo(out); } out.writeVInt(aliases.size()); - for (ObjectCursor cursor : aliases.values()) { - cursor.value.writeTo(out); + for (final AliasMetadata cursor : aliases.values()) { + cursor.writeTo(out); } out.writeOptionalVInt(version); } @@ -259,12 +264,17 @@ public String toString() { builder.startObject(); Builder.toXContentWithTypes(this, builder, ToXContent.EMPTY_PARAMS); builder.endObject(); - return Strings.toString(builder); + return builder.toString(); } catch (IOException e) { throw new UncheckedIOException(e); } } + /** + * Builder of index template metadata. + * + * @opensearch.internal + */ public static class Builder { private static final Set VALID_FIELDS = Sets.newHashSet( @@ -286,14 +296,14 @@ public static class Builder { private Settings settings = Settings.Builder.EMPTY_SETTINGS; - private final ImmutableOpenMap.Builder mappings; + private final Map mappings; - private final ImmutableOpenMap.Builder aliases; + private final Map aliases; public Builder(String name) { this.name = name; - mappings = ImmutableOpenMap.builder(); - aliases = ImmutableOpenMap.builder(); + mappings = new HashMap<>(); + aliases = new HashMap<>(); } public Builder(IndexTemplateMetadata indexTemplateMetadata) { @@ -303,8 +313,8 @@ public Builder(IndexTemplateMetadata indexTemplateMetadata) { patterns(indexTemplateMetadata.patterns()); settings(indexTemplateMetadata.settings()); - mappings = ImmutableOpenMap.builder(indexTemplateMetadata.mappings); - aliases = ImmutableOpenMap.builder(indexTemplateMetadata.aliases()); + mappings = new HashMap<>(indexTemplateMetadata.mappings); + aliases = new HashMap<>(indexTemplateMetadata.aliases()); } public Builder order(int order) { @@ -353,7 +363,7 @@ public Builder putAlias(AliasMetadata.Builder aliasMetadata) { } public IndexTemplateMetadata build() { - return new IndexTemplateMetadata(name, order, version, indexPatterns, settings, mappings.build(), aliases.build()); + return new IndexTemplateMetadata(name, order, version, indexPatterns, settings, mappings, aliases); } /** @@ -425,8 +435,8 @@ private static void toInnerXContent( } builder.startObject("aliases"); - for (ObjectCursor cursor : indexTemplateMetadata.aliases().values()) { - AliasMetadata.Builder.toXContent(cursor.value, builder, params); + for (final AliasMetadata cursor : indexTemplateMetadata.aliases().values()) { + AliasMetadata.Builder.toXContent(cursor, builder, params); } builder.endObject(); } @@ -471,7 +481,7 @@ public static IndexTemplateMetadata fromXContent(XContentParser parser, String t Map mappingSource = MapBuilder.newMapBuilder() .put(mappingType, parser.mapOrdered()) .map(); - builder.putMapping(mappingType, Strings.toString(XContentFactory.jsonBuilder().map(mappingSource))); + builder.putMapping(mappingType, XContentFactory.jsonBuilder().map(mappingSource).toString()); } } } else if ("aliases".equals(currentFieldName)) { @@ -487,7 +497,7 @@ public static IndexTemplateMetadata fromXContent(XContentParser parser, String t Map mapping = parser.mapOrdered(); if (mapping.size() == 1) { String mappingType = mapping.keySet().iterator().next(); - String mappingSource = Strings.toString(XContentFactory.jsonBuilder().map(mapping)); + String mappingSource = XContentFactory.jsonBuilder().map(mapping).toString(); if (mappingSource == null) { // crap, no mapping source, warn? diff --git a/server/src/main/java/org/opensearch/cluster/metadata/Manifest.java b/server/src/main/java/org/opensearch/cluster/metadata/Manifest.java index 8205886cf9df6..79a8f4fb95f51 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/Manifest.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/Manifest.java @@ -32,15 +32,15 @@ package org.opensearch.cluster.metadata; -import org.opensearch.common.ParseField; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ObjectParser; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.ParseField; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.ObjectParser; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.gateway.MetadataStateFormat; -import org.opensearch.index.Index; import java.io.IOException; import java.util.Collections; @@ -55,6 +55,8 @@ * When new version of metadata is written it's assigned some generation long value. * Global metadata generation could be obtained by calling {@link #getGlobalGeneration()}. * Index metadata generation could be obtained by calling {@link #getIndexGenerations()}. + * + * @opensearch.internal */ public class Manifest implements ToXContentFragment { // TODO revisit missing and unknown constants once Zen2 BWC is ready @@ -231,6 +233,11 @@ public boolean isGlobalGenerationMissing() { return globalGeneration == MISSING_GLOBAL_GENERATION; } + /** + * An index entry. + * + * @opensearch.internal + */ private static final class IndexEntry implements ToXContentFragment { private static final ParseField INDEX_GENERATION_PARSE_FIELD = new ParseField("generation"); private static final ParseField INDEX_PARSE_FIELD = new ParseField("index"); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java index 620542f8f1bde..89eda77648021 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java @@ -36,13 +36,13 @@ import org.opensearch.OpenSearchParseException; import org.opensearch.cluster.AbstractDiffable; import org.opensearch.cluster.Diff; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.MapperService; @@ -56,6 +56,8 @@ /** * Mapping configuration for a type. + * + * @opensearch.internal */ public class MappingMetadata extends AbstractDiffable { public static final MappingMetadata EMPTY_MAPPINGS = new MappingMetadata(MapperService.SINGLE_MAPPING_NAME, Collections.emptyMap()); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/Metadata.java b/server/src/main/java/org/opensearch/cluster/metadata/Metadata.java index 6e9c30877f9c2..e7dcf10fe6ca2 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/Metadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/Metadata.java @@ -32,12 +32,8 @@ package org.opensearch.cluster.metadata; -import com.carrotsearch.hppc.ObjectHashSet; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; - -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.lucene.util.CollectionUtil; import org.opensearch.LegacyESVersion; import org.opensearch.action.AliasesRequest; @@ -51,28 +47,28 @@ import org.opensearch.cluster.block.ClusterBlock; import org.opensearch.cluster.block.ClusterBlockLevel; import org.opensearch.cluster.coordination.CoordinationMetadata; +import org.opensearch.cluster.decommission.DecommissionAttributeMetadata; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; import org.opensearch.common.UUIDs; -import org.opensearch.common.collect.HppcMaps; -import org.opensearch.common.collect.ImmutableOpenMap; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.regex.Regex; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedObjectNotFoundException; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.ToXContentFragment; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.index.Index; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedObjectNotFoundException; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.ToXContentFragment; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.gateway.MetadataStateFormat; -import org.opensearch.index.Index; import org.opensearch.index.IndexNotFoundException; +import org.opensearch.indices.replication.common.ReplicationType; import org.opensearch.plugins.MapperPlugin; -import org.opensearch.rest.RestStatus; import java.io.IOException; import java.util.ArrayList; @@ -89,6 +85,7 @@ import java.util.Optional; import java.util.Set; import java.util.SortedMap; +import java.util.Spliterators; import java.util.TreeMap; import java.util.function.Function; import java.util.function.Predicate; @@ -99,14 +96,40 @@ import static org.opensearch.common.settings.Settings.readSettingsFromStream; import static org.opensearch.common.settings.Settings.writeSettingsToStream; +/** + * Metadata information + * + * @opensearch.internal + */ public class Metadata implements Iterable, Diffable, ToXContentFragment { private static final Logger logger = LogManager.getLogger(Metadata.class); public static final String ALL = "_all"; - public static final String UNKNOWN_CLUSTER_UUID = "_na_"; + public static final String UNKNOWN_CLUSTER_UUID = Strings.UNKNOWN_UUID_VALUE; public static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+$"); + /** + * Utility to identify whether input index uses SEGMENT replication strategy in established cluster state metadata. + * Note: Method intended for use by other plugins as well. + * + * @param indexName Index name + * @return true if index uses SEGMENT replication, false otherwise + */ + public boolean isSegmentReplicationEnabled(String indexName) { + return Optional.ofNullable(index(indexName)) + .map( + indexMetadata -> ReplicationType.parseString(indexMetadata.getSettings().get(IndexMetadata.SETTING_REPLICATION_TYPE)) + .equals(ReplicationType.SEGMENT) + ) + .orElse(false); + } + + /** + * Context of the XContent. + * + * @opensearch.internal + */ public enum XContentContext { /* Custom metadata should be returns as part of API call */ API, @@ -141,11 +164,23 @@ public enum XContentContext { */ public static EnumSet ALL_CONTEXTS = EnumSet.allOf(XContentContext.class); + /** + * Custom metadata. + * + * @opensearch.internal + */ public interface Custom extends NamedDiffable, ToXContentFragment, ClusterState.FeatureAware { EnumSet context(); } + public static final Setting DEFAULT_REPLICA_COUNT_SETTING = Setting.intSetting( + "cluster.default_number_of_replicas", + 1, + Property.Dynamic, + Property.NodeScope + ); + public static final Setting SETTING_READ_ONLY_SETTING = Setting.boolSetting( "cluster.blocks.read_only", false, @@ -163,6 +198,16 @@ public interface Custom extends NamedDiffable, ToXContentFragment, Clust EnumSet.of(ClusterBlockLevel.WRITE, ClusterBlockLevel.METADATA_WRITE) ); + public static final ClusterBlock CLUSTER_CREATE_INDEX_BLOCK = new ClusterBlock( + 10, + "cluster create-index blocked (api)", + false, + false, + false, + RestStatus.FORBIDDEN, + EnumSet.of(ClusterBlockLevel.CREATE_INDEX) + ); + public static final Setting SETTING_READ_ONLY_ALLOW_DELETE_SETTING = Setting.boolSetting( "cluster.blocks.read_only_allow_delete", false, @@ -170,6 +215,13 @@ public interface Custom extends NamedDiffable, ToXContentFragment, Clust Property.NodeScope ); + public static final Setting SETTING_CREATE_INDEX_BLOCK_SETTING = Setting.boolSetting( + "cluster.blocks.create_index", + false, + Property.Dynamic, + Property.NodeScope + ); + public static final ClusterBlock CLUSTER_READ_ONLY_ALLOW_DELETE_BLOCK = new ClusterBlock( 13, "cluster read-only / allow delete (api)", @@ -204,9 +256,9 @@ public interface Custom extends NamedDiffable, ToXContentFragment, Clust private final Settings persistentSettings; private final Settings settings; private final DiffableStringMap hashesOfConsistentSettings; - private final ImmutableOpenMap indices; - private final ImmutableOpenMap templates; - private final ImmutableOpenMap customs; + private final Map indices; + private final Map templates; + private final Map customs; private final transient int totalNumberOfShards; // Transient ? not serializable anyway? private final int totalOpenIndexShards; @@ -228,9 +280,9 @@ public interface Custom extends NamedDiffable, ToXContentFragment, Clust Settings transientSettings, Settings persistentSettings, DiffableStringMap hashesOfConsistentSettings, - ImmutableOpenMap indices, - ImmutableOpenMap templates, - ImmutableOpenMap customs, + final Map indices, + final Map templates, + final Map customs, String[] allIndices, String[] visibleIndices, String[] allOpenIndices, @@ -247,15 +299,15 @@ public interface Custom extends NamedDiffable, ToXContentFragment, Clust this.persistentSettings = persistentSettings; this.settings = Settings.builder().put(persistentSettings).put(transientSettings).build(); this.hashesOfConsistentSettings = hashesOfConsistentSettings; - this.indices = indices; - this.customs = customs; - this.templates = templates; + this.indices = Collections.unmodifiableMap(indices); + this.customs = Collections.unmodifiableMap(customs); + this.templates = Collections.unmodifiableMap(templates); int totalNumberOfShards = 0; int totalOpenIndexShards = 0; - for (ObjectCursor cursor : indices.values()) { - totalNumberOfShards += cursor.value.getTotalNumberOfShards(); - if (IndexMetadata.State.OPEN.equals(cursor.value.getState())) { - totalOpenIndexShards += cursor.value.getTotalNumberOfShards(); + for (IndexMetadata cursor : indices.values()) { + totalNumberOfShards += cursor.getTotalNumberOfShards(); + if (IndexMetadata.State.OPEN.equals(cursor.getState())) { + totalOpenIndexShards += cursor.getTotalNumberOfShards(); } } this.totalNumberOfShards = totalNumberOfShards; @@ -319,8 +371,7 @@ public boolean hasAlias(String alias) { } public boolean equalsAliases(Metadata other) { - for (ObjectCursor cursor : other.indices().values()) { - IndexMetadata otherIndex = cursor.value; + for (IndexMetadata otherIndex : other.indices().values()) { IndexMetadata thisIndex = index(otherIndex.getIndex()); if (thisIndex == null) { return false; @@ -345,7 +396,7 @@ public SortedMap getIndicesLookup() { * @return A map of index name to the list of aliases metadata. If a concrete index does not have matching * aliases then the result will not include the index's key. */ - public ImmutableOpenMap> findAllAliases(final String[] concreteIndices) { + public Map> findAllAliases(final String[] concreteIndices) { return findAliases(Strings.EMPTY_ARRAY, concreteIndices); } @@ -358,7 +409,7 @@ public ImmutableOpenMap> findAllAliases(final String * @return A map of index name to the list of aliases metadata. If a concrete index does not have matching * aliases then the result will not include the index's key. */ - public ImmutableOpenMap> findAliases(final AliasesRequest aliasesRequest, final String[] concreteIndices) { + public Map> findAliases(final AliasesRequest aliasesRequest, final String[] concreteIndices) { return findAliases(aliasesRequest.aliases(), concreteIndices); } @@ -371,11 +422,11 @@ public ImmutableOpenMap> findAliases(final AliasesRe * @return A map of index name to the list of aliases metadata. If a concrete index does not have matching * aliases then the result will not include the index's key. */ - private ImmutableOpenMap> findAliases(final String[] aliases, final String[] concreteIndices) { + private Map> findAliases(final String[] aliases, final String[] concreteIndices) { assert aliases != null; assert concreteIndices != null; if (concreteIndices.length == 0) { - return ImmutableOpenMap.of(); + return Map.of(); } String[] patterns = new String[aliases.length]; boolean[] include = new boolean[aliases.length]; @@ -390,12 +441,11 @@ private ImmutableOpenMap> findAliases(final String[] } } boolean matchAllAliases = patterns.length == 0; - ImmutableOpenMap.Builder> mapBuilder = ImmutableOpenMap.builder(); + final Map> mapBuilder = new HashMap<>(); for (String index : concreteIndices) { IndexMetadata indexMetadata = indices.get(index); List filteredValues = new ArrayList<>(); - for (ObjectCursor cursor : indexMetadata.getAliases().values()) { - AliasMetadata value = cursor.value; + for (final AliasMetadata value : indexMetadata.getAliases().values()) { boolean matched = matchAllAliases; String alias = value.alias(); for (int i = 0; i < patterns.length; i++) { @@ -418,39 +468,7 @@ private ImmutableOpenMap> findAliases(final String[] mapBuilder.put(index, Collections.unmodifiableList(filteredValues)); } } - return mapBuilder.build(); - } - - /** - * Checks if at least one of the specified aliases exists in the specified concrete indices. Wildcards are supported in the - * alias names for partial matches. - * - * @param aliases The names of the index aliases to find - * @param concreteIndices The concrete indexes the index aliases must point to order to be returned. - * @return whether at least one of the specified aliases exists in one of the specified concrete indices. - */ - public boolean hasAliases(final String[] aliases, String[] concreteIndices) { - assert aliases != null; - assert concreteIndices != null; - if (concreteIndices.length == 0) { - return false; - } - - Iterable intersection = HppcMaps.intersection(ObjectHashSet.from(concreteIndices), indices.keys()); - for (String index : intersection) { - IndexMetadata indexMetadata = indices.get(index); - List filteredValues = new ArrayList<>(); - for (ObjectCursor cursor : indexMetadata.getAliases().values()) { - AliasMetadata value = cursor.value; - if (Regex.simpleMatch(aliases, value.alias())) { - filteredValues.add(value); - } - } - if (!filteredValues.isEmpty()) { - return true; - } - } - return false; + return mapBuilder; } /** @@ -461,29 +479,27 @@ public boolean hasAliases(final String[] aliases, String[] concreteIndices) { * @see MapperPlugin#getFieldFilter() * */ - public ImmutableOpenMap findMappings(String[] concreteIndices, Function> fieldFilter) + public Map findMappings(String[] concreteIndices, Function> fieldFilter) throws IOException { assert concreteIndices != null; if (concreteIndices.length == 0) { - return ImmutableOpenMap.of(); + return Map.of(); } - ImmutableOpenMap.Builder indexMapBuilder = ImmutableOpenMap.builder(); - Iterable intersection = HppcMaps.intersection(ObjectHashSet.from(concreteIndices), indices.keys()); - for (String index : intersection) { - IndexMetadata indexMetadata = indices.get(index); - Predicate fieldPredicate = fieldFilter.apply(index); - indexMapBuilder.put(index, filterFields(indexMetadata.mapping(), fieldPredicate)); - } - return indexMapBuilder.build(); + final Map indexMapBuilder = new HashMap<>(); + Arrays.stream(concreteIndices) + .filter(indices.keySet()::contains) + .forEach((idx) -> indexMapBuilder.put(idx, filterFields(indices.get(idx).mapping(), fieldFilter.apply(idx)))); + + return Collections.unmodifiableMap(indexMapBuilder); } /** * Finds the parent data streams, if any, for the specified concrete indices. */ - public ImmutableOpenMap findDataStreams(String[] concreteIndices) { + public Map findDataStreams(String[] concreteIndices) { assert concreteIndices != null; - final ImmutableOpenMap.Builder builder = ImmutableOpenMap.builder(); + final Map builder = new HashMap<>(); final SortedMap lookup = getIndicesLookup(); for (String indexName : concreteIndices) { IndexAbstraction index = lookup.get(indexName); @@ -493,7 +509,7 @@ public ImmutableOpenMap findDataStreams(Str builder.put(indexName, index.getParentDataStream()); } } - return builder.build(); + return Collections.unmodifiableMap(builder); } @SuppressWarnings("unchecked") @@ -778,20 +794,20 @@ public IndexMetadata getIndexSafe(Index index) { throw new IndexNotFoundException(index); } - public ImmutableOpenMap indices() { + public Map indices() { return this.indices; } - public ImmutableOpenMap getIndices() { + public Map getIndices() { return indices(); } - public ImmutableOpenMap templates() { + public Map templates() { return this.templates; } - public ImmutableOpenMap getTemplates() { - return this.templates; + public Map getTemplates() { + return templates(); } public Map componentTemplates() { @@ -812,14 +828,18 @@ public Map dataStreams() { .orElse(Collections.emptyMap()); } - public ImmutableOpenMap customs() { - return this.customs; + public DecommissionAttributeMetadata decommissionAttributeMetadata() { + return custom(DecommissionAttributeMetadata.TYPE); } - public ImmutableOpenMap getCustoms() { + public Map customs() { return this.customs; } + public Map getCustoms() { + return this.customs(); + } + /** * The collection of index deletions in the cluster. */ @@ -827,6 +847,14 @@ public IndexGraveyard indexGraveyard() { return custom(IndexGraveyard.TYPE); } + /** + * * + * @return The weighted routing metadata for search requests + */ + public WeightedRoutingMetadata weightedRoutingMetadata() { + return custom(WeightedRoutingMetadata.TYPE); + } + public T custom(String type) { return (T) customs.get(type); } @@ -888,7 +916,7 @@ public boolean routingRequired(String concreteIndex) { @Override public Iterator iterator() { - return indices.valuesIt(); + return indices.values().iterator(); } public static boolean isGlobalStateEquals(Metadata metadata1, Metadata metadata2) { @@ -912,15 +940,15 @@ public static boolean isGlobalStateEquals(Metadata metadata1, Metadata metadata2 } // Check if any persistent metadata needs to be saved int customCount1 = 0; - for (ObjectObjectCursor cursor : metadata1.customs) { - if (cursor.value.context().contains(XContentContext.GATEWAY)) { - if (!cursor.value.equals(metadata2.custom(cursor.key))) return false; + for (Map.Entry cursor : metadata1.customs.entrySet()) { + if (cursor.getValue().context().contains(XContentContext.GATEWAY)) { + if (!cursor.getValue().equals(metadata2.custom(cursor.getKey()))) return false; customCount1++; } } int customCount2 = 0; - for (ObjectCursor cursor : metadata2.customs.values()) { - if (cursor.value.context().contains(XContentContext.GATEWAY)) { + for (final Custom cursor : metadata2.customs.values()) { + if (cursor.context().contains(XContentContext.GATEWAY)) { customCount2++; } } @@ -947,6 +975,11 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder; } + /** + * A diff of metadata. + * + * @opensearch.internal + */ private static class MetadataDiff implements Diff { private final long version; @@ -956,9 +989,9 @@ private static class MetadataDiff implements Diff { private final Settings transientSettings; private final Settings persistentSettings; private final Diff hashesOfConsistentSettings; - private final Diff> indices; - private final Diff> templates; - private final Diff> customs; + private final Diff> indices; + private final Diff> templates; + private final Diff> customs; MetadataDiff(Metadata before, Metadata after) { clusterUUID = after.clusterUUID; @@ -996,9 +1029,9 @@ private static class MetadataDiff implements Diff { } else { hashesOfConsistentSettings = DiffableStringMap.DiffableStringMapDiff.EMPTY; } - indices = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), INDEX_METADATA_DIFF_VALUE_READER); - templates = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), TEMPLATES_DIFF_VALUE_READER); - customs = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); + indices = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), INDEX_METADATA_DIFF_VALUE_READER); + templates = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), TEMPLATES_DIFF_VALUE_READER); + customs = DiffableUtils.readJdkMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER); } @Override @@ -1089,20 +1122,20 @@ public void writeTo(StreamOutput out) throws IOException { indexMetadata.writeTo(out); } out.writeVInt(templates.size()); - for (ObjectCursor cursor : templates.values()) { - cursor.value.writeTo(out); + for (final IndexTemplateMetadata cursor : templates.values()) { + cursor.writeTo(out); } // filter out custom states not supported by the other node int numberOfCustoms = 0; - for (final ObjectCursor cursor : customs.values()) { - if (FeatureAware.shouldSerialize(out, cursor.value)) { + for (final Custom cursor : customs.values()) { + if (FeatureAware.shouldSerialize(out, cursor)) { numberOfCustoms++; } } out.writeVInt(numberOfCustoms); - for (final ObjectCursor cursor : customs.values()) { - if (FeatureAware.shouldSerialize(out, cursor.value)) { - out.writeNamedWriteable(cursor.value); + for (final Custom cursor : customs.values()) { + if (FeatureAware.shouldSerialize(out, cursor)) { + out.writeNamedWriteable(cursor); } } } @@ -1115,6 +1148,11 @@ public static Builder builder(Metadata metadata) { return new Builder(metadata); } + /** + * Builder of metadata. + * + * @opensearch.internal + */ public static class Builder { private String clusterUUID; @@ -1126,15 +1164,17 @@ public static class Builder { private Settings persistentSettings = Settings.Builder.EMPTY_SETTINGS; private DiffableStringMap hashesOfConsistentSettings = new DiffableStringMap(Collections.emptyMap()); - private final ImmutableOpenMap.Builder indices; - private final ImmutableOpenMap.Builder templates; - private final ImmutableOpenMap.Builder customs; + private final Map indices; + private final Map templates; + private final Map customs; + private final Metadata previousMetadata; public Builder() { clusterUUID = UNKNOWN_CLUSTER_UUID; - indices = ImmutableOpenMap.builder(); - templates = ImmutableOpenMap.builder(); - customs = ImmutableOpenMap.builder(); + indices = new HashMap<>(); + templates = new HashMap<>(); + customs = new HashMap<>(); + previousMetadata = null; indexGraveyard(IndexGraveyard.builder().build()); // create new empty index graveyard to initialize } @@ -1146,9 +1186,10 @@ public Builder(Metadata metadata) { this.persistentSettings = metadata.persistentSettings; this.hashesOfConsistentSettings = metadata.hashesOfConsistentSettings; this.version = metadata.version; - this.indices = ImmutableOpenMap.builder(metadata.indices); - this.templates = ImmutableOpenMap.builder(metadata.templates); - this.customs = ImmutableOpenMap.builder(metadata.customs); + this.indices = new HashMap<>(metadata.indices); + this.templates = new HashMap<>(metadata.templates); + this.customs = new HashMap<>(metadata.customs); + this.previousMetadata = metadata; } public Builder put(IndexMetadata.Builder indexMetadataBuilder) { @@ -1201,7 +1242,7 @@ public Builder removeAllIndices() { return this; } - public Builder indices(ImmutableOpenMap indices) { + public Builder indices(final Map indices) { this.indices.putAll(indices); return this; } @@ -1220,14 +1261,13 @@ public Builder removeTemplate(String templateName) { return this; } - public Builder templates(ImmutableOpenMap templates) { + public Builder templates(Map templates) { this.templates.putAll(templates); return this; } public Builder put(String name, ComponentTemplate componentTemplate) { Objects.requireNonNull(componentTemplate, "it is invalid to add a null component template: " + name); - // ಠ_ಠ at ImmutableOpenMap Map existingTemplates = Optional.ofNullable( (ComponentTemplateMetadata) this.customs.get(ComponentTemplateMetadata.TYPE) ).map(ctm -> new HashMap<>(ctm.componentTemplates())).orElse(new HashMap<>()); @@ -1237,7 +1277,6 @@ public Builder put(String name, ComponentTemplate componentTemplate) { } public Builder removeComponentTemplate(String name) { - // ಠ_ಠ at ImmutableOpenMap Map existingTemplates = Optional.ofNullable( (ComponentTemplateMetadata) this.customs.get(ComponentTemplateMetadata.TYPE) ).map(ctm -> new HashMap<>(ctm.componentTemplates())).orElse(new HashMap<>()); @@ -1258,7 +1297,6 @@ public Builder indexTemplates(Map indexTemplate public Builder put(String name, ComposableIndexTemplate indexTemplate) { Objects.requireNonNull(indexTemplate, "it is invalid to add a null index template: " + name); - // ಠ_ಠ at ImmutableOpenMap Map existingTemplates = Optional.ofNullable( (ComposableIndexTemplateMetadata) this.customs.get(ComposableIndexTemplateMetadata.TYPE) ).map(itmd -> new HashMap<>(itmd.indexTemplates())).orElse(new HashMap<>()); @@ -1268,7 +1306,6 @@ public Builder put(String name, ComposableIndexTemplate indexTemplate) { } public Builder removeIndexTemplate(String name) { - // ಠ_ಠ at ImmutableOpenMap Map existingTemplates = Optional.ofNullable( (ComposableIndexTemplateMetadata) this.customs.get(ComposableIndexTemplateMetadata.TYPE) ).map(itmd -> new HashMap<>(itmd.indexTemplates())).orElse(new HashMap<>()); @@ -1319,8 +1356,9 @@ public Builder removeCustom(String type) { return this; } - public Builder customs(ImmutableOpenMap customs) { - StreamSupport.stream(customs.spliterator(), false).forEach(cursor -> Objects.requireNonNull(cursor.value, cursor.key)); + public Builder customs(Map customs) { + StreamSupport.stream(Spliterators.spliterator(customs.entrySet(), 0), false) + .forEach(cursor -> Objects.requireNonNull(cursor.getValue(), cursor.getKey())); this.customs.putAll(customs); return this; } @@ -1335,9 +1373,18 @@ public IndexGraveyard indexGraveyard() { return graveyard; } + public Builder decommissionAttributeMetadata(final DecommissionAttributeMetadata decommissionAttributeMetadata) { + putCustom(DecommissionAttributeMetadata.TYPE, decommissionAttributeMetadata); + return this; + } + + public DecommissionAttributeMetadata decommissionAttributeMetadata() { + return (DecommissionAttributeMetadata) getCustom(DecommissionAttributeMetadata.TYPE); + } + public Builder updateSettings(Settings settings, String... indices) { if (indices == null || indices.length == 0) { - indices = this.indices.keys().toArray(String.class); + indices = this.indices.keySet().toArray(new String[0]); } for (String index : indices) { IndexMetadata indexMetadata = this.indices.get(index); @@ -1427,6 +1474,44 @@ public Builder generateClusterUuidIfNeeded() { } public Metadata build() { + DataStreamMetadata dataStreamMetadata = (DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE); + DataStreamMetadata previousDataStreamMetadata = (previousMetadata != null) + ? (DataStreamMetadata) this.previousMetadata.customs.get(DataStreamMetadata.TYPE) + : null; + + boolean recomputeRequiredforIndicesLookups = (previousMetadata == null) + || (indices.equals(previousMetadata.indices) == false) + || (previousDataStreamMetadata != null && previousDataStreamMetadata.equals(dataStreamMetadata) == false) + || (dataStreamMetadata != null && dataStreamMetadata.equals(previousDataStreamMetadata) == false); + + return (recomputeRequiredforIndicesLookups == false) + ? buildMetadataWithPreviousIndicesLookups() + : buildMetadataWithRecomputedIndicesLookups(); + } + + protected Metadata buildMetadataWithPreviousIndicesLookups() { + return new Metadata( + clusterUUID, + clusterUUIDCommitted, + version, + coordinationMetadata, + transientSettings, + persistentSettings, + hashesOfConsistentSettings, + indices, + templates, + customs, + Arrays.copyOf(previousMetadata.allIndices, previousMetadata.allIndices.length), + Arrays.copyOf(previousMetadata.visibleIndices, previousMetadata.visibleIndices.length), + Arrays.copyOf(previousMetadata.allOpenIndices, previousMetadata.allOpenIndices.length), + Arrays.copyOf(previousMetadata.visibleOpenIndices, previousMetadata.visibleOpenIndices.length), + Arrays.copyOf(previousMetadata.allClosedIndices, previousMetadata.allClosedIndices.length), + Arrays.copyOf(previousMetadata.visibleClosedIndices, previousMetadata.visibleClosedIndices.length), + Collections.unmodifiableSortedMap(previousMetadata.indicesLookup) + ); + } + + protected Metadata buildMetadataWithRecomputedIndicesLookups() { // TODO: We should move these datastructures to IndexNameExpressionResolver, this will give the following benefits: // 1) The datastructures will be rebuilt only when needed. Now during serializing we rebuild these datastructures // while these datastructures aren't even used. @@ -1439,8 +1524,7 @@ public Metadata build() { final List allClosedIndices = new ArrayList<>(); final List visibleClosedIndices = new ArrayList<>(); final Set allAliases = new HashSet<>(); - for (ObjectCursor cursor : indices.values()) { - final IndexMetadata indexMetadata = cursor.value; + for (final IndexMetadata indexMetadata : indices.values()) { final String name = indexMetadata.getIndex().getName(); boolean added = allIndices.add(name); assert added : "double index named [" + name + "]"; @@ -1459,7 +1543,7 @@ public Metadata build() { visibleClosedIndices.add(name); } } - indexMetadata.getAliases().keysIt().forEachRemaining(allAliases::add); + indexMetadata.getAliases().keySet().iterator().forEachRemaining(allAliases::add); } final Set allDataStreams = new HashSet<>(); @@ -1475,10 +1559,10 @@ public Metadata build() { ArrayList duplicates = new ArrayList<>(); if (aliasDuplicatesWithIndices.isEmpty() == false) { // iterate again and constructs a helpful message - for (ObjectCursor cursor : indices.values()) { + for (final IndexMetadata cursor : indices.values()) { for (String alias : aliasDuplicatesWithIndices) { - if (cursor.value.getAliases().containsKey(alias)) { - duplicates.add(alias + " (alias of " + cursor.value.getIndex() + ") conflicts with index"); + if (cursor.getAliases().containsKey(alias)) { + duplicates.add(alias + " (alias of " + cursor.getIndex() + ") conflicts with index"); } } } @@ -1488,10 +1572,10 @@ public Metadata build() { aliasDuplicatesWithDataStreams.retainAll(allDataStreams); if (aliasDuplicatesWithDataStreams.isEmpty() == false) { // iterate again and constructs a helpful message - for (ObjectCursor cursor : indices.values()) { + for (final IndexMetadata cursor : indices.values()) { for (String alias : aliasDuplicatesWithDataStreams) { - if (cursor.value.getAliases().containsKey(alias)) { - duplicates.add(alias + " (alias of " + cursor.value.getIndex() + ") conflicts with data stream"); + if (cursor.getAliases().containsKey(alias)) { + duplicates.add(alias + " (alias of " + cursor.getIndex() + ") conflicts with data stream"); } } } @@ -1537,9 +1621,9 @@ public Metadata build() { transientSettings, persistentSettings, hashesOfConsistentSettings, - indices.build(), - templates.build(), - customs.build(), + indices, + templates, + customs, allIndicesArray, visibleIndicesArray, allOpenIndicesArray, @@ -1576,9 +1660,7 @@ private SortedMap buildIndicesLookup() { } } - for (ObjectCursor cursor : indices.values()) { - IndexMetadata indexMetadata = cursor.value; - + for (final IndexMetadata indexMetadata : indices.values()) { IndexAbstraction.Index index; DataStream parent = indexToDataStreamLookup.get(indexMetadata.getIndex().getName()); if (parent != null) { @@ -1591,8 +1673,7 @@ private SortedMap buildIndicesLookup() { IndexAbstraction existing = indicesLookup.put(indexMetadata.getIndex().getName(), index); assert existing == null : "duplicate for " + indexMetadata.getIndex(); - for (ObjectObjectCursor aliasCursor : indexMetadata.getAliases()) { - AliasMetadata aliasMetadata = aliasCursor.value; + for (final AliasMetadata aliasMetadata : indexMetadata.getAliases().values()) { indicesLookup.compute(aliasMetadata.getAlias(), (aliasName, alias) -> { if (alias == null) { return new IndexAbstraction.Alias(aliasMetadata, indexMetadata); @@ -1675,8 +1756,8 @@ public static void toXContent(Metadata metadata, XContentBuilder builder, ToXCon } builder.startObject("templates"); - for (ObjectCursor cursor : metadata.templates().values()) { - IndexTemplateMetadata.Builder.toXContentWithTypes(cursor.value, builder, params); + for (final IndexTemplateMetadata cursor : metadata.templates().values()) { + IndexTemplateMetadata.Builder.toXContentWithTypes(cursor, builder, params); } builder.endObject(); @@ -1688,10 +1769,10 @@ public static void toXContent(Metadata metadata, XContentBuilder builder, ToXCon builder.endObject(); } - for (ObjectObjectCursor cursor : metadata.customs()) { - if (cursor.value.context().contains(context)) { - builder.startObject(cursor.key); - cursor.value.toXContent(builder, params); + for (final Map.Entry cursor : metadata.customs().entrySet()) { + if (cursor.getValue().context().contains(context)) { + builder.startObject(cursor.getKey()); + cursor.getValue().toXContent(builder, params); builder.endObject(); } } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateDataStreamService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateDataStreamService.java index 13e470eed617d..9ca244660663b 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateDataStreamService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateDataStreamService.java @@ -36,7 +36,6 @@ import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchStatusException; import org.opensearch.ResourceAlreadyExistsException; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest; import org.opensearch.action.support.ActiveShardCount; import org.opensearch.action.support.ActiveShardsObserver; @@ -45,15 +44,18 @@ import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateRequest; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.ObjectPath; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.ObjectPath; import org.opensearch.index.mapper.MapperService; import org.opensearch.index.mapper.MetadataFieldMapper; -import org.opensearch.rest.RestStatus; import org.opensearch.threadpool.ThreadPool; import java.io.IOException; @@ -62,6 +64,11 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; +/** + * Creates a data stream of metadata + * + * @opensearch.internal + */ public class MetadataCreateDataStreamService { private static final Logger logger = LogManager.getLogger(MetadataCreateDataStreamService.class); @@ -69,6 +76,7 @@ public class MetadataCreateDataStreamService { private final ClusterService clusterService; private final ActiveShardsObserver activeShardsObserver; private final MetadataCreateIndexService metadataCreateIndexService; + private final ClusterManagerTaskThrottler.ThrottlingKey createDataStreamTaskKey; public MetadataCreateDataStreamService( ThreadPool threadPool, @@ -78,6 +86,8 @@ public MetadataCreateDataStreamService( this.clusterService = clusterService; this.activeShardsObserver = new ActiveShardsObserver(clusterService, threadPool); this.metadataCreateIndexService = metadataCreateIndexService; + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + createDataStreamTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.CREATE_DATA_STREAM_KEY, true); } public void createDataStream(CreateDataStreamClusterStateUpdateRequest request, ActionListener finalListener) { @@ -90,7 +100,9 @@ public void createDataStream(CreateDataStreamClusterStateUpdateRequest request, new String[] { firstBackingIndexName }, ActiveShardCount.DEFAULT, request.masterNodeTimeout(), - shardsAcked -> { finalListener.onResponse(new AcknowledgedResponse(true)); }, + shardsAcked -> { + finalListener.onResponse(new AcknowledgedResponse(true)); + }, finalListener::onFailure ); } else { @@ -108,6 +120,11 @@ public ClusterState execute(ClusterState currentState) throws Exception { return clusterState; } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return createDataStreamTaskKey; + } + @Override protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { return new ClusterStateUpdateResponse(acknowledged); @@ -120,6 +137,11 @@ public ClusterState createDataStream(CreateDataStreamClusterStateUpdateRequest r return createDataStream(metadataCreateIndexService, current, request); } + /** + * A request to create a data stream cluster state update + * + * @opensearch.internal + */ public static final class CreateDataStreamClusterStateUpdateRequest extends ClusterStateUpdateRequest { private final String name; diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java index cb76b7217624f..1f16fdd380cfc 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java @@ -40,7 +40,6 @@ import org.opensearch.OpenSearchException; import org.opensearch.ResourceAlreadyExistsException; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.alias.Alias; import org.opensearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest; import org.opensearch.action.admin.indices.shrink.ResizeType; @@ -59,22 +58,28 @@ import org.opensearch.cluster.routing.ShardRouting; import org.opensearch.cluster.routing.ShardRoutingState; import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.routing.allocation.AwarenessReplicaBalance; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.UUIDs; import org.opensearch.common.ValidationException; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.io.PathUtils; import org.opensearch.common.logging.DeprecationLogger; +import org.opensearch.common.settings.ClusterSettings; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.XContentHelper; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.env.Environment; -import org.opensearch.index.Index; import org.opensearch.index.IndexModule; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.IndexService; @@ -84,12 +89,15 @@ import org.opensearch.index.mapper.MapperService.MergeReason; import org.opensearch.index.query.QueryShardContext; import org.opensearch.index.shard.IndexSettingProvider; +import org.opensearch.index.translog.Translog; import org.opensearch.indices.IndexCreationException; import org.opensearch.indices.IndicesService; import org.opensearch.indices.InvalidIndexNameException; import org.opensearch.indices.ShardLimitValidator; -import org.opensearch.indices.SystemIndexDescriptor; import org.opensearch.indices.SystemIndices; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.node.Node; +import org.opensearch.node.remotestore.RemoteStoreNodeAttribute; import org.opensearch.threadpool.ThreadPool; import java.io.IOException; @@ -103,6 +111,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; @@ -116,14 +125,24 @@ import static java.util.stream.Collectors.toList; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING; +import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_REPLICATION_TYPE_SETTING; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_CREATION_DATE; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_INDEX_UUID; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_SEGMENT_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; +import static org.opensearch.cluster.metadata.Metadata.DEFAULT_REPLICA_COUNT_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REPLICATION_TYPE_SETTING; +import static org.opensearch.node.remotestore.RemoteStoreNodeAttribute.isRemoteStoreAttributePresent; /** * Service responsible for submitting create index requests + * + * @opensearch.internal */ public class MetadataCreateIndexService { private static final Logger logger = LogManager.getLogger(MetadataCreateIndexService.class); @@ -144,6 +163,8 @@ public class MetadataCreateIndexService { private final ShardLimitValidator shardLimitValidator; private final boolean forbidPrivateIndexSettings; private final Set indexSettingProviders = new HashSet<>(); + private final ClusterManagerTaskThrottler.ThrottlingKey createIndexTaskKey; + private AwarenessReplicaBalance awarenessReplicaBalance; public MetadataCreateIndexService( final Settings settings, @@ -157,7 +178,8 @@ public MetadataCreateIndexService( final ThreadPool threadPool, final NamedXContentRegistry xContentRegistry, final SystemIndices systemIndices, - final boolean forbidPrivateIndexSettings + final boolean forbidPrivateIndexSettings, + final AwarenessReplicaBalance awarenessReplicaBalance ) { this.settings = settings; this.clusterService = clusterService; @@ -171,6 +193,10 @@ public MetadataCreateIndexService( this.systemIndices = systemIndices; this.forbidPrivateIndexSettings = forbidPrivateIndexSettings; this.shardLimitValidator = shardLimitValidator; + this.awarenessReplicaBalance = awarenessReplicaBalance; + + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + createIndexTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.CREATE_INDEX_KEY, true); } /** @@ -214,17 +240,9 @@ public void validateIndexName(String index, ClusterState state) { * @param isHidden Whether or not this is a hidden index */ public boolean validateDotIndex(String index, @Nullable Boolean isHidden) { - boolean isSystem = false; if (index.charAt(0) == '.') { - SystemIndexDescriptor matchingDescriptor = systemIndices.findMatchingDescriptor(index); - if (matchingDescriptor != null) { - logger.trace( - "index [{}] is a system index because it matches index pattern [{}] with description [{}]", - index, - matchingDescriptor.getIndexPattern(), - matchingDescriptor.getDescription() - ); - isSystem = true; + if (systemIndices.validateSystemIndex(index)) { + return true; } else if (isHidden) { logger.trace("index [{}] is a hidden index", index); } else { @@ -237,14 +255,14 @@ public boolean validateDotIndex(String index, @Nullable Boolean isHidden) { } } - return isSystem; + return false; } /** * Validate the name for an index or alias against some static rules. */ public static void validateIndexOrAliasName(String index, BiFunction exceptionCtor) { - if (!Strings.validFileName(index)) { + if (Strings.validFileName(index) == false) { throw exceptionCtor.apply(index, "must not contain the following characters " + Strings.INVALID_FILENAME_CHARS); } if (index.isEmpty()) { @@ -328,6 +346,11 @@ protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { return new ClusterStateUpdateResponse(acknowledged); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return createIndexTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) throws Exception { return applyCreateIndexRequest(currentState, request, false); @@ -546,7 +569,6 @@ private ClusterState applyCreateIndexRequestWithV1Templates( xContentRegistry ) ); - final Settings aggregatedIndexSettings = aggregateIndexSettings( currentState, request, @@ -555,7 +577,8 @@ private ClusterState applyCreateIndexRequestWithV1Templates( settings, indexScopedSettings, shardLimitValidator, - indexSettingProviders + indexSettingProviders, + clusterService.getClusterSettings() ); int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, null); IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards); @@ -619,7 +642,8 @@ private ClusterState applyCreateIndexRequestWithV2Template( settings, indexScopedSettings, shardLimitValidator, - indexSettingProviders + indexSettingProviders, + clusterService.getClusterSettings() ); int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, null); IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards); @@ -699,7 +723,8 @@ private ClusterState applyCreateIndexRequestWithExistingMetadata( settings, indexScopedSettings, shardLimitValidator, - indexSettingProviders + indexSettingProviders, + clusterService.getClusterSettings() ); final int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, sourceMetadata); IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards); @@ -722,7 +747,7 @@ private ClusterState applyCreateIndexRequestWithExistingMetadata( // shard id and the current timestamp indexService.newQueryShardContext(0, null, () -> 0L, null) ), - org.opensearch.common.collect.List.of(), + List.of(), metadataTransformer ); } @@ -782,7 +807,8 @@ static Settings aggregateIndexSettings( Settings settings, IndexScopedSettings indexScopedSettings, ShardLimitValidator shardLimitValidator, - Set indexSettingProviders + Set indexSettingProviders, + ClusterSettings clusterSettings ) { // Create builders for the template and request settings. We transform these into builders // because we may want settings to be "removed" from these prior to being set on the new @@ -860,7 +886,7 @@ static Settings aggregateIndexSettings( indexSettingsBuilder.put(SETTING_NUMBER_OF_SHARDS, numberOfShards); } if (INDEX_NUMBER_OF_REPLICAS_SETTING.exists(indexSettingsBuilder) == false) { - indexSettingsBuilder.put(SETTING_NUMBER_OF_REPLICAS, INDEX_NUMBER_OF_REPLICAS_SETTING.get(settings)); + indexSettingsBuilder.put(SETTING_NUMBER_OF_REPLICAS, DEFAULT_REPLICA_COUNT_SETTING.get(currentState.metadata().settings())); } if (settings.get(SETTING_AUTO_EXPAND_REPLICAS) != null && indexSettingsBuilder.get(SETTING_AUTO_EXPAND_REPLICAS) == null) { indexSettingsBuilder.put(SETTING_AUTO_EXPAND_REPLICAS, settings.get(SETTING_AUTO_EXPAND_REPLICAS)); @@ -872,6 +898,9 @@ static Settings aggregateIndexSettings( indexSettingsBuilder.put(IndexMetadata.SETTING_INDEX_PROVIDED_NAME, request.getProvidedName()); indexSettingsBuilder.put(SETTING_INDEX_UUID, UUIDs.randomBase64UUID()); + updateReplicationStrategy(indexSettingsBuilder, request.settings(), settings); + updateRemoteStoreSettings(indexSettingsBuilder, settings); + if (sourceMetadata != null) { assert request.resizeType() != null; prepareResizeIndexSettings( @@ -890,7 +919,7 @@ static Settings aggregateIndexSettings( * We can not validate settings until we have applied templates, otherwise we do not know the actual settings * that will be used to create this index. */ - shardLimitValidator.validateShardLimit(indexSettings, currentState); + shardLimitValidator.validateShardLimit(request.index(), indexSettings, currentState); if (IndexSettings.INDEX_SOFT_DELETES_SETTING.get(indexSettings) == false && IndexMetadata.SETTING_INDEX_VERSION_CREATED.get(indexSettings).onOrAfter(Version.V_2_0_0)) { throw new IllegalArgumentException( @@ -900,10 +929,54 @@ static Settings aggregateIndexSettings( } validateTranslogRetentionSettings(indexSettings); validateStoreTypeSettings(indexSettings); + validateRefreshIntervalSettings(request.settings(), clusterSettings); + validateTranslogDurabilitySettings(request.settings(), clusterSettings, settings); return indexSettings; } + /** + * Updates index settings to set replication strategy by default based on cluster level settings or remote store + * node attributes + * @param settingsBuilder index settings builder to be updated with relevant settings + * @param requestSettings settings passed in during index create request + * @param clusterSettings cluster level settings + */ + private static void updateReplicationStrategy(Settings.Builder settingsBuilder, Settings requestSettings, Settings clusterSettings) { + if (INDEX_REPLICATION_TYPE_SETTING.exists(requestSettings)) { + settingsBuilder.put(SETTING_REPLICATION_TYPE, INDEX_REPLICATION_TYPE_SETTING.get(requestSettings)); + } else if (CLUSTER_REPLICATION_TYPE_SETTING.exists(clusterSettings)) { + settingsBuilder.put(SETTING_REPLICATION_TYPE, CLUSTER_REPLICATION_TYPE_SETTING.get(clusterSettings)); + } else if (isRemoteStoreAttributePresent(clusterSettings)) { + settingsBuilder.put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT); + } else { + settingsBuilder.put(SETTING_REPLICATION_TYPE, CLUSTER_REPLICATION_TYPE_SETTING.getDefault(clusterSettings)); + } + } + + /** + * Updates index settings to enable remote store by default based on node attributes + * @param settingsBuilder index settings builder to be updated with relevant settings + * @param clusterSettings cluster level settings + */ + private static void updateRemoteStoreSettings(Settings.Builder settingsBuilder, Settings clusterSettings) { + if (isRemoteStoreAttributePresent(clusterSettings)) { + settingsBuilder.put(SETTING_REMOTE_STORE_ENABLED, true) + .put( + SETTING_REMOTE_SEGMENT_STORE_REPOSITORY, + clusterSettings.get( + Node.NODE_ATTRIBUTES.getKey() + RemoteStoreNodeAttribute.REMOTE_STORE_SEGMENT_REPOSITORY_NAME_ATTRIBUTE_KEY + ) + ) + .put( + SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, + clusterSettings.get( + Node.NODE_ATTRIBUTES.getKey() + RemoteStoreNodeAttribute.REMOTE_STORE_TRANSLOG_REPOSITORY_NAME_ATTRIBUTE_KEY + ) + ); + } + } + public static void validateStoreTypeSettings(Settings settings) { // deprecate simplefs store type: if (IndexModule.Type.SIMPLEFS.match(IndexModule.INDEX_STORE_TYPE_SETTING.get(settings))) { @@ -1178,7 +1251,7 @@ private void validate(CreateIndexClusterStateUpdateRequest request, ClusterState public void validateIndexSettings(String indexName, final Settings settings, final boolean forbidPrivateIndexSettings) throws IndexCreationException { - List validationErrors = getIndexSettingsValidationErrors(settings, forbidPrivateIndexSettings); + List validationErrors = getIndexSettingsValidationErrors(settings, forbidPrivateIndexSettings, indexName); if (validationErrors.isEmpty() == false) { ValidationException validationException = new ValidationException(); @@ -1187,11 +1260,32 @@ public void validateIndexSettings(String indexName, final Settings settings, fin } } - List getIndexSettingsValidationErrors(final Settings settings, final boolean forbidPrivateIndexSettings) { + List getIndexSettingsValidationErrors(final Settings settings, final boolean forbidPrivateIndexSettings, String indexName) { + List validationErrors = getIndexSettingsValidationErrors(settings, forbidPrivateIndexSettings, Optional.of(indexName)); + return validationErrors; + } + + List getIndexSettingsValidationErrors( + final Settings settings, + final boolean forbidPrivateIndexSettings, + Optional indexName + ) { List validationErrors = validateIndexCustomPath(settings, env.sharedDataFile()); if (forbidPrivateIndexSettings) { validationErrors.addAll(validatePrivateSettingsNotExplicitlySet(settings, indexScopedSettings)); } + if (indexName.isEmpty() || indexName.get().charAt(0) != '.') { + // Apply aware replica balance validation only to non system indices + int replicaCount = settings.getAsInt( + IndexMetadata.SETTING_NUMBER_OF_REPLICAS, + DEFAULT_REPLICA_COUNT_SETTING.get(this.clusterService.state().metadata().settings()) + ); + AutoExpandReplicas autoExpandReplica = AutoExpandReplicas.SETTING.get(settings); + Optional error = awarenessReplicaBalance.validate(replicaCount, autoExpandReplica); + if (error.isPresent()) { + validationErrors.add(error.get()); + } + } return validationErrors; } @@ -1221,7 +1315,7 @@ private static List validatePrivateSettingsNotExplicitlySet(Settings set private static List validateIndexCustomPath(Settings settings, @Nullable Path sharedDataPath) { String customPath = IndexMetadata.INDEX_DATA_PATH_SETTING.get(settings); List validationErrors = new ArrayList<>(); - if (!Strings.isEmpty(customPath)) { + if (Strings.isEmpty(customPath) == false) { if (sharedDataPath == null) { validationErrors.add("path.shared_data must be set in order to use custom data paths"); } else { @@ -1302,9 +1396,11 @@ static IndexMetadata validateResize(ClusterState state, String sourceIndex, Stri ); } - // ensure index is read-only + // ensure write operations on the source index is blocked if (state.blocks().indexBlocked(ClusterBlockLevel.WRITE, sourceIndex) == false) { - throw new IllegalStateException("index " + sourceIndex + " must be read-only to resize index. use \"index.blocks.write=true\""); + throw new IllegalStateException( + "index " + sourceIndex + " must block write operations to resize index. use \"index.blocks.write=true\"" + ); } if (IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.exists(targetIndexSettings)) { @@ -1419,4 +1515,50 @@ public static void validateTranslogRetentionSettings(Settings indexSettings) { } } } + + /** + * Validates {@code index.refresh_interval} is equal or below the {@code cluster.minimum.index.refresh_interval}. + * + * @param requestSettings settings passed in during index create/update request + * @param clusterSettings cluster setting + */ + static void validateRefreshIntervalSettings(Settings requestSettings, ClusterSettings clusterSettings) { + if (IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.exists(requestSettings) == false) { + return; + } + TimeValue requestRefreshInterval = IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.get(requestSettings); + TimeValue clusterMinimumRefreshInterval = clusterSettings.get(IndicesService.CLUSTER_MINIMUM_INDEX_REFRESH_INTERVAL_SETTING); + if (requestRefreshInterval.millis() < clusterMinimumRefreshInterval.millis()) { + throw new IllegalArgumentException( + "invalid index.refresh_interval [" + + requestRefreshInterval + + "]: cannot be smaller than cluster.minimum.index.refresh_interval [" + + clusterMinimumRefreshInterval + + "]" + ); + } + } + + /** + * Validates {@code index.translog.durability} is not async if the {@code cluster.remote_store.index.restrict.async-durability} is set to true. + * + * @param requestSettings settings passed in during index create/update request + * @param clusterSettings cluster setting + */ + static void validateTranslogDurabilitySettings(Settings requestSettings, ClusterSettings clusterSettings, Settings settings) { + if (isRemoteStoreAttributePresent(settings) == false + || IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.exists(requestSettings) == false + || clusterSettings.get(IndicesService.CLUSTER_REMOTE_INDEX_RESTRICT_ASYNC_DURABILITY_SETTING) == false) { + return; + } + Translog.Durability durability = IndexSettings.INDEX_TRANSLOG_DURABILITY_SETTING.get(requestSettings); + if (durability.equals(Translog.Durability.ASYNC)) { + throw new IllegalArgumentException( + "index setting [index.translog.durability=async] is not allowed as cluster setting [" + + IndicesService.CLUSTER_REMOTE_INDEX_RESTRICT_ASYNC_DURABILITY_SETTING.getKey() + + "=true]" + ); + } + + } } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataDeleteIndexService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataDeleteIndexService.java index 4805cb215fc09..5352a8a3fb994 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataDeleteIndexService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataDeleteIndexService.java @@ -34,7 +34,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.delete.DeleteIndexClusterStateUpdateRequest; import org.opensearch.cluster.AckedClusterStateUpdateTask; import org.opensearch.cluster.ClusterState; @@ -43,18 +42,21 @@ import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.routing.RoutingTable; import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.set.Sets; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.Index; import org.opensearch.snapshots.RestoreService; import org.opensearch.snapshots.SnapshotInProgressException; import org.opensearch.snapshots.SnapshotsService; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -62,6 +64,8 @@ /** * Deletes indices. + * + * @opensearch.internal */ public class MetadataDeleteIndexService { @@ -71,12 +75,17 @@ public class MetadataDeleteIndexService { private final ClusterService clusterService; private final AllocationService allocationService; + private final ClusterManagerTaskThrottler.ThrottlingKey deleteIndexTaskKey; @Inject public MetadataDeleteIndexService(Settings settings, ClusterService clusterService, AllocationService allocationService) { this.settings = settings; this.clusterService = clusterService; this.allocationService = allocationService; + + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + deleteIndexTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.DELETE_INDEX_KEY, true); + } public void deleteIndices( @@ -96,6 +105,11 @@ protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { return new ClusterStateUpdateResponse(acknowledged); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return deleteIndexTaskKey; + } + @Override public ClusterState execute(final ClusterState currentState) { return deleteIndices(currentState, Sets.newHashSet(request.indices())); @@ -171,13 +185,13 @@ public ClusterState deleteIndices(ClusterState currentState, Set indices) ClusterBlocks blocks = clusterBlocksBuilder.build(); // update snapshot restore entries - ImmutableOpenMap customs = currentState.getCustoms(); + Map customs = currentState.getCustoms(); final RestoreInProgress restoreInProgress = currentState.custom(RestoreInProgress.TYPE, RestoreInProgress.EMPTY); RestoreInProgress updatedRestoreInProgress = RestoreService.updateRestoreStateWithDeletedIndices(restoreInProgress, indices); if (updatedRestoreInProgress != restoreInProgress) { - ImmutableOpenMap.Builder builder = ImmutableOpenMap.builder(customs); + final Map builder = new HashMap<>(customs); builder.put(RestoreInProgress.TYPE, updatedRestoreInProgress); - customs = builder.build(); + customs = Collections.unmodifiableMap(builder); } return allocationService.reroute( diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexAliasesService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexAliasesService.java index a490bae65ca00..96ba3d60ce9a6 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexAliasesService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexAliasesService.java @@ -33,18 +33,20 @@ package org.opensearch.cluster.metadata; import org.opensearch.OpenSearchException; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.alias.IndicesAliasesClusterStateUpdateRequest; import org.opensearch.cluster.AckedClusterStateUpdateTask; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.metadata.AliasAction.NewAliasValidator; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.inject.Inject; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.index.IndexNotFoundException; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.MapperService; @@ -64,6 +66,8 @@ /** * Service responsible for submitting add and remove aliases requests + * + * @opensearch.internal */ public class MetadataIndexAliasesService { @@ -76,6 +80,7 @@ public class MetadataIndexAliasesService { private final MetadataDeleteIndexService deleteIndexService; private final NamedXContentRegistry xContentRegistry; + private final ClusterManagerTaskThrottler.ThrottlingKey indexAliasTaskKey; @Inject public MetadataIndexAliasesService( @@ -90,6 +95,10 @@ public MetadataIndexAliasesService( this.aliasValidator = aliasValidator; this.deleteIndexService = deleteIndexService; this.xContentRegistry = xContentRegistry; + + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + indexAliasTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.INDEX_ALIASES_KEY, true); + } public void indicesAliases( @@ -104,6 +113,11 @@ protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { return new ClusterStateUpdateResponse(acknowledged); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return indexAliasTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) { return applyAliasActions(currentState, request.actions()); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexStateService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexStateService.java index 25071a60bdfb5..dc98b60484b20 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexStateService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexStateService.java @@ -32,16 +32,13 @@ package org.opensearch.cluster.metadata; -import com.carrotsearch.hppc.cursors.IntObjectCursor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; import org.opensearch.LegacyESVersion; import org.opensearch.OpenSearchException; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.ActionRunnable; -import org.opensearch.action.NotifyOnceListener; import org.opensearch.action.admin.indices.close.CloseIndexClusterStateUpdateRequest; import org.opensearch.action.admin.indices.close.CloseIndexResponse; import org.opensearch.action.admin.indices.close.CloseIndexResponse.IndexResult; @@ -70,9 +67,7 @@ import org.opensearch.cluster.routing.allocation.AllocationService; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.UUIDs; -import org.opensearch.common.collect.ImmutableOpenIntMap; import org.opensearch.common.collect.Tuple; import org.opensearch.common.inject.Inject; import org.opensearch.common.settings.Setting; @@ -81,16 +76,19 @@ import org.opensearch.common.util.concurrent.AtomicArray; import org.opensearch.common.util.concurrent.ConcurrentCollections; import org.opensearch.common.util.concurrent.CountDown; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.action.NotifyOnceListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.index.Index; +import org.opensearch.core.index.shard.ShardId; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.tasks.TaskId; import org.opensearch.index.IndexNotFoundException; -import org.opensearch.index.shard.ShardId; import org.opensearch.indices.IndicesService; import org.opensearch.indices.ShardLimitValidator; -import org.opensearch.rest.RestStatus; import org.opensearch.snapshots.RestoreService; import org.opensearch.snapshots.SnapshotInProgressException; import org.opensearch.snapshots.SnapshotsService; -import org.opensearch.tasks.TaskId; import org.opensearch.threadpool.ThreadPool; import java.util.ArrayList; @@ -112,6 +110,8 @@ /** * Service responsible for submitting open/close index requests as well as for adding index blocks + * + * @opensearch.internal */ public class MetadataIndexStateService { private static final Logger logger = LogManager.getLogger(MetadataIndexStateService.class); @@ -581,6 +581,8 @@ public TimeValue timeout() { * This step iterates over the indices previously blocked and sends a {@link TransportVerifyShardBeforeCloseAction} to each shard. If * this action succeed then the shard is considered to be ready for closing. When all shards of a given index are ready for closing, * the index is considered ready to be closed. + * + * @opensearch.internal */ class WaitForClosedBlocksApplied extends ActionRunnable> { @@ -635,12 +637,12 @@ private void waitForShardsReadyForClosing( return; } - final ImmutableOpenIntMap shards = indexRoutingTable.getShards(); + final Map shards = indexRoutingTable.getShards(); final AtomicArray results = new AtomicArray<>(shards.size()); final CountDown countDown = new CountDown(shards.size()); - for (IntObjectCursor shard : shards) { - final IndexShardRoutingTable shardRoutingTable = shard.value; + for (final IndexShardRoutingTable shard : shards.values()) { + final IndexShardRoutingTable shardRoutingTable = shard; final int shardId = shardRoutingTable.shardId().id(); sendVerifyShardBeforeCloseRequest(shardRoutingTable, closingBlock, new NotifyOnceListener() { @Override @@ -713,6 +715,8 @@ public void onFailure(Exception e) { /** * Helper class that coordinates with shards to ensure that blocks have been properly applied to all shards using * {@link TransportVerifyShardIndexBlockAction}. + * + * @opensearch.metadata */ class WaitForBlocksApplied extends ActionRunnable> { @@ -766,12 +770,12 @@ private void waitForShardsReady( return; } - final ImmutableOpenIntMap shards = indexRoutingTable.getShards(); + final Map shards = indexRoutingTable.getShards(); final AtomicArray results = new AtomicArray<>(shards.size()); final CountDown countDown = new CountDown(shards.size()); - for (IntObjectCursor shard : shards) { - final IndexShardRoutingTable shardRoutingTable = shard.value; + for (final IndexShardRoutingTable shard : shards.values()) { + final IndexShardRoutingTable shardRoutingTable = shard; final int shardId = shardRoutingTable.shardId().id(); sendVerifyShardBlockRequest(shardRoutingTable, clusterBlock, new NotifyOnceListener() { @Override diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexTemplateService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexTemplateService.java index 896679206aec5..71b86ec853ce4 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexTemplateService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexTemplateService.java @@ -31,27 +31,24 @@ package org.opensearch.cluster.metadata; -import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.CollectionUtil; import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.Operations; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.alias.Alias; +import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.action.support.master.MasterNodeRequest; import org.opensearch.cluster.ClusterState; import org.opensearch.cluster.ClusterStateUpdateTask; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.UUIDs; import org.opensearch.common.ValidationException; -import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.inject.Inject; import org.opensearch.common.logging.HeaderWarning; @@ -60,11 +57,14 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.set.Sets; -import org.opensearch.common.xcontent.NamedXContentRegistry; -import org.opensearch.common.xcontent.XContentBuilder; import org.opensearch.common.xcontent.XContentFactory; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.index.Index; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.XContentBuilder; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.MapperParsingException; import org.opensearch.index.mapper.MapperService; @@ -97,6 +97,8 @@ /** * Service responsible for submitting index templates updates + * + * @opensearch.internal */ public class MetadataIndexTemplateService { @@ -107,6 +109,12 @@ public class MetadataIndexTemplateService { private final MetadataCreateIndexService metadataCreateIndexService; private final IndexScopedSettings indexScopedSettings; private final NamedXContentRegistry xContentRegistry; + private final ClusterManagerTaskThrottler.ThrottlingKey createIndexTemplateTaskKey; + private final ClusterManagerTaskThrottler.ThrottlingKey createIndexTemplateV2TaskKey; + private final ClusterManagerTaskThrottler.ThrottlingKey removeIndexTemplateTaskKey; + private final ClusterManagerTaskThrottler.ThrottlingKey removeIndexTemplateV2TaskKey; + private final ClusterManagerTaskThrottler.ThrottlingKey createComponentTemplateTaskKey; + private final ClusterManagerTaskThrottler.ThrottlingKey removeComponentTemplateTaskKey; @Inject public MetadataIndexTemplateService( @@ -123,6 +131,20 @@ public MetadataIndexTemplateService( this.metadataCreateIndexService = metadataCreateIndexService; this.indexScopedSettings = indexScopedSettings; this.xContentRegistry = xContentRegistry; + + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + createIndexTemplateTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.CREATE_INDEX_TEMPLATE_KEY, true); + createIndexTemplateV2TaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.CREATE_INDEX_TEMPLATE_V2_KEY, true); + removeIndexTemplateTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.REMOVE_INDEX_TEMPLATE_KEY, true); + removeIndexTemplateV2TaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.REMOVE_INDEX_TEMPLATE_V2_KEY, true); + createComponentTemplateTaskKey = clusterService.registerClusterManagerTask( + ClusterManagerTaskKeys.CREATE_COMPONENT_TEMPLATE_KEY, + true + ); + removeComponentTemplateTaskKey = clusterService.registerClusterManagerTask( + ClusterManagerTaskKeys.REMOVE_COMPONENT_TEMPLATE_KEY, + true + ); } public void removeTemplates(final RemoveRequest request, final RemoveListener listener) { @@ -130,7 +152,7 @@ public void removeTemplates(final RemoveRequest request, final RemoveListener li @Override public TimeValue timeout() { - return request.masterTimeout; + return request.clusterManagerTimeout; } @Override @@ -138,11 +160,15 @@ public void onFailure(String source, Exception e) { listener.onFailure(e); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return removeIndexTemplateTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) { Set templateNames = new HashSet<>(); - for (ObjectCursor cursor : currentState.metadata().templates().keys()) { - String templateName = cursor.value; + for (final String templateName : currentState.metadata().templates().keySet()) { if (Regex.simpleMatch(request.name, templateName)) { templateNames.add(templateName); } @@ -196,6 +222,11 @@ public void onFailure(String source, Exception e) { listener.onFailure(e); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return createComponentTemplateTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) throws Exception { return addComponentTemplate(currentState, create, name, template); @@ -271,9 +302,11 @@ ClusterState addComponentTemplate( if (stringMappings != null) { Map parsedMappings = MapperService.parseMapping(xContentRegistry, stringMappings); if (parsedMappings.size() > 0) { - stringMappings = Strings.toString( - XContentFactory.jsonBuilder().startObject().field(MapperService.SINGLE_MAPPING_NAME, parsedMappings).endObject() - ); + stringMappings = XContentFactory.jsonBuilder() + .startObject() + .field(MapperService.SINGLE_MAPPING_NAME, parsedMappings) + .endObject() + .toString(); } } @@ -356,6 +389,11 @@ public void onFailure(String source, Exception e) { listener.onFailure(e); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return removeComponentTemplateTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) { Set templateNames = new HashSet<>(); @@ -445,6 +483,11 @@ public void onFailure(String source, Exception e) { listener.onFailure(e); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return createIndexTemplateV2TaskKey; + } + @Override public ClusterState execute(ClusterState currentState) throws Exception { return addIndexTemplateV2(currentState, create, name, template); @@ -550,9 +593,11 @@ public ClusterState addIndexTemplateV2( if (stringMappings != null) { Map parsedMappings = MapperService.parseMapping(xContentRegistry, stringMappings); if (parsedMappings.size() > 0) { - stringMappings = Strings.toString( - XContentFactory.jsonBuilder().startObject().field(MapperService.SINGLE_MAPPING_NAME, parsedMappings).endObject() - ); + stringMappings = XContentFactory.jsonBuilder() + .startObject() + .field(MapperService.SINGLE_MAPPING_NAME, parsedMappings) + .endObject() + .toString(); } } final Template finalTemplate = new Template( @@ -669,9 +714,9 @@ public static Map> findConflictingV1Templates( ) { Automaton v2automaton = Regex.simpleMatchToAutomaton(indexPatterns.toArray(Strings.EMPTY_ARRAY)); Map> overlappingTemplates = new HashMap<>(); - for (ObjectObjectCursor cursor : state.metadata().templates()) { - String name = cursor.key; - IndexTemplateMetadata template = cursor.value; + for (final Map.Entry cursor : state.metadata().templates().entrySet()) { + String name = cursor.getKey(); + IndexTemplateMetadata template = cursor.getValue(); Automaton v1automaton = Regex.simpleMatchToAutomaton(template.patterns().toArray(Strings.EMPTY_ARRAY)); if (Operations.isEmpty(Operations.intersection(v2automaton, v1automaton)) == false) { logger.debug( @@ -762,6 +807,11 @@ public void onFailure(String source, Exception e) { listener.onFailure(e); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return removeIndexTemplateV2TaskKey; + } + @Override public ClusterState execute(ClusterState currentState) { return innerRemoveIndexTemplateV2(currentState, name); @@ -858,7 +908,7 @@ public void putTemplate(final PutRequest request, final PutListener listener) { @Override public TimeValue timeout() { - return request.masterTimeout; + return request.clusterManagerTimeout; } @Override @@ -866,6 +916,11 @@ public void onFailure(String source, Exception e) { listener.onFailure(e); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return createIndexTemplateTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) throws Exception { validateTemplate(request.settings, request.mappings, indicesService, xContentRegistry); @@ -960,8 +1015,7 @@ static ClusterState innerPutTemplate( public static List findV1Templates(Metadata metadata, String indexName, @Nullable Boolean isHidden) { final Predicate patternMatchPredicate = pattern -> Regex.simpleMatch(pattern, indexName); final List matchedTemplates = new ArrayList<>(); - for (ObjectCursor cursor : metadata.templates().values()) { - final IndexTemplateMetadata template = cursor.value; + for (final IndexTemplateMetadata template : metadata.templates().values()) { if (isHidden == null || isHidden == Boolean.FALSE) { final boolean matched = template.patterns().stream().anyMatch(patternMatchPredicate); if (matched) { @@ -1101,7 +1155,7 @@ public static List collectMappings(final ClusterState state, Optional.ofNullable(template.getDataStreamTemplate()) .map(ComposableIndexTemplate.DataStreamTemplate::getDataStreamMappingSnippet) .map(mapping -> { - try (XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent())) { + try (XContentBuilder builder = MediaTypeRegistry.JSON.contentBuilder()) { builder.value(mapping); return new CompressedXContent(BytesReference.bytes(builder)); } catch (IOException e) { @@ -1184,8 +1238,8 @@ public static List> resolveAliases(final List { if (template.aliases() != null) { Map aliasMeta = new HashMap<>(); - for (ObjectObjectCursor cursor : template.aliases()) { - aliasMeta.put(cursor.key, cursor.value); + for (final Map.Entry cursor : template.aliases().entrySet()) { + aliasMeta.put(cursor.getKey(), cursor.getValue()); } resolvedAliases.add(aliasMeta); } @@ -1469,7 +1523,11 @@ private void validate(String name, @Nullable Settings settings, List ind validationErrors.add(t.getMessage()); } } - List indexSettingsValidation = metadataCreateIndexService.getIndexSettingsValidationErrors(settings, true); + List indexSettingsValidation = metadataCreateIndexService.getIndexSettingsValidationErrors( + settings, + true, + Optional.empty() + ); validationErrors.addAll(indexSettingsValidation); } @@ -1496,6 +1554,11 @@ private void validate(String name, @Nullable Settings settings, List ind } } + /** + * Listener for putting metadata in the template + * + * @opensearch.internal + */ public interface PutListener { void onResponse(PutResponse response); @@ -1503,6 +1566,11 @@ public interface PutListener { void onFailure(Exception e); } + /** + * A PUT request. + * + * @opensearch.internal + */ public static class PutRequest { final String name; final String cause; @@ -1514,7 +1582,11 @@ public static class PutRequest { String mappings = null; List aliases = new ArrayList<>(); - TimeValue masterTimeout = MasterNodeRequest.DEFAULT_MASTER_NODE_TIMEOUT; + TimeValue clusterManagerTimeout = ClusterManagerNodeRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; + + /** @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #clusterManagerTimeout} */ + @Deprecated + TimeValue masterTimeout = ClusterManagerNodeRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; public PutRequest(String cause, String name) { this.cause = cause; @@ -1551,17 +1623,28 @@ public PutRequest aliases(Set aliases) { return this; } - public PutRequest masterTimeout(TimeValue masterTimeout) { - this.masterTimeout = masterTimeout; + public PutRequest clusterManagerTimeout(TimeValue clusterManagerTimeout) { + this.clusterManagerTimeout = clusterManagerTimeout; return this; } + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #clusterManagerTimeout(TimeValue)} */ + @Deprecated + public PutRequest masterTimeout(TimeValue masterTimeout) { + return clusterManagerTimeout(masterTimeout); + } + public PutRequest version(Integer version) { this.version = version; return this; } } + /** + * The PUT response. + * + * @opensearch.internal + */ public static class PutResponse { private final boolean acknowledged; @@ -1574,20 +1657,40 @@ public boolean acknowledged() { } } + /** + * A remove Request. + * + * @opensearch.internal + */ public static class RemoveRequest { final String name; - TimeValue masterTimeout = MasterNodeRequest.DEFAULT_MASTER_NODE_TIMEOUT; + TimeValue clusterManagerTimeout = ClusterManagerNodeRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; + + /** @deprecated As of 2.1, because supporting inclusive language, replaced by {@link #clusterManagerTimeout} */ + @Deprecated + TimeValue masterTimeout = ClusterManagerNodeRequest.DEFAULT_CLUSTER_MANAGER_NODE_TIMEOUT; public RemoveRequest(String name) { this.name = name; } - public RemoveRequest masterTimeout(TimeValue masterTimeout) { - this.masterTimeout = masterTimeout; + public RemoveRequest clusterManagerTimeout(TimeValue clusterManagerTimeout) { + this.clusterManagerTimeout = clusterManagerTimeout; return this; } + + /** @deprecated As of 2.2, because supporting inclusive language, replaced by {@link #clusterManagerTimeout} */ + @Deprecated + public RemoveRequest masterTimeout(TimeValue masterTimeout) { + return clusterManagerTimeout(masterTimeout); + } } + /** + * A remove Response. + * + * @opensearch.internal + */ public static class RemoveResponse { private final boolean acknowledged; @@ -1600,6 +1703,11 @@ public boolean acknowledged() { } } + /** + * A remove listener. + * + * @opensearch.internal + */ public interface RemoveListener { void onResponse(RemoveResponse response); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexUpgradeService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexUpgradeService.java index f6f42e0d81063..54797dfa0c22c 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexUpgradeService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataIndexUpgradeService.java @@ -37,11 +37,12 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.search.similarities.BM25Similarity; import org.apache.lucene.search.similarities.Similarity; +import org.opensearch.LegacyESVersion; import org.opensearch.Version; import org.opensearch.common.TriFunction; import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.NamedXContentRegistry; +import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.index.IndexSettings; import org.opensearch.index.analysis.AnalyzerScope; import org.opensearch.index.analysis.IndexAnalyzers; @@ -64,6 +65,8 @@ * to upgrade the existing index metadata to the latest version of the cluster. It typically * occurs during cluster upgrade, when dangling indices are imported into the cluster or indices * are restored from a repository. + * + * @opensearch.internal */ public class MetadataIndexUpgradeService { @@ -93,7 +96,7 @@ public MetadataIndexUpgradeService( } /** - * Checks that the index can be upgraded to the current version of the master node. + * Checks that the index can be upgraded to the current version of the cluster-manager node. * *

    * If the index does not need upgrade it returns the index metadata unchanged, otherwise it returns a modified index metadata. If index @@ -134,20 +137,35 @@ boolean isUpgraded(IndexMetadata indexMetadata) { private void checkSupportedVersion(IndexMetadata indexMetadata, Version minimumIndexCompatibilityVersion) { if (indexMetadata.getState() == IndexMetadata.State.OPEN && isSupportedVersion(indexMetadata, minimumIndexCompatibilityVersion) == false) { - throw new IllegalStateException( - "The index [" - + indexMetadata.getIndex() - + "] was created with version [" - + indexMetadata.getCreationVersion() - + "] but the minimum compatible version is [" - - + minimumIndexCompatibilityVersion - + "]. It should be re-indexed in OpenSearch " - + minimumIndexCompatibilityVersion.major - + ".x before upgrading to " - + Version.CURRENT - + "." - ); + if (minimumIndexCompatibilityVersion.equals(LegacyESVersion.V_7_0_0)) { + // This branch can be removed when LegacyESVersion is removed + throw new IllegalStateException( + "The index [" + + indexMetadata.getIndex() + + "] was created with version [" + + indexMetadata.getCreationVersion() + + "] but the minimum compatible version is " + + "OpenSearch 1.0.0 (or Elasticsearch 7.0.0). " + + "It should be re-indexed in OpenSearch 1.x " + + "(or Elasticsearch 7.x) before upgrading to " + + Version.CURRENT + + "." + ); + } else { + throw new IllegalStateException( + "The index [" + + indexMetadata.getIndex() + + "] was created with version [" + + indexMetadata.getCreationVersion() + + "] but the minimum compatible version is [" + + minimumIndexCompatibilityVersion + + "]. It should be re-indexed in OpenSearch " + + minimumIndexCompatibilityVersion.major + + ".x before upgrading to " + + Version.CURRENT + + "." + ); + } } } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataMappingService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataMappingService.java index 3795961d39143..1406287149e8d 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataMappingService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataMappingService.java @@ -35,7 +35,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.mapping.put.PutMappingClusterStateUpdateRequest; import org.opensearch.cluster.AckedClusterStateTaskListener; import org.opensearch.cluster.ClusterState; @@ -43,15 +42,18 @@ import org.opensearch.cluster.ClusterStateTaskExecutor; import org.opensearch.cluster.ack.ClusterStateUpdateResponse; import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Nullable; import org.opensearch.common.Priority; -import org.opensearch.common.Strings; import org.opensearch.common.compress.CompressedXContent; import org.opensearch.common.inject.Inject; import org.opensearch.common.unit.TimeValue; -import org.opensearch.core.internal.io.IOUtils; -import org.opensearch.index.Index; +import org.opensearch.common.util.io.IOUtils; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.Strings; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexService; import org.opensearch.index.mapper.DocumentMapper; import org.opensearch.index.mapper.MapperService; @@ -69,6 +71,8 @@ /** * Service responsible for submitting mapping changes + * + * @opensearch.internal */ public class MetadataMappingService { @@ -76,6 +80,7 @@ public class MetadataMappingService { private final ClusterService clusterService; private final IndicesService indicesService; + private final ClusterManagerTaskThrottler.ThrottlingKey putMappingTaskKey; final RefreshTaskExecutor refreshExecutor = new RefreshTaskExecutor(); final PutMappingExecutor putMappingExecutor = new PutMappingExecutor(); @@ -84,6 +89,10 @@ public class MetadataMappingService { public MetadataMappingService(ClusterService clusterService, IndicesService indicesService) { this.clusterService = clusterService; this.indicesService = indicesService; + + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + putMappingTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.PUT_MAPPING_KEY, true); + } static class RefreshTask { @@ -244,6 +253,11 @@ public ClusterTasksResult execute( } } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return putMappingTaskKey; + } + private ClusterState applyRequest( ClusterState currentState, PutMappingClusterStateUpdateRequest request, diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataUpdateSettingsService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataUpdateSettingsService.java index 1b7aee48e3232..7d4c3512ed757 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataUpdateSettingsService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataUpdateSettingsService.java @@ -36,7 +36,6 @@ import org.apache.logging.log4j.Logger; import org.opensearch.ExceptionsHelper; import org.opensearch.Version; -import org.opensearch.action.ActionListener; import org.opensearch.action.admin.indices.settings.put.UpdateSettingsClusterStateUpdateRequest; import org.opensearch.action.admin.indices.upgrade.post.UpgradeSettingsClusterStateUpdateRequest; import org.opensearch.cluster.AckedClusterStateUpdateTask; @@ -46,6 +45,9 @@ import org.opensearch.cluster.block.ClusterBlocks; import org.opensearch.cluster.routing.RoutingTable; import org.opensearch.cluster.routing.allocation.AllocationService; +import org.opensearch.cluster.routing.allocation.AwarenessReplicaBalance; +import org.opensearch.cluster.service.ClusterManagerTaskKeys; +import org.opensearch.cluster.service.ClusterManagerTaskThrottler; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.Priority; import org.opensearch.common.ValidationException; @@ -55,7 +57,8 @@ import org.opensearch.common.settings.IndexScopedSettings; import org.opensearch.common.settings.Setting; import org.opensearch.common.settings.Settings; -import org.opensearch.index.Index; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.index.Index; import org.opensearch.index.IndexSettings; import org.opensearch.indices.IndicesService; import org.opensearch.indices.ShardLimitValidator; @@ -70,10 +73,15 @@ import java.util.Set; import static org.opensearch.action.support.ContextPreservingActionListener.wrapPreservingContext; +import static org.opensearch.cluster.metadata.MetadataCreateIndexService.validateRefreshIntervalSettings; +import static org.opensearch.cluster.metadata.MetadataCreateIndexService.validateTranslogDurabilitySettings; +import static org.opensearch.common.settings.AbstractScopedSettings.ARCHIVED_SETTINGS_PREFIX; import static org.opensearch.index.IndexSettings.same; /** * Service responsible for submitting update index settings requests + * + * @opensearch.internal */ public class MetadataUpdateSettingsService { private static final Logger logger = LogManager.getLogger(MetadataUpdateSettingsService.class); @@ -86,6 +94,9 @@ public class MetadataUpdateSettingsService { private final IndicesService indicesService; private final ShardLimitValidator shardLimitValidator; private final ThreadPool threadPool; + private final ClusterManagerTaskThrottler.ThrottlingKey updateSettingsTaskKey; + + private AwarenessReplicaBalance awarenessReplicaBalance; @Inject public MetadataUpdateSettingsService( @@ -94,7 +105,8 @@ public MetadataUpdateSettingsService( IndexScopedSettings indexScopedSettings, IndicesService indicesService, ShardLimitValidator shardLimitValidator, - ThreadPool threadPool + ThreadPool threadPool, + AwarenessReplicaBalance awarenessReplicaBalance ) { this.clusterService = clusterService; this.threadPool = threadPool; @@ -102,6 +114,10 @@ public MetadataUpdateSettingsService( this.indexScopedSettings = indexScopedSettings; this.indicesService = indicesService; this.shardLimitValidator = shardLimitValidator; + this.awarenessReplicaBalance = awarenessReplicaBalance; + + // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction. + updateSettingsTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.UPDATE_SETTINGS_KEY, true); } public void updateSettings( @@ -112,6 +128,10 @@ public void updateSettings( .put(request.settings()) .normalizePrefix(IndexMetadata.INDEX_SETTING_PREFIX) .build(); + + validateRefreshIntervalSettings(normalizedSettings, clusterService.getClusterSettings()); + validateTranslogDurabilitySettings(normalizedSettings, clusterService.getClusterSettings(), clusterService.getSettings()); + Settings.Builder settingsForClosedIndices = Settings.builder(); Settings.Builder settingsForOpenIndices = Settings.builder(); final Set skippedSettings = new HashSet<>(); @@ -119,12 +139,16 @@ public void updateSettings( indexScopedSettings.validate( normalizedSettings.filter(s -> Regex.isSimpleMatchPattern(s) == false), // don't validate wildcards false, // don't validate dependencies here we check it below never allow to change the number of shards + false, + true, // Ignore archived setting. true ); // validate internal or private index settings for (String key : normalizedSettings.keySet()) { Setting setting = indexScopedSettings.get(key); boolean isWildcard = setting == null && Regex.isSimpleMatchPattern(key); + boolean isArchived = key.startsWith(ARCHIVED_SETTINGS_PREFIX); assert setting != null // we already validated the normalized settings + || isArchived || (isWildcard && normalizedSettings.hasValue(key) == false) : "unknown setting: " + key + " isWildcard: " @@ -132,7 +156,8 @@ public void updateSettings( + " hasValue: " + normalizedSettings.hasValue(key); settingsForClosedIndices.copy(key, normalizedSettings); - if (isWildcard || setting.isDynamic()) { + // Only allow dynamic settings and wildcards for open indices. Skip archived settings. + if (isArchived == false && (isWildcard || setting.isDynamic())) { settingsForOpenIndices.copy(key, normalizedSettings); } else { skippedSettings.add(key); @@ -155,6 +180,11 @@ protected ClusterStateUpdateResponse newResponse(boolean acknowledged) { return new ClusterStateUpdateResponse(acknowledged); } + @Override + public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey() { + return updateSettingsTaskKey; + } + @Override public ClusterState execute(ClusterState currentState) { @@ -191,6 +221,22 @@ public ClusterState execute(ClusterState currentState) { if (IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.exists(openSettings)) { final int updatedNumberOfReplicas = IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(openSettings); if (preserveExisting == false) { + for (Index index : request.indices()) { + if (index.getName().charAt(0) != '.') { + // No replica count validation for system indices + Optional error = awarenessReplicaBalance.validate( + updatedNumberOfReplicas, + AutoExpandReplicas.SETTING.get(openSettings) + ); + + if (error.isPresent()) { + ValidationException ex = new ValidationException(); + ex.addValidationError(error.get()); + throw ex; + } + } + } + // Verify that this won't take us over the cluster shard limit. int totalNewShards = Arrays.stream(request.indices()) .mapToInt(i -> getTotalNewShards(i, currentState, updatedNumberOfReplicas)) @@ -271,6 +317,8 @@ public ClusterState execute(ClusterState currentState) { Settings finalSettings = indexSettings.build(); indexScopedSettings.validate( finalSettings.filter(k -> indexScopedSettings.isPrivateSetting(k) == false), + true, + false, true ); metadataBuilder.put(IndexMetadata.builder(indexMetadata).settings(finalSettings)); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/ProcessClusterEventTimeoutException.java b/server/src/main/java/org/opensearch/cluster/metadata/ProcessClusterEventTimeoutException.java index 079c4c680e42d..8f2b9c3bbd36a 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/ProcessClusterEventTimeoutException.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/ProcessClusterEventTimeoutException.java @@ -33,12 +33,17 @@ package org.opensearch.cluster.metadata; import org.opensearch.OpenSearchException; -import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.unit.TimeValue; -import org.opensearch.rest.RestStatus; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.rest.RestStatus; import java.io.IOException; +/** + * Exception thrown when there is a timeout processing cluster events + * + * @opensearch.internal + */ public class ProcessClusterEventTimeoutException extends OpenSearchException { public ProcessClusterEventTimeoutException(TimeValue timeValue, String source) { diff --git a/server/src/main/java/org/opensearch/cluster/metadata/RepositoriesMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/RepositoriesMetadata.java index 3076ede7ced14..a5ef337c3b62a 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/RepositoriesMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/RepositoriesMetadata.java @@ -38,13 +38,14 @@ import org.opensearch.cluster.NamedDiff; import org.opensearch.cluster.metadata.Metadata.Custom; import org.opensearch.common.Nullable; -import org.opensearch.common.Strings; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ToXContent; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentParser; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.repositories.RepositoryData; import java.io.IOException; @@ -53,8 +54,12 @@ import java.util.EnumSet; import java.util.List; +import static org.opensearch.repositories.blobstore.BlobStoreRepository.SYSTEM_REPOSITORY_SETTING; + /** * Contains metadata about registered snapshot repositories + * + * @opensearch.internal */ public class RepositoriesMetadata extends AbstractNamedDiffable implements Custom { @@ -205,6 +210,7 @@ public static RepositoriesMetadata fromXContent(XContentParser parser) throws IO Settings settings = Settings.EMPTY; long generation = RepositoryData.UNKNOWN_REPO_GEN; long pendingGeneration = RepositoryData.EMPTY_REPO_GEN; + CryptoMetadata cryptoMetadata = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { String currentFieldName = parser.currentName(); @@ -228,6 +234,11 @@ public static RepositoriesMetadata fromXContent(XContentParser parser) throws IO throw new OpenSearchParseException("failed to parse repository [{}], unknown type", name); } pendingGeneration = parser.longValue(); + } else if (CryptoMetadata.CRYPTO_METADATA_KEY.equals(currentFieldName)) { + if (parser.nextToken() != XContentParser.Token.START_OBJECT) { + throw new OpenSearchParseException("failed to parse repository [{}], unknown type", name); + } + cryptoMetadata = CryptoMetadata.fromXContent(parser); } else { throw new OpenSearchParseException( "failed to parse repository [{}], unknown field [{}]", @@ -242,7 +253,7 @@ public static RepositoriesMetadata fromXContent(XContentParser parser) throws IO if (type == null) { throw new OpenSearchParseException("failed to parse repository [{}], missing repository type", name); } - repository.add(new RepositoryMetadata(name, type, settings, generation, pendingGeneration)); + repository.add(new RepositoryMetadata(name, type, settings, generation, pendingGeneration, cryptoMetadata)); } else { throw new OpenSearchParseException("failed to parse repositories"); } @@ -276,8 +287,15 @@ public EnumSet context() { public static void toXContent(RepositoryMetadata repository, XContentBuilder builder, ToXContent.Params params) throws IOException { builder.startObject(repository.name()); builder.field("type", repository.type()); + if (repository.cryptoMetadata() != null) { + repository.cryptoMetadata().toXContent(repository.cryptoMetadata(), builder, params); + } + Settings settings = repository.settings(); + if (SYSTEM_REPOSITORY_SETTING.get(settings)) { + settings = repository.settings().filter(s -> !s.equals(SYSTEM_REPOSITORY_SETTING.getKey())); + } builder.startObject("settings"); - repository.settings().toXContent(builder, params); + settings.toXContent(builder, params); builder.endObject(); if (params.paramAsBoolean(HIDE_GENERATIONS_PARAM, false) == false) { @@ -289,6 +307,6 @@ public static void toXContent(RepositoryMetadata repository, XContentBuilder bui @Override public String toString() { - return Strings.toString(this); + return Strings.toString(MediaTypeRegistry.JSON, this); } } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/RepositoryMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/RepositoryMetadata.java index d839e4bafade7..58637df4efe8f 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/RepositoryMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/RepositoryMetadata.java @@ -33,10 +33,10 @@ import org.opensearch.LegacyESVersion; import org.opensearch.Version; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; -import org.opensearch.common.io.stream.Writeable; import org.opensearch.common.settings.Settings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.common.io.stream.Writeable; import org.opensearch.repositories.RepositoryData; import java.io.IOException; @@ -44,6 +44,8 @@ /** * Metadata about registered repository + * + * @opensearch.internal */ public class RepositoryMetadata implements Writeable { @@ -52,6 +54,7 @@ public class RepositoryMetadata implements Writeable { private final String name; private final String type; private final Settings settings; + private final CryptoMetadata cryptoMetadata; /** * Safe repository generation. @@ -71,14 +74,29 @@ public class RepositoryMetadata implements Writeable { * @param settings repository settings */ public RepositoryMetadata(String name, String type, Settings settings) { - this(name, type, settings, RepositoryData.UNKNOWN_REPO_GEN, RepositoryData.EMPTY_REPO_GEN); + this(name, type, settings, RepositoryData.UNKNOWN_REPO_GEN, RepositoryData.EMPTY_REPO_GEN, null); + } + + public RepositoryMetadata(String name, String type, Settings settings, CryptoMetadata cryptoMetadata) { + this(name, type, settings, RepositoryData.UNKNOWN_REPO_GEN, RepositoryData.EMPTY_REPO_GEN, cryptoMetadata); } public RepositoryMetadata(RepositoryMetadata metadata, long generation, long pendingGeneration) { - this(metadata.name, metadata.type, metadata.settings, generation, pendingGeneration); + this(metadata.name, metadata.type, metadata.settings, generation, pendingGeneration, metadata.cryptoMetadata); } public RepositoryMetadata(String name, String type, Settings settings, long generation, long pendingGeneration) { + this(name, type, settings, generation, pendingGeneration, null); + } + + public RepositoryMetadata( + String name, + String type, + Settings settings, + long generation, + long pendingGeneration, + CryptoMetadata cryptoMetadata + ) { this.name = name; this.type = type; this.settings = settings; @@ -89,6 +107,7 @@ public RepositoryMetadata(String name, String type, Settings settings, long gene + "] must be greater or equal to generation [" + generation + "]"; + this.cryptoMetadata = cryptoMetadata; } /** @@ -118,6 +137,15 @@ public Settings settings() { return this.settings; } + /** + * Returns crypto metadata of repository + * + * @return crypto metadata of repository + */ + public CryptoMetadata cryptoMetadata() { + return this.cryptoMetadata; + } + /** * Returns the safe repository generation. {@link RepositoryData} for this generation is assumed to exist in the repository. * All operations on the repository must be based on the {@link RepositoryData} at this generation. @@ -153,6 +181,11 @@ public RepositoryMetadata(StreamInput in) throws IOException { generation = RepositoryData.UNKNOWN_REPO_GEN; pendingGeneration = RepositoryData.EMPTY_REPO_GEN; } + if (in.getVersion().onOrAfter(Version.V_2_10_0)) { + cryptoMetadata = in.readOptionalWriteable(CryptoMetadata::new); + } else { + cryptoMetadata = null; + } } /** @@ -169,6 +202,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeLong(generation); out.writeLong(pendingGeneration); } + if (out.getVersion().onOrAfter(Version.V_2_10_0)) { + out.writeOptionalWriteable(cryptoMetadata); + } } /** @@ -178,7 +214,10 @@ public void writeTo(StreamOutput out) throws IOException { * @return {@code true} if both instances equal in all fields but the generation fields */ public boolean equalsIgnoreGenerations(RepositoryMetadata other) { - return name.equals(other.name) && type.equals(other.type()) && settings.equals(other.settings()); + return name.equals(other.name) + && type.equals(other.type()) + && settings.equals(other.settings()) + && Objects.equals(cryptoMetadata, other.cryptoMetadata()); } @Override @@ -192,16 +231,21 @@ public boolean equals(Object o) { if (!type.equals(that.type)) return false; if (generation != that.generation) return false; if (pendingGeneration != that.pendingGeneration) return false; - return settings.equals(that.settings); + if (!settings.equals(that.settings)) return false; + return Objects.equals(cryptoMetadata, that.cryptoMetadata); } @Override public int hashCode() { - return Objects.hash(name, type, settings, generation, pendingGeneration); + return Objects.hash(name, type, settings, generation, pendingGeneration, cryptoMetadata); } @Override public String toString() { - return "RepositoryMetadata{" + name + "}{" + type + "}{" + settings + "}{" + generation + "}{" + pendingGeneration + "}"; + String toStr = "RepositoryMetadata{" + name + "}{" + type + "}{" + settings + "}{" + generation + "}{" + pendingGeneration + "}"; + if (cryptoMetadata != null) { + return toStr + "{" + cryptoMetadata + "}"; + } + return toStr; } } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/SystemIndexMetadataUpgradeService.java b/server/src/main/java/org/opensearch/cluster/metadata/SystemIndexMetadataUpgradeService.java index 51a2557ef80bb..22d7235fd1f98 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/SystemIndexMetadataUpgradeService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/SystemIndexMetadataUpgradeService.java @@ -32,7 +32,6 @@ package org.opensearch.cluster.metadata; -import com.carrotsearch.hppc.cursors.ObjectObjectCursor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.cluster.ClusterChangedEvent; @@ -40,14 +39,16 @@ import org.opensearch.cluster.ClusterStateListener; import org.opensearch.cluster.ClusterStateUpdateTask; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.collect.ImmutableOpenMap; import org.opensearch.indices.SystemIndices; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * A service responsible for updating the metadata used by system indices. + * + * @opensearch.internal */ public class SystemIndexMetadataUpgradeService implements ClusterStateListener { @@ -56,9 +57,9 @@ public class SystemIndexMetadataUpgradeService implements ClusterStateListener { private final SystemIndices systemIndices; private final ClusterService clusterService; - private boolean master = false; + private boolean clusterManager = false; - private volatile ImmutableOpenMap lastIndexMetadataMap = ImmutableOpenMap.of(); + private volatile Map lastIndexMetadataMap = Map.of(); private volatile boolean updateTaskPending = false; public SystemIndexMetadataUpgradeService(SystemIndices systemIndices, ClusterService clusterService) { @@ -68,17 +69,17 @@ public SystemIndexMetadataUpgradeService(SystemIndices systemIndices, ClusterSer @Override public void clusterChanged(ClusterChangedEvent event) { - if (event.localNodeMaster() != master) { - this.master = event.localNodeMaster(); + if (event.localNodeClusterManager() != clusterManager) { + this.clusterManager = event.localNodeClusterManager(); } - if (master && updateTaskPending == false) { - final ImmutableOpenMap indexMetadataMap = event.state().metadata().indices(); + if (clusterManager && updateTaskPending == false) { + final Map indexMetadataMap = event.state().metadata().indices(); if (lastIndexMetadataMap != indexMetadataMap) { - for (ObjectObjectCursor cursor : indexMetadataMap) { - if (cursor.value != lastIndexMetadataMap.get(cursor.key)) { - if (systemIndices.isSystemIndex(cursor.value.getIndex()) != cursor.value.isSystem()) { + for (final Map.Entry cursor : indexMetadataMap.entrySet()) { + if (cursor.getValue() != lastIndexMetadataMap.get(cursor.getKey())) { + if (systemIndices.isSystemIndex(cursor.getValue().getIndex()) != cursor.getValue().isSystem()) { updateTaskPending = true; clusterService.submitStateUpdateTask( "system_index_metadata_upgrade_service {system metadata change}", @@ -92,16 +93,21 @@ public void clusterChanged(ClusterChangedEvent event) { } } + /** + * Task to update system index metadata. + * + * @opensearch.internal + */ public class SystemIndexMetadataUpdateTask extends ClusterStateUpdateTask { @Override public ClusterState execute(ClusterState currentState) throws Exception { - final ImmutableOpenMap indexMetadataMap = currentState.metadata().indices(); + final Map indexMetadataMap = currentState.metadata().indices(); final List updatedMetadata = new ArrayList<>(); - for (ObjectObjectCursor cursor : indexMetadataMap) { - if (cursor.value != lastIndexMetadataMap.get(cursor.key)) { - if (systemIndices.isSystemIndex(cursor.value.getIndex()) != cursor.value.isSystem()) { - updatedMetadata.add(IndexMetadata.builder(cursor.value).system(!cursor.value.isSystem()).build()); + for (Map.Entry cursor : indexMetadataMap.entrySet()) { + if (cursor.getValue() != lastIndexMetadataMap.get(cursor.getKey())) { + if (systemIndices.isSystemIndex(cursor.getValue().getIndex()) != cursor.getValue().isSystem()) { + updatedMetadata.add(IndexMetadata.builder(cursor.getValue()).system(!cursor.getValue().isSystem()).build()); } } } diff --git a/server/src/main/java/org/opensearch/cluster/metadata/Template.java b/server/src/main/java/org/opensearch/cluster/metadata/Template.java index 0a070ced198c6..8e367c71ed166 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/Template.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/Template.java @@ -34,19 +34,18 @@ import org.opensearch.cluster.AbstractDiffable; import org.opensearch.common.Nullable; -import org.opensearch.common.ParseField; -import org.opensearch.common.Strings; import org.opensearch.common.compress.CompressedXContent; -import org.opensearch.common.io.stream.StreamInput; -import org.opensearch.common.io.stream.StreamOutput; import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.ConstructingObjectParser; -import org.opensearch.common.xcontent.ToXContentObject; -import org.opensearch.common.xcontent.XContentBuilder; -import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.XContentHelper; -import org.opensearch.common.xcontent.XContentParser; -import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.ParseField; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.xcontent.ConstructingObjectParser; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.core.xcontent.XContentParser; import org.opensearch.index.mapper.MapperService; import java.io.IOException; @@ -58,6 +57,8 @@ * A template consists of optional settings, mappings, or alias configuration for an index, however, * it is entirely independent from an index. It's a building block forming part of a regular index * template and a {@link ComponentTemplate}. + * + * @opensearch.internal */ public class Template extends AbstractDiffable